Spring 源码分析
sschrodinger
2019/08/14
注解代理如何应用
bean 生命周期
bean 的生命周期如下:
image.png详见 Spring 源码分析(二)
,其中,代理生成最主要的过程为第 9 步,即调用 beanPostProcessor
的 postProcessAfterInitialization()
方法。该方法接受一个未被包装的 bean,然后返回一个包装的 bean(即代理的 bean),并将包装的 bean 注入到 spring 上下文容器中。
实际上,Spring 容器上下文初始化时,会将实现了 beanPostProcessor
的类提前初始化,并将其注入到 spring 容器中,接下来初始化其他 bean 时,就会根据注解判断是否 beanPostProcessor
满足当前 bean 的注解需求,如果满足,则需要对 bean 进行处理。
整个 Spring 容器初始化的过程如下(ApplicationContext
类 的 refresh
方法):
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//...
// step 1. 获得所有的 bean 定义
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
//...
try {
// ...
// step 2. 将所有的 beanPostProcessor 注入到容器中
registerBeanPostProcessors(beanFactory);
// ...
// step 3. 初始化其他 bean
finishBeanFactoryInitialization(beanFactory);
//...
}
catch (BeansException ex) {
// ...
}
finally {
// ...
}
}
}
我们重点关注上面的三步代码:
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
该方法主要的目的就是获得所有 bean 的定义,封装在一个beanDefinition
结构中,这个时候并不会初始化任何的 bean。registerBeanPostProcessors(beanFactory);
这个函数就是加载所有的beanPostProcessor
到容器中。finishBeanFactoryInitialization(beanFactory);
这一步是具体的初始化。
方法上的注解
对于 Aop 来说,可以将注解写在方法上,用于代理的生成,以 @Transactional
注解为例,声明方法如下:
@Service
public class ServiceImpl {
@Transactional
public void findById(long id) {}
}
对于方法上的注解,他的解析过程放在初始化的 step 3 中,我们接下来去分析 step 3。
我们知道,在初始化一个 bean 时,需要调用 beanPostProcessor
的 postProcessAfterInitialization()
方法进行包装,在 spring 中,默认使用 InfrastructureAdvisorAutoProxyCreator
这个 beanPostProcessor
处理内置的事务通知,所以我们直接看这个类的方法,如下:
// 该方法由 InfrastructureAdvisorAutoProxyCreator 的父类 AbstractAutoProxyCreator 提供
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (!this.earlyProxyReferences.contains(cacheKey)) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
// 该方法由 InfrastructureAdvisorAutoProxyCreator 的父类 AbstractAutoProxyCreator 提供
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;
}
// step 1. 获得所有 Intercepter
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
// step 2. 利用动态代理生成代理类
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;
}
最重点的是 step 1,获得所有的 Intercepter,所谓的 Intercepter 在 JDK 动态代理中,既是实现了 InvocationHandler 的类,只有获得了这个,才能够进行动态代理。
通过研究 getAdvicesAndAdvisorsForBean,我们可以知道注解在什么时候有效,什么时候无效。
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) {
// step 1.
List<Advisor> candidateAdvisors = findCandidateAdvisors();
// step 2.
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
// ...
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;
}
- step 1 在容器中找到所有的 Advisor 对象,该对象一般持有了一个过滤器,用来过滤不需要代理的方法。
- step 2 主要是对目标类的方法进行过滤,找到需要过滤的方法
最终,第二步会调用 AopUtils
的 canApply
方法,判断当前的目标类是否可以用 Advisor
代理。
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
// ...
MethodMatcher methodMatcher = pc.getMethodMatcher();
// ...
Set<Class<?>> classes = new LinkedHashSet<>();
if (!Proxy.isProxyClass(targetClass)) {
classes.add(ClassUtils.getUserClass(targetClass));
}
classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
for (Class<?> clazz : classes) {
// step 1.
Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
for (Method method : methods) {
// step 2.
if (introductionAwareMethodMatcher != null ?
introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
methodMatcher.matches(method, targetClass)) {
return true;
}
}
}
return false;
}
- step 1 获得该类及其父类的所有方法。
- step 2 循环遍历所有的方法,查找是否有满足条件(有注解)的方法。
以 @Transactional
注解举例,这个注解所对应的 Advisor
为 TransactionAttributeSourcePointcut
,即会调用 TransactionAttributeSourcePointcut
的 matches
方法。
依次进入
getTransactionAttribute
-> computeTransactionAttribute()
-> findTransactionAttribute(specificMethod)
-> determineTransactionAttribute(method)
-> parseTransactionAnnotation(element)
-> findMergedAnnotationAttributes()
-> searchWithFindSemantics()
,最终的注解是由 searchWithFindSemantics
函数完成的。
searchWithFindSemantics
的参数形式如下:
private static <T> T searchWithFindSemantics(AnnotatedElement element,
Set<Class<? extends Annotation>> annotationTypes, @Nullable String annotationName,
@Nullable Class<? extends Annotation> containerType, Processor<T> processor)
参数解释
AnnotatedElement element
:当前的annotatedElement
对象,如Method
Set<Class<? extends Annotation>> annotationTypes
:需要查找的annotation
注解对象集合
对于事务来说,他所需要查找的注解集合为 Transactional
,element
为对应方法。
searchWithFindSemantics
寻找策略
searchWithFindSemantics
函数提供的方法可以防止无止尽的循环寻找注解,使用一个 visit
列表,将访问过的 element
保存在该列表中,保证 element
不会被循环寻找。
searchWithFindSemantics
在四个地方查找是否有注解:
- 当前
element
是否有注解(不考虑 @Inherited,即不考虑注解的继承) - 如果
element
是方法类型,即element instanceof Method == true
,在定义该element
的接口中查找注解 - 如果
element
是方法类型,从该类的直接父类开始,依次在父类中查找该方法的父方法,判断是否有该注解。 - 如果
element
是类类型,那么会依次查找该类及其父类,看有没有注解。
note
- 除了查找直接注解是否所需的注解,该算法还需要递归,注解的注解,即,如果该
element
有注解,但是没有直接注解,那么就需要循环注解,看该注解是否被所需的注解修饰。
==综上,使用方法上的注解,不管是在父类中、在接口方法中或者在注解的注解中都可以被找到并加载==
类上的注解
同样的,我们也可以把注解写在类或者接口的定义上,如下:
@Service
@Transactional
public class ServiceImpl {
}
@Transactional
public interface Service {
}
我们继续分析如何获得类上的注解定义。
回到 mputeTransactionAttribute
方法,如下:
protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
// ...
// First try is the method in the target class.
TransactionAttribute txAttr = findTransactionAttribute(specificMethod);
if (txAttr != null) {
return txAttr;
}
// Second try is the transaction attribute on the target class.
txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());
if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
return txAttr;
}
// ...
return null;
}
在方法上获得 txAttr
失败时,会尝试在方法上找到 txAttr
,该方法最终也会调用 searchWithFindSemantics
方法,在该类及其父类用依次查找是否有注解。
==综上,使用在注解上的注解,不管是在父类中、在接口方法中或者在注解的注解中都可以被找到并加载==。
==note==
- ==不管是注解注解在什么位置,在 spring 中都可以使用代理==
- ==注意注解的覆盖性:即方法上的注解可以覆盖类上的注解;子类的方法注解可以覆盖父类的方法注解;接口的方法注解可以覆盖父类的方法注解==
网友评论