欢迎访问昆山宝鼎软件有限公司网站! 设为首页 | 网站地图 | XML | RSS订阅 | 宝鼎邮箱 | 后台管理


新闻资讯

MENU

软件开发知识
原文出处: 光闪

媒介

在Spring中利用MyBatis的Mapper接口自动生成时,用一个自界说的注解标志在Mapper接口的要领中,再操作@Aspect界说一个切面,拦截这个注解以记录日志可能执行时长。可是诧异的发明这样做之后,在Spring Boot 1.X(Spring Framework 4.x)中,并不能生效,而在Spring Boot 2.X(Spring Framework 5.X)中却能生效。

这毕竟是为什么呢?Spring做了哪些更新发生了这样的变革?此文将教育你摸索这个奥秘。

案例

焦点代码

@SpringBootApplication
public class Starter {
  public static void main(String[] args) {
    SpringApplication.run(DynamicApplication.class, args);
  }
}

@Service
public class DemoService {

    @Autowired
    DemoMapper demoMapper;

    public List<Map<String, Object>> selectAll() {
        return demoMapper.selectAll();
    }
}

/**
 * mapper类
 */
@Mapper
public interface DemoMapper {

  @Select("SELECT * FROM demo")
  @Demo
  List<Map<String, Object>> selectAll();

}

/**
 * 切入的注解
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Demo {
  String value() default "";
}

/**
 * aspect切面,用于测试是否乐成切入
 */
@Aspect
@Order(-10)
@Component
public class DemoAspect {

  @Before("@annotation(demo)")
  public void beforeDemo(JoinPoint point, Demo demo) {
    System.out.println("before demo");
  }

  @AfterDemo("@annotation(demo)")
  public void afterDemo(JoinPoint point, Demo demo) {
    System.out.println("after demo");
  }

}

测试类

@RunWith(SpringRunner.class) 
@SpringBootTest(classes = Starter.class)
public class BaseTest {

    @Autowired
    DemoService demoService;

    @Test
    public void testDemo() {
        demoService.selectAll();
    }

}

在Spring Boot 1.X中,@Aspect里的两个println都没有正常打印,而在Spring Boot 2.X中,都打印了出来。

调试研究

已知@Aspect注解声明的拦截器,会自动切入切合其拦截条件的Bean。这个成果是通过@EnableAspectJAutoProxy注解来启用和设置的(默认是启用的,通过AopAutoConfiguration),由@EnableAspectJAutoProxy中的@Import(AspectJAutoProxyRegistrar.class)可知,@Aspect相存眷解自动切入的依赖是AnnotationAwareAspectJAutoProxyCreator这个BeanPostProcessor。在这个类的postProcessAfterInitialization要领中打上条件断点:beanName.equals(“demoMapper”)

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;
}

在wrapIfNecessary要领中,有自动包装Proxy的逻辑:

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    // 假如是声明的需要原始Bean,则直接返回
	if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
		return bean;
	}
	// 假如不需要署理,则直接返回
	if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
		return bean;
	}
	// 假如是Proxy的基本组件如Advice、Pointcut、Advisor、AopInfrastructureBean则跳过
	if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
		this.advisedBeans.put(cacheKey, Boolean.FALSE);
		return bean;
	}

	// Create proxy if we have advice.
	// 按拍照关条件,查找interceptor,包罗@Aspect生成的相关Interceptor。
	// 这里是问题的要害点,昆山软件开发,Spring Boot 1.X中这里返回为空,而Spring Boot 2.X中,则不是空
	Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
	if (specificInterceptors != DO_NOT_PROXY) {
	    // 返回不是null,则需要署理
		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;
}

调试发明,Spring Boot 1.X中specificInterceptors返回为空,而Spring Boot 2.X中则不是空,那么这里就是问题的焦点点了,昆山软件开发,查察源码:

protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, 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) {
    // 找到当前BeanFactory中的Advisor
	List<Advisor> candidateAdvisors = findCandidateAdvisors();
	// 遍历Advisor,按照Advisor中的PointCut判定,返回所有符合的Advisor
	List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
	// 扩展advisor列表,这里会默认插手一个ExposeInvocationInterceptor用于袒露动态署理工具,之前文章有表明过
	extendAdvisors(eligibleAdvisors);
	if (!eligibleAdvisors.isEmpty()) {
	    // 按照@Order可能接口Ordered排序
		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);
	}
}