在 spring-tx源码分析(1)_Import注解 中,了解到Import注解、PointcutAdvisor的基础使用,并且编写了一个ServiceLog的示例程序,本文将继续优化这个程序:
- 支持指定包扫描,指定包下面被ServiceLog注解标注的方法才会进行拦截
- 兼容spring的原生aop
代码优化
概述
为了实现上面的功能,需要对之前的示例代码进行修改:
- EnableServiceLog注解增加一个字段,用于指定扫描的包名
- Pointcut实现类需要在matches方法中对比目标方法所在包是否与EnableServiceLog注解指定的包名匹配
- 使用ImportBeanDefinitionRegistrar将Pointcut实现类注入到容器,以便后续的装配
EnableServiceLog修改
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({ServiceLogConfigurationSelector.class, ServiceLogScannerRegistrar.class})
public @interface EnableServiceLog {
String basePackage() default "";
}
可以看到:
- 增加了basePackage参数
- Import了ServiceLogConfigurationSelector和ServiceLogScannerRegistrar两个组件。ServiceLogConfigurationSelector用于导入ServiceLogConfiguration来注入ServiceLogAdviser对象,这个之前就存在。ServiceLogScannerRegistrar用于在BeanDefinition阶段注入Pointcut Bean定义,这个后续会介绍
ServiceLogPointcut修改
public class ServiceLogPointcut extends StaticMethodMatcherPointcut {
private String basePackage;
public ServiceLogPointcut(String basePackage) {
this.basePackage = basePackage;
}
@Override
public boolean matches(Method method, Class<?> targetClass) {
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
Class<?> clazz = specificMethod.getDeclaringClass();
if (!clazz.getName().startsWith(this.basePackage)) {
return false;
}
return specificMethod.getAnnotation(ServiceLog.class) != null;
}
}
matches方法做了修改,首先匹配目标Class与指定包名,然后再判断方法是否被标注了ServiceLog注解。
ServiceLogScannerRegistrar
这个类实现了ImportBeanDefinitionRegistrar接口,是Import注解支持导入的三种组件的一种。
在实现方法里面,将向BeanDefinitionRegistry中注入ServiceLogPointcut Bean定义:
public class ServiceLogScannerRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
Map<String, Object> attributes = importingClassMetadata
.getAnnotationAttributes(EnableServiceLog.class.getName());
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClassName("serviceLogPointcut");
beanDefinition.setBeanClass(ServiceLogPointcut.class);
ConstructorArgumentValues constructorArgumentValues = new ConstructorArgumentValues();
constructorArgumentValues.addGenericArgumentValue(attributes.get("basePackage"));
beanDefinition.setConstructorArgumentValues(constructorArgumentValues);
registry.registerBeanDefinition("serviceLogPointcut", beanDefinition);
}
}
示例程序
- 编写Configuration类,标注EnableServiceLog注解,指定basePackage
- 使用EnableAspectJAutoProxy注解开启aop代理支持
- 编写业务代码,使用ServiceLog注解标注业务方法
示例程序下载
https://download.csdn.net/download/xuguofeng2016/86727144