AOP是一種面向切面編程思想,也是面向對象設計(OOP)的一種延伸。
在Spring實現AOP有兩種實現方式,一種是采用JDK動態代理實現,另外一種就是采用CGLIB代理實現,Spring是如何實現的在上篇已經講到了Spring Bean的生命周期以及IOC源碼解析
AOP可以做日志記錄,或者事務回滾,在Spring的事務使用就是通過AOP進行事務的回滾的
JDK動態代理
這個是屬于JDK提供的一種代理方式,需提供接口才能使用,主要用的類有兩個:1、Proxy:這個主要是生成接口代理對象;2、InvocationHandler:反射射包下的一個接口,Proxy生成的代理接口對象,調用接口方法就會走InvocationHandler實現類的invoke()?方法
?使用示例:
//創建一個接口
public class UserDao {String getUserInfo();
}
//實現一個InvocationHandler接口的實現類
public class MyInvocationHandler implements InvocationHandler {//需實現的方法//proxy 代表當前對象自己,建議不要使用,如果使用的話會反復的調用自己,而調用自己會反復走當前invoke方法,容易出現棧溢出;//method 指調用的方法Method;//args 調用方法的參數;@Overridepublic Object invoke(Object proxy, Method method, Object[] args){//....return null;}//獲取接口代理對象public <T> T getProxy(CLass<T> cls){//第一個參數為類加載器,這個可以默認使用當前的AppClassLoader,即使用getClassLoader()方法接口//需創建代理對象的接口class數組,//invocationHandler的實現類對象,這里調用的是當前對象,調用的時候就會走當前對象的invoke方法return (E) Proxy.newProxyInstance(cls.getClassLoader(), new Class<?>[] {cls},this);}}public static void main(Stirng[] args){UserDao dao = new MyInvocationHandler().getProxy(UserDao.class);//這樣就能獲取到了UserDao接口的代理對象了
}
在Spring當中是采用?JdkDynamicAopProxy?這個類實現的,可以去源碼里看一下這個類繼承了InvocationHandler
CGLIB代理
這個代理就比較厲害了,是通過ASM修改 .class字節碼進行實現的,并且不需要接口,具體的組成結構可以去看一下大佬寫的這篇CGLIB(Code Generation Library)
使用示例:
/*** CGLIB 代理*/Enhancer enhancer = new Enhancer();enhancer.setSuperclass(SpringTest06Service.class);enhancer.setCallback(new MethodInterceptor() {public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {System.out.println(Thread.currentThread().getName()+"run start...");Object result = arg3.invokeSuper(arg0, arg2);System.out.println(Thread.currentThread().getName()+"result:"+result);System.out.println(Thread.currentThread().getName()+"run end...");return result;}});service = (SpringTest06Service) enhancer.create();System.out.println(service);String testAop = service.testAop("aaaaaaaa", "bbbbbbbbbbb");System.out.println(testAop);
在Spring里面實現的類是?ObjenesisCglibAopProxy?
上面就是兩種代理的實現方式,但是在Spring里面是怎么使用的呢
Spring使用AOP
需引入Spring相關的AOP的包
<dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>5.1.12.RELEASE</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-aop</artifactId><version>5.1.12.RELEASE</version></dependency>
在配置類上需開啟AOP進行方法攔截,@EnableAspectJAutoProxy?啟動AOP代理,這個注解有兩個參數
1、proxyTargetClass:這個是指定使用JDK動態代理攔截(false)還是使用CGLIB進行攔截(true),默認為false,
2、exposeProxy:表示是否能夠讓AopContext訪問,默認為false就是不能訪問。
源碼是怎么解析可以自己去看,這個注解通過使用@Import注入了一個SmartInstantiationAwareBeanPostProcessor的實現類,可以自己調試查看,這里就不具體講了,有空就在聊聊吧
最后配置相對應的切面攔截類
@Aspect
@Component
public class AopConfig {private static final Log log = LogFactory.getLog(AopConfig.class);//切點,需指定到具體修飾符,具體方法名以及具體參數類型,這里表示所有修飾符,com.test.SpringCoreTest.test06.service這個包下的所有類的所有方法并且參數為兩個的類型為String的方法進行攔截@Pointcut("execution(* com.test.SpringCoreTest.test06.service.*.*(String,String))")public void pointCut() {}//執行前,位于Around環繞方法前的后面執行@Before(value = "pointCut()")public void before() {System.out.println("方法執行前 @Before ....");}//執行后,位于Around環繞方法后的后面執行@After(value = "pointCut()")public void after() {System.out.println("方法執行后 @After ....");}//即將返回執行@AfterReturning(value = "pointCut()")public void afterReturning() {System.out.println("方法執行后返回 @AfterReturning ....");}//拋出異常執行@AfterThrowing(value="pointCut()",throwing="e")public void afterThrowing(Exception e) {System.out.println("方法執行異常后返回 @AfterThrowing ....");System.out.println(e);}//環繞方法,能夠攔截執行參數,并且能夠修改@Around(value = "pointCut()")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {System.out.println("around方法執行前@Around ....");Object obj = joinPoint.proceed();System.out.println("around方法執行后@Around ....");return obj;}}
配置好后,調用攔截的方法就可以觸發AOP攔截了。