咱們在業務開發中,使用得最多的是面向對象編程(OOP),由於它的代碼邏輯直觀,從上往下就能查看完整的執行鏈路。php
在這個基礎上延伸,出現了面向切面編程(AOP),將能夠重複性的橫切邏輯抽取到統一的模塊中。html
例如日誌打印、安全監測,若是按照 OOP
的思想,在每一個方法的先後都要加上重複的代碼,以後要修改的話,更改的地方就會太多,致使很差維護。因此出現了 AOP
編程, AOP
所關注的方向是橫向的,不一樣於 OOP
的縱向。java
因此接下來一塊兒來學習 AOP
是如何使用以及 Spring
容器裏面的處理邏輯~git
以前因爲業務開發中須要用到 AOP
,因此也整理一篇 Spring自定義註解實現AOP,感興趣的同窗能夠去看看~github
接下來是書中的例子:spring
public class TestAopBean {
private String testStr = "testStr";
public void testAop() {
// 被攔截的方法,簡單打印
System.out.println("I am the true aop bean");
}
}
複製代碼
@Aspect
public class AspectJTest {
@Pointcut("execution(* *.testAop(..))")
public void test() {
}
@Before("test()")
public void beforeTest() {
System.out.println("before Test");
}
@After("test()")
public void afterTest() {
System.out.println("after Test");
}
@Around("test()")
public Object aroundTest(ProceedingJoinPoint joinPoint) {
System.out.println("around Before");
Object o = null;
try {
// 調用切面的方法
o = joinPoint.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
System.out.println("around After");
return o;
}
}
複製代碼
首先類打上了 @Aspect
註解,讓 Spring
認識到這個是一個切面 bean
,在方法打上 @Pointcut("execution(* *.testAop(..))")
,表示這是一個切點方法,execution()
內部的表達式指明被攔截的方法,Before
、After
、Around
分別表示在被攔截方法的前、後已經環繞執行。express
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--開啓 AOP 功能-->
<aop:aspectj-autoproxy />
<bean id="aopTestBean" class="aop.TestAopBean"/>
<bean class="aop.AspectJTest" />
</beans>
複製代碼
public class AopTestBootstrap {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("aop/aop.xml");
TestAopBean bean = (TestAopBean) context.getBean("aopTestBean");
bean.testAop();
// 輸出內容 看輸出順序,瞭解到加強方法的執行順序 :
// Around proceed 以前 -> Before -> Around proceed 以後 -> After
//around Before
//before Test
//I am the true aop bean
//around After
//after Test
}
}
複製代碼
根據上面的啓動例子,發如今本身寫的核心業務方法 testAop()
上,明明只是簡單打印了 I am the true aop bean
,但執行結果輸出了其它內容,說明這個類被加強了,在不修改核心業務方法上,咱們對它進行了擴展。證實了 AOP 可使輔助功能獨立於核心業務以外,方便了程序的擴展和解耦。編程
使用起來很方便,接下來一塊兒來看看 Spring
是如何實現 AOP
功能的吧~緩存
以前在介紹自定義標籤時,提到了 AOP
的實現也藉助了自定義註解,根據自定義標籤的思想:每一個自定義的標籤,都有對應的解析器,而後藉助強大的開發工具 IDEA
定位功能,找到解析器註冊的地方:安全
ctrl
,定位標籤對應的 xsd
文件META-INF
目錄下找到了 spring.handlers
文件AopNamespaceHandler
public class AopNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
// In 2.0 XSD as well as in 2.1 XSD.
registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
// 註釋 8.1 自定義註解,註冊解析器,元素名是 aspectj-autoproxy
registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
// Only in 2.0 XSD: moved to context namespace as of 2.1
registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
}
}
複製代碼
處理器繼承自 NamespaceHandlerSupport
,在加載過程當中,將會執行 init
初始化方法,在這裏,會註冊 aspectj-autoproxy
類型的解析器 AspectJAutoProxyBeanDefinitionParser
如何註冊自定義解析器以前也瞭解過了,因此接下來直接來看看,遇到 aspectj-autoproxy
類型的 bean
,程序是如何解析的。
來看下解析時,它的入口方法以下:
public BeanDefinition parse(Element element, ParserContext parserContext) {
// aop 註解的解析入口,註冊 AnnotationAwareAspectJAutoProxyCreator
AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
// 對註解中子類的處理
extendBeanDefinition(element, parserContext);
return null;
}
複製代碼
入口方法一如既往的簡潔,交代了要作的事情,而後具體複雜邏輯再交給工具類或者子類繼續實現,因此接下來要看的是如何註冊 AnnotationAwareAspectJAutoProxyCreator
。
public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary( ParserContext parserContext, Element sourceElement) {
// 經過工具類,註冊或升級 AspectJAnnotationAutoProxyCreator
BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
parserContext.getRegistry(), parserContext.extractSource(sourceElement));
// 處理 proxy-target-class 以及 expose-proxy 屬性
useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
// 註冊組件並通知,讓監聽器進行處理
registerComponentIfNecessary(beanDefinition, parserContext);
}
複製代碼
能夠看到這個方法內部有三個處理邏輯,因此咱們來一個一個去分析瞭解:
對於 AOP
的實現,基本上都是靠 AnnotationAwareAspectJAutoProxyCreator
去完成,它能夠根據 @Point
註解定義的切點來自動代理相匹配的 bean
。
因爲 Spring
替咱們作了不少工做,因此開發 AOP
業務時才能夠這麼簡單,連配置也簡化了許多,因此來看下 Spring
是如何使用自定義配置來幫助咱們自動註冊 AnnotationAwareAspectJAutoProxyCreator
。
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary( BeanDefinitionRegistry registry, @Nullable Object source) {
// 實際註冊的 bean 類型是 AnnotationAwareAspectJAutoProxyCreator
return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}
private static BeanDefinition registerOrEscalateApcAsRequired( Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
// 若是在 registry 已經存在自動代理建立器,而且傳入的代理器類型與註冊的不一致,根據優先級判斷是否須要修改
BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
// 根據優先級選擇使用哪個
int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
int requiredPriority = findPriorityForClass(cls);
if (currentPriority < requiredPriority) {
// 傳進來的參數優先級更大,修改註冊的 beanName,使用傳進來的代理建立器
apcDefinition.setBeanClassName(cls.getName());
}
}
// 由於已經存在代理器,不須要以後的默認設置,直接返回
return null;
}
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
beanDefinition.setSource(source);
// 默認的是最小優先級
beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
// 自動代理建立器的註冊名字永遠是 org.springframework.aop.config.internalAutoProxyCreator
registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
return beanDefinition;
}
複製代碼
這個步驟中,實現了自動註冊 AnnotationAwareAspectJAutoProxyCreator
類,同時能看到涉及到優先級的概念和註冊名一直都是 AUTO_PROXY_CREATOR_BEAN_NAME
。
private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, @Nullable Element sourceElement) {
if (sourceElement != null) {
// 這方法做用挺簡單的,就是解析下面兩個屬性,若是是 true,將它們加入代理註冊器的屬性列表中
// definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE)
boolean proxyTargetClass = Boolean.parseBoolean(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE));
if (proxyTargetClass) {
// 處理 proxy-target-class 屬性
// 與代碼生成方式有關,在以後步驟中決定使用 jdk 動態代理 或 cglib
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
boolean exposeProxy = Boolean.parseBoolean(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE));
if (exposeProxy) {
// 處理 expose-proxy 屬性
// 擴展加強,有時候目標對象內部的自我調用沒法實施切面中的加強,經過這個屬性能夠同時對兩個方法進行加強
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}
複製代碼
關於 AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
方法,它是一個屬性設置的過程,若是解析到的屬性爲 true
,將它們加入代理註冊器的屬性列表中,這裏不細說下去。
將這兩個屬性分開熟悉:
Spring AOP
部分使用 JDK
動態代理 (Proxy + InvocationHandler),或者 CGLIB
(Code Generation LIB)來爲目標對象建立代理。書中提到,推薦使用的是 JDK
動態代理。
若是被代理的目標對象實現了至少一個接口,則會使用 JDK
動態代理。全部該目標類型實現的接口都將被代理。
若該目標對象**沒有實現任何接口,則建立一個 CGLIB
代理。**若是但願代理目標對象的全部方法,而不僅是實現自接口的方法,能夠經過該屬性 proxy-target-class
開啓強制使用 CGLIB
代理。
可是強制開啓 CGLIB
會有如下兩個問題:
若是考慮好上面兩個方面,那就能夠經過如下兩個地方來強制開啓 CGLIB
代理:
<!-- one -->
<aop:config proxy-target-class="true">...</aop:config>
<!-- two -->
<aop:aspectj-autoproxy proxy-target-class="true"/>
複製代碼
其中有關 CGLIB
代理,這位老哥講得很透徹,建議你們能夠去了解一下~ Cglib及其基本使用
有時候目標對象內部的自我調用將沒法實施切面中的加強。
例如兩個方法都加上了事務註解 @Transactional
可是事務類型不同:
public interface TestService {
void a();
void b();
}
public class TestServiceImpl implements TestService {
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void a() {
this.b();
}
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void b() {
System.out.println("Hello world");
}
}
複製代碼
此處的 this
指向了目標對象, this.b()
方法將不會執行 b
事務的切面,即不會執行事務加強。
爲了解決這個問題,使 a()
和 b()
方法同時加強,能夠經過 expose-proxy
來實現:
<!-- one -->
<aop:config expose-proxy="true">...</aop:config>
<!-- two -->
<aop:aspectj-autoproxy expose-proxy="true"/>
複製代碼
emmmm,這個方法內部邏輯如名字同樣清晰,因此不細說啦。
前面主要圍繞着自動代理器 AnnotationAwareAspectJAutoProxyCreator
的註冊流程來說解,接下來看自動代理器作了什麼來完成 AOP
的操做。
下面是 AnnotationAwareAspectJAutoProxyCreator
的繼承體系:
在圖片右上角,發現它實現了 BeanPostProcessor
接口,以前文章提到過,它是一個後處理器,能夠在 bean
實例化先後進行擴展。查看了實現了該接口的兩個方法,postProcessBeforeInitialization
沒有作處理,直接返回該對象。
實際進行處理的是 postProcessAfterInitialization
方法,在 bean
實例化以後的處理,在這一步中進行裏代理加強,因此來看下這個方法:
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
// 組裝 key
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
// 若是適合被代理,則須要封裝指定的 bean
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
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;
}
// 若是存在加強方法則建立代理
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;
}
複製代碼
來提取一下核心流程:
@Before
、@After
之類的,就是加強方法,AOP
處理時,要先找出這些加強方法。bean
已經不徹底是原來的類型了,會變成代理後的類型。入口方法在這裏:
protected Object[] getAdvicesAndAdvisorsForBean(
Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
// 尋找符合的切面
List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
if (advisors.isEmpty()) {
return DO_NOT_PROXY;
}
return advisors.toArray();
}
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
// 從 beanFactory 中獲取聲明爲 AspectJ 註解的類,對並這些類進行加強器的提取
// 委派給子類實現 org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator.extendAdvisors
List<Advisor> candidateAdvisors = findCandidateAdvisors();
// 尋找匹配的加強器
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
複製代碼
對於指定 bean
的加強方法的獲取包含這兩個步驟,獲取全部的加強以及尋找全部加強中適用於 bean
的加強並應用。對應於 findCandidateAdvisors
和 findAdvisorsThatCanApply
這兩個方法。若是沒找到對應的加強器,那就返回 DO_NOT_PROXY
,表示不須要進行加強。
因爲邏輯太多,因此接下來貼的代碼不會太多,主要來了解它的大體流程,有須要的能夠跟着源碼工程的註釋跟蹤完整的流程~:
protected List<Advisor> findCandidateAdvisors() {
List<Advisor> advisors = super.findCandidateAdvisors();
if (this.aspectJAdvisorsBuilder != null) {
// 註釋 8.3 實際調用的是 org.springframework.aop.aspectj.annotation.BeanFactoryAspectJAdvisorsBuilder.buildAspectJAdvisors
advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
}
return advisors;
}
複製代碼
實際來看,關鍵是這個方法 this.aspectJAdvisorsBuilder.buildAspectJAdvisors()
這個方法看起來簡單,可是實際處理的邏輯不少,代碼深度也不少,因此爲了不太多代碼,我羅列了主要流程,和關鍵的處理方法作了什麼
主要流程以下:
能夠查詢代碼中的註釋,從 [註釋 8.3] 到 [註釋 8.8 根據切點信息生成加強器] 都是這個方法的處理邏輯
※※在這個流程的最後一步中,會將識別到的切點信息(PointCut)和加強方法(Advice)進行封裝,具體是由 Advisor
的實現類 InstantiationModelAwarePointcutAdvisorImpl
進行統一封裝。
public InstantiationModelAwarePointcutAdvisorImpl(AspectJExpressionPointcut declaredPointcut, Method aspectJAdviceMethod, AspectJAdvisorFactory aspectJAdvisorFactory, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
// 簡單賦值
this.declaredPointcut = declaredPointcut;
...
if (aspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
Pointcut preInstantiationPointcut = Pointcuts.union(aspectInstanceFactory.getAspectMetadata().getPerClausePointcut(), this.declaredPointcut);
this.pointcut = new PerTargetInstantiationModelPointcut(
this.declaredPointcut, preInstantiationPointcut, aspectInstanceFactory);
this.lazy = true;
}
else {
// A singleton aspect.
this.pointcut = this.declaredPointcut;
this.lazy = false;
// 初始化加強器
this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);
}
}
複製代碼
封裝體前半部分邏輯只是簡單賦值。關鍵是這個方法 instantiateAdvice(this.declaredPointcut)
,在這一步中,對不一樣的加強(Before/After/Around)實現的邏輯是不同的。在 ReflectiveAspectJAdvisorFactory#getAdvice
方法中區別實現了根據不一樣的註解類型封裝不一樣的加強器。
public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
...
// 註釋 8.7 根據不一樣的註解類型封裝不一樣的加強器
switch (aspectJAnnotation.getAnnotationType()) {
case AtPointcut:
}
return null;
case AtAround:
springAdvice = new AspectJAroundAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
case AtBefore:
springAdvice = new AspectJMethodBeforeAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
case AtAfter:
springAdvice = new AspectJAfterAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
case AtAfterReturning:
springAdvice = new AspectJAfterReturningAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
if (StringUtils.hasText(afterReturningAnnotation.returning())) {
springAdvice.setReturningName(afterReturningAnnotation.returning());
}
break;
case AtAfterThrowing:
springAdvice = new AspectJAfterThrowingAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
}
break;
default:
}
}
複製代碼
最後切點方法經過解析和封裝成 Advisor
,提取到的結果加入到緩存中。細心的你可能會發現**除了普通的加強器外,還有另外兩種加強器:同步實例化加強器和引介加強器。**因爲用的比較少,因此我看到源碼中這兩個分支處理沒有深刻去學習,感興趣的同窗請繼續深刻學習這兩種加強器~
在前面流程中,已經完成了全部加強器的解析,可是對於前面解析到的加強器,並不必定都適用於當前處理的 bean
,因此還須要經過一個方法來挑選出合適的加強器。
protected List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {
ProxyCreationContext.setCurrentProxiedBeanName(beanName);
try {
// 在這一步中進行過濾加強器
return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
}
finally {
ProxyCreationContext.setCurrentProxiedBeanName(null);
}
}
複製代碼
能夠看到,具體實現過濾操做的是工具類方法 AopUtils.findAdvisorsThatCanApply
:
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
if (candidateAdvisors.isEmpty()) {
return candidateAdvisors;
}
List<Advisor> eligibleAdvisors = new ArrayList<>();
// 遍歷全部加強器
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;
}
// 處理普通加強器類型
if (canApply(candidate, clazz, hasIntroductions)) {
eligibleAdvisors.add(candidate);
}
}
return eligibleAdvisors;
}
複製代碼
具體判斷邏輯在 canApply()
方法中,若是判斷符合條件的,加入到 eligibleAdvisors
中,最後返回對於這個 bean
適合的加強器列表。
經過前面的流程,獲取到了全部對應 bean
的加強器後,能夠開始代理的建立。
protected Object createProxy(Class<?> beanClass, @Nullable String beanName, @Nullable Object[] specificInterceptors, TargetSource targetSource) {
ProxyFactory proxyFactory = new ProxyFactory();
// 拷貝,獲取當前類中的相關屬性
proxyFactory.copyFrom(this);
// 決定對於給定 bean 是否應該使用 targetClass 而不是他的接口代理
if (!proxyFactory.isProxyTargetClass()) {
// 檢查 proxyTargetClass 設置以及 preserveTargetClass 屬性
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
// 添加代理接口
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
// 這一步中,主要將攔截器封裝爲加強器
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
proxyFactory.addAdvisors(advisors);
proxyFactory.setTargetSource(targetSource);
// 定製代理
customizeProxyFactory(proxyFactory);
// 用來控制代理工廠被配置以後,是否含容許修改通知
// 缺省值爲 false,不容許修改代理的配置
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
// 生成代理,委託給了 ProxyFactory 去處理。
return proxyFactory.getProxy(getProxyClassLoader());
}
複製代碼
對於代理類的建立和處理, Spring
委託給了 ProxyFactory
去處理,在上面貼出的函數主要是對 ProxyFactory
的初始化操做,進而對真正的建立代理作準備,主要流程以下:
比較關鍵的是第三個步驟和第六個步驟,其中在第三個步驟中,進行的是攔截器包裝,詳細代碼流程請查 [註釋 8.9 爲給定的bean建立AOP代理] 和 [註釋 8.10 包裝攔截器,封裝成 Advisor]。
接着,完成了全部加強器的封裝過程,到了解析的最後一步,進行代理的建立和獲取。
public Object getProxy(@Nullable ClassLoader classLoader) {
return createAopProxy().getProxy(classLoader);
}
複製代碼
定位到建立代理的代碼:
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() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
複製代碼
從上面代碼中能看出,根據了幾個關鍵屬性,判斷建立的是哪一種類型的 AopProxy
,一種是 JDK 動態代理,另外一種是 CGLIB 動態代理。
前面提到過的 proxy-target-class
屬性和 targetClass
屬性,在這裏判斷了應該建立哪個代理。
觀察圖片以及前面分析,能夠知道有兩種代理方式:[JDK 動態代理] 和 [CGLIB 動態代理]
同時先說下動態代理的含義:抽象類在編譯期間是未肯定具體實現子類,在運行時才生成最終對象。
JDK
代理是默認推薦的代理方式,使用的是 Proxy
+ InvocationHandler
。
能夠經過如下方式實現:定義一個接口、實現類,和一個處理器繼承於 InvocationHandler
,而後重載處理器中的 invoke
方法,對代理對象進行加強。
JdkDynamicAopProxy.java
public Object getProxy(@Nullable ClassLoader classLoader) {
// 註釋 8.11 JDK 動態代理
if (logger.isTraceEnabled()) {
logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
}
Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
複製代碼
獲取代理的核心步驟在 Proxy.newProxyInstance(classLoader, proxiedInterfaces, this)
,第三個參數是 JdkDynamicAopProxy
自己,並且它實現了 InvocationHandler
接口,重載了 invoke
方法。
org.springframework.aop.framework.JdkDynamicAopProxy#invoke
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 註釋 8.12 jdk 動態代理重載的 invoke 方法
MethodInvocation invocation;
Object oldProxy = null;
boolean setProxyContext = false;
TargetSource targetSource = this.advised.targetSource;
Object target = null;
try {
Object retVal;
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
// Get the interception chain for this method.
// 獲取此方法的攔截鏈
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
// 檢查咱們是否有任何切面邏輯。若是咱們不這樣作,咱們能夠回退直接反射調用目標,並避免建立 MethodInvocation。
if (chain.isEmpty()) {
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
// 將攔截器封裝在 ReflectiveMethodInvocation,便於使用 proceed 執行攔截器
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// 執行攔截器鏈
retVal = invocation.proceed();
}
...
return retVal;
}
finally {
if (target != null && !targetSource.isStatic()) {
targetSource.releaseTarget(target);
}
if (setProxyContext) {
AopContext.setCurrentProxy(oldProxy);
}
}
}
複製代碼
建立 JDK
代理過程當中,主要的工做時建立了一個攔截器鏈,並使用 ReflectiveMethodInvocation
類進行封裝,封裝以後,逐一調用它的 proceed
方法, 用來實如今目標方法的前置加強和後置加強。
org.springframework.aop.framework.ReflectiveMethodInvocation#proceed
public Object proceed() throws Throwable {
// 執行完全部加強器後執行切點方法
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
// 獲取下一個要執行的攔截器
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// 動態匹配
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
else {
// 匹配失敗,跳過攔截器,直接返回
return proceed();
}
}
else {
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
複製代碼
具體代碼和註釋請定位到該方法查看。關於 JDK
動態代理,深刻學習的話也能夠單獨拎出來,因此推薦看這篇資料 小豹子帶你看源碼:JDK 動態代理,進行了和學習
CGLIB[Code Generation LIB]
是一個強大的高性能的代碼生成包。它普遍應用於許多 AOP
框架。
再次推薦參考資料一,這位老哥將 CGLIB
代理, 詳細介紹了 CGLIB
在什麼場景使用,以及被它加強後代碼處理順序,Cglib及其基本使用。
但願看完這篇文章,能過了解到 CGLIB
代碼生成包具體是如何對類進行加強。
經過前面一系列步驟,解析標籤、屬性、加強方法,到最後獲取 CGLIB
代理,經過代理建立 bean
來看下最後被代理的 bean
內部:
從圖中能夠看到,最終建立的是被修飾後的 bean
,內部很明顯是 CGGLIB
代理生成的代碼,咱們在不修改業務代碼的狀況下,實現了方法加強。
既然有動態代理,那麼也會有靜態代理。
使用靜態 AOP
的時候,須要用到 LTW
(Load-Time Weaving 加載時織入),指的是在虛擬機載入字節碼文件時動態織入 AspectJ
切面。
AOP
的靜態代理主要是在虛擬機啓動時經過改變目標對象字節碼的方式來完成對目標對象的加強,它與動態代理相比具備更高的效率,由於在動態代理調用的過程當中,還須要一個動態建立代理類並代理目標對象的步驟,而靜態代理則是在啓動時便完成了字節碼增減,當系統再次調用目標類時,與調動正常的類並沒有區別,因此在效率上會相對高些。
關於靜態 AOP
的使用和學習,能夠參考這篇文章:從代理機制到Spring AOP
動態 AOP
使用起來很簡單,對於如何實現,總結起來就兩點:
AOP
標籤AOP
代理但在 Spring
底層實現邏輯倒是複雜到不行,從 Spring
框架中能夠看到這是良好的代碼設計思路,頂層入口儘可能簡單,使用者很容易就能掌握該功能,複雜實現邏輯都被隱藏了。
寫這一篇 AOP
學習總結,花了將近一週,先看了一遍書籍, 下班後花了一晚,將大體流程理了一遍,次日晚上走讀代碼,發現有些地方還存在疑惑,例如 JDK
和 cglib
動態代理是怎麼回事,翻閱查詢資料,弄懂後又過了一天。
將代碼註釋加上,分析動態代理每個步驟作的事情,結合以前學的後處理器 BeanPostProcessor
知識和自定義標籤解析知識一塊兒又梳理一遍。零零散散,終於整理完成。
在靜態 AOP
知識點,按照個人理解,越往系統底層深刻,它的執行效率越高,因此減小了動態建立代理類和代理目標對象的步驟,靜態代理的速度會獲得提高。同時因爲接近底層後,代碼編寫的複雜度一樣會增長,因此我在權衡高頻率使用場景(動態代理),本次學習沒有詳細去了解,留下這個坑,之後有機會再填吧~
因爲我的技術有限,若是有理解不到位或者錯誤的地方,請留下評論,我會根據朋友們的建議進行修正
Gitee 地址 https://gitee.com/vip-augus/spring-analysis-note.git
Github 地址 https://github.com/Vip-Augus/spring-analysis-note
Spring 源碼深度解析 / 郝佳編著. -- 北京 : 人民郵電出版社