前言
在去年研發(fā)XX項目時,需要一種字節(jié)碼增強機制,用于增強HSF、Tair、TDDL等相關(guān)類,用于信息采集。當(dāng)時考慮了好幾種方案,也踩到了一些坑,特別是關(guān)于Spring AOP代理機制的一個缺陷,讓我最后決定放棄使用Spring AOP,而采用了基于JVM-Sandbox的方案。寫此文特地記錄下這個坑,避免后人重復(fù)入坑(當(dāng)然這個問題在5.0.5后應(yīng)該是已經(jīng)修復(fù)了)
問題表現(xiàn)
當(dāng)混用BeanNameAutoProxyCreator(或者其他類似的基于JDK proxy的,例如Sentinel中自己實現(xiàn)了BeanNameAutoProxyCreator)和使用AnnotationAwareAspectJAutoProxyCreator(或者其他基于AspectJ+注解識別的)代理同一個Spring Bean的時候,會出現(xiàn)一個詭異的問題。AnnotationAwareAspectJAutoProxyCreator偶爾會代理不成功(注意是偶現(xiàn),所以從嚴格意義上來說這應(yīng)該是Spring的一個設(shè)計bug)。
Spring AOP原理
簡單來講,Spring AOP是通過BeanPostProcessor實現(xiàn)的,BeanPostProcessor是在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean(java.lang.String, java.lang.Object, org.springframework.beans.factory.support.RootBeanDefinition)中進行遍歷調(diào)用,典型的用于AOP代理的BeanPostProcessor是AbstractAutoProxyCreator及其子類,其中最重要的就是這兩個方法:
@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;
}
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;
}
// Create proxy if we have 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;
}
protected Object createProxy(
Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
}
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);
proxyFactory.addAdvisors(advisors);
proxyFactory.setTargetSource(targetSource);
customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
return proxyFactory.getProxy(getProxyClassLoader());
}
protected abstract Object[] getAdvicesAndAdvisorsForBean(
Class<?> beanClass, String beanName, TargetSource customTargetSource) throws BeansException;
其中g(shù)etAdvicesAndAdvisorsForBean是個抽象方法,從代碼中可以看出getAdvicesAndAdvisorsForBean這個方法很重要,既決定了是否要進行代理,也決定了用于代理的特定的interceptors。




BeanNameAutoProxyCreator
在BeanNameAutoProxyCreator進行代理的時候,getAdvicesAndAdvisorsForBean是簡單通過beanName匹配的,如果匹配上就會進行代碼,當(dāng)然可能會使用JDK proxy,也可能使用 CGLIB proxy,但請記住不論是哪種方式,都會生成新的class,并且這個class的方法上并不會繼承target class上的注解信息(targe class方法上的注解信息丟了)!
AnnotationAwareAspectJAutoProxyCreator
AnnotationAwareAspectJAutoProxyCreator識別是否要進行的代理的代碼路徑如下:
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
List<Advisor> candidateAdvisors = findCandidateAdvisors();
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
protected List<Advisor> findAdvisorsThatCanApply(
List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {
ProxyCreationContext.setCurrentProxiedBeanName(beanName);
try {
return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
}
finally {
ProxyCreationContext.setCurrentProxiedBeanName(null);
}
}
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
if (candidateAdvisors.isEmpty()) {
return candidateAdvisors;
}
List<Advisor> eligibleAdvisors = new LinkedList<Advisor>();
for (Advisor candidate : candidateAdvisors) {
if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
eligibleAdvisors.add(candidate);
}
}
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;
}
public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
if (advisor instanceof IntroductionAdvisor) {
return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
}
else if (advisor instanceof PointcutAdvisor) {
PointcutAdvisor pca = (PointcutAdvisor) advisor;
return canApply(pca.getPointcut(), targetClass, hasIntroductions);
}
else {
// It doesn't have a pointcut so we assume it applies.
return true;
}
}
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
Assert.notNull(pc, "Pointcut must not be null");
if (!pc.getClassFilter().matches(targetClass)) {
return false;
}
MethodMatcher methodMatcher = pc.getMethodMatcher();
if (methodMatcher == MethodMatcher.TRUE) {
// No need to iterate the methods if we're matching any method anyway...
return true;
}
IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
}
Set<Class<?>> classes = new LinkedHashSet<Class<?>>(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
classes.add(targetClass);
for (Class<?> clazz : classes) {
Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
for (Method method : methods) {
if ((introductionAwareMethodMatcher != null &&
introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) ||
methodMatcher.matches(method, targetClass)) {
return true;
}
}
}
return false;
}
@Override
public boolean matches(Method method, Class<?> targetClass) {
if (method.isAnnotationPresent(this.annotationType)) {
return true;
}
// The method may be on an interface, so let's check on the target class as well.
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
return (specificMethod != method && specificMethod.isAnnotationPresent(this.annotationType));
}
public static Method getMostSpecificMethod(Method method, Class<?> targetClass) {
if (method != null && isOverridable(method, targetClass) &&
targetClass != null && targetClass != method.getDeclaringClass()) {
try {
if (Modifier.isPublic(method.getModifiers())) {
try {
return targetClass.getMethod(method.getName(), method.getParameterTypes());
}
catch (NoSuchMethodException ex) {
return method;
}
}
else {
Method specificMethod =
ReflectionUtils.findMethod(targetClass, method.getName(), method.getParameterTypes());
return (specificMethod != null ? specificMethod : method);
}
} catch (SecurityException ex) {
// Security settings are disallowing reflective access; fall back to 'method' below.
}
}
return method;
}
當(dāng)使用Annotation進行AOP識別的時候,4.0版本的Spring是通過在當(dāng)前的beanClass(也就是AopUtils.getMostSpecificMethod的targetClass)的最特定方法上尋找對應(yīng)注解;結(jié)合上節(jié)的分析,當(dāng)class被BeanNameAutoProxyCreator代理過后,原class方法上的注解已經(jīng)丟了,所以當(dāng)bean被AnnotationAwareAspectJAutoProxyCreator處理的時候,會被跳過,不會進行代理
原因定位
經(jīng)過對于Spring AOP基本原理的簡單分析,其實原因已經(jīng)很明顯了:如果某個BeanClass(考慮是類不是接口,并且注解標(biāo)識在類方法上)同時被BeanNameAutoProxyCreator和AnnotationAwareAspectJAutoProxyCreator代理時(單獨使用其中一個都一定會生效),如果BeanClass被BeanNameAutoProxyCreator先處理了,后被AnnotationAwareAspectJAutoProxyCreator處理,則AnnotationAwareAspectJAutoProxyCreator的代碼不會成功(因為注解信息丟失了);如果BeanClass先被AnnotationAwareAspectJAutoProxyCreator處理,然后被BeanNameAutoProxyCreator處理,則兩個代理都會成效(因為BeanName沒有變)。但由此其實引申出了另外一個問題,那到底哪個BeanPostProcessor先執(zhí)行呢?
public static void registerBeanPostProcessors(
ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {
String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);
// Register BeanPostProcessorChecker that logs an info message when
// a bean is created during BeanPostProcessor instantiation, i.e. when
// a bean is not eligible for getting processed by all BeanPostProcessors.
int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));
// Separate between BeanPostProcessors that implement PriorityOrdered,
// Ordered, and the rest.
List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<BeanPostProcessor>();
List<BeanPostProcessor> internalPostProcessors = new ArrayList<BeanPostProcessor>();
List<String> orderedPostProcessorNames = new ArrayList<String>();
List<String> nonOrderedPostProcessorNames = new ArrayList<String>();
for (String ppName : postProcessorNames) {
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
priorityOrderedPostProcessors.add(pp);
if (pp instanceof MergedBeanDefinitionPostProcessor) {
internalPostProcessors.add(pp);
}
}
else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
orderedPostProcessorNames.add(ppName);
}
else {
nonOrderedPostProcessorNames.add(ppName);
}
}
// First, register the BeanPostProcessors that implement PriorityOrdered.
sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);
// Next, register the BeanPostProcessors that implement Ordered.
List<BeanPostProcessor> orderedPostProcessors = new ArrayList<BeanPostProcessor>();
for (String ppName : orderedPostProcessorNames) {
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
orderedPostProcessors.add(pp);
if (pp instanceof MergedBeanDefinitionPostProcessor) {
internalPostProcessors.add(pp);
}
}
sortPostProcessors(orderedPostProcessors, beanFactory);
registerBeanPostProcessors(beanFactory, orderedPostProcessors);
// Now, register all regular BeanPostProcessors.
List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<BeanPostProcessor>();
for (String ppName : nonOrderedPostProcessorNames) {
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
nonOrderedPostProcessors.add(pp);
if (pp instanceof MergedBeanDefinitionPostProcessor) {
internalPostProcessors.add(pp);
}
}
registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);
// Finally, re-register all internal BeanPostProcessors.
sortPostProcessors(internalPostProcessors, beanFactory);
registerBeanPostProcessors(beanFactory, internalPostProcessors);
// Re-register post-processor for detecting inner beans as ApplicationListeners,
// moving it to the end of the processor chain (for picking up proxies etc).
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
}
從上述代碼可以看出來,在排除處理上spring會先分成三個等級,一個是PriorityOrdered代表最高等級,一個Ordered的代表第二級,一個是NonOrdered代表沒有級別,各個等級的分別進行排序,PriorityOrdered和Ordered會根據(jù)getOrder的返回值大小排序,當(dāng)然如果同個等級中order大小一樣的話,那兩者的順序就隨緣了...當(dāng)BeanNameAutoProxyCreator和AnnotationAwareAspectJAutoProxyCreator的Order一樣大的時候,兩者的排序順序隨緣,所以就有可能會出現(xiàn)文首提到的問題,在碰到這個問題后給Spring官方提了一個issue,該問題在Spring5.0.5版本及以后應(yīng)該已經(jīng)被修復(fù)
總結(jié)
- Spring BeanNameAutoProxyCreator代理后會丟失target bean方法上的注解
- 代理通過BeanPostProcessors進行,多個BeanPostProcessors的執(zhí)行順序可能存在隨機性
- 最好不要混用多個BeanPostProcessor對同個bean進行代理(這個確實不好做好,因為有可能其他人在框架或者二方包中進行了代理)