1 aop是什么
java的核心思想是面向對象,aop是面向切面編程.是對面向對象的一個補充,簡單通俗的理解下aop,假設我們上樓梯,我的目標是三樓,我直接朝我的三樓直接過去,但是我可以在二樓的時候去朋友那里玩下,坐下其他的,然后再去三樓
這也不會影響我們的目標去三樓,那么去朋友那里玩,相當於增加的生活的樂趣去那里玩.相當於增強了我們的活動.
那么aop可以做啥呢,可以進行日志記錄,可以進行事務管理,可以進行安全控制,可以進行異常處理,可以進行性能統計
2 理解幾個關鍵字
切面
關注點形成的類,就叫切面(類)!
面向切面編程,就是指對很多功能都有的重復的代碼抽取,再在運行的時候網業務方法上動態植入“切面類代碼”
切點
執行目標對象方法,動態植入切面代碼。
可以通過切入點表達式,指定攔截哪些類的哪些方法; 給指定的類在運行的時候植入切面類代碼。
通知
在對象上面執行的內容
3 aop的實現原理:
aop的底層實現是代理模式加反射.
反射就不用多講了,代理模式分為多種,靜態代理和動態代理,動態代理面又分為jdk動態代理和cglib動態代理
代理模式的好處:可以防止對方得到我們真實的方法;
靜態代理的實現方式:
package com.cxy.cyclicBarrier; /** * Created by Administrator on 2017/4/24. */ public interface IUserDao { void save(); }
package com.cxy.cyclicBarrier; /** * Created by Administrator on 2019/4/24. */ public class UserDao implements IUserDao { @Override public void save() { System.out.println("已經保存數據"); } }
代理類:
package com.cxy.cyclicBarrier; /** * Created by Administrator on 2019/4/24. */ public class UserDaoProxy implements IUserDao{ private IUserDao target; public UserDaoProxy(IUserDao iuserDao) { this.target = iuserDao; } public void save() { System.out.println("開啟事物..."); target.save(); System.out.println("關閉事物..."); } public static void main(String[] args) { UserDao userDao=new UserDao(); UserDaoProxy userDaoProxy=new UserDaoProxy(userDao ); userDaoProxy.save(); } }
在最后可以看到執行結果:
結果分析:
靜態代理:
由程序員創建或工具生成代理類的源碼,再編譯代理類。所謂靜態也就是在程序運行前就已經存在代理類的字節碼文件,代理類和委托類的關系在運行前就確定了。
然后再看jdk動態代理:
package com.cxy.cyclicBarrier; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; // 每次生成動態代理類對象時,實現了InvocationHandler接口的調用處理器對象 public class InvocationHandlerImpl implements InvocationHandler { private Object target;// 這其實業務實現類對象,用來調用具體的業務方法 // 通過構造函數傳入目標對象 public InvocationHandlerImpl(Object target) { this.target = target; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null; System.out.println("調用開始處理"); result = method.invoke(target, args); System.out.println("調用結束處理"); return result; } public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { // 被代理對象 IUserDao userDao = new UserDao(); InvocationHandlerImpl invocationHandlerImpl = new InvocationHandlerImpl(userDao); ClassLoader loader = userDao.getClass().getClassLoader(); Class<?>[] interfaces = userDao.getClass().getInterfaces(); // 主要裝載器、一組接口及調用處理動態代理實例 IUserDao newProxyInstance = (IUserDao) Proxy.newProxyInstance(loader, interfaces, invocationHandlerImpl); newProxyInstance.save(); } }
結果分析:
jdk動態代理:
1. 通過實現InvocationHandler接口創建自己的調用處理器 IvocationHandler handler = new InvocationHandlerImpl(…);
2. 通過為Proxy類指定ClassLoader對象和一組interface創建動態代理類Class clazz = Proxy.getProxyClass(classLoader,new Class[]{…});
3. 通過反射機制獲取動態代理類的構造函數,其參數類型是調用處理器接口類型Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});
4. 通過構造函數創建代理類實例,此時需將調用處理器對象作為參數被傳入Interface Proxy = (Interface)constructor.newInstance(new Object[] (handler));
cglib代理:
package com.cxy.cyclicBarrier; import java.lang.reflect.Method; public class CglibProxy implements MethodInterceptor { private Object targetObject; // 這里的目標類型為Object,則可以接受任意一種參數作為被代理類,實現了動態代理 public Object getInstance(Object target) { // 設置需要創建子類的類 this.targetObject = target; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(target.getClass()); enhancer.setCallback(this); return enhancer.create(); } public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("開啟事物"); Object result = proxy.invoke(targetObject, args); System.out.println("關閉事物"); // 返回代理對象 return result; } public static void main(String[] args) { CglibProxy cglibProxy = new CglibProxy(); UserDao userDao = (UserDao) cglibProxy.getInstance(new UserDao()); userDao.save(); } }
執行結果:
在執行上面代碼前需要引入asm包
原理:利用asm開源包,對代理對象類的class文件加載進來,通過修改其字節碼生成子類來處理。
4 區別比較:
CGLIB動態代理與JDK動態區別
java動態代理是利用反射機制生成一個實現代理接口的匿名類,在調用具體方法前調用InvokeHandler來處理。
而cglib動態代理是利用asm開源包,對代理對象類的class文件加載進來,通過修改其字節碼生成子類來處理。
Spring中。
1)、如果目標對象實現了接口,默認情況下會采用JDK的動態代理實現AOP
2)、如果目標對象實現了接口,可以強制使用CGLIB實現AOP
3)、如果目標對象沒有實現了接口,必須采用CGLIB庫,spring會自動在JDK動態代理和CGLIB之間轉換
JDK動態代理只能對實現了接口的類生成代理,而不能針對類 。
CGLIB是針對類實現代理,主要是對指定的類生成一個子類,覆蓋其中的方法 。
因為是繼承,所以該類或方法最好不要聲明成final ,final可以阻止繼承和多態。
5 aop的使用方式:
1) 注解方式:
<aop:aspectj-autoproxy></aop:aspectj-autoproxy> 開啟事物注解權限 @Aspect 指定一個類為切面類 @Pointcut("execution(* com.xhz.service.UserService.add(..))") 指定切入點表達式 @Before("pointCut_()") 前置通知: 目標方法之前執行 @After("pointCut_()") 后置通知:目標方法之后執行(始終執行) @AfterReturning("pointCut_()") 返回后通知: 執行方法結束前執行(異常不執行) @AfterThrowing("pointCut_()") 異常通知: 出現異常時候執行 @Around("pointCut_()") 環繞通知: 環繞目標方法執行 @Component @Aspect public class AopLog { // 前置通知 @Before("execution(* com.cxy.service.UserService.add(..))") public void begin() { System.out.println("前置通知"); } // // 后置通知 @After("execution(* com.cxy.service.UserService.add(..))") public void commit() { System.out.println("后置通知"); } // 運行通知 @AfterReturning("execution(* com.cxy.service.UserService.add(..))") public void returning() { System.out.println("運行通知"); } // 異常通知 @AfterThrowing("execution(* com.cxy.service.UserService.add(..))") public void afterThrowing() { System.out.println("異常通知"); } // 環繞通知 @Around("execution(* com.cxy.service.UserService.add(..))") public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { System.out.println("環繞通知開始"); proceedingJoinPoint.proceed(); System.out.println("環繞通知結束"); } }
2)xml方式:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" 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/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- dao 實例 --> <bean id="userService" class="com.cxy.service.UserService"></bean> <!-- 切面類 --> <bean id="aop" class="com.cxy.aop2.AopLog2"></bean> <!-- Aop配置 --> <aop:config> <!-- 定義一個切入點表達式: 攔截哪些方法 --> <aop:pointcut expression="execution(* com.cxy.service.UserService.*(..))" id="pt" /> <!-- 切面 --> <aop:aspect ref="aop"> <!-- 環繞通知 --> <aop:around method="around" pointcut-ref="pt" /> <!-- 前置通知: 在目標方法調用前執行 --> <aop:before method="begin" pointcut-ref="pt" /> <!-- 后置通知: --> <aop:after method="after" pointcut-ref="pt" /> <!-- 返回后通知 --> <aop:after-returning method="afterReturning" pointcut-ref="pt" /> <!-- 異常通知 --> <aop:after-throwing method="afterThrowing" pointcut-ref="pt" /> </aop:aspect> </aop:config> </beans>
public class AopLog2 { // 前置通知 public void begin() { System.out.println("前置通知"); } // // 后置通知 public void commit() { System.out.println("后置通知"); } // 運行通知 public void returning() { System.out.println("運行通知"); } // 異常通知 public void afterThrowing() { System.out.println("異常通知"); } // 環繞通知 public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { System.out.println("環繞通知開始"); proceedingJoinPoint.proceed(); System.out.println("環繞通知結束"); } }