1.元注解
java有四种元注解:@Retention、@Inherited、@Documented、@Target,他们是注解的注解。
①@Retention是注解的保留策略,共有三种
@Retention(RetentionPolicy.SOURCE) 注解仅存在于源码中,在class字节码文件中不包含
@Retention(RetentionPolicy.CLASS) 默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得
@Retention(RetentionPolicy.RUNTIME) 注解会在class字节码文件中存在,在运行时可以通过反射获取到
②@Inherited表示该注解可以被继承
③@Documented表示一个简单的Annotations标记注解,表示是否将注解信息添加在java文档中。
④ @Target(ElementType.TYPE) 接口、类、枚举、注解
@Target(ElementType.FIELD) 字段、枚举的常量
@Target(ElementType.METHOD) 方法
@Target(ElementType.PARAMETER) 方法参数
@Target(ElementType.CONSTRUCTOR) 构造函数
@Target(ElementType.LOCAL_VARIABLE) 局部变量
@Target(ElementType.ANNOTATION_TYPE) 注解
@Target(ElementType.PACKAGE) 包
2.注解参数支持的数据类型:
1.所有基本数据类型(int,float,boolean,byte,double,char,long,short)
2.String类型
3.Class类型
4.enum类型
5.Annotation类型
6.以上所有类型的数组
Annotation类型中的参数只能用public或者默认的default修饰。如果只有一个参数,最好把参数名称设为 value,这样在使用注解时,参数直接写即可。
下面是一个自定义注解的应用场景:使用自定义注解和拦截器处理权限问题:
- 自定义一个权限注解,有角色和权限两个属性
@Document @Inherited @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface PermissionAnnotation { String roles(); String permissions(); }
2.在controller中使用注解,并为每一个方法设置需要校验的角色和权限
@RestController public class UserController { @Autowired private UserService userService; @PermissionAnnotation(roles = "admin", permissions = "user:insert") @RequestMapping("insert") public String insert(){ User user = new User(3,"三上悠亚",20); userService.insert(user); return "插入成功"; } @PermissionAnnotation(roles = "admin",permissions = "user:delete") @RequestMapping("delete") public String delete(){ userService.delete(); return "删除成功"; } @PermissionAnnotation(roles = "admin",permissions = "user:update") @RequestMapping("update") public String update(){ userService.update(); return "修改成功"; } @PermissionAnnotation(roles = "visitor",permissions = "user:find") @RequestMapping("find") public String find(){ List<User> byId = userService.findByName("铃木心春"); return "查询成功成功"; } @PermissionAnnotation(roles = "visitor",permissions = "user:find") @RequestMapping("findOne") public String findById(){ User user = userService.findById(1); return "查询单个成功"; } }
3.自定义一个拦截器,在拦截器中获取注解的信息,并完成校验
@Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if(!(handler instanceof HandlerMethod)){ return false; } HandlerMethod handlerMethod= (HandlerMethod) handler; Method method = handlerMethod.getMethod(); String name = method.getName(); PermissionAnnotation annotation = method.getAnnotation(PermissionAnnotation.class); String permissions = annotation.permissions(); String roles = annotation.roles(); //模拟从数据库查询出当前登录用户的角色和权限(实际应从数据库中查询用户对应的角色和权限) String currentPer = "user:insert"; String currentRol = "admin"; if(!permissions.equals(currentPer)||!roles.equals(currentRol)){ response.getWriter().write("permission denied"); return false; } return true;
4.验证结果
当访问insert接口时,程序正常执行返回最终结果。
当访问其他接口时,页面显示permission denied。
自定义注解配合spring aop实现统一处理缓存
再介绍一个应用场景。在开发中经常要把查出来的数据存入redis缓存。但是这些写入缓存的代码和业务逻辑并没有太大的关系,如果可以将这些重复的、和业务无关的代码统一抽取出来。
首先创建一个自定义注解
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @Inherited @Documented public @interface RedisAnnotation { }
其次准备好一个RedisClient类。然后使用spring aop 配置切点,使用环绕通知将返回结果进行切面处理
@Aspect @Component public class RedisAspect { @Autowired private RedisClient redisClient; //监控被RedisAnotation标记的方法(@annotation:用于匹配当前执行方法持有指定注解的方法;) @Pointcut("@annotation(RedisAnnotation)") public void setJoinPoint(){} @Around(value = "setJoinPoint()")//环绕通知=前置通知+目标方法执行+后置通知 public Object aroundMethod(ProceedingJoinPoint proceedingJoinPoint){ Object result = null; try { //执行目标方法 result = proceedingJoinPoint.proceed(); //获取被注解标记的方法参数 Object[] args = proceedingJoinPoint.getArgs(); redisClient.set(result.getClass().getName()+"-"+args[0],result); }catch (Throwable e) { } return result; } }
最后是一段业务代码
@Override @RedisAnnotation public List<User> findByName(String name) { Criteria criteria = Criteria.where("name").is("三上悠亚"); Query query = Query.query(criteria); List<User> list = mongoTemplate.find(query, User.class); return list; }
由于findByName方法被注解RedisAnnotation标记,此方法的返回结果会被aop切面获取到,从而进行redis缓存处理。
这样的话通过注解实现优化代码的作用。