這是一個沉澱的過程,大概第一次接觸Spring是在去年的這個時候,當初在實訓,初次接觸Java web,直接學習SSM框架(當是Servlet都沒有學),因而,養成了一個很很差的學習習慣,就是「照貓畫虎」。別人作什麼,照着樣子就是了,沒有任何的思考,這樣的學習習慣確定不會走太遠。如今我產生不少疑惑,這是什麼?爲何這麼作?如何作的更好?所以此次筆記的主題就是《這是什麼?》java
PointCut切入點:已經被加強的鏈接點。例如:addUser()web
Aspect切面:是切入點pointcut和通知advice的結合
一個線是一個特殊的面。
一個切入點和一個通知,組成成一個特殊的面。
spring
測試
sql
public interface UserService { void addUser(); void updateUser(); void deleteUser(); }
public class MyAspect { public void before(){ System.out.println("this is before"); } public void after(){ System.out.println("this is after"); } }
public class MyBeanFactory { public static UserService createService(){ //1 目標類 final UserService userService = new UserServiceImpl(); //2 切面類 final MyAspect myAspect = new MyAspect(); /** * 3 代理類:將目標類(切入點)和 切面類(通知) 結合 --> 切面 * Proxy.newProxyInstance * 參數1:loader ,類加載器,動態代理類 運行時建立,任何類都須要類加載器將其加載到內存。 * 通常狀況:當前類.class.getClassLoader(); * 目標類實例.getClass().get... * 參數2:Class[] interfaces 代理類須要實現的全部接口 * 方式1:目標類實例.getClass().getInterfaces() ;注意:只能得到本身接口,不能得到父元素接口 * 方式2:new Class[]{UserService.class} * 例如:jdbc 驅動 --> DriverManager 得到接口 Connection * 參數3:InvocationHandler 處理類,接口,必須進行實現類,通常採用匿名內部 * 提供 invoke 方法,代理類的每個方法執行時,都將調用一次invoke * 參數31:Object proxy :代理對象 * 參數32:Method method : 代理對象當前執行的方法的描述對象(反射) * 執行方法名:method.getName() * 執行方法:method.invoke(對象,實際參數) * 參數33:Object[] args :方法實際參數 * */ UserService proxService = (UserService) Proxy.newProxyInstance( MyBeanFactory.class.getClassLoader(), userService.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //前執行 myAspect.before(); //執行目標類的方法 Object obj = method.invoke(userService,args); //後方法 myAspect.after(); return obj; } } ); return proxService; } } }
@Test public void demo01(){ UserService userService = MyBeanFactory.createService(); userService.addUser(); userService.updateUser(); userService.deleteUser(); }
public class MyBeanFactory { public static UserService createService(){ //1 目標類 final UserService userService = new UserServiceImpl(); //2 切面類 final MyAspect myAspect = new MyAspect(); // 3.代理類 ,採用cglib ,底層建立目標類的子類 // 3.1 核心類 Enhancer enhancer = new Enhancer(); // 3.2 肯定父類 enhancer.setSuperclass(userService.getClass()); /** * 3.3 設置回調函數 , MethodInterceptor接口 等效 jdk InvocationHander接口 * intercept() 等效 jdk invoke() * 參數一、參數二、參數3:以invoke同樣 * 參數4:methodProxy 方法的代理 */ enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { //前執行 myAspect.before(); //執行目標類方法 Object obj = method.invoke(userService,objects); // * 執行代理類的父類,執行目標類(目標類和代理類 父子關係) // methodProxy.invokeSuper(o,objects); //後執行 myAspect.after(); return obj; } }); //3.4 建立代理 UserServiceImpl proxService = (UserServiceImpl) enhancer.create(); return proxService; } }
環繞通知,必須手動執行目標方法 try{ //前置通知 //執行目標方法 //後置通知 } catch(){ //拋出異常通知 }
public interface UserService { void addUser(); void updateUser(); void deleteUser(); }
/** * 切面類中肯定通知,須要實現不一樣接口,接口就是規範,從而就肯定方法名稱。 * 採用「環繞通知」 MethodInterceptor */ public class MyAspect implements MethodInterceptor { @Override public Object invoke(MethodInvocation methodInvocation) throws Throwable { System.out.println("this is before"); //手動執行目標方法 Object obj = methodInvocation.proceed(); System.out.println("this is after"); return obj; } }
<!-- 1 建立目標類--> <bean id="userService" class="com.springlearning.spring_proxy.UserServiceImpl"></bean> <!-- 2 建立切面類--> <bean id="myAspect" class="com.springlearning.spring_proxy.MyAspect"></bean> <!-- 3 建立代理類 * 使用工廠bean FactoryBean ,底層調用getObject() 返回特殊bean * ProxyFactoryBean 用於建立代理工廠bean ,生成特殊代理對象 interfaces : 肯定接口們 經過<array> 能夠設置多個值 只有一個值時, value="" target : 肯定目標類 interceptorNames : 通知 切面類的名稱,類型String[], 若是設置一個值 value="" optimize :強制使用cglib <property name="optimize" value="true"></property> 底層機制 若是目標類有接口,採用jdk動態代理 若是沒有接口,採用cglib 字節碼加強 若是聲明 optimize = true ,不管是否有接口 ,都採用cglib --> <bean id="proxyService" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="interfaces" value="com.springlearning.spring_proxy.UserService"></property> <property name="target" ref="userService"></property> <property name="interceptorNames" value="myAspect"></property> </bean>
@Test public void demo01(){ String xmlPath = "com/springlearning/spring_proxy/beans.xml"; ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath); //得到代理類 UserService userService = (UserService) applicationContext.getBean("proxyService"); userService.addUser(); userService.updateUser(); userService.deleteUser(); }
在spring-aop-5.0.4版本中包含了aopalliance和aspectj
數據庫
這裏我在調試的時候出了問題,其中在spring-aop-5.0.4版本中包含了aopalliance,而我看的教學是3.x的版本,須要導入另外的aopalliance包,還有錯把aspectjrt 當成 aspectjweaver,然而兩個包有着不一樣的功能。express
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 1 建立目標類 --> <bean id="userService" class="com.springlearning.spring_proxy.UserServiceImpl"></bean> <!-- 2 建立切面類(通知)--> <bean id="myAspect" class="com.springlearning.spring_proxy.MyAspect"></bean> <!-- 3 aop編程 3.1 導入命名空間 3.2 使用 <aop:config> 進行配置 proxy-target-class="true" 聲明時使用cglib代理 <aop:pointcut> 切入點 , 從目標對象得到具體方法 <aop:advisor> 特殊的切面, 只用一個通知 和一個切入點 advice-ref 通知引用 pointcut-ref 切入點引用 3.3 切入點表達式 execution(* com.springlearning.spring_proxy.*.(..)) 選擇方法 返回值任意 包 類名稱任意 方法名任意 參數任意 --> <aop:config proxy-target-class="true"> <aop:pointcut id="myPointCut" expression="execution(* com.springlearning.spring_proxy.*.*(..))"/> <aop:advisor advice-ref="myAspect" pointcut-ref="myPointCut"></aop:advisor> </aop:config> </beans>
@Test public void demo01(){ String xmlPath = "com/springlearning/spring_proxy/beans.xml"; ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath); //得到代理類 UserService userService = (UserService) applicationContext.getBean("userService"); userService.addUser(); userService.updateUser(); userService.deleteUser(); }
綜合 1
execution(* com.springlearning.proxy..service...*(..))apache
綜合 2
<aop:pointcut expression="execution(* com.springlearning.WithCommit.(..)) ||
execution(* com.springlearning.Service.(..))" id="myPointCut"/>編程
環繞 try{ //前置:before //手動執行目標方法 //後置:afterRetruning } catch(){ //拋出異常 afterThrowing } finally{ //最終 after }
/** * 切面類,含有多個通知 */ public class MyAspect { public void myBefore(JoinPoint joinPoint){ System.out.println("前置通知:" + joinPoint.getSignature().getName()); } public void myAfterReturning(JoinPoint joinPoint,Object ret){ System.out.println("後置通知 : " + joinPoint.getSignature().getName() + " , -->" + ret); } public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{ System.out.println("前"); //手動執行目標方法 Object obj = joinPoint.proceed(); System.out.println("後"); return obj; } public void myAfterThrowing(JoinPoint joinPoint,Throwable throwable){ System.out.println("拋出異常通知:" + throwable.getMessage()); } public void myAfter(JoinPoint joinPoint){ System.out.println("最終通知"); } }
<!-- 1 建立目標類 --> <bean id="userService" class="com.springlearning.aspectj.UserServiceImpl"></bean> <!-- 2 建立切面類(通知)--> <bean id="myAspect" class="com.springlearning.aspectj.MyAspect"></bean> <!-- 3 aop編程 <aop:aspect> 將切面類 聲明「切面」,從而得到通知(方法) ref 切面類引用 <aop:pointcut> 聲明一個切入點,全部通知均可以使用。 expression 切入點表達式 id 名稱,用於其餘通知引用 --> <aop:config> <aop:aspect ref="myAspect"> <aop:pointcut id="myPointcut" expression="execution(* com.springlearning.aspectj.UserServiceImpl.*(..))"/> <!-- 3.1 前置通知 <aop:before method="" pointcut="" pointcut-ref=""/> method : 通知,及方法名 pointcut :切入點表達式,此表達式只能當前通知使用。 pointcut-ref : 切入點引用,能夠與其餘通知共享切入點。 通知方法格式:public void myBefore(JoinPoint joinPoint){ 參數1:org.aspectj.lang.JoinPoint 用於描述鏈接點(目標方法),得到目標方法名等 例如: <aop:before method="myBefore" pointcut-ref="myPointCut"/> --> <!-- 3.2後置通知 ,目標方法後執行,得到返回值 <aop:after-returning method="" pointcut-ref="" returning=""/> returning 通知方法第二個參數的名稱 通知方法格式:public void myAfterReturning(JoinPoint joinPoint,Object ret){ 參數1:鏈接點描述 參數2:類型Object,參數名 returning="ret" 配置的 例如: <aop:after-returning method="myAfterReturning" pointcut-ref="myPointCut" returning="ret" /> --> <!-- 3.3 環繞通知 <aop:around method="" pointcut-ref=""/> 通知方法格式:public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{ 返回值類型:Object 方法名:任意 參數:org.aspectj.lang.ProceedingJoinPoint 拋出異常 執行目標方法:Object obj = joinPoint.proceed(); 例如: <aop:around method="myAround" pointcut-ref="myPointCut"/> --> <!-- 3.4 拋出異常 <aop:after-throwing method="" pointcut-ref="" throwing=""/> throwing :通知方法的第二個參數名稱 通知方法格式:public void myAfterThrowing(JoinPoint joinPoint,Throwable e){ 參數1:鏈接點描述對象 參數2:得到異常信息,類型Throwable ,參數名由throwing="e" 配置 例如: <aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointCut" throwing="e"/> --> <!-- 3.5 最終通知--> <aop:after method="myAfter" pointcut-ref="myPointcut"></aop:after> </aop:aspect> </aop:config>
<!-- 1 建立目標類 --> <bean id="userService" class="com.springlearning.aspectj.UserServiceImpl"></bean> <!-- 2 建立切面類(通知)--> <bean id="myAspect" class="com.springlearning.aspectj.MyAspect"></bean>
@Component public class MyAspect { @Service("userService") public class UserServiceImpl implements UserService {
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 1. 掃描 註解類 --> <context:component-scan base-package="com.springlearning.aspectj"></context:component-scan>
<!-- 2.肯定 aop註解生效 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<aop:aspect ref="myAspect"> <!--被下面註解替換 -->
@Component @Aspect //添加註解 public class MyAspect {
<aop:before method="myBefore" pointcut="execution(* com.ithspringlearning.aspectj.UserServiceImpl.*(..))"/> <!--被下面註解替換 -->
//切入點當前有效 @Before("execution(* com.springlearning.aspectj.UserServiceImpl.*(..))") public void myBefore(JoinPoint joinPoint){ System.out.println("前置通知:" + joinPoint.getSignature().getName()); }
<aop:pointcut id="myPointcut" expression="execution(* com.springlearning.aspectj.UserServiceImpl.*(..))"/> <!--被下面註解替換 -->
//聲明公共切入點 @Pointcut("execution(* com.springlearning.aspectj.UserServiceImpl.*(..))") private void myPointCut(){}
<aop:after-returning method="myAfterReturning" pointcut-ref="myPointCut" returning="ret" />
@AfterReturning(value = "myPointCut()",returning = "ret") public void myAfterReturning(JoinPoint joinPoint,Object ret){ System.out.println("後置通知 : " + joinPoint.getSignature().getName() + " , -->" + ret); }
<aop:around method="myAround" pointcut-ref="myPointCut"/>
@Around(value = "myPointCut()") public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{ System.out.println("前"); //手動執行目標方法 Object obj = joinPoint.proceed(); System.out.println("後"); return obj; }
<aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointCut" throwing="throwable"/>
@AfterThrowing(value = "myPointCut()" ,throwing = "throwable") public void myAfterThrowing(JoinPoint joinPoint,Throwable throwable){ System.out.println("拋出異常通知:" + throwable.getMessage()); }
/** * 切面類,含有多個通知 */ @Component @Aspect public class MyAspect { //聲明公共切入點 @Pointcut("execution(* com.springlearning.aspectj.UserServiceImpl.*(..))") private void myPointCut(){} // @AfterReturning(value = "myPointCut()",returning = "ret") public void myAfterReturning(JoinPoint joinPoint,Object ret){ System.out.println("後置通知 : " + joinPoint.getSignature().getName() + " , -->" + ret); } //切入點當前有效 // @Before("execution(* com.springlearning.aspectj.UserServiceImpl.*(..))") public void myBefore(JoinPoint joinPoint){ System.out.println("前置通知:" + joinPoint.getSignature().getName()); } // @Around(value = "myPointCut()") public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{ System.out.println("前"); //手動執行目標方法 Object obj = joinPoint.proceed(); System.out.println("後"); return obj; } // @AfterThrowing(value = "myPointCut()" ,throwing = "throwable") public void myAfterThrowing(JoinPoint joinPoint,Throwable throwable){ System.out.println("拋出異常通知:" + throwable.getMessage()); } @After(value = "myPointCut()") public void myAfter(JoinPoint joinPoint){ System.out.println("最終通知"); } }
<!-- 1. 掃描 註解類 --> <context:component-scan base-package="com.springlearning.aspectj"></context:component-scan> <!-- 2.肯定 aop註解生效 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
create table t_user( id int primary key , username varchar(50), password varchar(32) );
public class User { private Integer id; private String username; private String password; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
@Test public void demo(){ //1 建立數據源(鏈接池) dbcp BasicDataSource dataSource = new BasicDataSource(); // * 基本4項 dataSource.setDriverClassName("oracle.jdbc.driver.OracleDriver"); dataSource.setUrl("jdbc:oracle:thin:@localhost:1530/orcl"); dataSource.setUsername("sys as sysdba"); dataSource.setPassword("sys"); //2 建立模板 JdbcTemplate jdbcTemplate = new JdbcTemplate(); jdbcTemplate.setDataSource(dataSource); //3 經過api操做 jdbcTemplate.update("insert into t_user(id,username,password) values(?,?,?)","6", "tom","998"); }
<!-- 建立數據源 --> <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"> <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"></property> <property name="url" value="jdbc:oracle:thin:@localhost:1530/orcl"></property> <property name="username" value="sys as sysdba"></property> <property name="password" value="sys"></property> </bean> <!-- 建立模板 ,須要注入數據源--> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 配置dao --> <bean id="userDao" class="com.springlearning.jdbctemplate.UserDao"> <property name="jdbcTemplate" ref="jdbcTemplate"></property> </bean>
<!-- 建立C3P0數據源 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="oracle.jdbc.driver.OracleDriver"></property> <property name="jdbcUrl" value="jdbc:oracle:thin:@localhost:1530/orcl"></property> <property name="user" value="sys as sysdba"></property> <property name="password" value="sys"></property> </bean>
package com.springlearning.jdbctemplate; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.support.JdbcDaoSupport; import java.util.List; /** * @ClassName:UserDao * @author: donkey-boy * @date:2019/7/19 10:52 */ public class UserDao extends JdbcDaoSupport { /** * @Author: donkey-boy * @Description: 更新操做 * @Param: user * @Return: void * @Date: 2019/7/19 15:04 */ public void update(User user){ String sql = "UPDATE t_user SET username=?,password=? WHERE id=?"; Object[] args = {user.getUsername(),user.getPassword(),user.getId()}; this.getJdbcTemplate().update(sql,args); } /** * @Author: donkey-boy * @Description: 查詢所有 * @Param: [] * @Return: java.util.List<com.springlearning.jdbctemplate.User> * @Date: 2019/7/19 15:06 */ public List<User> findAll(){ List<User> userList = this.getJdbcTemplate().query("select * from t_user", BeanPropertyRowMapper.newInstance(User.class)); return userList; } /** * @Author: donkey-boy * @Description: 獲取單個用戶 * @Param: [id] * @Return: com.springlearning.jdbctemplate.User * @Date: 2019/7/19 15:06 */ public User getUser(int id){ User user = this.getJdbcTemplate().queryForObject("select * from t_user where id=?", BeanPropertyRowMapper.newInstance(User.class), id); return user; } }
<!-- 配置dao * dao 繼承 JdbcDaoSupport,以後只須要注入數據源,底層將自動建立模板 --> <!-- 配置dao --> <bean id="userDao" class="com.springlearning.jdbctemplate.UserDao"> <property name="dataSource" ref="dataSource"></property> </bean>
jdbc.driverClass=oracle.jdbc.driver.OracleDriver jdbc.jdbcUrl=jdbc:oracle:thin:@localhost:1530/orcl jdbc.user=sys as sysdba jdbc.password=sys
<!-- 加載配置文件 "classpath:"前綴表示 src下 在配置文件以後經過 ${key} 得到內容 --> <context:property-placeholder location="classpath:com/springlearning/jdbctemplate/jdbcInfo.properties"></context:property-placeholder> <!-- 建立C3P0數據源 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driverClass}"></property> <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property> <property name="user" value="${jdbc.user}"></property> <property name="password" value="${jdbc.password}"></property> </bean>
筆記源自傳智播客Spring教學設計模式