Spriing Aop中动态Pointcut的实践
为什么需要AspectJ中具有动态值的Pointcut
工作中需要抽离出公司项目的一些监控组件打成一个公共包,需要对一些Aspect作动态适配(根据引入项目的ComponentScan进行特定package的Pointcut/配置PointCut)
@Pointcut的用法
- 格式
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)throws-pattern?)
修饰符匹配(modifier-pattern?)
返回值匹配(ret-type-pattern)可以为*表示任何返回值,全路径的类名等
类路径匹配(declaring-type-pattern?)
方法名匹配(name-pattern)可以指定方法名 或者 *代表所有, set* 代表以set开头的所有方法
参数匹配((param-pattern))可以指定具体的参数类型,多个参数间用“,”隔开,各个参数也可以用“*”来表示匹配任意类型的参数,如(String)表示匹配一个String参数的方法;(*,String) 表示匹配有两个参数的方法,第一个参数可以是任意类型,而第二个参数是String类型;可以用(..)表示零个或多个任意参数
异常类型匹配(throws-pattern?)
其中后面跟着“?”的是可选项
1)execution(* *(..))
//表示匹配所有方法
2)execution(public * com. savage.service.UserService.*(..))
//表示匹配com.savage.server.UserService中所有的公有方法
3)execution(* com.savage.server..*.*(..))
//表示匹配com.savage.server包及其子包下的所有方法
- Spring中的Pointcut(Pointcut表示式(expression)和Pointcut签名(signature))
//Pointcut表示式(可以使用&& || ! 这三个运算)
@Pointcut("execution(* com.savage.aop.MessageSender.*(..))")
//Point签名
private void log(){}
javassist实现动态Pointcut
- 关于SpringAop
AspectJ方式织入的核心,是一个BeanPostProcess(会扫描所有的Pointcut与遍历所有Bean,并对需要的Bean进行织入-自动代理,当对象实例化的时候,为其生成代理对象并返回)
- 思路
在Aop的BeanPostProcess执行之前( springApplication.run之前),使用javassist修改目标Aop类字节码,动态设置@Pointcut,设置 value为我们自己动态查询到的值。
- 其他
由于Spring boot的类加载机制,运行时javassist会扫描不到包,要通过insertClassPath添加扫描路径
修改@Pointcut的切点值后,通过toClass覆盖原有类,需要通过类加载器重新加载。
- 实现
@SpringBootApplication
public class UidServerApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
AspectPoincutScan();
SpringApplication springApplication = new SpringApplication(UidServerApplication.class);
springApplication.addInitializers(new UidApplicationContextInitializer());
springApplication.run(args);
}
@Override
protected SpringApplicationBuilder configure(
SpringApplicationBuilder builder) {
AspectPoincutScan();
return builder.sources(UidServerApplication.class)
.initializers(new UidApplicationContextInitializer())
.listeners(new UidApplicationRefreshedListener())
.listeners(new UidApplicationCloseListener());
}
private static void AspectPoincutScan() {
try {
ClassPool pool = ClassPool.getDefault();
// 添加包的扫描路径
ClassClassPath classPath = new ClassClassPath(UidServerApplication.class);
pool.insertClassPath(classPath);
//获取要修改的Class
CtClass ct = pool.get("mobi.meishuo.uidserver.monitor.springaop.ServiceMonitorAop");
CtMethod[] cms = ct.getDeclaredMethods();
for (CtMethod cm : cms) {
//找到@pointcut 注解的方法
if (cm.getName().equals("pointcut")) {
MethodInfo methodInfo = cm.getMethodInfo();
ConstPool cPool = methodInfo.getConstPool();
AnnotationsAttribute attribute = new AnnotationsAttribute(cPool, AnnotationsAttribute.visibleTag);
//获取@pointcut 注解,修改其value值
Annotation annotation = new Annotation("org.aspectj.lang.annotation.Pointcut", cPool);
annotation.addMemberValue("value", new StringMemberValue("execution(xxxx", cPool));
attribute.setAnnotation(annotation);
methodInfo.addAttribute(attribute);
//覆盖原有类
ct.toClass();
//使用类加载器重新加载Aop类
pool.getClassLoader().loadClass("mobi.meishuo.uidserver.monitor.springaop.ServiceMonitorAop");
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
编程式AOP实现动态PointCut
- PonitCut
public static Pointcut getAdapterServicePointcut(){
AspectJExpressionPointcut adapterPointcut = new AspectJExpressionPointcut();
//从配置文件中获取PointCut表达式
adapterPointcut.setExpression(MonitorPropertyConfig.getPoinitcutAdapter());
return adapterPointcut;}
//扩展Spring中AbstractBeanFactoryPointcutAdvisor
public class AdapterServiceAdvisor extends AbstractBeanFactoryPointcutAdvisor {
@Override
public Pointcut getPointcut() {
return getAdapterServicePointcut();
}
}
- Advice
public class AdapterServiceMonitorInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
//做一些操作...
}
}
- 配置Advisor Bean
@Configuration
public class MonitorProxyConfiguration {
@Bean(name = "adapterServiceAdvisor")
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public AdapterServiceAdvisor adapterServiceAdvisor() {
AdapterServiceAdvisor advisor = new AdapterServiceAdvisor();
advisor.setAdviceBeanName("adapterServiceAdvice");
advisor.setAdvice(new AdapterServiceMonitorInterceptor());
advisor.setOrder(Ordered.HIGHEST_PRECEDENCE);
return advisor;
}
}