Spring AOP使用簡介
Spring的兩大核心功能是IOC和AOP。當我們使用Spring的AOP功能時是很方便的。只需要進行下面的配置即可。
@Component
@Aspect
public class MyAspect {
//PointCut匹配的方法必須是Spring中bean的方法
//Pointcut可以有下列方式來定義或者通過&& || 和!的方式進行組合.
//下面定義的這些切入點就可以通過&& ||組合
private static Logger logger = LoggerFactory.getLogger(MyAspect.class);
//*:代表方法的返回值可以是任何型別
//整個表示式匹配controller包下面任何的的echo方法,方法入參樂意是任意
@Pointcut("execution(* com.csx.demo.spring.boot.controller.*.echo(..))")
public void pointCut1(){}
@Before("pointCut1()")
public void befor(){
logger.info("前置通知vvvv...");
logger.info("我要做些事情...");
}
}
然後再開啟註解
//自動選擇合適的AOP代理
//傳統xml這樣配置:<aop:aspectj-autoproxy/>
//exposeProxy = true屬性設定成true,意思是將動態生成的代理類expose到AopContext的ThreadLocal執行緒
//可以通過AopContext.currentProxy();獲取到生成的動態代理類。
//proxyTargetClass屬性設定動態代理使用JDK動態代理還是使用CGlib代理,設定成true是使用CGlib代理,false的話是使用JDK動態代理
//注意:如果使用Spring Boot的話,下面的配置可以不需要。AopAutoConfiguration這個自動配置類中已經自動開啟了AOP
//預設使用CGLIB動態代理,Spring Boot配置的優先順序高於下面的配置
@Configuration
@EnableAspectJAutoProxy(exposeProxy = true,proxyTargetClass = false)
public class AopConfig {
}
通上面的配置,當我們呼叫controller包下面的任何類的echo方法時就會觸發前置通知。其實這個說法不是很準確。因為我們呼叫的類已經不是我們自己寫的類了。而是Spring框架通過動態代理生成的類。
稍微瞭解一點Spring AOP的同學都會知道Spring的AOP是通過動態代理實現的。那Spring是怎麼生成動態代理類,並將Advice織入代理類的呢?整個流程是怎樣的呢?下面就分析下Spring生成動態代理類的過程。
需要說明下的是,本部落格旨在梳理整個AOP動態代理的過程,細節方面需要大家自己去看。
@EnableAspectJAutoProxy幹了些啥
如果讓你從頭開始研究下AOP的原理,你是不是一頭霧水,根本不知道從何入手。但其實看Spring
的程式碼有個小技巧:如果你要研究一個功能,可以從開啟這個功能的Enable註解開始看。Spring的很多功能都是通過Enable註解開啟的,所以這些註解肯定和這些功能相關。
那麼這邊我們可以從@EnableAspectJAutoProxy這個註解開始著手,看下這個註解做了些什麼操作。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
//設定為true的話就一直使用cglib動態代理
//設定為false的話,對於介面使用jdk動態代理,對於類使用cglib代理
boolean proxyTargetClass() default false;
boolean exposeProxy() default false;
}
看到上面的@Impoer註解,我們很自然就會想到去看AspectJAutoProxyRegistrar這個類。
//AspectJAutoProxyRegistrar原始碼
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
/**
* 主要作用也就是註冊AnnotationAwareAspectJAutoProxyCreator
*/
@Override
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//註冊AnnotationAwareAspectJAutoProxyCreator的BeanDefinition
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
AnnotationAttributes enableAspectJAutoProxy =
AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
if (enableAspectJAutoProxy != null) {
//給上面註冊的BeanDefinition中新增兩個屬相proxyTargetClass和exposeProxy
if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}
}
我們可以看到上面的類中也沒幹什麼特別的事情,就註冊了一個BeanDefinition。如果我們點進去看下AnnotationAwareAspectJAutoProxyCreator這個類的原始碼會發現這個類竟然實現了InstantiationAwareBeanPostProcessor這個介面。熟悉Spring尿性的朋友會敏銳的感覺到Spring可能是在postProcessBeforeInstantiation或者postProcessAfterInstantiation這些方法中對Bean進行動態代理的。
“大膽假設,小心求證”,讓我們帶著這個猜想去看看AnnotationAwareAspectJAutoProxyCreator到底幹了些什麼?
AnnotationAwareAspectJAutoProxyCreator生成動態代理類
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
Object cacheKey = getCacheKey(beanClass, beanName);
if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
if (this.advisedBeans.containsKey(cacheKey)) {
return null;
}
if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return null;
}
}
// 一般不指定CustomTargetSource,所以不會進入這段程式碼,所以關鍵程式碼在
// postProcessAfterInitialization中
// Create proxy here if we have a custom TargetSource.
// Suppresses unnecessary default instantiation of the target bean:
// The TargetSource will handle target instances in a custom fashion.
TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
if (targetSource != null) {
if (StringUtils.hasLength(beanName)) {
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;
}
下面是建立動態代理類的關鍵程式碼。
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
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;
}
// Create proxy if we have advice.
//獲取當前Bean配置的advice,這步是關鍵
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;
}
層層dedug進去我們能看到下面這段程式碼,我們口中常說的JDK動態代理和Cglib動態代理就是在這邊生成的。
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
@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)) {
//生成JDK動態代理
return new JdkDynamicAopProxy(config);
}
//生成Cglib動態代理
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
/**
* Determine whether the supplied {@link AdvisedSupport} has only the
* {@link org.springframework.aop.SpringProxy} interface specified
* (or no proxy interfaces specified at all).
*/
private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
Class<?>[] ifcs = config.getProxiedInterfaces();
return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0])));
}
到此,我們已經簡單分析了Spring動態代理類的生成流程。
PS:關於InstantiationAwareBeanPostProcessor介面和BeanPostProcessor介面大家可以自行了解下,這兩個介面是Spring中非常重要的介面。看懂了這兩個介面,Spring很多“神祕”的功能你就能理解了。
簡單總結
通過上面分析,其實我們發現如果不去看AOP動態代理類生成的細節的話,整個Spring AOP的流程還是挺簡單的:
- @EnableAspectJAutoProxy註解通過AopConfigUtils這個工具類註冊AnnotationAwareAspectJAutoProxyCreator這個類,這個類實現了InstantiationAwareBeanPostProcessor介面,所以會在Bean例項化前後對Bean做一系列額外的操作;
- AnnotationAwareAspectJAutoProxyCreator的postProcessAfterInitialization中會找出所有和當前Bean相關的Advice,如果找到就建立相應的動態代理類,如果找不到就不生成,返回原始類。
所以整個大流程就這麼簡單。
一些重要類:
- @EnableAspectJAutoProxy;
- AspectJAutoProxyRegistrar:註冊AnnotationAwareAspectJAutoProxyCreator
- AnnotationAwareAspectJAutoProxyCreator:AOP動態代理自動生成的處理類,其他類似的類有AspectJAwareAdvisorAutoProxyCreator和InfrastructureAdvisorAutoProxyCreator等;
- AopConfigUtils:AOP配置工具類
- ProxyFactory:代理工廠
- AopProxy介面:常見實現類ObjenesisCglibAopProxy、JdkDynamicAopProxy