目录
一、学习自定义注解
二、自己编写一个注解,并在controller上使用这个注解,看一下5种通知类型的调用顺序。
三、过滤器、拦截器、AOP的区别
一、学习自定义注解
自定义注解常用在日志,权限,加密等场景下。
我们使用最常见的@SpringbootApplication,看一下注解的定义中会用到哪些注解。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
@Documented 注解
指明修饰的注解,可以被例如javadoc此类的工具文档化,只负责标记,没有成员取值。-
@Retention 注解
指明修饰的注解的生存周期,即会保留到哪个阶段。 RetentionPolicy的取值包含以下三种: SOURCE:源码级别保留,编译后即丢弃 CLASS:编译级别保留,编译后的class文件中存在,在jvm运行时丢弃,这是默认值。 RUNTIME: 运行级别保留,编译后的class文件中存在,在jvm运行时保留,可以被反射调用。
-
@Target 注解
指明了修饰的这个注解的使用范围,即被描述的注解可以用在哪里。 ElementType的取值包含以下几种: TYPE:类,接口或者枚举 FIELD:域,包含枚举常量 METHOD:方法 PARAMETER:参数 CONSTRUCTOR:构造方法 LOCAL_VARIABLE:局部变量 ANNOTATION_TYPE:注解类型 PACKAGE:包
二、自己编写一个注解,并在controller上使用这个注解,看一下5种通知类型的调用顺序。
MyAnnotation.java(我自定义的一个测试注解)
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAnnotation {
String value() default "";
}
MyAnnotationInterceptor.java(自定义测试注解的拦截器)
/**
* MyAnnotation注解对应的拦截器
* AspectJ 支持 5 种类型的通知注解:
*
* @Before: 前置通知, 在方法执行之前执行
* @After: 后置通知, 在方法执行之后执行
* @AfterRunning: 返回通知, 在方法返回结果之后执行
* @AfterThrowing: 异常通知, 在方法抛出异常之后
* @Around: 环绕通知, 围绕着方法执行
**/
@Component
@Aspect
public class MyAnnotationInterceptor {
public static final Logger logger = LoggerFactory.getLogger(MyAnnotationInterceptor.class);
@Pointcut("@annotation(com.lbc.demo.aop.MyAnnotation)")
public void functionAspect() {
}
@Around(value = "functionAspect()")
public void doAround(ProceedingJoinPoint pjp) throws Throwable {
logger.info("around执行方法之前");
Object object = pjp.proceed();
logger.info("around执行方法之后--返回值:" + object);
}
@Before("functionAspect()")
public void doBefore(JoinPoint joinPoint) {
logger.info("doBefore");
}
@After("functionAspect()")
public void doAfter(JoinPoint joinPoint) {
logger.info("doAfter");
}
@AfterReturning(returning = "returnObj", pointcut = "functionAspect()")
public void doAfterReturning(JoinPoint joinPoint, Object returnObj) {
logger.info("AfterReturning");
}
@AfterThrowing(value = "functionAspect()", throwing = "e")
public void doAfterThrowing(JoinPoint joinPoint, Exception e) {
logger.info("AfterThrowing");
}
}
执行结果:
直接放结论:
-
正常情况
-
异常情况
@Order注解的作用:
- @Order(Ordered.HIGHEST_PRECEDENCE)代表这个过滤器在众多过滤器中级别最高,也就是过滤的时候最先执行
- @Order(Ordered.LOWEST_PRECEDENCE)恰恰相反,表示级别最低,最后执行过滤操作
@Inherited注解的作用:
- 类继承关系中@Inherited的作用
类继承关系中,子类会继承父类使用的注解中被@Inherited修饰的注解 - 接口继承关系中@Inherited的作用
接口继承关系中,子接口不会继承父接口中的任何注解,不管父接口中使用的注解有没有被@Inherited修饰 - 类实现接口关系中@Inherited的作用
类实现接口时不会继承任何接口中定义的注解
过滤器、拦截器、AOP的区别
过滤器
过滤器可以拦截到方法的请求和响应(ServletRequest request, SetvletResponse response),
并对请求响应做出响应的过滤操作,比如设置字符编码、鉴权操作。
拦截器
Spring中拦截器有三个方法:preHandle,postHandle,afterCompletion。
public boolean preHandle(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse, Object o)
表示被拦截的URL对应的方法执行前的自定义处理
public void postHandle(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView)
表示此时还未将modelAndView进行渲染,被拦截的URL对应的方法执行后的自定义处理,。
public void afterCompletion(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse, Object o, Exception e)
表示此时modelAndView已被渲染,执行拦截器的自定义处理。
AOP切片
AOP操作可以对操作进行横向的拦截,最大的优势在于可以获取执行方法的参数,对方法进行统一的处理,常见使用日志,事务,请求参数安全验证等。
顺序
请求->>过滤器->>拦截器-->Aspect->>拦截器->>过滤器->>响应
三者使用场景类似
**从过滤器--》拦截器--》切面,拦截规则越来越细致,执行顺序依次是过滤器、拦截器、切面。**
一般情况下数据被过滤的时机越早对服务的性能影响越小,因此我们在编写相对比较公用的代码时,优先考虑过滤器,然后是拦截器,最后是aop。
比如权限校验,一般情况下,所有的请求都需要做登陆校验,此时就应该使用过滤器在最顶层做校验;日志记录,一般日志只会针对部分逻辑做日志记录,
而且牵扯到业务逻辑完成前后的日志记录,因此使用过滤器不能细致地划分模块,此时应该考虑拦截器,
然而拦截器也是依据URL做规则匹配,因此相对来说不够细致,因此我们会考虑到使用AOP实现,AOP可以针对代码的方法级别做拦截,很适合日志功能。
参考:https://www.jianshu.com/p/8cbfff715581
https://zhuanlan.zhihu.com/p/96597358
https://blog.csdn.net/u011277123/article/details/91532149
https://www.jianshu.com/p/7f54e7250be3
https://www.cnblogs.com/natian-ws/p/10824363.html
https://www.cnblogs.com/quartz/p/12601091.html