概念
IOC和AOP作爲Spring的兩大核心,如果你接觸過Spring框架,那就一定需要了解這兩個核心部分的工作原理。IOC控制反轉(或者說依賴注入)的部分已經在我的從源碼解讀Spring的IOC文章中有介紹,這篇文章主要來講解AOP面向切面編程的原理
AOP,即Aspect Oriented Programming,面向切面編程,關於什麼是面向切面編程,爲了避免喧賓奪主,我在這裏不進行過多的探討,只需要簡單地瞭解面向切面編程就是相當於給一個方法在運行時動態添加新的功能,如果你瞭解過代理模式就非常好理解AOP的知識了
同時,在開始本文之前,也希望你瞭解動態代理的一些知識,比如Jdk代理和Cglib代理,如果對這兩種代理方式一無所知的話,那就暫時先知道Jdk代理是通過在運行時編譯生成某接口對象的實現類,而Cglib代理是在運行時動態生成某對象的子類對象
AOP的使用
spring中用到aop的地方有很多,但是大部分地方都是在你不知道的情況下自動開啓的,比如事務管理這樣的功能,你僅僅只是添加一個註解而已,spring就自動幫你做了aop的切面,這裏我們通過手動寫一個小的demo,來讓你快速理解一下aop是如何使用的,而如果你已經瞭解了AOP的使用方法,就可以直接跳過這一部分
首先在pom.xml中添加aop的依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
然後在配置文件中添加以下配置(不配置也可以,但是接下來可能會用到):
# 開啓註解(默認開啓)
spring.aop.auto=true
# 使用cglib代理(默認不使用)
spring.aop.proxy-target-class=false
準備工作完成了,我們開始接下來的操作,現在我們有一個歌手:
@Component
public class Singer implements Singable {
public final String name = "小紅";
@Override
public void sing() {
System.out.println("la la la...");
}
}
我們想在她唱歌前打開燈光,在唱歌后打掃舞臺,於是我們做了下面這樣的類:
public class Manager {
void turnLightOn() {
System.out.println("燈光打開了...");
}
void cleanStage() {
System.out.println("打掃舞臺...");
}
}
好了,接下來我們怎麼在sing方法調用前後調用這兩個方法呢,難不成將對象注入到Singer中然後手動調用?這樣當然可以,但是我們既然學了aop,就要使用aop的思想,不主動侵入代碼,於是我們給Manger類添加以下註解:
@Aspect
@Component
public class Manager {
@Pointcut("execution(* com.akira.demo.interfaces.Singable.sing(..))")
public void singsong() {}
@Before("singsong()")
void turnLightOn() {
System.out.println("燈光打開了...");
}
@After("singsong()")
void cleanStage() {
System.out.println("打掃舞臺...");
}
}
這樣我們就完成aop的操作,來測試一下(一定要通過自動注入的方式):
@Autowired
Singable singer;
@Test
public void sing() {
singer.sing();
}
結果:
AOP的實現原理
從剛纔的代碼演示,有過一些基礎的應該可以瞭解spring中aop的大概使用方法,接下來我們就要從剛纔的方法入手,詳細瞭解spring是怎麼把切面注入方法的
首先,方法的起點是AopNamespaceHandler類,其中只有一個init方法:
@Override
public void init() {
// 在 2.0 XSD 和 2.1 XSD 中
registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
// 僅在 2.0 XSD 中,在2.1 XSD 被移入context命名空間
registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
}
用過xml配置aop的應該對這些命名空間很熟悉,我們關注aspectj-autoproxy這個標籤,這是用來配置自動開啓aop註解的,我們進入對應的解析器AspectJAutoProxyBeanDefinitionParser中,發現parse方法中有這麼一行:
AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
不理解這個方法幹什麼沒關係,看名稱上有一個registerAspectJAnnotation,說明是要註冊aop,我們進入這個方法
public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(
ParserContext parserContext, Element sourceElement) {
BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
parserContext.getRegistry(), parserContext.extractSource(sourceElement));
useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
registerComponentIfNecessary(beanDefinition, parserContext);
}
實際上代碼只有三行,第一行我們知道BeanDefinition就是bean的實體封裝對象,這裏實際上轉發調用了registerOrEscalateApcAsRequired方法,在其中註冊AnnotationAwareAspectJAutoProxyCreator對象,實際上相當於確定切點匹配的bean,即確定被代理的bean
第二行useClassProxyingIfNecessary方法主要是用於選擇代理方式,可以忽略
第三行則是給剛纔的bean註冊組件,用於監聽器的監聽
接下來好像就一頭霧水了,別急,我們發現registerOrEscalateApcAsRequired傳入的第一個參數就是AnnotationAwareAspectJAutoProxyCreator.class,這實際上也是aop的核心類,我們一路往上,找到它的一個父類AbstractAutoProxyCreator,定義如下:
public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {
我們發現裏面有一個postProcessBeforeInstantiation方法,這個是加載實例化之前執行的方法,來自於BeanPostProcessor接口。既然是動態代理,那肯定與對象的實例化有關,這個方法源碼如下:
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
核心方法很顯然就是wrapIfNecessary,源碼如下:
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// 如果我們聲明瞭通知,就創建代理對象
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
終於到核心方法了,我們先來看getAdvicesAndAdvisorsForBean方法,顧名思義,這個方法是用來獲取bean的所有通知方法,或者可以叫增強方法,具體執行過程不再此贅述,感興趣的可以自行查閱
獲取了增強方法之後,會發現這裏調用了createProxy創建了代理對象(這個方法的實現還是在AbstractAutoProxyCreator中,不愧是核心類),這個方法源碼如下:
protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
@Nullable Object[] specificInterceptors, TargetSource targetSource) {
if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
}
// 創建代理工廠
ProxyFactory proxyFactory = new ProxyFactory();
// 把增強器添加到代理工廠中
proxyFactory.copyFrom(this);
if (!proxyFactory.isProxyTargetClass()) {
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
// 確定增強方法,包括攔截器
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
proxyFactory.addAdvisors(advisors);
proxyFactory.setTargetSource(targetSource);
// 空方法
customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
// 返回代理對象
return proxyFactory.getProxy(getProxyClassLoader());
}
看到這裏你可能大失所望,怎麼還是沒有調用代理方法,別急,這裏只是進行初始化而已,不過我們也能發現,這裏的操作已經完全針對特定的注入的bean和增強方法了
我們進入最後的getProxy方法,來尋找創建代理類的位置:
public Object getProxy(@Nullable ClassLoader classLoader) {
return createAopProxy().getProxy(classLoader);
}
進入ProxyCreatorSupport中,這是一個同步方法:
protected final synchronized AopProxy createAopProxy() {
if (!this.active) {
// 激活監聽器
activate();
}
return getAopProxyFactory().createAopProxy(this);
}
這裏getAopProxyFactory默認返回DefaultAopProxyFactory,重點來了,我們進入createAopProxy方法:
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
// config.isOptimize():判斷是否進行代理器的主動優化
// config.isProxyTargetClass():判斷是否直接代理目標類和接口
// hasNoUserSuppliedProxyInterfaces(config)):判斷是否指定了代理接口
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
// 如果被代理類是接口,或動態生成指定的類作爲代理類,則使用Jdk代理
return new JdkDynamicAopProxy(config);
}
// 否則使用Cglib代理
return new ObjenesisCglibAopProxy(config);
}
else {
// 默認使用Jdk代理
return new JdkDynamicAopProxy(config);
}
}
終於發現真正代理類的位置了,配合註釋基本可以大致理清流程:
- 如果開發者沒有進行主動配置,則選用Jdk代理
- 如果被代理類是接口,或者需要代理getProxyClass/newProxyInstance動態生成方法時,也選用Jdk代理
- 其餘情況選擇Cglib代理
我們明白這個代理對象是如果創建的就已經足夠了,關於具體的代理對象的執行過程,如果有機會我會單獨寫一篇文章來詳細講解,到這裏整個代理流程就結束了,剩下的就是調用方法而已
總結
整個代理過程確實有些複雜,這裏爲大家做一個簡要的總結:
- AopNamespaceHandler加載配置
- 調用AopNamespaceUtils工具類註冊核心類AnnotationAwareAspectJAutoProxyCreator和其他組件
- AnnotationAwareAspectJAutoProxyCreator因爲繼承了BeanPostProcessor接口,會在實例化前調用postProcessBeforeInstantiation方法,在這個方法中調用了wrapIfNecessary方法
- wrapIfNecessary方法中獲取聲明的通知(增強方法),然後調用createProxy創建代理對象
- createProxy中創建代理工廠,確定增強方法,調用工廠方法getProxy返回代理對象
- getProxy最終是調用了DefaultAopProxyFactory的createAopProxy來根據配置和代理類的特點選用代理方式
- 最終返回代理對象