导航
死磕Spring AOP系列2:剖析Bean处理器之BeanNameAutoProxyCreator
这是<死磕Spring AOP系列>的第三篇。经过前面的讲解,已经掌握了以下知识点
- Spring AOP的底层支持,是基于ProxyFactory+ProxyConfig+Advisor生成的
- Spring容器的代理对象生成:在Bean生命周期过长中调用BeanPostProcessor,将对象进行包装,生成代理对象。
- Advisor的指定:不管编程式指定,还是自动匹配指定。虽然形式不同,但本质是相同的。
如果,还对上面的问题存疑,请回看以上内容。
本篇主要对剖析另外一个BeanPostProcessor,他就是DefaultAdvisorAutoProxyCreator,他是BeanNameAutoProxyCreator的升级版。形象点说:如果ProxyFactory是弓箭的话,代表原始,BeanNameAutoProxyCreator就是步枪,需要自己装配,DefaultAdvisorAutoProxyCreator就是自动步枪了,Spring可以完成自动匹配的部分工作了。
本章主要内容
- 使用DefaultAdvisorAutoProxyCreator,完成一个“AOP"例子,讲解下如何配置
- 对比BeanNameAutoProxyCreator,对DefaultAdvisorAutoProxyCreator进行剖析
- 分析DefaultAdvisorAutoProxyCreator的Advisor的匹配过程
- 体会自动化
1.使用DefaultAdvisorAutoProxyCreator,完成一个“AOP"例子
/** *模拟业务接口 */ public interface UserService { public void updateUser(); } /** *模拟具体业务 */ public class UserServiceImpl implements UserService{ @Override public void updateUser() { System.out.println("$$$$$$执行业务逻辑$$$$$"); } } /** * 模拟切面1 */ public class SecurityInterceptor implements MethodInterceptor { @Override public Object invoke(MethodInvocation methodInvocation) throws Throwable { System.out.println("==========执行安全校验===================="); return methodInvocation.proceed(); } } /** * 模拟切面2 */ public class LoggerBeforeAdvice implements MethodBeforeAdvice { @Override public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("=======保存更新日志========="); } }
xml(proxy_test.xml)
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd"> <beans> <bean class="org.springaop.chap01.UserServiceImpl" id="userService"></bean> <bean class="org.springaop.chap01.LoggerBeforeAdvice" id="loggerBeforeAdvice"></bean> <bean class="org.springaop.chap01.SecurityInterceptor" id="securityInterceptor"></bean> <!-- 自动代理所有的advisor --> <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"> <property name="usePrefix" value="true"></property> <property name="advisorBeanNamePrefix" value="advisor"></property> </bean> <bean id="advisor1" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="pattern"> <value>.*update.*</value> <!-- 业务实现方法名匹配 --> </property> <property name="advice"> <ref bean="loggerBeforeAdvice"/> </property> </bean> <bean id="advisor2" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="pattern"> <value>.*update.*</value> <!-- 业务实现方法名匹配 --> </property> <property name="advice"> <ref bean="securityInterceptor"/> </property> </bean> </beans>
Main
public class ContextMain { public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("org/springaop/chap03/proxy_test.xml"); UserService userService =(UserService) ctx.getBean("userService"); // UserService userService2 =(UserService) ctx.getBean("userService2"); userService.updateUser(); // userService2.updateUser(); } }
执行结果
=======保存更新日志=========
==========执行安全校验====================
$$$$$$执行业务逻辑$$$$$
说明:usePrefix和advisorBeanNamePrefix配合使用,如果usePrefix设置为false,advisorBeanNamePrefix设置没有作用。通过上面的设置配置,advisor2和advisor都有效果。
2.剖析DefaultAdvisorAutoProxyCreator类
2.1 结构层次图
可知,DefaultAdvisorAutoProxyCreator,BeanNameAutoProxyCreator是AbstractAutoProxyCreator的子类。通过前面讲解,知道AbstractAutoProxyCreator有个抽象方法。
方法声明如下
//是否被代理?返回interceptors //Return whether the given bean is to be proxied, what additional advices (e.g. AOP Alliance interceptors) and advisors to apply. protected abstract Object[] getAdvicesAndAdvisorsForBean( Class<?> beanClass, String beanName, TargetSource customTargetSource) throws BeansException;
DefaultAdvisorAutoProxyCreator 和BeanNameAutoProxyCreator分别实现各自逻辑。BeanNameAutoProxyCreator的实现不再赘述,在死磕Spring AOP系列2已经讲过,代码很简单。
2.2 剖析getAdvicesAndAdvisorsForBean方法
public abstract class AbstractAdvisorAutoProxyCreator extends AbstractAutoProxyCreator { ... protected Object[] getAdvicesAndAdvisorsForBean(Class beanClass, String beanName, TargetSource targetSource) { List advisors = findEligibleAdvisors(beanClass, beanName); if (advisors.isEmpty()) { return DO_NOT_PROXY; } return advisors.toArray(); } protected List<Advisor> findEligibleAdvisors(Class beanClass, String beanName) { List<Advisor> candidateAdvisors = findCandidateAdvisors();//获取所有的Advisors //获取可以应用于beanName的Advisor(逻辑尽在此) List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName); extendAdvisors(eligibleAdvisors); if (!eligibleAdvisors.isEmpty()) { eligibleAdvisors = sortAdvisors(eligibleAdvisors); } return eligibleAdvisors; } //Find all candidate Advisors to use in auto-proxying. protected List<Advisor> findCandidateAdvisors() { //委托给BeanFactoryAdvisorRetrievalHelper处理 return this.advisorRetrievalHelper.findAdvisorBeans(); } } public class BeanFactoryAdvisorRetrievalHelper { public List<Advisor> findAdvisorBeans() { ... if (advisorNames == null) { //获取容器中声明的Advisor // Do not initialize FactoryBeans here: We need to leave all regular beans // uninitialized to let the auto-proxy creator apply to them! advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( this.beanFactory, Advisor.class, true, false); ... } if (advisorNames.length == 0) { return new LinkedList<Advisor>(); } List<Advisor> advisors = new LinkedList<Advisor>(); for (String name : advisorNames) { //判断返回 if (isEligibleBean(name) && !this.beanFactory.isCurrentlyInCreation(name)) { try { advisors.add(this.beanFactory.getBean(name, Advisor.class)); } catch (BeanCreationException ex) { ... throw ex; } } } return advisors; } }
通过层层抽丝剥茧,定位到了AopUtils.findAdvisorsThatCanApply,负责具体的Advisor匹配工作。
3.剖析AopUtils.findAdvisorsThatCanApply方法,(匹配逻辑)
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)) { //是否IntroductionAdvisor 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(); IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null; if (methodMatcher instanceof IntroductionAwareMethodMatcher) { introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher; } Set<Class> classes = new HashSet<Class>(ClassUtils.getAllInterfacesForClassAsSet(targetClass)); classes.add(targetClass); for (Class<?> clazz : classes) { Method[] methods = clazz.getMethods(); for (Method method : methods) { if ((introductionAwareMethodMatcher != null && introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) || methodMatcher.matches(method, targetClass)) {//methodMatcher具体完成匹配 return true; } } } return false; }
通过图得知Advisor,持有Advice的聚合信息和PointCut对象,而PointCut接口持有MethodMatcher。
最后MethodMatcher完成方法匹配工作。以当前例子为例,AbstractRegexpMethodPointcut同时也是MethodMatcher.
public abstract class AbstractRegexpMethodPointcut extends StaticMethodMatcherPointcut implements Serializable { public boolean matches(Method method, Class targetClass) { return ((targetClass != null && matchesPattern(targetClass.getName() + "." + method.getName())) || matchesPattern(method.getDeclaringClass().getName() + "." + method.getName())); } } protected boolean matchesPattern(String signatureString) { for (int i = 0; i < this.patterns.length; i++) { boolean matched = matches(signatureString, i); if (matched) { for (int j = 0; j < this.excludedPatterns.length; j++) { boolean excluded = matchesExclusion(signatureString, j); if (excluded) { return false; } } return true; } } return false; } public class JdkRegexpMethodPointcut extends AbstractRegexpMethodPointcut { /** * Returns {@code true} if the {@link Pattern} at index {@code patternIndex} * matches the supplied candidate {@code String}. */ @Override protected boolean matches(String pattern, int patternIndex) { Matcher matcher = this.compiledPatterns[patternIndex].matcher(pattern); return matcher.matches(); } /** * Returns {@code true} if the exclusion {@link Pattern} at index {@code patternIndex} * matches the supplied candidate {@code String}. */ @Override protected boolean matchesExclusion(String candidate, int patternIndex) { Matcher matcher = this.compiledExclusionPatterns[patternIndex].matcher(candidate); return matcher.matches(); } /** * Compiles the supplied {@code String[]} into an array of * {@link Pattern} objects and returns that array. */ private Pattern[] compilePatterns(String[] source) throws PatternSyntaxException { Pattern[] destination = new Pattern[source.length]; for (int i = 0; i < source.length; i++) { destination[i] = Pattern.compile(source[i]); } return destination; } }
4.自动化
比起BeanNameAutoProxyCreator,DefaultAdvisorAutoProxyCreator有一个非常优越的地方。那就是advisor已有了自动匹配方法的能力。具体实现逻辑,通过上面的分析就是正则表达式的使用。其实很简单,确定一个方法是否被应用于advice,其实就是讲方法的签名字符串与定义PointCut的pattern进行匹配。今天提到的JdkRegexpMethodPointcut,只是实现之一。另外一个更强大的PointCut是什么?下次再说。