一、注解
1、注解原理
Annotation (注解) 是 Java5 开始引入的新特性,可以看作是一种特殊的注释,主要用于修饰类、方法或者变量,提供某些信息供程序在编译或者运行时使用。
注解本质是一个继承了Annotation的特殊接口。
编译器将在编译期扫描每个类或者方法上的注解信息,会将这些信息存在class文件中。
java运行时,采用 JDK 动态代理机制生成一个实现注解(接口)的代理类。使用反射(也就是我们这里的 getAnnotation 方法)获取注解类实例时,返回的是Java运行时生成的动态代理对象$Proxy1。通过代理对象调用自定义注解(接口)的方法,会最终调用AnnotationInvocationHandler的invoke方法。
注解只有被解析之后才会生效,常见的解析方法有两种:
1、编译期直接扫描:编译器在编译 Java 代码的时候扫描对应的注解并处理,比如某个方法使用@Override 注解,编译器在编译的时候就会检测当前的方法是否重写了父类对应的方法。
2、运行期通过反射处理:像框架中自带的注解(比如 Spring 框架的 @Value、@Component)都是通过反射来进行处理的。
2、JDK内置注解
JDK中内置了三个基本的注解:
@Override: 限定重写父类中方法, 该注解只能用于方法;
@Deprecated: 用于表示所修饰的元素(类, 方法等)已过时,通常是因为所修饰的结构危险或存在更好的选择;
@SuppressWarnings: 抑制编译器警告。
3、元注解
元注解就是修饰注解的注解。JDK中有四个元注解:
@Retention:表示注解的生命周期,即注解被保留的阶段。
(1) RetentionPolicy.SOURCE :在源文件中有效(即源文件保留),编译时编译器会直接丢弃这种策略的注解;
(2) RetentionPolicy.CLASS : 在class文件中有效(即class保留),当运行Java程序时, JVM不会保留注解。这是默认值
(3) RetentionPolicy.RUNTIME : 在运行时有效(即运行时保留),当运行 Java 程序时, JVM会保留注解。程序可以通过反射获取该注释。
@Target:表示注解能够作用的位置。
(1) ElementType.TYPE :可以作用在类、接口和枚举类上;
(2) ElementType.METHOD :可以作用在方法上;
(3) ElementType.FIELD :可以作用在成员变量上;
(4) ElementType.CONSTRUCTOR :可以作用在构造器上;
(5) ElementType.LOCAL_VARIABLE :可以作用在局部变量上。
(6) ElementType.ANNOTATION_TYPE:可以作用在注解上;
(7) ElementType.PACKAGE:可以作用在包上;
注:可以指定多个位置,例如:@Target({ElementType.METHOD, ElementType.TYPE}),也就是此注解可以在方法和类上面使用
@Documented:表示该注解修饰的注解,可以被抽取到API文档中。
注意:定义为Documented的注解必须设置Retention值为RetentionPolicy.RUNTIME 。
@Inherited:指定被修饰的Annotation将具有继承性,表示该注解可以被子类继承。
二、AOP方式实现自定义注解
1、定义注解

2、定义AOP切面
@Slf4j
@Component
@Aspect
public class DemoAspect {
/**
* 定义切点
*/
@Pointcut("@annotation(com.wyl.fund.demo.annotation.DemoAnno)")
public void logPointCut() {}
/**
* 环绕通知
* @param joinPoint
* @return
* @throws Throwable
*/
@Around("logPointCut()")
public Object saveLog(ProceedingJoinPoint joinPoint) throws Throwable {
long startTimeMillis = System.currentTimeMillis();
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest httpServletRequest = servletRequestAttributes.getRequest();
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
DemoAnno demoAnno = method.getAnnotation(DemoAnno.class);
log.info("请求链接:{}", httpServletRequest.getRequestURL().toString());
log.info("请求类型:{}", httpServletRequest.getMethod());
log.info("请求方法:{}", signature.getDeclaringTypeName(), signature.getName());
log.info("请求ip:{}", httpServletRequest.getRemoteAddr());
log.info("请求入参:{}", joinPoint.getArgs());
// 处理注解的字段值,并根据字段值 do something!
log.info("demoAnno value: " + demoAnno.value());
log.info("demoAnno des: " + demoAnno.des());
Object proceedResult = null;
try {
// 执行目标方法
proceedResult = joinPoint.proceed();
} catch (Exception e) {
log.error("目标方法执行异常:", e);
throw e;
}
//
long endTimeMillis = System.currentTimeMillis();
log.info("请求耗时:{}ms", endTimeMillis - startTimeMillis);
//
log.info("目标方法执行结果:{}", proceedResult);
return proceedResult;
}
}
3、方法上加注解

三、拦截器方式实现自定义注解
1、定义注解
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DemoAnno {
String value() default "weng";
String des() default "";
}
2、定义拦截器
@Component
@Slf4j
public class DemoInterceptor extends HandlerInterceptorAdapter {
//before the actual handler will be executed
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HandlerMethod handlerMethod = (HandlerMethod)handler;
Method method = handlerMethod.getMethod();//获得被拦截的方法对象
DemoAnno demoAnno = method.getAnnotation(DemoAnno.class);//获得方法上的注解
if(demoAnno != null){
//方法上加了自定义注解,需要进行日志记录
long startTime = System.currentTimeMillis();
request.setAttribute("startTime", startTime);
log.info("[" + handler + "] executeTime开始");
return true;
}
return false;
}
//after the handler is executed
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
HandlerMethod handlerMethod = (HandlerMethod)handler;
Method method = handlerMethod.getMethod();//获得被拦截的方法对象
DemoAnno demoAnno = method.getAnnotation(DemoAnno.class);//获得方法上的注解
String requestUri = request.getRequestURI();
String methodName = method.getDeclaringClass().getName() + "." +
method.getName();
String methodDesc = demoAnno.des();
log.info("请求uri:" + requestUri);
log.info("请求方法名:" + methodName);
log.info("方法描述:" + methodDesc);
long startTime = (Long)request.getAttribute("startTime");
long endTime = System.currentTimeMillis();
//统计耗时
long executeTime = endTime - startTime;
log.info("[" + handler + "] executeTime结束 : " + executeTime + "ms");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
log.info("[" + handler + "] afterCompletion结束 ");
}
}
3、配置拦截器
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new DemoInterceptor()).addPathPatterns("/**");
// .excludePathPatterns("/user/get")/.addPathPatterns(). 可以排除和指定设置
}
}
4、方法上加注解

本文详细介绍了Java中的注解原理,包括其作用、JDK内置注解如@Override、@Deprecated等,以及AOP方式实现自定义注解和拦截器的示例。元注解和注解的生命周期也得到了讨论。
1070

被折叠的 条评论
为什么被折叠?



