AOP像OOP一樣,是一種獨立于語言的編程范式抠刺,實現AOP協議的方式多種多樣,包括:運行時摘昌、編譯器植入速妖、代理等,而SpringAop的采用的是動態(tài)代理與Cglib靜態(tài)植入聪黎。
添加依賴
在IOC容器的基礎上添加Aop相關依賴买优。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>
編程式Aop演示
ProxyFactory生成代理類是SpringAop底層真正的實現
目標類與接口
interface RunIntf {
void work();
void work1();
}
static class SimpleRun implements RunIntf {
@Override
public void work() {
System.out.println("SimpleRun.work()");
}
@Override
public void work1() {
System.out.println("SimpleRun.work1()");
}
}
通知
static class SimpleAdvice implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("SimpleAdvice.before");
return invocation.proceed();
}
}
切面
static class SimplePointCutAdvisor extends AbstractPointcutAdvisor {
@Override
public Advice getAdvice() {
return new SimpleAdvice();
}
@Override
public Pointcut getPointcut() {
JdkRegexpMethodPointcut jdkRegexpMethodPointcut = new JdkRegexpMethodPointcut();
jdkRegexpMethodPointcut.setPattern(".*work1.*");
return jdkRegexpMethodPointcut;
}
}
運行
public static void main(String[] args) {
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.addAdvisors(new SimplePointCutAdvisor());
proxyFactory.setTarget(new SimpleRun());
proxyFactory.setInterfaces(RunIntf.class);
RunIntf runIntf = (RunIntf) proxyFactory.getProxy();
runIntf.work();
runIntf.work1();
}
結果
SimpleRun.work()
SimpleAdvice.before
SimpleRun.work1()
可以看到,work1方法被植入了切面邏輯挺举。由此可見杀赢,我們通過提供目標類、目標類接口湘纵、切面邏輯脂崔,通過ProxyFactory生成了帶有切面功能的代理類。
AOP概念:
- Aspect:切面梧喷,包括切入點和切面砌左,SimplePointCutAdvisor就是一個切面脖咐。
- Advice:通知,指的是切面提供的處理邏輯汇歹,例如上例中的SimpleAdvice屁擅。
- Pointcut:切入點,指的是如何定位切面的手段产弹,在Spring中指的是切面表達式派歌。
分析
ProxyFactory是Spring中用來生成代理類的工廠,包裝了生成代理類的細節(jié)痰哨,包括:Cglib方式和Jdk動態(tài)代理方式胶果。
接下來我們說一說通過JDK動態(tài)代理方式生成代理類的JdkDynamicAopProxy,而這是建立在熟悉Jdk動態(tài)代理基礎上的:動態(tài)代理系列(二)JDK動態(tài)代理
調用切面通知鏈
執(zhí)行切面邏輯斤斧,很簡單早抠,如下圖:
說白了,依次執(zhí)行切面邏輯撬讽,最后執(zhí)行目標類方法蕊连。
如果每個切面方法都返回布爾類型,我們可以順序遍歷切面邏輯游昼,根據返回值來判斷是否繼續(xù)傳遞咪奖,這是鏈式處理比較常見的方式,實現也比較簡單酱床,比較容易想到。
但Spring并不是使用的這種方式趟佃,切面方法也并沒有返回布爾類型扇谣。
那么Spring是如何執(zhí)行切面邏輯的呢?
在代理類中闲昭,通過把目標類和切面包裝為MethodInvocation對象罐寨,切面方法的入參正好也是這個對象。
代理類被調用時序矩,會先執(zhí)行MethodInvocation.proceed()鸯绿,執(zhí)行切面邏輯,而切面是否繼續(xù)傳遞就看是否繼續(xù)執(zhí)行MethodInvocation.proceed()方法簸淀,實現如下:
public class SimpleMethodInvocation implements MethodInvocation {
Object target;
List<MethodInterceptor> list;
int i = 0;
@Override
public Object proceed() throws Throwable {
if (i != list.size()) {
list.get(i++).invoke(this); //調用切面邏輯
} else {
return 反射調用(target);
}
return null;
}
}
基于IOC的Aop
先通過例子展示基于IOC的Aop的實現
業(yè)務類
package com.esy.stu.aop;
import org.springframework.stereotype.Component;
@Component
public class Car {
public void work() {
System.out.println("Car動了");
}
}
切面邏輯
package com.esy.stu.aop;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class SimpleAspact {
@Before("within(com.esy.stu.aop.Car)")
public void t1() {
System.out.println("before");
}
}
啟動類
package com.esy.stu.aop;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@EnableAspectJAutoProxy
@ComponentScan(basePackages = "com.esy.stu.aop")
public class Boot {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Boot.class);
applicationContext.getBean(Car.class).work();
}
}
執(zhí)行結果:
before
Car動了
分析
從上面可以發(fā)現瓶蝴,與一般IOC容器啟動相比較,多了一個使用@Aspect標記的切面邏輯的類租幕,并且配置類上面也多了一個注解EnableAspectJAutoProxy舷手,先分析下這個配置注解。
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
EnableAspectJAutoProxy注解其實是引入AspectJAutoProxyRegistrar注冊代理類創(chuàng)建器劲绪,如下圖:
它Aop的關鍵男窟,這張圖也把AnnotationAwareAspectJAutoProxyCreator分為兩個部分:
- BeanPostProcessor(AOP就是對業(yè)務類進行代理盆赤,是由Bean處理器來觸發(fā)的)
- ProxyCreator(代理類生成工廠)
創(chuàng)建代理
如果了解IOC實例化過程,就會知道歉眷,Bean完全初始化之前可以作為半成品被使用牺六,并且能夠被其他bean注入。由此可以推測汗捡,在引用加入半成品之前淑际,對象就已經被代理對象所替換了,不然其他bean注入就該是目標類了凉唐。
下面是核心的源碼:
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
throws BeanCreationException {
// 反射生成Bean
BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args);
//這里將完成AOP替換庸追,將目標類的引用替換成代理類,并存儲到半成品容器中
addSingletonFactory(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
return getEarlyBeanReference(beanName, mbd, bean);
}
});
// 裝配Bean
populateBean(beanName, mbd, instanceWrapper);
return exposedObject;
}
getEarlyBeanReference方法中調用組件台囱,其中就有AbstractAutoProxyCreator#getEarlyBeanReference
使用的ProxyFactory生成代理類淡溯。
關鍵點
找到切面聲明
BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors
遍歷所有的bean定義,找到Aspect注解標注的類
獲取advice
通過Around簿训、Before咱娶、After、AfterReturning强品、AfterThrowing這幾種不同的注解包裝方法成為不同的通知對象膘侮。
注解 | 通知對象 |
---|---|
Around | AspectJAroundAdvice |
Before | AspectJMethodBeforeAdvice |
After | AspectJAfterAdvice |
AfterReturning | AspectJAfterReturningAdvice |
AfterThrowing | AspectJAfterThrowingAdvice |
如果研究就會發(fā)現AspectJMethodBeforeAdvice、AspectJAfterReturningAdvice的榛、AspectJAfterThrowingAdvice并不滿足MethodInterceptor接口琼了,需要進一部包裝成AfterReturningAdviceAdapter、MethodBeforeAdviceAdapter夫晌、ThrowsAdviceAdapter雕薪。
構造Advisor
ReflectiveAspectJAdvisorFactory#getAdvisors
包裝通知和切面表達式為InstantiationModelAwarePointcutAdvisorImpl。
可以說整個Aop的核心就是ProxyFactory晓淀,而IOC容器提供的功能就是構造advice和pointcut所袁。