aop原始碼解析三-postProcessAfterInitialization
在aop原始碼解析二:postProcessBeforeInstantiation博文中 我們花了很長的一個篇幅介紹一個方法shouldSkip
方法 雖然看名字很簡單 但裡面確實做了很多的事情 雖然最終也是沒有去建立代理 但這些工作並不是白做的 解析完以後快取起來了 後面就可以直接使用了 這次我們看下AnnotationAwareAspectJAutoProxyCreator##postProcessAfterInitialization(Object bean, String beanName)
方法
public Object postProcessAfterInitialization (Object bean, String beanName) throws BeansException {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (!this.earlyProxyReferences.containsKey(cacheKey)) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
在這裡看到了我們一直想看到的代理相關的程式碼 因為建立代理是一件耗時的事情 要做很多的解析 轉換等工作 所以根據beanClass``beanName
作為快取的key
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
// 因為尋找切面方法等操作是很浪費時間的操作 所以如果不必操作的話或者已經建立過代理的話 儘量提前返回
//已經建立過
if (beanName != null && this .targetSourcedBeans.containsKey(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;
}
// 可以看出作者的思路是很清晰的 幾句程式碼概括了要做的事情 然後在方法裡面展開
// advice advisor pointcut 等之間的名次的區別與聯絡
// advice : action to take at a joinpoint
// 1. 尋找適合此bean的切面方法
// 2. 建立代理
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
//建立代理物件 java動態代理或者是基於cglib的代理 cglib代理更高效
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;
}
我們又看到了shouldSkip(bean.getClass(), beanName)
方法 跟我們上篇博文介紹的是同一個方法
這裡主要看下getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null)
方法
其實這個方法的主要作用是對於尋找到的所有的切面對beanClass
做過濾 涉及到切點的匹配 例如正則表示式 註解等
@Override
protected Object[] getAdvicesAndAdvisorsForBean(Class beanClass, String beanName, TargetSource targetSource) {
// 尋找勝任的或者說適合此bean的advior
List advisors = findEligibleAdvisors(beanClass, beanName);
if (advisors.isEmpty()) {
return DO_NOT_PROXY;
}
return advisors.toArray();
}
protected List<Advisor> findEligibleAdvisors(Class beanClass, String beanName) {
//Advisor.class
//尋找所有的候選項
// 這裡注意 findCandidateAdvisors() 方法在AnnotationAwareAspectJAutoProxyCreator類有繼承
List<Advisor> candidateAdvisors = findCandidateAdvisors();
//從候選項中挑出適合此bean的advisor
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
//對advisor排序 按照順序:
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
findCandidateAdvisors()
方法 我們在介紹shouldSkip(xxx)
方法的時候就已經講解過 不熟悉的讀者可以多讀幾遍 這個方法解析出了所有的advisor
advisor
包括advice``pointcut
資訊
protected List<Advisor> findAdvisorsThatCanApply(
List<Advisor> candidateAdvisors, Class beanClass, String beanName) {
ProxyCreationContext.setCurrentProxiedBeanName(beanName);
try {
return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
}
finally {
ProxyCreationContext.setCurrentProxiedBeanName(null);
}
}
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
if (candidateAdvisors.isEmpty()) {
return candidateAdvisors;
}
List<Advisor> eligibleAdvisors = new LinkedList<Advisor>();
for (Advisor candidate : candidateAdvisors) {
if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
eligibleAdvisors.add(candidate);
}
}
boolean hasIntroductions = !eligibleAdvisors.isEmpty();
for (Advisor candidate : candidateAdvisors) {
if (candidate instanceof IntroductionAdvisor) {
// already processed
continue;
}
//todo 寫註釋
if (canApply(candidate, clazz, hasIntroductions)) {
eligibleAdvisors.add(candidate);
}
}
return eligibleAdvisors;
}
public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
if (advisor instanceof IntroductionAdvisor) {
return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
}
else if (advisor instanceof PointcutAdvisor) {
PointcutAdvisor pca = (PointcutAdvisor) advisor;
return canApply(pca.getPointcut(), targetClass, hasIntroductions);
}
else {
// It doesn't have a pointcut so we assume it applies.
return true;
}
}
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
Assert.notNull(pc, "Pointcut must not be null");
if (!pc.getClassFilter().matches(targetClass)) {
return false;
}
MethodMatcher methodMatcher = pc.getMethodMatcher();
IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
}
// 類繼承關係中 只要有一個類的一個方法被匹配 就返回true
Set<Class> classes = new LinkedHashSet<Class>(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
classes.add(targetClass);
for (Class<?> clazz : classes) {
Method[] methods = clazz.getMethods();
for (Method method : methods) {
if ((introductionAwareMethodMatcher != null &&
introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) ||
methodMatcher.matches(method, targetClass)) {
return true;
}
}
}
return false;
}
最重要的還是最後一個canApply
方法 根據pointcut
資訊去做匹配 判斷是否符合要求 因為這裡是返回類的代理 所以只要有一個方法被匹配到 就人為滿足條件 至於是怎麼做匹配的 感興趣的讀者可以深入閱讀 我們這裡只要知道做了什麼就可以了
回到wrapIfNecessary
方法 繼續往下看 下面的就是我們一直想看到的方法了 建立代理
看一下aop
的相關配置
<aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="true"/>
這行程式碼註冊了BPP 另外還指定了proxy-target-class
屬性 值為true
的意思是強制使用cglib
建立代理 還有另外一種java jdk
提供的代理 需要被代理的類的方法繼承自某個介面方法才可以 cglib
並沒有這個限制 推薦使用cglib
的方式 執行速度更快
但我們下面是以jdk
建立代理為例講解 這種方式更簡單 讀者也可能更熟悉這種方式 那麼就把這行xml
配置改成下面這樣
<aop:aspectj-autoproxy proxy-target-class="false" expose-proxy="true"/>
對java提供的動態代理實現不瞭解的讀者 建議先看這篇部落格java動態代理介紹 這樣下面的代理就更容易理解
我們看下createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean))
方法
protected Object createProxy(
Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
ProxyFactory proxyFactory = new ProxyFactory();
// Copy our properties (proxyTargetClass etc) inherited from ProxyConfig.
proxyFactory.copyFrom(this);
//使用java動態代理
if (!shouldProxyTargetClass(beanClass, beanName)) {
// Must allow for introductions; can't just set interfaces to
// the target's interfaces only.
Class<?>[] targetInterfaces = ClassUtils.getAllInterfacesForClass(beanClass, this.proxyClassLoader);
for (Class<?> targetInterface : targetInterfaces) {
proxyFactory.addInterface(targetInterface);
}
}
//
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
for (Advisor advisor : advisors) {
proxyFactory.addAdvisor(advisor);
}
proxyFactory.setTargetSource(targetSource);
customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
return proxyFactory.getProxy(this.proxyClassLoader);
}
ProxyFactory
是一個很重要的類 裡面有各種與切面相關的屬性,方法等
public Object getProxy(ClassLoader classLoader) {
return createAopProxy().getProxy(classLoader);
}
getProxy(ClassLoader classLoader)
方法返回的就是目標類的代理類 分成兩大步完成
- 返回要使用的
AopProxy
CglibAopProxy
或者JdkDynamicAopProxy
- 建立代理
protected final synchronized AopProxy createAopProxy() {
if (!this.active) {
activate();
}
//預設是DefaultAopProxyFactory
return getAopProxyFactory().createAopProxy(this);
}
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
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()) {
return new JdkDynamicAopProxy(config);
}
return CglibProxyFactory.createCglibProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
預設使用的建立AopProxy
的是DefaultAopProxyFactory 是一個工廠類 類的實現很簡單 根據條件返回對應的AopProxy
當然都把proxyFactory
作為引數傳遞了進去
我們接下來看JdkDynamicAopProxy##getProxy
方法
public Object getProxy(ClassLoader classLoader) {
if (logger.isDebugEnabled()) {
logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
}
Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
//尋找equals hashCode方法
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
completeProxiedInterfaces(AdvisedSupport advised)
方法在代理類要實現的所有介面中加入了兩個介面SpringProxy
與 Advised
介面 SpringProxy
是一個標誌介面 沒有什麼方法需要去實現 Advised
提供的介面方法就比較多了 裡面有代理相關的所有配置屬性
下面就是建立代理了 相信讀者如果閱讀了java動態代理介紹這篇部落格的話 讀者應該理解做了什麼事情 在下一篇部落格中我們看一下方法的呼叫過程
相關推薦
aop原始碼解析三-postProcessAfterInitialization
在aop原始碼解析二:postProcessBeforeInstantiation博文中 我們花了很長的一個篇幅介紹一個方法shouldSkip方法 雖然看名字很簡單 但裡面確實做了很多的事情 雖然最終也是沒有去建立代理 但這些工作並不是白做的 解析完以後快取起
AOP原始碼解析(三)增強器的獲取
普通增強器的獲取邏輯通過getAdvisor方法實現,實現步驟包括對切點的註解的獲取及根據註解資訊生成增強。 public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectIns
Elastic-Job原始碼解析(三)之分片定時任務執行
通過本篇的閱讀你將學會了解Elastic-Job的定時時機,及如何通過分片方式做一個分散式的定時任務框架。瞭解常用的三種分片策略,及如何自定義分散式分片策略 目錄 Elastic-Job如何通過SpringJobScheduler啟動定時 Ela
spring cloud Config原始碼解析(三)
前面說完我們如何從github上面去取資料,這裡說說server端剩餘的類。ConfigServerEncryptionConfiguration類。從類的名字我們可以看出主要是加解密相關的配置類,進入類中可以看到定義了EncryptionController encry
iOS總結-網路框架-AFNetworking原始碼解析(三)
參考https://www.jianshu.com/p/f32bd79233da 有關解析的主要是AFHTTPResponseSerializer: NSObject,其他的都是繼承AFHTTPResponseSerializer AFURLResponseSerialization的協議方
ReactiveSwift原始碼解析(三) Signal程式碼的基本實現
上篇部落格我們詳細的聊了ReactiveSwift原始碼中的Bag容器,詳情請參見《》。本篇部落格我們就來聊一下訊號量,也就是Signal的的幾種狀態以及Signal的基本實現。當然本篇部落格所解析的原始碼會用到上篇部落格介紹的Bag容器。本篇部落格我們先通過一個示例來看一下Signal是如何工作的,具體說來
RxDownload2 原始碼解析(三)
原始碼解析,如需轉載,請註明作者:Yuloran (t.cn/EGU6c76) 前言 造輪子者:Season_zlc 本文主要講述 RxDownload2 的多執行緒斷點下載技術 斷點下載技術前提 伺服器必須支援按 byte-range 下載,也就是支援 Range: bytes=xxx-
Mybatis 原始碼解析三、Mapper介面與mapper.xml檔案繫結
一、流程圖介紹整體過程 1、首先根據MapperScannerConfigurer進行包掃描,掃描Mapper介面,生成Spring特定的描述,並將其交
【進階】RecyclerView原始碼解析(三)——深度解析快取機制
上一篇部落格從原始碼角度分析了RecyclerView讀取快取的步驟,讓我們對於RecyclerView的快取有了一個初步的理解,但對於RecyclerView的快取的原理還是不能理解。本篇部落格將從實際專案角度來理解RecyclerView的快取原理。
vue2.x原始碼解析三——原始碼構建
1.Rollup介紹 1.2rollup和webpack都是區別 webpack更加強大 可以將圖片,css等都解析為js。 rollup 更適合於js庫的編譯,只適用於js部分,別的檔案是不管的,並且更加友好 2.Vue.js 原始碼構建指令碼
java執行緒池(ThreadPoolExecutor)原始碼解析三
ctl成員變數 /** * ctl 儲存了兩部分資訊 * workerCount : 執行緒數 * runStatus: 執行緒池的狀態 * * ctl 是一個 AtomicI
Mybatis 原始碼解析(三)
文章個人學習原始碼所得,若存在不足或者錯誤之處,請大家指出。 Properties配置格式如下: Configuration.xml中: <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE
mybatis原始碼解析(三)-SqlSession.selectOne類似方法呼叫過程
上篇部落格中介紹了mybatis的載入過程,這篇部落格介紹一下org.apache.ibatis.session.SqlSession的增、刪、改、查方法是怎麼實現的。我們使用mybatis時,基本都是使用Mapper進行增、刪、改、查操作,但是SqlS
aop原始碼解析二:postProcessBeforeInstantiation
我們就不再從bean的載入過程開始看了 在aop原始碼解析一:註冊BPP中 介紹到了一個後置處理器 AnnotationAwareAspectJAutoProxyCreator 我們就從這個類入手aop 先看postProcessBeforeInstantia
Spring AOP原始碼解析(二)獲取增強器
一、方法入口 上一節中,Spring會建立兩個工廠來完成獲取增強方法的功能: AspectMetadata amd = new AspectMetadata(beanType, beanName); if (amd.getAjType().getPerClause()
分散式任務排程平臺XXL-JOB--原始碼解析三:xxl-job-admin排程中心原始碼解析之初始化兩個Thread工作執行緒
xxl-job-admin初始化工作 1.1 啟動時, 載入applicationcontext-xxl-job-admin.xml, 初始化spring容器 <!-- 這個排程中心,在啟動的時候,會做很多初始化的工作 ,比如:執行器資訊,註冊機器列表等資訊 --
Spring Aop原始碼解析
AspectJAwareAdvisorAutoProxyCreator及為Bean生成代理時機分析 上篇文章說了,org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator這
Spring系列(五):Spring AOP原始碼解析
一、@EnableAspectJAutoProxy註解 在主配置類中新增@EnableAspectJAutoProxy註解,開啟aop支援,那麼@EnableAspectJAutoProxy到底做了什麼?接下來分析下: @EnableAspectJAutoProxy點進去如下: &nb
5.2 spring5原始碼--spring AOP原始碼分析三---切面原始碼分析
一. AOP切面原始碼分析 原始碼分析分為三部分 1. 解析切面 2. 建立動態代理 3. 呼叫 原始碼的入口 原始碼分析的入口, 從註解開始: 元件的入口是一個註解, 比如啟用AOP的註解@EnableAspectJAutoProxy. 在註解的實現類裡面, 會有一個@Import("
Spring基於註解形式的 AOP的原理流程及原始碼解析(三)
此篇部落格主要講解Spring如何驗證將要例項化的Bean是否應該被代理,生成代理物件的時機問題。 在第二篇部落格中,Spring對容器內所有的標識了@Aspect註解的的類的切面方法(標識了@Around, @Before, @After, @AfterRe