河东手机网站建设,门户网站app开发,网站的二维码怎么做,网站的用户体验#x1f473;我亲爱的各位大佬们好#x1f618;#x1f618;#x1f618; ♨️本篇文章记录的为 AOP的另类用法 (权限校验自定义注解) 相关内容#xff0c;适合在学Java的小白,帮助新手快速上手,也适合复习中#xff0c;面试中的大佬#x1f649;#x1f649… 我亲爱的各位大佬们好 ♨️本篇文章记录的为 AOP的另类用法 (权限校验自定义注解) 相关内容适合在学Java的小白,帮助新手快速上手,也适合复习中面试中的大佬。 ♨️如果文章有什么需要改进的地方还请大佬不吝赐教❤️ 个人主页 : 阿千弟 上期内容 : “速通“ 老生常谈的HashMap [实现原理源码解读] 前言 :
告别了OOP编程, 迎来了一个新的AOP编程时代, 最近有同学问我AOP除了写日志还能干什么, 其实AOP能干的事情挺多的, 可能只是他们写的代码中暂时用不到. 其实如果当我们写一些简单的程序的时候, SpringSecurity完全用不到的时候, 就可以使用AOP与自定义注解来为角色的访问权限进行鉴定, 绝对比Security更轻量. 文章目录前言 :自定义注解AOP回顾基本概念声明一个切入点切入点指示器(PCD)基本通知通知的参数AOP整合自定义注解校验接口权限引入依赖自定义注解定义切面AopController测试结果自定义注解
我们在接触框架的时候经常会用到注解, 但是我们自己自定义注解的情况比较少, 一般都是配合切面编程使用 自定义一个注解非常简单, 它就和创建一个接口一样, 如果我们创建的是一个接口的话, 那么就用interface关键字表示, 如果我们是要自定义一个注解, 那么就在interface关键字前面加上一个符号 Target(ElementType.METHOD)
Retention(RetentionPolicy.RUNTIME)
public interface HasRole {String name() default ;String value() default ;
}自定义注解里面还存在一些属性, 这些属性有点类似与我们的抽象方法, 是不能加方法体的, 加了是会报错的 自定义的属性是可以赋默认值的
// name的默认值是
String name() default ;它的返回类型必须是基础类型或者是String类型, 不可以是封装类型 Target标志自定义的注解的作用范围
类型作用范围TYPE允许被修饰的注解作用在类, 接口和枚举上FIELD允许作用在属性字段上METHOD允许作用在方法上PARAMETER允许作用在方法参数上CONSTRUCTOR允许作用在构造器上LOCAL_VARIABLE允许作用在本地局部变量上ANNOTATION_TYPE允许作用在注解上PACKAGE允许作用在包上
5. Retention 标志自定义的作用是定义被它所注解的注解保留多久(生命周期)
一共有三种策略定义在 RetentionPolicy 枚举中. 从注释上看
类型解释source注解只保留在源文件当 Java 文件编译成 cláss 文件的时候注解被遗弃被编译器忽略class注解被保留到 class 文件但 jvm 加载 class 文件时候被遗弃这是默认的生命周期runtime注解不仅被保存到 class 文件中jym 加载 class 文件之后仍然存在
AOP回顾
基本概念
Aspect切面 一个关注点的模块化这个关注点可能会横切多个对象。事务管理是J2EE应用中一个关于横切关注点的很好的例子。Join point连接点 在程序执行过程中某个特定的点比如某方法调用的时候或者处理异常的时候。Advice通知 在切面的某个特定的连接点Joinpoint上执行的动作。通知有各种类型其中包括“around”、“before”和“after”等通知。 通知的类型将在后面部分进行讨论。许多AOP框架包括Spring都是以拦截器做通知模型 并维护一个以连接点为中心的拦截器链。Pointcut切入点 匹配连接点Joinpoint的断言。通知和一个【切入点表】达式关联并在满足这个切入点的连接点上运行。 【切入点表达式如何和连接点匹配】是AOP的核心Spring缺省使用AspectJ切入点语法。 Introduction引入 Spring允许引入新的接口以及一个对应的实现到任何被代理的对象。例如你可以使用一个引入来使bean实现 IsModified 接口以便简化缓存机制。Target object目标对象被一个或者多个切面aspect所通知advise的对象。也有人把它叫做 被通知advised 对象。 既然Spring AOP是通过运行时代理实现的这个对象永远是一个 被代理proxied 对象。AOP代理 AOP proxy 在Spring中AOP代理可以是JDK动态代理或者CGLIB代理。 8 . Weaving织入) 把切面aspect连接到其它的应用程序类型或者对象上并创建一个被通知advised的对象这个过程叫织入。 这些可以在编译时例如使用AspectJ编译器类加载时和运行时完成。 Spring和其他纯Java AOP框架一样在运行时完成织入。
声明一个切入点
【切入点确定感兴趣的连接点】从而使我们能够控制通知何时运行。 切入点声明由两部分组成包含【名称和方法签名】以及确定我们感兴趣的方法执行的【切入点表达式】。 怎么确定一个方法public void com.ddd.service.impl.*(…)
Pointcut(execution(* transfer(..))) // the pointcut expression
private void anyOldTransfer() {} // the pointcut signature切入点指示器(PCD)
这里只先回顾一下这两个常用的注解
execution: 常用用于匹配方法执行的连接点这是在使用Spring AOP时使用的主要切入点指示符。匹配方法
模式描述public * *(…)任何公共方法的执行cn.javass…IPointcutService.*()cn.javass包及所有子包下IPointcutService接口中的任何无参方法cn.javass….*(…)cn.javass包及所有子包下任何类的任何方法cn.javass…IPointcutService.(*)cn.javass包及所有子包下IPointcutService接口的任何只有一个参数方法(!cn.javass…IPointcutService).(…)非“cn.javass包及所有子包下IPointcutService接口及子类型”的任何方法cn.javass…IPointcutService.()cn.javass包及所有子包下IPointcutService接口及子类型的的任何无参方法cn.javass…IPointcut.test*(java.util.Date)cn.javass包及所有子包下IPointcut前缀类型的的以test开头的只有一个参数类型为java.util.Date的方法注意该匹配是根据方法签名的参数类型进行匹配的而不是根据执行时传入的参数类型决定的.如定义方法public void test(Object obj)即使执行时传入java.util.Date也不会匹配的
annotation: 常用于匹配当前执行方法持有指定注解的方法。方法上的注解 基本通知
注解说明Before前置通知在被切的方法执行前执行After后置通知在被切的方法执行后执行比return更后AfterRunning返回通知在被切的方法return后执行AfterThrowing异常通知在被切的方法抛异常时执行Around环绕通知这是功能最强大的Advice可以自定义执行顺序
通知的参数
任何通知方法都可以声明一个类型为【org.aspectj.lang.JoinPoint】的参数作为它的【第一个参数】注意around通知需要声明类型为( ProceedingJoinPoint 的第一个参数它是【JoinPoint】的一个子类。 【JoinPoint】接口提供了许多有用的方法:
getArgs(): 返回方法参数。getThis(): 返回代理对象。getTarget(): 返回目标对象。getSignature(): 返回被通知的方法的签名。toString(): 打印被建议的方法的有用描述。 AOP整合自定义注解校验接口权限
在我们的业务开发中, 常常会遇到这样的问题 : 每当我们进行一个对数据库的操作, 通常需要鉴权用户是否具有一个正确的访问权限, 然而每次都对不同的业务流程中添加相同的鉴权的执行流程, 势必会造成代码的冗余, 而且也不利于后期代码的拓展
这时候我们可以对鉴权的业务进行切点织入, 织入切面, 化繁为简
引入依赖
pom.xml dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-aop/artifactId/dependencydependencygroupIdcom.google.guava/groupIdartifactIdguava/artifactIdversion29.0-jre/version/dependency自定义注解
这里先模拟一个缓存的类, 因为在真实业务交易权限的过程中我们一般都是把查出的用户数据放在缓存中, 以至于不用每次使用用户信息时都需要从数据库中查询
public class CacheManager {//保存用户和具有的角色之间对应关系public static final MapString, SetString USER_ROLE_MAP new HashMap();static{//用户zhangsan具有user和admin两个角色SetString roleSet3 Sets.newHashSet(admin,user);USER_ROLE_MAP.put(zhangsan,roleSet3);//用户lisi具有user一个角色SetString roleSet4 Sets.newHashSet(user);USER_ROLE_MAP.put(lisi,roleSet4);}}定义切面
【编写方法】声明一个切入点该切入点在匹配连接点时“提供”‘Account’对象值然后从通知中引用指定的切入点
Component
Aspect
public class AopConfig {//定义一个切点通过注解Pointcut(annotation(com.example.aopdemo.annotation.HasRole)))public void pointcut(){}//前置通知Before(pointcut())public void before(JoinPoint joinPoint){System.out.println(before-------------);//获取到HttpServletRequestThreadLocalRequestAttributes requestAttributes RequestContextHolder.getRequestAttributes();ServletRequestAttributes servletRequestAttributes (ServletRequestAttributes) requestAttributes;HttpServletRequest request servletRequestAttributes.getRequest();String username request.getParameter(username);//获取当前用户的角色集合SetString userRoles CacheManager.USER_ROLE_MAP.get(username);//获取当前请求的方法上的注解hasRole中设置的角色MethodSignature signature (MethodSignature)joinPoint.getSignature();//反射获取当前被调用的方法Method method signature.getMethod();//判断当前方法是否有hasRole注解//如果有判断是否用户具有注解属性中要求的角色//如果没有hasRole注解那么说明方法不需要判断用户的角色可以匿名访问HasRole hasRole method.getDeclaredAnnotation(HasRole.class);if(hasRole ! null (userRoles null || !userRoles.contains(hasRole.value()))){throw new RuntimeException(用户没有访问权限);}}
}这里只是配置了一个固定的注解再在pointcut上是不需要判断的。但是如果我们不是指定某一个具体的注解的话那么需要增加判断。因为我们的前置通知里面除了判断接口权限还可以做很多其他的事情
AopController
RestController
public class AopController {//访问这个方法需要用户具有admin的角色HasRole(admin)GetMapping(query1)public String query1(){return query1 需要 admin 角色;}//访问这个方法需要用户具有user的角色HasRole(user)GetMapping(query2)public String query2(){return query2 需要 user 角色;}//任何用户都可以访问GetMapping(query3)public String query3(){return query3 可以匿名访问;}
}测试结果
测试1 因为我们本次的传参是usernamelbw, 然而lbw并没有admin权限, 所以此时我们的AOP生效进行拦截
测试2 测试3 这里可以匿名访问是因为, 我们并没有在query3方法上添加HasRole注解, 这时请求的参数不会走AOP, AOP拦截不生效, 所以可以匿名访问 如果这篇【文章】有帮助到你希望可以给我点个赞创作不易如果有对Java后端或者对spring感兴趣的朋友,请多多关注 个人主页 : 阿千弟