Spring注解
初学spring的时候使用注解总觉得使用注解很神奇,加一个注解就能实现想要的功能,很好奇,也想自己根据需要写一些自己实现的自定义注解。问题来了,自定义注解到底是什么?
查阅资料后发现,注解就是一种标志,单独使用注解,就相当于在类、方法、参数和包上加上一个装饰,什么功能也没有,仅仅是一个标志。这个标志可以加上一些自己定义的参数。
但是可以通过AOP、拦截器等识别出注解标记的类或方法,然后实现相应的增加功能。
创建一个注解的基本元素
- 修饰符:必须为public,默认也为public
- 关键字:@interface
- 注解名称:自定义注解的名称
- 注解类型元素:可以比作是在方法或类上标记注解时传递的参数。
元注解
@Target:描述注解适用的范围
- ElementType.TYPE 应用于类、接口(包括注解类型)、枚举
- ElementType.FIELD 应用于属性(包括枚举中的常量)
- ElementType.METHOD 应用于方法
- ElementType.PARAMETER 应用于方法的形参
- ElementType.CONSTRUCTOR 应用于构造函数
- ElementType.LOCAL_VARIABLE 应用于局部变量
- ElementType.ANNOTATION_TYPE 应用于注解类型
- ElementType.PACKAGE 应用于包
@Retention:表明注解的生命周期
RetentionPolicy.SOURCE 编译时被丢弃,不包含在类文件中 RetentionPolicy.CLASS JVM加载时被丢弃,包含在类文件中,默认值 RetentionPolicy.RUNTIME 由JVM 加载,包含在类文件中,在运行时可以被获取到 @Inherited:@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。
@Documented:表明该注解标记的元素可以被Javadoc 或类似的工具文档化
自定义注解用法
依赖于@Conditional
原理是是否满足@Conditional中绑定类的条件,如果满足,就将使用注解的类注入进factory,不满足就不注入,只能在类中使用或者配合@bean使用
// 查找有标有注解的类
DynamicTp dynamicTp = beanFactory.findAnnotationOnBean(beanName, DynamicTp.class);
配合AOP使用
将注解作为切入点,执行AOP的增加功能。
在spring中可以使用
class.isAnnotationPresent
或者class.getAnnotation
获取类的注解,但是使用AOP代理了这个类后你会发现获取不到注解了。
拿不到自定义注解
补充:其实这样做根本不是拿方法的注解,拿的是
joinPoint.getTarget().getClass()
的注解
// 所切对象的类
Class<?> aClass = joinPoint.getTarget().getClass();
log.info("目标对象:[{}]--[{}]",aClass,aClass.getName());
// 所切方法名
String methodName = joinPoint.getSignature().getName();
log.info("所切方法名:[{}]",methodName);
StringBuilder sb = new StringBuilder();
// 所切方法参数
Object[] args = joinPoint.getArgs();
for(Object arg:args){
sb.append(arg);
}
log.info("所切方法的传入参数列表:[{}]",sb.toString());
Annotation[] annotations = aClass.getAnnotations();
for(Annotation a:annotations){
log.info("所切方法上的注解:[{}] type:[{}]",a,a.annotationType());
}
Signature signature = joinPoint.getSignature();
MyAnnotation annotation = signature.getClass().getAnnotation(MyAnnotation.class);
String args1 = annotation.args();
int num = annotation.num();
log.info("注解MyAnnotation的参数:[{}] [{}]",args1,num);
能拿到注解的方法
上面的方法拿不到注解是因为,直接通过
signture
拿到的是代理的方法,而代理的方法是不会加上原方法的注解的。只有拿到原始的方法才能拿到注解的值。
Signature signature = joinPoint.getSignature(); // 方法签名
Method method = ((MethodSignature) signature).getMethod();
Method realMethod = joinPoint.getTarget().getClass().getDeclaredMethod(signature.getName(), method.getParameterTypes());
MyAnnotation annotation = realMethod.getAnnotation(MyAnnotation.class);
// MyAnnotation annotation = joinPoint.getTarget().getClass().getAnnotation(MyAnnotation.class);
// MyAnnotation annotation = signature.getClass().getAnnotation(MyAnnotation.class);
String args1 = annotation.args();
int num = annotation.num();
log.info("注解MyAnnotation的参数:[{}] [{}]",args1,num);
配合拦截器使用
通过注解决定是否拦截
可以通过检查方法关于注解的信息来决定拦截结果,例如可以根据有无注解拦截或者根据注解的参数决定是否拦截。
以下Demo就是通过判断开发者打上注解时是否添加
authorities = {"admin"}
判断是否要拦截。
// Controller:
@PostMapping("/t")
@InterceptorAnnotation(authorities = {"admin","role"})
public void isAdmin(){
log.info("Controller 执行结束");
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("===== PreHandler开始");
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
InterceptorAnnotation annotation = method.getAnnotation(InterceptorAnnotation.class);
if(annotation == null){
log.info("该方法没有打上InterceptorAnnotation注解");
return true;
}
log.info("该方法打上InterceptorAnnotation注解");
String[] authorities = null;
if(annotation.authorities().length > 0){
authorities = annotation.authorities();
for(String auth:authorities){
if(auth.equals("admin")){
log.info("该用户是admin");
log.info("===== prehandler结束");
return true;
}
}
}
if(authorities == null){
log.info("注解上没有注明用户身份");
return false;
}
log.info("该用户是:[{}]",authorities[0]);
log.info("该用户不是admin");
log.info("===== prehandler结束");
return false;
}