纵观整个Spring的发展历史,注解的配置正逐步替代xml的配置,到SpringBoot时代,完全可以用注解的配置替换繁杂的xml配置,例如我们需要开启AOP功能只要在代码上配置上@EnableAspectJAutoProxy。废话不多说,我们这节来分析下@EnableAspectJAutoProxy注解的背后的实现。
@EnableAspectJAutoProxy配置参数
先看下@EnableAspectJAutoProxy属性详情:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
boolean proxyTargetClass() default false;
}
@EnableAspectJAutoProxy有2个配置参数proxyTargetClass和@Import,@Import是个固定配置,写死成AspectJAutoProxyRegistrar类型,Spring在解析此注解配置时会创建AspectJAutoProxyRegistrar并调用registerBeanDefinitions方法。
proxyTargetClass配置
在Spring中,动态代理有2种实现方式:
-
基于CGLIB来实现
-
基于Java原生的Proxy实现,这种方式原类必须要定义接口。
这个参数就是表示动态代理实现方式,如果值设置true,表示需要代理类都基于CGLIB来实现;默认情况下值是设置成false表示如果原类如果定义了接口则通过Proxy实现否则基于CGLIB来实现。
@Import配置
在Spring中@Import可以配置3种类型:
-
在基于@Configuration的类上引入bean
这个配置比较简单,直接在配置了@Configuration的类上配置@Import引入bean即可,举个例子:
@Configuration
@Import(value={Bean.class})
public class Config {
}
以上例子就会把Bean加入到Spring容器。
- 基于ImportSelector引入bean
如果引入类需要经过注解上的参数来决定可以使用此方式。
public interface ImportSelector {
String[] selectImports(AnnotationMetadata importingClassMetadata);
}
AnnotationMetadata类型可以获取注解上的参数配置。@EnableTransactionManagement就是通过次方式配置,来看下他配置:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {
boolean proxyTargetClass() default false;
AdviceMode mode() default AdviceMode.PROXY;
int order() default Ordered.LOWEST_PRECEDENCE;
}
public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector {
@Override
protected String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
case PROXY:
return new String[] {AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName()};
case ASPECTJ:
return new String[] {TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME};
default:
return null;
}
}
}
以上例子就是通过获取AnnotationMetadata里的配置来决定引入那些bean。
- 基于ImportBeanDefinitionRegistrar引入bean
在解析@Import配置传入AnnotationMetadata和BeanDefinitionRegistry两个参数到registerBeanDefinitions方法。
这种方式更加灵活,可以直接通过BeanDefinitionRegistry将自己想要的bean加入到Spring容器。
@EnableAspectJAutoProxy注解就是通过这种方式将代理创建器AnnotationAwareAspectJAutoProxyCreator加入到到Spring容器。
@EnableAspectJAutoProxy背后的实现
AOP的组成
Spring的AOP的实现是基于Aspectj项目的注解及注解的解析实现,其核心的组件还是Spring自己的实现包括Advisor(切面),Pointcut(切点),Advice(增强)。
- 1.什么是切面
切面就是将非逻辑代码抽离到一个指定位置,让编写逻辑代码的人感觉不到非逻辑代码的存在,实际执行却能让非逻辑代码发挥效果,说白了就是切点和增强的组合
- 2.什么是切点
切点是对逻辑代码增强的位置,比如在逻辑代码执行前增强。
- 2.什么是增强
增强就是对切点位置的具体的实现,比如在逻辑代码执行前记录操作日志,而记录操作日志这个操作的具体实现就是增强
举个例子:
@Aspect //声明一个切面
public class LogAspect {
@After(value="@annotation(com.just.spring4.ch1.aop.TestAction)") //通过@after注解声明一个建言,并使用@Pointcut定义的切点
public void after(JoinPoint joinPoint){
System.out.println("记录日志");
}
}
@Aspect注解的LogAspect类就是切面。
@After注解的并匹配上的方法就是切点。
而System.out.println("记录日志")输出就是增强。
Advisor
Advisor是一个接口它的实现代表了切面,切面包含了切点和增强,先看下Advisor接口的定义:
public interface Advisor {
Advice getAdvice();
boolean isPerInstance();
}
getAdvice()方法返回了一个增强组件Advice。isPerInstance()方法在Spring框架中暂未被使用。
根据上面定义Advisor接口其实少了一个切点组件返回,所以Advisor一般不会被直接实现,Spring定义了2个接口来扩展Advisor的实现:
- IntroductionAdvisor
public interface IntroductionAdvisor extends Advisor, IntroductionInfo {
ClassFilter getClassFilter();
void validateInterfaces() throws IllegalArgumentException;
}
getClassFilter()方法返回了一个切点组件ClassFilter,它是Class的切点,来匹配Class是否满足条件来增强实现。validateInterfaces()方法的功能是Advice是否实现指定的接口。
IntroductionAdvisor主要做Class的匹配而不关心Method匹配情况。
- PointcutAdvisor
public interface PointcutAdvisor extends Advisor {
Pointcut getPointcut();
}
getPointcut()返回了一个切点组件Pointcut,Pointcut和ClassFilter不同Pointcut包装了ClassFilter和MethodMatcher,也就是说Pointcut即匹配Class也匹配Method,2者同时满足情况下才能增强实现。
Pointcut
Pointcut表示切入的位置,在Spring中Pointcut接口是做一个匹配的功能包括Class和Method的匹配,只有匹配上才能做进一步增强。Pointcut接口如下
public interface Pointcut {
ClassFilter getClassFilter();
MethodMatcher getMethodMatcher();
Pointcut TRUE = TruePointcut.INSTANCE;
}
getClassFilter()返回的ClassFilter是Class匹配器,getMethodMatcher()返回的MethodMatcher是Class匹配器,但不一定每个匹配器都会有作用,举个例子:
- AnnotationMatchingPointcut
AnnotationMatchingPointcut是注解的切面类,它可以匹配Class上的注解或者Method的上的注解,看下它的构造方法:
public AnnotationMatchingPointcut(
Class extends Annotation> classAnnotationType, Class extends Annotation> methodAnnotationType) {
Assert.isTrue((classAnnotationType != null || methodAnnotationType != null),
"Either Class annotation type or Method annotation type needs to be specified (or both)");
if (classAnnotationType != null) {
this.classFilter = new AnnotationClassFilter(classAnnotationType);
}
else {
this.classFilter = ClassFilter.TRUE;
}
if (methodAnnotationType != null) {
this.methodMatcher = new AnnotationMethodMatcher(methodAnnotationType);
}
else {
this.methodMatcher = MethodMatcher.TRUE;
}
}
构造方法有2个参数classAnnotationType表示Class上的注解,methodAnnotationType表示Method上的注解,2参数必传一个如果其中一个不传,会设置ClassFilter.TRUE或MethodMatcher.TRUE表示全类型匹配,这种匹配一般对整个类或单个方法进行增强。
- StaticMethodMatcherPointcut
StaticMethodMatcherPointcut是抽象类,本身是做一种规范,其规范必须要实现MethodMatcher的匹配逻辑来匹配Method,ClassFilter可不实现,不实现会全类型匹配。
- ComposablePointcut
Advice
Advice是增强的接口,Spring提供很多增强的实现。举例如下:
-
AspectJMethodBeforeAdvice
对应AspectJ中的@Before注解的增强实现,在方法执行前增强。
-
AspectJAfterAdvice
对应AspectJ中的@After注解的增强实现,在方法执行后增强。
-
AspectJAfterReturningAdvice
对应AspectJ中的@AfterReturning注解的增强实现,在方法执行后并获取返回值,可以根据返回值做增强。
-
AspectJAfterThrowingAdvice
对应AspectJ中的@AfterThrowing注解的增强实现,在方法执行后并获取执行错误信息,可以根据错误信息做增强。
-
AspectJAroundAdvice
对应AspectJ中的@Around注解的增强实现,在方法执行前后都可以做增强。
如何实现AOP
实现AOP关键有2个类AspectJAutoProxyRegistrar和AnnotationAwareAspectJAutoProxyCreator。
利用AspectJAutoProxyRegistrar注册创建代理类
前面提到Spring通过@EnableAspectJAutoProxy的@Import配置在解析注解时会创建AspectJAutoProxyRegistrar并调用registerBeanDefinitions方法。
看下它的实现
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
AnnotationAttributes enableAJAutoProxy =
AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
if (enableAJAutoProxy.getBoolean("proxyTargetClass")) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
}
代码的功能分2部分,第1部分将AnnotationAwareAspectJAutoProxyCreator类型包装成BeanDefinition注册到Spring容器。第2部分将proxyTargetClass的配置设置到此BeanDefinition里。
这里AnnotationAwareAspectJAutoProxyCreator类是实现AOP的核心后面详细说明。
利用AnnotationAwareAspectJAutoProxyCreator创建代理对象
先看下AnnotationAwareAspectJAutoProxyCreator的结构
可以看到AnnotationAwareAspectJAutoProxyCreator实现了BeanPostProcessor接口,这个接口做什么用的呢?
先看下这个接口的定义:
public interface BeanPostProcessor {
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
在Spring容器中BeanDefinition被创建成bean后会依次调用实现了这个接口的类里postProcessBeforeInitialization和postProcessAfterInitialization,这2个方法作用可以对bean做修改就是可以对bean做代理或对bean做属性修改,而2这个方法前后执行分别对应bean初始化前后:
好了,我们知道AnnotationAwareAspectJAutoProxyCreator就是利用BeanPostProcessor来做代理。
AnnotationAwareAspectJAutoProxyCreator代理是放在postProcessAfterInitialization方法里处理,所以对代理对象本身的初始化不受影响。
来看下它的实现:
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (!this.earlyProxyReferences.contains(cacheKey)) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
先查看earlyProxyReferences缓存判断是否已经创建代理。(在循环引用情况下会调用getEarlyBeanReference提前创建代理),如果还未创建调用wrapIfNecessary方法去创建代理,我们看下其实现:
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (beanName != null && 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;
}
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;
}
前2个if先判断是否已经创建再判断是否需要创建代理对象, 如果需要则调用getAdvicesAndAdvisorsForBean方法获取切面Advisor,再根据Advisor调用createProxy来创建代理对下,我们分两部分来讲。
解析和获取Advisor
getAdvicesAndAdvisorsForBean方法的实现在AbstractAdvisorAutoProxyCreator类中实现,委托给了findEligibleAdvisors方法去获取,看下它的实现:
protected List findEligibleAdvisors(Class> beanClass, String beanName) {
List candidateAdvisors = findCandidateAdvisors();
List eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
第一步调用findCandidateAdvisors方法先获取注册到Spring容器中的Advisor,在获取并解析注解了@Aspect的bean中的所有Advisor。
@Override
protected List findCandidateAdvisors() {
List advisors = super.findCandidateAdvisors();
advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
return advisors;
}
this.aspectJAdvisorsBuilder.buildAspectJAdvisors()这块代码就是利用了aspectj来解析注解了@Aspect。
第二步调用findAdvisorsThatCanApply来过滤Advisor,Advisor(切点)包含了Pointcut(切点)和Advice(增强),findAdvisorsThatCanApply方法的过滤就是利用Advisor中Pointcut匹配Class或Method来达到过滤的目的。
第三步调用extendAdvisors方法,extendAdvisors在AnnotationAwareAspectJAutoProxyCreator作用就是在所有的advisors节点最前面插入一个Advisor(有advisors节点前提下),此Advisor比较特殊它的Pointcut是全类型匹配的(匹配所有Class和Method),它主要功能是在于它的Advice(增强),它的Advice实现是ExposeInvocationInterceptor类,看类的名称就知道,对外暴露的类,就是所有Advice调用链的第一环,ExposeInvocationInterceptor作用就是将调用信息存在ThreadLocal实现的上下文信息里,供调用链后续的Advice获取使用,可以看下它实现:
第四步如果存在advisors节点则调用sortAdvisors对其排序,这排序规则是根据Advisor里的order字段排序,当然如果存在第三步所说的特殊Advice它会排在最前面。
回到wrapIfNecessary方法获取到advisors接下去就是创建代理了。
创建代理对象
创建代理对象调用的createProxy方法,来看下它的实现:
protected Object createProxy(
Class> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
if (!proxyFactory.isProxyTargetClass()) {
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
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(getProxyClassLoader());
}
代理对象的创建主要依托于代理工厂ProxyFactory类来创建,看下它的继承关系:
它和AnnotationAwareAspectJAutoProxyCreator都是继承了ProxyConfig类。
createProxy方法第一步先通过ProxyConfig里的copyFrom方法将AnnotationAwareAspectJAutoProxyCreator里的配置拷贝至ProxyFactory。
第二步重新设置proxyTargetClass,Java的Proxy代理能对象的前提是此对象必须实现了接口,这步如果原先proxyTargetClass设置false,需要先判断其是否实现了接口并且其接口非InitializingBean,DisposableBean,Aware,Spring自带接口。
第三步设置Advisors,并且冻结设置使后面不能修改Advisors。
最后调用ProxyFactory里的getProxy方法去代理对象。
ProxyFactory里代理的是有2种:
-
基于CGLIB来实现
-
基于Java原生的Proxy实现
主要取决于proxyTargetClass参数。
无论是CGLIB创建代理的CglibAopProxy还是Java原生的Proxy实现创建JdkDynamicAopProxy都是基于接口AopProxy:
public interface AopProxy {
Object getProxy();
Object getProxy(ClassLoader classLoader);
}
AopProxy有2个方法,基于默认ClassLoader创建代理和基于用户自定义创建。
以JdkDynamicAopProxy实现为例看下实现。
JdkDynamicAopProxy实现了InvocationHandler,也就是代理方法的调用,会分发到本身。
看下其getProxy方法如果创建代理:
@Override
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);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
先获取需要代理的接口,然后标记equals或hashCode方法是是否被覆盖,供调用时用,最后创建代理对象。
调用流程
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
...
try {
...
target = targetSource.getTarget();
if (target != null) {
targetClass = target.getClass();
}
List
先获取代理的advice(增强器)数组(这里命名chain),如果advice直接调用被代理对象的方法,否则调用invocation.proceed()方法。invocation.proceed()的调用过程先是链式调用advice,最后执行其被代理对象的方法。看下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;
if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
else {
return proceed();
}
}
else {
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
此方法的advice的链式调用的原理是递归调用:
proceed() -> invoke(this) -> proceed() ->...->invoke(this)->proceed()->invokeJoinpoint()
每做一次代码的增强,currentInterceptorIndex指针加1,直到所有的advice被调用完成,才执行其被代理对象的方法。