1 JDK元注解
所谓元注解,可以理解为JDK内部自带的注解,就好比几个包装类一样(String、Integer等),是一切注解的依赖注解,并且在JDK1.5之后可以直接使用,以下罗列了这几个注解:
- @Retention
- @Documented
- @Target
- @Inherited
- @Repeatable
具体每个注解都有什么作用,请看下文
1.1 @Retention
它的作用说明这个注解的存活时间
public enum RetentionPolicy {
/**
* 只在源码中可见,编译时丢弃
*/
SOURCE,
/**
* 默认值,编译时被编译器记录在类文件中,但在运行时不被虚拟机保留
*/
CLASS,
/**
* 编译记录在类文件中由虚拟机在运行时保留,因此它们可能被反射式读取
*/
RUNTIME
}
1.2 @Documented
它的作用是能够将注解中的元素包含到 Javadoc 中去。
1.3 @Target
指定了注解运用的地方,比如是只能放在方法上还是类上还是都能放。
public enum ElementType {
/** 能放在类、接口、枚举上 */
TYPE,
/** 能放在字段上 */
FIELD,
/** 能放在方法上 */
METHOD,
/** 能放在方法的参数上 */
PARAMETER,
/** 能放在构造器上 */
CONSTRUCTOR,
/** 能放在局部变量上 */
LOCAL_VARIABLE,
/** 能放在注解上 */
ANNOTATION_TYPE,
/** 能放在包上 */
PACKAGE,
/**
* 只针对类型参数TypeParameterClass<@TypeParameterAnnotation T>
* @since 1.8
*/
TYPE_PARAMETER,
/**
* 能在局部变量、泛型类、父类和接口的实现处使用,甚至能在方法上声明异常的地方使用
* @since 1.8
*/
TYPE_USE
}
1.4 @Inherited
@Inherited修饰的注解,只有作用在类上时,会被子类继承此自定义的注解,其余情况都不会继承。
1.5 @Repeatable
@Repeatable是java1.8加进来的,表示的是可重复。
1.6 其他常见的原生注解
- @Override:用于修饰此方法覆盖了父类的方法;
- @Deprecated:用于修饰已经过时的方法;
- @SuppressWarnnings:用于通知java编译器禁止特定的编译警告。
2自定义注解
2.1 简单使用
自定义注解规则:
[元注解]
public @interface [注解名称] {
[值类型] [值的key]() default [key的默认值];
[值类型] [值的key]();
...
}
自定义注解实践:
/**
* @desc: 类注解
* @author: YanMingXin
* @create: 2022/4/3-10:52
**/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface YmxClazz {
int level() default 10;
String name() default "vip";
}
/**
* @desc: 方法注解
* @author: YanMingXin
* @create: 2022/4/5-8:07
**/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface YmxMethod {
boolean isVip() default true;
}
/**
* @desc: 字段注解
* @author: YanMingXin
* @create: 2022/4/3-10:52
**/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface YmxValue {
String strValue() default "";
int intValue() default 0;
}
使用自定义注解:
/**
* @desc:
* @author: YanMingXin
* @create: 2022/4/3-10:52
**/
@YmxClazz(name = "ymx", level = 999)
public class StudentController {
@YmxValue(strValue = "yyy")
private String val;
@YmxMethod
public String methodA() {
return "ymx";
}
}
验证方法:
/**
* @desc: 验证自定义注解
* @author: YanMingXin
* @create: 2022/4/3-10:52
**/
public class Main {
public static void main(String[] args) throws Exception {
StudentController controller = new StudentController();
isYmxClass(controller);
isYmxFiled(controller);
isYmxMethod(controller);
}
public static void isYmxClass(Object obj) {
Class<?> clazz = obj.getClass();
String name = null;
int level = -1;
//获取类模板
Annotation[] annotations = clazz.getAnnotations();
for (Annotation annotation : annotations) {
if (annotation instanceof YmxClazz) {
name = ((YmxClazz) annotation).name();
level = ((YmxClazz) annotation).level();
}
}
System.out.println("name=" + name + ",level=" + level);
}
public static void isYmxFiled(Object obj) {
Field[] fields = obj.getClass().getDeclaredFields();
for (Field field : fields) {
int intValue = 0;
String strValue = null;
for (Annotation annotation : field.getAnnotations()) {
if (annotation instanceof YmxValue) {
intValue = ((YmxValue) annotation).intValue();
strValue = ((YmxValue) annotation).strValue();
}
}
System.out.println("intVal=" + intValue + ",strVal=" + strValue);
}
}
public static void isYmxMethod(Object obj) {
Method[] methods = obj.getClass().getDeclaredMethods();
for (Method method : methods) {
boolean vip = false;
for (Annotation annotation : method.getAnnotations()) {
if (annotation instanceof YmxMethod) {
vip = ((YmxMethod) annotation).isVip();
}
}
System.out.println(vip);
}
}
}
运行结果:
2.2 总结说明
以上的演示仅为了能体现出获取注解值的流程,在实际的项目使用中可能会比以上稍微复杂,但归根结底都是利用的Java反射机制,我们可以理解为Java的注解和反射是不一定是相辅相成的,没有注解的反射还是反射,但是没有反射的注解可能就没用用武之地。
3实战:自定义注解实现拦截器判断
3.1 回顾Spring Boot自定义拦截器
3.1.1 需求
我们首先定义一个Controller,设置三个方法,分别为thank()、please()、sorry()
然后我们自定义拦截器,拦截全部请求,除了一个sorry的请求,因为这个要在用户被拦截时让他们知道,代码如下
3.1.2 代码
UserController.java
@RestController
@RequestMapping("/user")
public class UserController {
@RequestMapping("/thank")
public String thank() {
return "Thanks!";
}
@RequestMapping("/please")
public String please() {
return "Please!";
}
@RequestMapping("/sorry")
public String sorry() {
return "Sorry,You've been intercepted~";
}
}
AppWebInterceptor.java
@Component
public class AppWebInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
response.sendRedirect("/user/sorry");
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
AppWebInterceptorConfig.java
@Configuration
public class AppWebInterceptorConfig extends WebMvcConfigurationSupport {
/**
* 注入自定义拦截器
*/
@Autowired
private AppWebInterceptor appWebInterceptor;
/**
* 配置拦截器和拦截、放行路径
*
* @param registry
*/
@Override
protected void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(appWebInterceptor)
.excludePathPatterns("/user/sorry")
.addPathPatterns("/**");
}
}
这样的话我们无论请求/user/thank还是/user/please都会被拦截然后跳转到/user/sorry,所以自定义注解登场!
3.2 创建自定义注解
如下,无需多言了吧
/**
* @desc: 自定义注解
* @author: YanMingXin
* @create: 2022/4/5-11:01
**/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface NoIntercept {
/**
* 该参数表示再次确认:
* 1.加上@NoIntercept表示不拦截
* 2.isReal()会进行再次确认,就好比问一句“确定不拦截吗?”
* 默认的回答是‘true’代表‘确定’,值为‘false’时是‘不确定’
*
* @return
*/
boolean isReal() default true;
}
3.3 配置拦截规则
3.3.1 规则定义
因为这个注解的@Target({ElementType.TYPE, ElementType.METHOD}),所以它既能在类上使用也能在方法上使用,因此我们定义下规则:
- @NoIntercept标注的Controller类下所有方法均不拦截。
- 没有@NoIntercept标注的Controller类或者@NoIntercept(isReal=false)情况下方法上有@NoIntercept标注则不拦截,否则进行拦截。
3.3.2 规则代码
我们修改AppWebInterceptor类的preHandle方法,实现上面的规则:
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
boolean clazzIsAccess = false;
//为什么要分割下,见下图
String[] str = handler.toString().split("#");
Class<?> clazz = Class.forName(str[0]);
if (str[1].length() <= 2) {
return false;
}
String handlerMethodName = str[1].substring(0, str[1].length() - 2);
Annotation[] clazzAnnotations = clazz.getDeclaredAnnotations();
for (Annotation annotation : clazzAnnotations) {
if (annotation instanceof NoIntercept) {
clazzIsAccess = ((NoIntercept) annotation).isReal() ? true : false;
}
}
if (clazzIsAccess) {
return true;
}
Method[] clazzDeclaredMethods = clazz.getDeclaredMethods();
for (Method method : clazzDeclaredMethods) {
if (method.getName().equals(handlerMethodName)) {
Annotation[] annotations = method.getDeclaredAnnotations();
for (Annotation annotation : annotations) {
if (annotation instanceof NoIntercept) {
return ((NoIntercept) annotation).isReal() || clazzIsAccess ? true : false;
}
}
}
}
response.sendRedirect("/user/sorry");
return false;
}
3.3.3 验证规则
(1)我们将UserController类打上@NoIntercept注解:
@NoIntercept
@RestController
@RequestMapping("/user")
public class UserController {
......
}
测试:
(2)我们将UserController类打上@NoIntercept(isReal = false)注解:
@NoIntercept(isReal = false)
@RestController
@RequestMapping("/user")
public class UserController {
......
}
测试(什么都没有显示就是被拦截了,因为包含了重定向,终端不支持):
(3)我们将UserController类打上@NoIntercept(isReal = false)注解,将please方法打上@NoIntercept注解:
@NoIntercept(isReal = false)
@RestController
@RequestMapping("/user")
public class UserController {
@RequestMapping("/thank")
public String thank() {
return "Thanks!";
}
@NoIntercept
@RequestMapping("/please")
public String please() {
return "Please!";
}
@RequestMapping("/sorry")
public String sorry() {
return "Sorry,You've been intercepted~";
}
}
测试:
3.4 探究Spring内置注解解析方式
以上的代码和案例是不是很优雅,但是这件事可能早就被Spring知道了,因此在Spring中有更加简便的方式,我们来实现下:
还是修改AppWebInterceptor类的preHandle方法(为了方便起见,这里只演示放在方法上的注解):
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
NoIntercept annotation;
if (handler instanceof HandlerMethod) {
annotation = ((HandlerMethod) handler).getMethodAnnotation(NoIntercept.class);
} else {
return true;
}
if(annotation!=null) {
return true;
}
response.sendRedirect("/user/sorry");
return false;
}
测试:
就是这么简单~
网友评论