AOP,面向切面编程,可以不修改原来的代码,增加一些通用的、业务无关的逻辑,例如日志记录性能统计等。但一般都是使用spring框架提供的AOP支持和AspectJ,需要先写好切面逻辑,再在业务方法上加上设计好的注解:
/**
* 1.定义注解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface NILogBefore {
}
@Aspect
public class NILogAnnotationAspect {
/**
* 2.定义切点:在使用NILogBefore注解的地方
*/
@Pointcut("@annotation(com.null01.nonintrusivelog.annotation.NILogBefore)")
private void beforePointCut(){
}
/**
* 3.定义切面:@Before说明要在使用注解前就执行这个方法
*/
@Before("beforePointCut()")
public void before(JoinPoint joinPoint){
//进行日志记录
LogAction.before(joinPoint);
}
}
/**
* 4.切面配置:启用AspectJ自动代理
*/
@Configuration
@EnableAspectJAutoProxy
public class AspectJConfiguration {
@Bean
public NILogAnnotationAspect nILogAnnotationAspect(){
return new NILogAnnotationAspect();
}
}
/**
* 5.在目标方法上使用注解
*/
@RestController
public class TestController{
@NILogBefore
@RequestMapping("/h")
public String hhh(String i,Integer e) throws Exception{
System.err.println("h");
return "h";
}
}
但是一旦不需要这个功能,这些没用的注解就会造成代码污染。想象一下在一个不能装lombok插件的ide里发现了大量lombok注解是多么痛苦。所以最好莫过于规避这些侵入代码的注解。
一、AspectJ的切点表达式
AspectJ是一个很强大的面向切面框架,定义切点的时候,除了可以通过@Pointcut("@annotation(com.null01.nonintrusivelog.annotation.NILogBefore)")
指定注解为切点,还可以通过指示器中的execution()
匹配连接点的执行方法:
/**
* 1.直接定义切面
*/
@Aspect
public class NILogDesignatorAspect {
@Before("execution(* com.null01.nonintrusivelog.TestController.hhh(..))")
public void testBefore(JoinPoint joinPoint){
LogAction.before(joinPoint);
}
}
/**
* 2.切面配置:启用AspectJ自动代理
*/
@Configuration
@EnableAspectJAutoProxy
public class AspectJConfiguration {
@Bean
public NILogDesignatorAspect nILogDesignatorAspect() throws Exception {
return new NILogDesignatorAspect();
}
}
在execution()
指示器中直接指定目标方法,那么目标方法上就不用再加自定义注解了。其中指示器中的是切点表达式:*
表示返回任意类型,com.null01.nonintrusivelog.TestController.hhh
是指定了包名类名的目标方法,..
表示匹配任意参数。虽然切点表达式支持指定某个包某个类中的所有方法(*
表示匹配任意字符),但不支持匹配多个切点,要是遇到切点比较分散的情况,就要定义多个切点方法,比较繁琐。而且注解的参数一定要是编译期常量,写死在指示器上。这意味着配置切点的人一定要懂得切点表达式,而且不能通过设置解析配置文件的方式储存切点。
小小设想实践,更详细的代码见:https://github.com/RoxyJNg/non-intrusive-log