前言
在上一篇文章中講到了Spring是如何獲取對應的Bean的增強,然後本次主要講解一下Spring如何在獲取到增強後建立Spring代理的。
在步入正題之前先給大家看一下Spring建立代理的大致流程圖
接下來我們就回到AbstractAutoProxyCreator.class
類中的wrapIfNecessary
方法。
- 看原始碼(
AbstractAutoProxyCreator.class
)
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;
}
// 給定的bean類是否是一個基礎設施類,基礎設施類不應該被代理,或者配置了指定的bean不需要代理
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// 獲取能夠應用到當前 Bean 的所有 Advisor(已根據 @Order 排序)
// Create proxy if we have advice.
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
// 如果有 Advisor,則進行下面的動態代理建立過程
if (specificInterceptors != DO_NOT_PROXY) {
// 如果獲取到了增強則需要針對增強進行代理
this.advisedBeans.put(cacheKey, Boolean.TRUE);
// 建立代理 JDK 動態代理或者 CGLIB 動態代理
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
// 將代理物件的 Class 物件(目標類的子類)儲存
this.proxyTypes.put(cacheKey, proxy.getClass());
// 返回這個 Bean 物件
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
在上一篇Spring原始碼之建立AOP代理之增強器的獲取文章中,主要是圍繞著getAdvicesAndAdvisorsForBean
方法展開的,主要是獲取到了所有對應Bean的增強器,並獲取到了此目標Bean所匹配的Advisor,
接下來我們著手對接下來的方法createProxy
進行分析,
- 看原始碼(
AbstractAutoProxyCreator.class
)
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();
// 複製當前 ProxyConfig 的一些屬性(例如 proxyTargetClass、exposeProxy)
proxyFactory.copyFrom(this);
// 判斷是否是代理類(也就是是否開啟了CGLIB代理) 預設是false
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 {
// 如果這個 Bean 配置了進行類代理,則設定為 `proxyTargetClass` 為 `true`
// No proxyTargetClass flag enforced, let's apply our default checks...
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
// 檢測當前Bean 實現的介面是否包含可代理的介面 ,如果沒有,則將proxyTargetClass 設定為true 表示需要進行CGLIB 提升
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
// 對入參的 advisors 進一步處理,因為其中還可能存在Advice型別 需要將他們包裝成 DefaultPointcutAdvisor
// 如果配置了 `interceptorNames` 攔截器,也會新增進來
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
// 代理工廠新增 Advisor 陣列
proxyFactory.addAdvisors(advisors);
// 代理工廠設定 TargetSource 物件
proxyFactory.setTargetSource(targetSource);
// 對 ProxyFactory 進行加工處理,抽象方法,目前沒有子類實現
customizeProxyFactory(proxyFactory);
//用來控制代理工廠被配置之後,是否還允許修改通知。(預設為 false) (即在代理被配置之後,不允許修改代理的配置)。
proxyFactory.setFrozen(this.freezeProxy);
// 這個 AdvisedSupport 配置管理器是否已經過濾過目標類(預設為 false)
if (advisorsPreFiltered()) {
// 設定 `preFiltered` 為 `true`
// 這樣 Advisor 們就不會根據 ClassFilter 進行過濾了,而直接通過 MethodMatcher 判斷是否處理被攔截方法
proxyFactory.setPreFiltered(true);
}
// 如果 bean 類未在覆蓋類載入器中本地載入,則使用原始 ClassLoader
// 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();
}
// 通過 ProxyFactory 代理工廠建立代理物件
return proxyFactory.getProxy(classLoader);
}
-
原始碼分析
上述程式碼中的
ProxyFactory proxyFactory = new ProxyFactory();
新建了一個工廠類,並且往後看,明顯的看出對於代理類的建立Spring是委託給了ProxyFactory
處理的。接下來繼續跟蹤原始碼
proxyFactory.getProxy(classLoader);
該方法建立了代理物件。 -
看原始碼(Proxyfactory.java
)
public Object getProxy(@Nullable ClassLoader classLoader) {
// 先建立一個 AOP 代理類(JdkDynamicAopProxy 或者 ObjenesisCglibAopProxy) 其實現是在DefaultAopProxyFactory中
// 根據 AOP 代理為目標 Bean 建立一個代理物件,並返回
return createAopProxy().getProxy(classLoader);
}
-
原始碼分析
通過上述註釋可以感覺到終於要來到了主題,到底是如何決定使用哪種代理方式的。首先我們看到
getProxy
方法中的createAopProxy
方法,它的預設實現其實是在DefaultAopProxyFactory
類中。這中間它經過了ProxyCreatorSupport
類的createAopProxy
方法。 -
看原始碼(
ProxyCreatorSupport.java
)
protected final synchronized AopProxy createAopProxy() {
if (!this.active) {
activate();
}
// 先獲取 AOP 代理工廠,預設為 DefaultAopProxyFactory,只有這個實現
// 然後通過它根據建立當前 AdvisedSupport 配置管理器建立一個 AOP 代理(JdkDynamicAopProxy 或者 ObjenesisCglibAopProxy)
return getAopProxyFactory().createAopProxy(this);
}
緊接著我們直接來到具體實現createAopProxy方法
的實現類DefaultAopProxyFactory
類中。
- 看原始碼(
DefaultAopProxyFactory.java
)
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
// 判斷是否滿足下面條件的
/*
* config.isOptimize() 需要優化,預設為 `false`詳細來說就是:用來控制通過CGLIB建立的代理是否使用激進的優化策略
* 除非完全瞭解AOP代理如何處理優化,否則不推薦使用者使用這個設定,目前這個屬性僅用於CGLIB 代理,對於JDK動態代理(預設代理)無效
* config.isProxyTargetClass() 使用類代理,也就是使用 CGLIB 動態代理 預設為 `false`
* 設定方式:<aop:aspectj-autoproxy proxy-target-class="true"/>
* hasNoUserSuppliedProxyInterfaces(config) // 是否存在代理介面
*/
if (!NativeDetector.inNativeImage() &&
(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.");
}
// 如果目標類是一個介面或者是 java.lang.reflect.Proxy 的子類 則還是使用 JDK 動態代理,建立一個 JdkDynamicAopProxy 物件,
// 傳入 AdvisedSupport 配置管理器,並返回
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
// 使用 CGLIB 動態代理,建立一個 ObjenesisCglibAopProxy 物件,傳入 AdvisedSupport 配置管理器,並返回
return new ObjenesisCglibAopProxy(config);
}
else {
// 使用 JDK 動態代理,建立一個 JdkDynamicAopProxy 物件,傳入 AdvisedSupport 配置管理器,並返回
return new JdkDynamicAopProxy(config);
}
}
-
原始碼分析
在這個
DefaultAopProxyFactory
類中可以明顯的看到,這裡根據Optimize
、ProxyTargetClass
、hasNoUserSuppliedProxyInterfaces
三個屬性進行的決斷,看究竟使用哪種動態代理。
optimize 需要優化,預設為
false
詳細來說就是:用來控制通過CGLIB建立的代理是否使用激進的優化策略;除非完全瞭解AOP代理如何處理優化,否則不推薦使用者使用這個設定,目前這個屬性僅用於CGLIB 代理,對於JDK動態代理(預設代理)無效ProxyTargetClass使用類代理,也就是使用 CGLIB 動態代理 預設為
false
設定方式:<aop:aspectj-autoproxy proxy-target-class="true"/>
hasNoUserSuppliedProxyInterfaces(config) // 是否存在代理介面
JDK與Cglib的說明
- 如果目標物件實現了介面,預設情況下會採用JDK的動態代理實現AOP
- 如果目標物件實現了介面,可以強制使用CGLIB實現AOP。
- 如果目標物件沒有實現了介面,必須採用CGLIB庫,Spring會自動在JDK動態代理 和CGLIB之間轉換
如何強制使用CGLIB實現AOP?
- 新增 CGLIB 庫,Spring_HOME/cglib/*.jar
- Spring 配置檔案中加人<aop:aspectj-autoproxy proxy-target-class="true"/>。
JDK動態代理和CGLIB位元組碼生成的區別?
- JDK動態代理只能對實現了介面的類生成代理,而不能針對類。
- GLIB是針對類實現代理,主要是對指定的類生成一個子類,覆蓋其中的方法,因為是繼承,所以該類或方法最好不要宣告成final。
好了到這裡就講完了Spring是如何決定使用哪種動態代理的方式的。
想要獲取更多精彩內容請微信搜尋【碼上遇見你】