上一节我们讲到了SpringAop基于注解版本的织入和创建代理的流程。这一节我们来讲两个切面类。从前面知道 一个切面是有两部分构成 增强 和切点 。上节我们也列举了一些常用的切面 下面我们就来
介绍两个功能比较相近的切面类。
NameMatchMethodPointcutAdvisor的使用
启动配置类
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication application = new SpringApplicationBuilder(App.class).application();
ApplicationContext context = application.run(args);
UserService userServiceImpl = (UserService)context.getBean("userServiceImpl");
userServiceImpl.findUser();
}
@Configuration
public static class AppConfig{
/**
* 定义切面
* @return
*/
@Bean
public MethodBeforeAdvice methodBeforeAdvice(){
return new com.coledy.aop.advice.MethodBeforeAdvice();
}
/**
* 创建一个NameMatchMethodPointcutAdvisor 切面
* @return
*/
@Bean
public NameMatchMethodPointcutAdvisor nameMatchMethodPointcutAdvisor(){
NameMatchMethodPointcutAdvisor matchMethodPointcutAdvisor = new NameMatchMethodPointcutAdvisor();
matchMethodPointcutAdvisor.setAdvice(methodBeforeAdvice());
matchMethodPointcutAdvisor.setMappedNames("find*");
return matchMethodPointcutAdvisor;
}
}
}
被代理对象 和增强
public interface UserService {
void findUser();
}
@Component
public class UserServiceImpl implements UserService {
@Override
public void findUser() {
System.out.println("findUser....");
}
}
public class MethodBeforeAdvice implements org.springframework.aop.MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("我是一个前置增强 ");
}
}
启动app 输出
我是一个前置增强
findUser…
可以看到到这里 我们看到了我们想要看到的结果。对象被代理了。值得注意的是NameMatchMethodPointcutAdvisor这个切面类 的setMappedNames 需要配置的是方法名字。支持通配符。
RegexpMethodPointcutAdvisor的使用
我们只在上面的例子的基础上加一个后置增强 和一个切面配置
@SpringBootApplication
//@Import(RegisterAspectJAutoProxyRegisterar.class)
public class App {
public static void main(String[] args) {
SpringApplication application = new SpringApplicationBuilder(App.class).application();
ApplicationContext context = application.run(args);
UserService userServiceImpl = (UserService)context.getBean("userServiceImpl");
userServiceImpl.findUser();
}
@Configuration
public static class AppConfig{
@Bean
public MethodBeforeAdvice methodBeforeAdvice(){
return new com.coledy.aop.advice.MethodBeforeAdvice();
}
@Bean
public RegexpMethodPointcutAdvisor regexpMethodPointcutAdvisor(){
RegexpMethodPointcutAdvisor regexpMethodPointcutAdvisor = new RegexpMethodPointcutAdvisor();
regexpMethodPointcutAdvisor.setPatterns("*.findGoods");
regexpMethodPointcutAdvisor.setAdvice(methodBeforeAdvice());
return regexpMethodPointcutAdvisor;
}
}
}
public class CommonMethodAfterAdvice implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("common after return");
}
}
同样也达到了代理的目的。下面我们就看一下为什么我们只是简单的在容器中定义了一个切面 Spring就帮我们织入了被代理的对象里面。
SpringAop非注解式的源码分析
看过上一篇文章的应该有印象 getAdvicesAndAdvisorsForBean 这个方法是AbstractAutoProxyCreator类唯一一个留给子类必须实现的方法 (获取所有的切面由于不同子类的实现 获取切面的方式不同 所以这里交给了子类实现 模板方法。如BeanNameAutoProxyCreator就和我们经常使用的AnnotationAwareAspectJAutoProxyCreator 这个织入类的实现方式不一样)。
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();
}
跟进findEligibleAdvisors
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;
}
//这里为了节省篇幅 直接跳到findCandidateAdvisors这个方法里面来 我们上一节说过 AnnotationAwareAspectJAutoProxyCreator 重写了这个方法
//但是在子类的方法有行代码很关键List<Advisor> advisors = super.findCandidateAdvisors();
//先回去父类获取Advisor 然后再用自己的逻辑获取 父类是获取实现了Advisor接口的切面
//子类是获取有AspectJ注解的并且方法中有@Pointcut的方法 封装成切面。
protected List<Advisor> findCandidateAdvisors() {
Assert.state(this.advisorRetrievalHelper != null, "No BeanFactoryAdvisorRetrievalHelper available");
return this.advisorRetrievalHelper.findAdvisorBeans();
}
下面我们继续看看 父类获取Advisor的逻辑。其实和子类的获取逻辑很类似。父类的这个更简单因为本来就是Advisor不需要解析包装。
public List<Advisor> findAdvisorBeans() {
String[] advisorNames = this.cachedAdvisorBeanNames;
if (advisorNames == null) {
//获取beanFactory中实现了Advisor接口的实例
advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Advisor.class, true, false);
this.cachedAdvisorBeanNames = advisorNames;
}
if (advisorNames.length == 0) {
return new ArrayList<>();
}
List<Advisor> advisors = new ArrayList<>();
for (String name : advisorNames) {
if (isEligibleBean(name)) {
//判断当前bean是不是正在创建。 正在创建则不做任何处理。这里需要注意的是 即使当前正在
//创建的bean是Advisor的子类 也不必担心 这个类无法加入到advisors中因为这个方法不止调用一次。每一个bean实例化的时候都会调用。
if (this.beanFactory.isCurrentlyInCreation(name)) {
if (logger.isTraceEnabled()) {
logger.trace("Skipping currently created advisor '" + name + "'");
}
}
else {
try {
//这里如果没有创建就从beanFactory获取bean 如果没创建会创建 继而加入进缓存中
//bean的创建流程请参考https://blog.csdn.net/xiaomujiang_dafangzi/article/details/105183005
advisors.add(this.beanFactory.getBean(name, Advisor.class));
}
catch (BeanCreationException ex) {
Throwable rootCause = ex.getMostSpecificCause();
if (rootCause instanceof BeanCurrentlyInCreationException) {
BeanCreationException bce = (BeanCreationException) rootCause;
String bceBeanName = bce.getBeanName();
if (bceBeanName != null && this.beanFactory.isCurrentlyInCreation(bceBeanName)) {
if (logger.isTraceEnabled()) {
logger.trace("Skipping advisor '" + name +
"' with dependency on currently created bean: " + ex.getMessage());
}
// Ignore: indicates a reference back to the bean we're trying to advise.
// We want to find advisors other than the currently created bean itself.
continue;
}
}
throw ex;
}
}
}
}
return advisors;
}
到这里查找的过程就完了、、其实还是很简单的,就是如果有实现Advisor接口的类。至于 是如何织入到代理类中的请参考上一篇https://blog.csdn.net/xiaomujiang_dafangzi/article/details/105217189