Spring依賴注入時,什麼時候會建立代理類?

語言: CN / TW / HK

title: "Spring依賴注入時,什麼時候會建立代理類" linkTitle: "Spring依賴注入時,什麼時候會建立代理類" date: 2021-12-08 weight: 202112081717


用到的程式碼地址:https://github.com/mxsm/spring-sample/tree/spring-5.3.x/spring-proxy

1. 現象

第一種情況:

第二種情況:

第三種情況:

2. 現象說明

第一種是最常見的在普通的Spring應用中,會發現 UserServiceTeacherService 都沒有建立代理。這個就是最常見的使用方式。

第二種是使用了 @EnableAspectJAutoProxy 註解開啟了Spring AOP, 發現UserService 使用的是JDK代理,TeacherService 使用的是CGLIB代理

第三種是使用了 @EnableAspectJAutoProxy(proxyTargetClass = true) ,會發現 UserServiceTeacherService 都使用的是CGlib代理

現象總結:在沒有開啟AOP的情況下是不會建立代理的,在使用了 @EnableAspectJAutoProxy 預設的情況下介面的代理使用的是JDK代理實現,類的代理使用的CGLIB(jdk不能實現類的代理)。 @EnableAspectJAutoProxy(proxyTargetClass = true) 強制使用CGLIB代理。

3. 原始碼分析

3.1 上面時候會使用代理類

通過之前的《Spring AOP 原始碼解析》可以知道,註解方式下的AOP的建立代理類主要的類是: AnnotationAwareAspectJAutoProxyCreator ,這個類重寫了 initBeanFactoryfindCandidateAdvisors 兩個方法:

initBeanFactory方法

java @Override protected void initBeanFactory(ConfigurableListableBeanFactory beanFactory) { super.initBeanFactory(beanFactory); if (this.aspectJAdvisorFactory == null) { this.aspectJAdvisorFactory = new ReflectiveAspectJAdvisorFactory(beanFactory); } this.aspectJAdvisorsBuilder = new BeanFactoryAspectJAdvisorsBuilderAdapter(beanFactory, this.aspectJAdvisorFactory); }

功能:

  • 新建了ReflectiveAspectJAdvisorFactory

  • 新建了BeanFactoryAspectJAdvisorsBuilderAdapter

用來將包含了 @Aspect 註解的Bean構建成一個Advisor

findCandidateAdvisors:

java @Override protected List<Advisor> findCandidateAdvisors() { // Add all the Spring advisors found according to superclass rules. List<Advisor> advisors = super.findCandidateAdvisors(); // Build Advisors for all AspectJ aspects in the bean factory. if (this.aspectJAdvisorsBuilder != null) { advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors()); } return advisors; }

功能:

  • 查詢候選的Advisor(實現了Advisor介面的)
  • 將 AspectJ的構建成Advisor(@Aspec, @Before等等相關的Aspect的註解解析構建成Advisor)

Tips: 上面的第二種和第三種就是通過自己定義的Advisor來實現的。沒有使用Spring的註解例如:@Transactional (事務管理註解)。如果嫌麻煩也可以用這個註解或者其他的AOP的註解來觀察上面的三種情況。

所以從上面的程式碼就能看出來就能知道是否使用代理就是看是否開啟了 Spring AOP, 同時對應的類是否包含Aop需要處理的邏輯。例如上面自定義的 @Log 註解或者AspectJ的相關注解。

3.2 如何選擇JDK代理和Cglib代理

@EnableAspectJAutoProxy(proxyTargetClass = true) 強制使用Cglib代理。但是預設的情況下是怎麼樣的,我們還是根據看一下原始碼來分析一下。在建立代理類的時候在 AbstractAutoProxyCreator#createProxy 方法:

```java 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);
//設定強制使用Cglib代理
if (proxyFactory.isProxyTargetClass()) {
    // Explicit handling of JDK proxy targets (for introduction advice scenarios)
    if (Proxy.isProxyClass(beanClass)) {
        // Must allow for introductions; can't just set interfaces to the proxy's interfaces only.
        for (Class<?> ifc : beanClass.getInterfaces()) {
            proxyFactory.addInterface(ifc);
        }
    }
}
else {
    // 沒有proxyTargetClass強制的情況下判斷是否使用Cglib代理,這裡根據是代理介面還是代理類
    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);
}

// Use original ClassLoader if bean class not locally loaded in overriding class loader
ClassLoader classLoader = getProxyClassLoader();
if (classLoader instanceof SmartClassLoader && classLoader != beanClass.getClassLoader()) {
    classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader();
}
//建立代理類
return proxyFactory.getProxy(classLoader);

} ```

  1. 根據 proxyTargetClass 標記來判斷是否強制使用Cglib來建立代理
  2. 如果沒有設定 proxyTargetClass=true 就根據是代理介面還是代理類來動態處理選擇代理方式
  3. 建立代理類返回

4. 總結

  • 在沒有開啟AOP的時候如果不自己定義是不會使用和建立代理類。 也就是使用我們平時使用的不同類例項
  • @EnableAspectJAutoProxy(proxyTargetClass = true) 強制所有的方式都使用Cglib進行代理
  • @EnableAspectJAutoProxy 預設情況下會根據情況來判斷是否建立代理類,和用Jdk代理實現還是Cglib實現