前言
深入理解Spring源码分为7小节,本小节为Spring源码第四小节,各个小节目录如下。
- 扫描过程
- bean创建过程
- 容器扩展
4. AOP源码分析
- 事务源码分析
- Spring JDBC源码分析
- Spring常用设计模式
深入@Configuration代理
BeanDefinition接口实现了AttributeAccessor接口,这个接口用来设置或者获取一个属性,也就是说,每个Bean都可以有很多属性,而在配置类解析的时候,如果发现这个类有@Configuration注解,并且proxyBeanMethodss是true,那么会设置这个bean一个属性,这个属性叫org.springframework.context.annotation.ConfigurationClassPostProcessor.configurationClass,值是full,而其他bean的这个属性值是lite。
这个属性用于表示未来会为这个类生成代理类并放入容器。
Spring内部会有一个BeanFactoryPostProcessor的实现类ConfigurationClassPostProcessor,当所有bean信息收集完成后,会被调用,他内部有个方法叫enhanceConfigurationClasses(),可以理解为增强配置类。
内部主要做的是筛选出属性名为org.springframework.context.annotation.ConfigurationClassPostProcessor.configurationClass,并且值是full的bean,准备为他生产代理类,这就需要借助Enhancer,Enhancer会为原本的类生成一个代理类,并把新的类Class覆盖掉原本的。
比如原本类是com.xxx.TestConfig,经过这个阶段后,这个类变成了com.xxx.TestConfig$$EnhancerBySpringCGLIB$$
。
那么我们最关心的还是为这种类生成代理后要干嘛,比如调用一个方法时,Spring又做了哪些事?
所以我们要找到这个代理类的方法拦截里面的逻辑,需要对Enhancer有所了解,下面就是为类生成Enhancer的代码,主要关心setCallbackFilter、setCallbackTypes即可。
private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(configSuperClass);
enhancer.setInterfaces(new Class<?>[]{EnhancedConfiguration.class});
enhancer.setUseFactory(false);
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
enhancer.setCallbackFilter(CALLBACK_FILTER);
enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
return enhancer;
}
复制代码
Enhancer这里不说了,CALLBACKS这个数组保存的就是将来方法被调用时候的拦截器,但这三个拦截器不会应用于所有方法,需要挑选出来,通过setCallbackFilter完成,前两个拦截器都实现了ConditionalCallback接口,用来判断自己可不可以应用于这个方法。
private static final Callback[] CALLBACKS = new Callback[]{
new BeanMethodInterceptor(),
new BeanFactoryAwareMethodInterceptor(),
NoOp.INSTANCE
};
private static final ConditionalCallbackFilter CALLBACK_FILTER = new ConditionalCallbackFilter(CALLBACKS);
复制代码
我们从BeanMethodInterceptor开始看,他所拦截的方法要求所在的不是Object类并且不是BeanFactoryAware接口下的setBeanFactory方法并且有Bean注解,那么可能猜到,他对有@Bean注解的方法可能存在兴趣,要拦截。
@Override
public boolean isMatch(Method candidateMethod) {
/**
* 不是Object类 && 不是BeanFactoryAware接口下的setBeanFactory方法,&& 有Bean注解
*
*/
boolean b = candidateMethod.getDeclaringClass() != Object.class &&
!BeanFactoryAwareMethodInterceptor.isSetBeanFactory(candidateMethod) &&
BeanAnnotationHelper.isBeanAnnotated(candidateMethod);
return b;
}
复制代码
拦截后的逻辑主要在intercept()中,
这里大概说一下具体干了什么,由于@Configuration类下可能有@Bean方法,那么这个方法返回值最终也会被Spring收入到容器,全局唯一,如果我们把它当成普通方法来看,是不是每调用一次都会得到新的对象,那么在Spring管理范围内,不允许出现这种情况,那么这个拦截器主要的作用就是从容器中取出并返回,调用多次,实例还是一样的,当然还有其他情况,以后会细说。
在看BeanFactoryAwareMethodInterceptor,他拦截的是BeanFactoryAware接口下的setBeanFactory方法,这个我们可能还用不到,是Spring内部使用的,主要工作是对代理类中的$$beanFactory
字段赋值,就不看了。
如果想查看CGLIB生成的类,可以通过下面语句,生成的class会保存到指定路径下。
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/home/HouXinLin/test/cglib");
复制代码
最后的NoOp.INSTANCE表示不拦截,直接调用父类。
深入@Aspect
在了解这个之前,我们先熟悉一下几个概念。
-
通知(Advice):
A类下有个getUser()方法,在调用getUser()之前或者之后,想执行一些代码,那就需要通过@Before、@After这些注解定义方法,调用getUser()时就会执行到这里,这就是通知的意思,原方法调用时,通知到其他地方,Spring中抽象成Advice,具体实现有AspectJMethodBeforeAdvice、AspectJAfterAdvice等。 后面所用到通知这一词,都是指这个意思。
-
连接点(Join Point)&切入点(Pointcut Point)
这两个不太好理解,但我在stackoverflow找到了一个很好的例子, 当你去餐厅时,你会查看菜单,有很多你的候选项,你可以选择菜单上的任何一种或者多种,那么,连接点就是菜单上的候选项,切入点就是你选择的菜品。
拿实际举个例子。
class Employee{
public String getName(){....}
public void setName(String name){...}
}
复制代码
这些方法都称为连接点,当我们只对getName()方法进行拦截时,那么这个方法可以叫做切入点,切入点可以通过正则表示,比如特定包中的所有方法、所有返回值是void的方法、所有set开头的方法。
- 通知器:
通知器不是AOP中的概念,是Spring中的特定术语,组合通知和切入点成为一个单元,并将其传递给ProxyFactory,他的接口是PointcutAdvisor,如下。
public interface PointcutAdvisor {
Advice getAdvice();
Pointcut getPointcut();
}
复制代码
所有通知器都是PointcutAdvisor的实例。
下面做一个例子,这段代码也是Spring AOP的核心,他会拦截TestLog下的print方法,在方法之前做一些事,MethodBeforeAdvice相当于@Before,但是Spring在内部实例化的是AspectJMethodBeforeAdvice。
@Configuration
public class TestLog {
public void print() {
}
}
MethodBeforeAdvice advice = new MethodBeforeAdvice() {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("before");
}
};
final NameMatchMethodPointcutAdvisor nameMatchMethodPointcutAdvisor = new NameMatchMethodPointcutAdvisor();
nameMatchMethodPointcutAdvisor.setMappedName("print");
nameMatchMethodPointcutAdvisor.setAdvice(advice);
final ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.addAdvisor(nameMatchMethodPointcutAdvisor);
proxyFactory.setTarget(new TestLog());
TestLog proxy = (TestLog) proxyFactory.getProxy();
proxy.print();
复制代码
在看下另一个关键代码,这个代码解决的问题是:这个切入点可不可以应用于某个方法上,这在后面是非常重要的。
boolean matches = nameMatchMethodPointcutAdvisor.getPointcut().getMethodMatcher().matches(log, TestLog.class);
System.out.println(matches);
复制代码
下面我们正式开始Spring的AOP。
使用AOP,都会从这两个注解开始,@Aspect、@EnableAspectJAutoProxy,先说@EnableAspectJAutoProxy,他的定义如下。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
复制代码
看到@Import了吧,在前面说过,在Spring第二次收集bean的过程中会解析所有@Import注解,并从里面收集新的bean,所以在来看AspectJAutoProxyRegistrar,他实现了ImportBeanDefinitionRegistrar接口,意味着Spring会在某个时间点调用里面的registerBeanDefinitions()方法来进行收集。
而AspectJAutoProxyRegistrar类中把AnnotationAwareAspectJAutoProxyCreator封装为BeanDifinition添加到容器,这个类非常关键。
他实现了BeanPostProcessor接口,意味着可以在某个对象创建后对其进行更改。
他实现了BeanFactoryAware接口,意味着Spring会把BeanFactory告诉他,拿到BeanFactory的bean对容器的控制权很大。
他实现了InstantiationAwareBeanPostProcessor接口,意味着可以在Spring实例化某个对象之前,先手一步进行实例化,阻断后续对象创建过程。
我们只关注他对BeanPostProcessor的postProcessAfterInitialization()方法实现即可。
@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);//创建代理对象,如果这个bean有被代理的资格
}
}
return bean;
}
复制代码
wrapIfNecessary()方法用于如果这个对象需要被代理,那么就生成代理对象并返回。
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
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;
}
return bean;
}
复制代码
这里关键点是getAdvicesAndAdvisorsForBean()和createProxy(),getAdvicesAndAdvisorsForBean()会获取有关于指定bean的通知信息,如果结果不为null,则表示至少有一个通知可以应用到这个bean,那么就会通过createProxy()创建代理对象。
进入getAdvicesAndAdvisorsForBean()后没有具体逻辑,具体逻辑一路调用到下面这个方法,这个方法已经是经过删减的了,这样逻辑更清晰。
但是当调用顺序为我们所讲的这样的话,并不会进入if里面,这是由于先前已经有个地方调用过这里了,他调用时候aspectNames是null,然后才会进一步解析,解析后会缓存起来,后续调用的话直接从缓存获取,这个地方还是在InstantiationAwareBeanPostProcessor接口下,实现类还是上面说的AnnotationAwareAspectJAutoProxyCreator
public List<Advisor> buildAspectJAdvisors() {
/**
* 所有具有Aspect注解的bean
*/
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名称
*/
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Object.class, true, false);
for (String beanName : beanNames) {
if (!isEligibleBean(beanName)) {
continue;
}
/**
* 如果这类类是一个aspect类
*/
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);
/**
* 从这里获取所有增强
*/
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
advisors.addAll(classAdvisors);
}
}
}
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;
}
复制代码
我们继续看他具体的逻辑,下面这句是关键,这个方法用来获取这个bean中所有切入点和通知组合的Advisor信息,一个类中可以定义多个通知,就是可能有多个@Before或者@After。
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
复制代码
对于bean中的方法,通过下面这个方法提取信息,getPointcut()用来在方法上提取 @Pointcut, @Around, @Before, @After, @AfterReturning, @AfterThrowing注解,如果有的话。
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
int declarationOrderInAspect, String aspectName) {
/**
* 从这里在方法试图提取切入点信息
*/
AspectJExpressionPointcut expressionPointcut = getPointcut(
candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
if (expressionPointcut == null) {
return null;
}
/**
* 找到的话封装为InstantiationModelAwarePointcutAdvisorImpl
*
* InstantiationModelAwarePointcutAdvisorImpl的构造方法中会构建出通知
*/
return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
}
复制代码
提取后返回InstantiationModelAwarePointcutAdvisorImpl,但这个对象的构造方法中还做了一件事。
/**
* 更具注解生产不同的Advice
*/
this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);
复制代码
instantiateAdvice会调用到这里。
@Override
@Nullable
public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,
MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
/**
* 这里只有一处调用,在InstantiationModelAwarePointcutAdvisorImpl中
*/
Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
//获取方法上注解的类型
AspectJAnnotation<?> aspectJAnnotation =
AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
/**
* 根据不同的注解生产不同的Advice
*/
AbstractAspectJAdvice springAdvice;
switch (aspectJAnnotation.getAnnotationType()) {
case AtPointcut:
if (logger.isDebugEnabled()) {
logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
}
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:
throw new UnsupportedOperationException(
"Unsupported advice type on method: " + candidateAdviceMethod);
}
// Now to configure the advice...
springAdvice.setAspectName(aspectName);
springAdvice.setDeclarationOrder(declarationOrder);
String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
if (argNames != null) {
springAdvice.setArgumentNamesFromStringArray(argNames);
}
springAdvice.calculateArgumentBindings();
return springAdvice;
}
复制代码
到这里接结束了,现在的结果是所有标有@Aspect类下的通知器信息,通知器都实现了Advisor接口,这里都是InstantiationModelAwarePointcutAdvisorImpl的实现类。
InstantiationModelAwarePointcutAdvisorImpl中包含着切入点、通知等信息,但这还不能直接返回,因为这些信息可能对当前类没用,不需要代理,所以还少一步是从这些信息中提取能在本类用上的,这部分以后说。
现在到了为对象创建代理类的时候了,假设已经了解了Enhancer,在通过getProxy()获取对象时,最重要的还是给他设置回调,默认有7个Callback,这7个不能每个都作用于方法上,所以要通过ProxyCallbackFilter过滤,如果调用的是我们自定义的方法,那么最终都会进入DynamicAdvisedInterceptor,还有剩余的6个Callback暂时未看具体是什么。
//经过删减
public Object getProxy(@Nullable ClassLoader classLoader) {
try {
//创建Enhancer
Enhancer enhancer = createEnhancer();
//生成的代理类集成这个类
enhancer.setSuperclass(proxySuperClass);
//生成的代理类实现这个接口
enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader));
Callback[] callbacks = getCallbacks(rootClass);
Class<?>[] types = new Class<?>[callbacks.length];
for (int x = 0; x < types.length; x++) {
types[x] = callbacks[x].getClass();
}
//设置Callback过滤器
enhancer.setCallbackFilter(new ProxyCallbackFilter(
this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
enhancer.setCallbackTypes(types);
return createProxyClassAndInstance(enhancer, callbacks);
}
catch (CodeGenerationException | IllegalArgumentException ex) {
}
catch (Throwable ex) {
}
}
复制代码
到了最后一点了,Spring AOP的执行过程是个链式的,所以,会有一个方法来获取所有链,先看DynamicAdvisedInterceptor#intercept方法。
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;
if (chain.isEmpty() && CglibMethodInvocation.isMethodProxyCompatible(method)) {
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
try {
retVal = methodProxy.invoke(target, argsToUse);
}
catch (CodeGenerationException ex) {
CglibMethodInvocation.logFastClassGenerationFailure(method);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
}
else {
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}
复制代码
第一句就是获取所有能作用于这个方法的拦截链,如果调用this.advised.getAdvisors()那么返回的都是能作用于当前方法上的所有通知,getInterceptorsAndDynamicInterceptionAdvice()用于把所有通知转换成Interceptor,然后按顺序依次调用,比如@Before会被转换成MethodBeforeAdviceInterceptor,说是转换,其实内部是提取Advisor中的信息做包装。
要注意的是,所有链中Spring都会在第一个位置添加ExposeInvocationInterceptor链,所以进入的第一个链都是他。
控制链的走向在ReflectiveMethodInvocation的proceed方法中,比较简单,这里就不说了。