AOP編程中的基本概念
- 連接點: 程序執行某個特定位置,如類的初始化前後,某個方法調用前後,方法報出異常後,一個類或者一段代碼擁有一些邊界性質的特定點。Spring僅支持方法的連接點
- 切點: 每個程序都擁有多個連接點。AOP通過切點定位連接點,一個切點可以對應多個連接點。
- 增強: 增強是一段代碼。是織入到目標類的連接點上的一段代碼。
- 目標對象: 增強邏輯的織入目標類
- 引介: 引介是特殊的增強,他爲類添加一些屬性和方法
- 織入: 將增強添加到具體目標對象的連接點的過程
- 切面: 切面是由切點和增強組成
Spring中的概念
- advisor: 封裝了SpringAop的切點和通知(類似爲切面) 後面我們把advisor結尾的類稱爲切面類
- advice: 通知,也就是增強。後面我們把spring中以advice結尾的類稱爲增強類
- creator: 相當於織入(只不過spring是通過動態代理織入增強的) 爲了方便稱呼我們後面把creator結尾的類成爲織入類
核心類
advisorCreator自動織入類:
- BeanNameAutoProxyCreator: 根據指定的名稱創建代理對象,通過設置advisor,可以對指定的beanName進行代理。支持模糊
- AbstractAdvisorAutoProxyCreator: 掃描所有的advisor的實現類。動態匹配每一個類,判斷是否可以被代理。默認的實現類是DefaultAdvisorAutoProxyCreator
- AspectJAwareAdvisorAutoProxyCreator: AspectJ的實現方式,spring最常用的實現方式。其子類(AnnotationAwareAspectJAutoProxyCreator)是默認的支持方式,會掃描@Aspect註解的類,生產對應的切面。
advisor核心類(都集成Advisor接口)
- StaticMethodMatcherPointcut: 靜態方法切面,抽象類。定義了一個classFilter,通過重寫getClassFilter()方法來指定切面規則。另外實現了StaticMethodMatcher接口,通過重寫matches來指定方法匹配規則
- StaticMethodMatcherPointcutAdvisor: 靜態方法匹配切面,擴展了排序方法。
- NameMatchMethodPointcut: 名稱匹配切面,通過指定方法集合變量mappedNames,模糊匹配。
- NameMatchMethodPointcutAdvisor: 方法名稱切面,內部封裝了NameMatchMethodPointcut,通過設置方法名稱模糊匹配規則和通知來實現切面功能
- DefaultPointcutAdvisor: 默認的切面類
- RegexpMethodPointcutAdvisor: 用於支持正則表達式的切面類,可支持函多個正則表達式
- NameMatchMethodPointcutAdvisor: 方法名稱切面
- InstantiationModelAwarePointcutAdvisorImpl: 自動封裝的切面實現。在自動織入的時候會默認會把AspectJ註解默認包裝改該類。比較常用的切面類
advice增強核心類
- AspectJMethodBeforeAdvice: 前置增強(@Before標註的方法會被解析成該通知)
- AspectJAfterReturningAdvice: 後置增強(@AfterReturning 標註的方法會被解析成該通知)
- AspectJAroundAdvice: 環繞增強(@Around標註的方法會被解析成該通知)
- AspectJAfterAdvice: 返回增強(@After 標註的方法會被解析成該通知)忽略異常
SpringAop基於AspectJ註解方式
目標對象
@Component
public class TargetA {
public void sayHello(){
System.out.println("sayHello");
}
}
增強
@Component
@Aspect
public class ProxyA {
@Pointcut("execution(* org.springframework.apo..*.*(..))")
public void point(){
}
@Before("point()")
public void before(){
System.out.println("begin say..");
}
@After("point()")
public void after(){
System.out.println("after say ...");
}
}
啓動類
@ComponentScan("org.springframework.apo")
@EnableAspectJAutoProxy
public class App {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(App.class);
TargetA bean = applicationContext.getBean(TargetA.class);
bean.sayHello();
}
}
可以看到在spring中我們只需要定義增強 其他的不需要我們關心。 spring的creator會幫我織入到目標類。上面的流程最關鍵的兩點 其一是AspectJ連接點表達式比較關鍵 其二是在啓動類上加@EnableAspectAutoProxy註解(springboot引入了spring-boot-starter-aop就不需要再手動Enabled)。下面我們從註解入手看一下 spring織入的整個流程。
Spring AOP源碼分析
啓動註解EnableAspectJAutoProxy
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
boolean proxyTargetClass() default false;
boolean exposeProxy() default false;
}
我們看到上述代碼中使用@Import標籤向spring容器中導入了一個AspectJAutoProxyRegistrar。繼續跟進AspectJAutoProxyRegistrar
@Override
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//向IOC容器中注入AnnotationAwareAspectJAutoProxyCreator
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
AnnotationAttributes enableAspectJAutoProxy =
AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
if (enableAspectJAutoProxy != null) {
if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}
上面代碼其實主要是向容器中加了AnnotationAwareAspectJAutoProxyCreator 這個織入類。並根據我們的配置(註解中有兩個屬性 後續會講解)。到這這個註解的使命就完成了。那爲什麼像springIOC容器注入了一個AnnotationAwareAspectJAutoProxyCreator 就能實現織入的能力呢?下面我們從這個類入手瞭解一下spring的整個織入流程。
類的繼承關係
在看spring源碼的時候我們可以發現 spring框架中大量運用了模板方法模式。所以只要出現Abstract開頭的類 我們就應該好好的去看一下。分析這一部分的源碼我們就從第第一個抽象類入手AbstractAutoProxyCreator。
看一下初始化過程
父類初始化AbstractAutoProxyCreator
public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {
@Override
public void setBeanFactory(BeanFactory beanFactory) {
super.setBeanFactory(beanFactory);
if (!(beanFactory instanceof ConfigurableListableBeanFactory)) {
throw new IllegalArgumentException(
"AdvisorAutoProxyCreator requires a ConfigurableListableBeanFactory: " + beanFactory);
}
initBeanFactory((ConfigurableListableBeanFactory) beanFactory);
}
protected void initBeanFactory(ConfigurableListableBeanFactory beanFactory) {
this.advisorRetrievalHelper = new BeanFactoryAdvisorRetrievalHelperAdapter(beanFactory);
}
}
子類初始化
public class AnnotationAwareAspectJAutoProxyCreator extends AspectJAwareAdvisorAutoProxyCreator {
//這個方法是在bean初始化之前調用 在spring啓動期間會把BeanFactory注入進來 是一個初始化方法。
@Override
protected void initBeanFactory(ConfigurableListableBeanFactory beanFactory) {
super.initBeanFactory(beanFactory);
if (this.aspectJAdvisorFactory == null) {
//ReflectiveAspectJAdvisorFactory是一個支持AspectJ註解的類。可以解析AspectJ註解包裝成Advisor。簡單的理解是對AspectJ的語法的支持。
this.aspectJAdvisorFactory = new ReflectiveAspectJAdvisorFactory(beanFactory);
}
//這裏是對ReflectiveAspectJAdvisorFactory的包裝 內部真正解析AspectJ註解的是ReflectiveAspectJAdvisorFactory類。
this.aspectJAdvisorsBuilder =
new BeanFactoryAspectJAdvisorsBuilderAdapter(beanFactory, this.aspectJAdvisorFactory);
}
}
可以發現它實現了SmartInstantiationAwareBeanPostProcessor 。這個接口是springIOC容器的擴展接口其最頂層的父接口是BeanPostProcessor。在springIOC容器啓動和 bean的創建 初始化前後調用。如果對這一塊比較模糊可以去看一下BeanFactoryPostProcessor和BeanPostProcessor這兩個父接口衍生出來的幾個接口的註釋。裏面對於調用點的註釋還挺清楚。我們這裏就不做過多解釋了。
下面我們把這個類中我們需要用的方法複製出來
//這個方法是InstantiationAwareBeanPostProcessor接口中的方法 是在bean實例化之前被調用
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
Object cacheKey = getCacheKey(beanClass, beanName);
if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
//advisedBeans用於存儲不能被代理的bean 如果當前bean出現在集合中立馬返回
if (this.advisedBeans.containsKey(cacheKey)) {
return null;
}
//如果當前類繼承 Advice,Pointcut,Advisor,AopInfrastructureBean等這些類也不能被
//代理
if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return null;
}
}
//得到目標源 這是一次嘗試 因爲如果我們設置customTargetSourceCreators 這個屬性 這個時候就
//會獲取我們設置的TargetSource對象 如果沒設置就跳過了這個方法。 下面的代理過程我們暫且先不管
TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
if (targetSource != null) {
if (StringUtils.hasLength(beanName)) {
//如果找到確實設置了TargetSource 就加入到緩存集合
this.targetSourcedBeans.add(beanName);
}
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
return null;
}
下面我們看一下shouldSkip這個方法 子類的實現
@Override
protected boolean shouldSkip(Class<?> beanClass, String beanName) {
// 從bean工廠中查找所有的advisor 如果當前bean的名字在當前advisor的列表中 則跳過不需要代理
//暫時不展開
List<Advisor> candidateAdvisors = findCandidateAdvisors();
for (Advisor advisor : candidateAdvisors) {
if (advisor instanceof AspectJPointcutAdvisor &&
((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) {
return true;
}
}
return super.shouldSkip(beanClass, beanName);
}
上面我們分析了postProcessBeforeInstantiation方法 如果設置自定義的TargetSourceCreator 就只做了一件事件 找到所有的增強並封裝成切面
//這個方法BeanPostProcessor中的方法 在bean初始化(初始化方法)之後調用。
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != 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;
}
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
//前面都是判斷 直接來到這一行代碼 我們暫時不展開 大概的意思是從我們所有的切面中篩選符合當前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 方法和createProxy方法 其實整個過程已經結束了。
最後我們着重分析一下advisor的創建過程和代理類的創建過程
- advisor創建 getAdvicesAndAdvisorsForBean這個方法是一個抽象方法 是並且是AbstractAutoProxyCreator唯一的一個抽象方法 我們直接進入子類(AbstractAdvisorAutoProxyCreator)的實現
protected Object[] getAdvicesAndAdvisorsForBean(
Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
//這裏繼續調用查找Advisor的方法
List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
if (advisors.isEmpty()) {
return DO_NOT_PROXY;
}
return advisors.toArray();
}
繼續跟進findEligibleAdvisors 當前所在AbstractAdvisorAutoProxyCreator類
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
//這個方法被子類重寫就是查找被AspectJ語法修飾的切面
List<Advisor> candidateAdvisors = findCandidateAdvisors();
//找出所有切面應用在當前實例上面的
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
//擴展方法 子類實現的時候像 集合中加入了ExposeInvocationInterceptor
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
進入子類的findCandidateAdvisors方法
@Override
protected List<Advisor> findCandidateAdvisors() {
// 先調用父類的方法獲取容器中所有實現Advisor的子類的實例
List<Advisor> advisors = super.findCandidateAdvisors();
if (this.aspectJAdvisorsBuilder != null) {
//然後加入所有AspectJ註解修飾的切面
advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
}
return advisors;
}
繼續展開this.aspectJAdvisorsBuilder.buildAspectJAdvisors() 這個方法是查找的核心 代碼有點多
public List<Advisor> buildAspectJAdvisors() {
//aspectBeanNames這個是緩存的所有 有AspectJ註解的實例 第一次是null
List<String> aspectNames = this.aspectBeanNames;
if (aspectNames == null) {
synchronized (this) {
aspectNames = this.aspectBeanNames;
if (aspectNames == null) {
List<Advisor> advisors = new ArrayList<>();
aspectNames = new ArrayList<>();
//這裏就是找到所有的bean 因爲傳的類型是Object 所以所有的實例的名字都會查找出來
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Object.class, true, false);
for (String beanName : beanNames) {
if (!isEligibleBean(beanName)) {
continue;
}
Class<?> beanType = this.beanFactory.getType(beanName);
if (beanType == null) {
continue;
}
//這裏判斷當前類型是否有AspectJ註解
if (this.advisorFactory.isAspect(beanType)) {
aspectNames.add(beanName);
AspectMetadata amd = new AspectMetadata(beanType, beanName);
if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
MetadataAwareAspectInstanceFactory factory =
new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
//這裏找到當前實例的Advisor 這裏不作展開了 就是通過反射拿到對應的方法封裝成InstantiationModelAwarePointcutAdvisorImpl的過程
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
//這裏緩存分爲單例和原型的方式緩存
if (this.beanFactory.isSingleton(beanName)) {
this.advisorsCache.put(beanName, classAdvisors);
}
else {
this.aspectFactoryCache.put(beanName, factory);
}
//然後將本次解析結果加入集合
advisors.addAll(classAdvisors);
}
else {
// Per target or per this.
if (this.beanFactory.isSingleton(beanName)) {
throw new IllegalArgumentException("Bean with name '" + beanName +
"' is a singleton, but aspect instantiation model is not singleton");
}
MetadataAwareAspectInstanceFactory factory =
new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
this.aspectFactoryCache.put(beanName, factory);
advisors.addAll(this.advisorFactory.getAdvisors(factory));
}
}
}
this.aspectBeanNames = aspectNames;
return advisors;
}
}
}
if (aspectNames.isEmpty()) {
return Collections.emptyList();
}
List<Advisor> advisors = new ArrayList<>();
for (String aspectName : aspectNames) {
List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
if (cachedAdvisors != null) {
advisors.addAll(cachedAdvisors);
}
else {
MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
advisors.addAll(this.advisorFactory.getAdvisors(factory));
}
}
return advisors;
}
到此我們的解析Advisor流程就結束了
下面我們在看一下代理的創建
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區別於ProxyFactoryBean 關於ProxyFactoryBean後面會介紹
ProxyFactory proxyFactory = new ProxyFactory();
//設置一些ProxyFactory的屬性例如proxyTargetClass,exposeProxy 因爲 ProxyFactory和AbstractAutoProxyCreator都繼承自ProxyConfig
//下面都是對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);
//這個是擴展方法 留個子類實現可以對ProxyFactory自定義。
//例如我們可以自定義一種織入策略 實現AbstractAutoProxyCreator 然後重寫該方法 去自定義ProxyFactory
customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
//這裏是創建代理的邏輯
return proxyFactory.getProxy(getProxyClassLoader());
}
下面進入getProxy中 下面這個方法就是根據代理類是否有接口去創建不同的AopProxy
如果有接口通過JDK動態代理的創建模式 如果沒有接口通過CGLIB的創建方式。其實可以看到 springAop是側重於用JDK動態代理的 因爲不管我們配置的ProxyTargetClass這個參數是true還是false 只要當前實例是繼承接口的都會有JDK動態代理。
@Override
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);
}
}
好了到這裏 我們把基本的流程敘述了一遍。這一塊的代碼太多 不可能很詳細 讀者可以下去自行翻閱。