九讯鹿网站建设,企业营销策划案例分析,网页升级访问永久你懂的,黑龙江人事考试网文章目录1. Spring AOP 是什么2. 为什么要用 AOP3. 怎么学 Spring AOP4. AOP 组成5. Spring AOP 实现5.1 添加 Spring AOP 框架支持5.2 定义切面和切点5.3 实现通知方法5.4 使⽤ AOP 统计 UserController 每个⽅法的执⾏时间 StopWatch5.4 切点表达式说明 AspectJ6. Spring AOP…
文章目录1. Spring AOP 是什么2. 为什么要用 AOP3. 怎么学 Spring AOP4. AOP 组成5. Spring AOP 实现5.1 添加 Spring AOP 框架支持5.2 定义切面和切点5.3 实现通知方法5.4 使⽤ AOP 统计 UserController 每个⽅法的执⾏时间 StopWatch5.4 切点表达式说明 AspectJ6. Spring AOP 实现原理6.1 生成代理的时机 织入Weaving6.2 JDK 动态代理实现6.3 CGLIB 动态代理实现6.4 JDK 和 CGLIB 实现的区别1. Spring AOP 是什么
学习 Spring AOP 之前先要了解 AOP 是什么
AOPAspect Oriented Programming面向切面编程它和 OOP面向对象编程类似。
它是一种思想是对某一类事情的集中处理。
比如用户登录权限的效验在学习 AOP 之前在需要判断用户登录的页面都要各自实现或调用用户验证的方法学习 AOP 之后我们只需要在某一处配置一下那么所有需要判断用户登录的页面就全部可以实现用户登录验证了不用在每个方法中都写用户登录验证了 AOP 是一种思想而 Spring AOP 是实现框架这种关系和 IOC思想与 DI实现类似 2. 为什么要用 AOP
高频对于这种功能统一且使用地方较多的功能可以考虑用 AOP 来处理比如 用户登录验证使⽤ AOP 可以扩充多个对象的某个能⼒AOP 可以说是 OOP 的补充和完善比如 现在要实现的业务和这个通用的功能没什么关系但处于安全考虑又必须进行登录的验证
除了统一的用户登录判断外AOP 还可以实现
统一日志处理统一方法执行时间统计统一的返回格式设置统一的异常处理事务的开启和提交等 3. 怎么学 Spring AOP
Spring AOP 学习主要分为3个部分
学习 AOP 是如何组成的学习 Spring AOP 使用学习 Spring AOP 实现原理 4. AOP 组成
1切面Aspect
定义 AOP 是针对某个统一的功能的这个功能就叫做一个切面比如用户登录功能或方法的统计日志他们就各是一个切面。切面是由切点和通知组成的
2连接点Join Point
所有可能触发 AOP拦截方法的点就称为连接点
3切点Pointcut
切点的作用就是提供一组规则来匹配连接点给满足规则的连接点添加通知总的来说就是定义 AOP 拦截的规则的
切点相当于保存了众多连接点的一个集合如果把切点看成一个表而连接点就是表中一条一条的数据
4通知Advice
切面的工作就是通知
通知规定了 AOP 执行的时机和执行的方法
Spring 切面类中可以在方法上使用以下注解会设置方法为通知方法在满足条件后悔通知本方法进行调用
前置通知 Before通知方法会在目标方法调用之前执行后置通知 After通知方法会在目标方法返回或者抛出异常后调用返回之后通知 AfterReturning通知方法会在目标方法返回后调用抛异常后通知AfterThrowing通知方法会在目标方法爬出异常之后调用环绕通知Around通知包裹了被通知的方法在被通知的方法通知之前和调用之后执行自定义的行为
举个例子在一个生产型公司中
通知相当于底层的执行者切点是小领导制定规则切面是大领导制定公司的发展方向连接点是属于一个普通的消费者用户
以多个⻚⾯都要访问⽤户登录权限为例子AOP 整个组成部分如图所示 5. Spring AOP 实现
Spring AOP 实现步骤
添加 Spring AOP 框架支持定义切面和切点实现通知
接下来我们使⽤ Spring AOP 来实现⼀下 AOP 的功能完成的⽬标是拦截所有 UserController ⾥⾯的
⽅法每次调⽤ UserController 中任意⼀个⽅法时都执⾏相应的通知事件。
5.1 添加 Spring AOP 框架支持
在中央仓库中搜锁 Spring AOP Maven Repository: Search/Browse/Explore (mvnrepository.com) 在 pom.xml 中添加如下配置
!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-aop --
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-aop/artifactId
/dependency5.2 定义切面和切点
Aspect // 当前类是一个切面
Component
public class UserAspect {// 定义一个切点设置拦截规则Pointcut(execution(* com.example.springaop.controller.UserController.*(..)))public void pointcut() {}
}5.3 实现通知方法
前置通知 Before通知方法会在目标方法调用之前执行后置通知 After通知方法会在目标方法返回或者抛出异常后调用返回之后通知 AfterReturning通知方法会在目标方法返回后调用抛异常后通知AfterThrowing通知方法会在目标方法爬出异常之后调用环绕通知Around通知包裹了被通知的方法在被通知的方法通知之前和调用之后执行自定义的行为
实现通知方法也就是在什么时机执行什么方法
Aspect // 当前类是一个切面
Component
public class UserAspect {// 定义一个切点设置拦截规则Pointcut(execution(* com.example.springaop.controller.UserController.*(..)))public void pointcut() {}// 定义 pointcut 切点的前置通知Before(pointcut())public void doBefore() {System.out.println(执行前置通知);}// 后置通知After(pointcut())public void doAfter() {System.out.println(执行后置通知);}// 返回之后通知AfterReturning(pointcut())public void doAfterReturning() {System.out.println(执行返回之后通知);}// 抛出异常之后通知AfterThrowing(pointcut())public void doAfterThrowing() {System.out.println(执行抛出异常之后通知);}
}RestController
RequestMapping(/user)
public class UserController {RequestMapping(/sayhi)public String sayHi() {System.out.println(sayhi 方法被执行);int num 10/0;return 你好java;}RequestMapping(/sayhi2)public String sayHi2() {System.out.println(sayhi2 方法被执行);return 你好java2;}
}环绕通知Around通知包裹了被通知的方法在被通知的方法通知之前和调用之后执行自定义的行为
// 添加环绕通知
Around(pointcut())
public Object doAround(ProceedingJoinPoint joinPoint) {Object result null;System.out.println(环绕通知前置方法);try {// 执行拦截方法result joinPoint.proceed();} catch (Throwable e) {e.printStackTrace();}System.out.println(环绕通知后置方法);return result;
}5.4 使⽤ AOP 统计 UserController 每个⽅法的执⾏时间 StopWatch
Spring AOP 中统计时间用 StopWatch 对象 // 添加环绕通知Around(pointcut())public Object doAround(ProceedingJoinPoint joinPoint) {// spring 中的时间统计对象StopWatch stopWatch new StopWatch();Object result null;try {stopWatch.start(); // 统计方法的执行时间开始计时// 执行目标方法以及目标方法所对应的相应通知result joinPoint.proceed();stopWatch.stop(); // 统计方法的执行时间停止计时} catch (Throwable e) {e.printStackTrace();}System.out.println(joinPoint.getSignature().getDeclaringTypeName() . joinPoint.getSignature().getName() 执行花费的时间 stopWatch.getTotalTimeMillis() ms);return result;}5.4 切点表达式说明 AspectJ
AspectJ 表达式语法SpringAOP AspectJ Pointcut(execution(* com.example.springaop.controller.UserController.*(..)))AspectJ 语法Spring AOP 切点的匹配语法
切点表达式由切点函数组成其中 execution() 是最常⽤的切点函数⽤来匹配⽅法语法为 execution(修饰符返回类型包.类.⽅法(参数)异常) AspectJ ⽀持三种通配符
* 匹配任意字符只匹配⼀个元素包类或⽅法⽅法参数
… 匹配任意字符可以匹配多个元素 在表示类时必须和 * 联合使⽤。 表示按照类型匹配指定类的所有类必须跟在类名后⾯如 com.cad.Car ,表示继承该类的所有⼦类包括本身
修饰符一般省略
public 公共方法*任意
返回值不能省略
void 返回没有值String 返回值字符串*任意
包通常不省略但可以省略
com.gyf.crm 固定包com.gyf.crm.*.service crm 包下面子包任意例如com.gyf.crm.staff.servicecom.gyf.crm… crm 包下面的所有子包含自己com.gyf.crm.*service… crm 包下面任意子包固定目录 serviceservice 目录任意包
类通常不省略但可以省略
UserServiceImpl 指定类
*Impl 以 Impl 结尾
User* 以 User 开头
*任意
方法名不能省略
addUser 固定方法
add* 以 add 开头
*DO 以 DO 结尾
*任意
参数
() 无参
(int) 一个整形
(int,int)两个整型
(…) 参数任意
throws可省略一般不写
表达式示例
execution(* com.cad.demo.User.*(…)) 匹配 User 类⾥的所有⽅法execution(* com.cad.demo.User.*(…)) 匹配该类的⼦类包括该类的所有⽅法execution(* com.cad..(…)) 匹配 com.cad 包下的所有类的所有⽅法execution(* com.cad….(…)) 匹配 com.cad 包下、⼦孙包下所有类的所有⽅法execution(* addUser(String, int)) 匹配 addUser ⽅法且第⼀个参数类型是 String第⼆个参数类型是 int 6. Spring AOP 实现原理
Spring AOP 是构建在动态代理基础上因此 Spring 对 AOP 的支持局限于方法级别的拦截 Spring AOP 动态代理实现
默认情况下实现了接⼝的类使⽤ AOP 会基于 JDK ⽣成代理类没有实现接⼝的类会基于 CGLIB ⽣成代理类 JDK ProxyJDK 动态代理 CGLIB Proxy默认情况下 Spring AOP 都会采用 CGLIB 来实现动态代理因为效率高 CGLIB 实现原理通过继承代理对象来实现动态代理的子类拥有父类的所有功能 CGLIB 缺点不能代理最终类也就是被 final 修饰的类
6.1 生成代理的时机 织入Weaving
织入是把切面应用到目标对象并创建新的代理对象的过程切面在指定的连接点被织入到目标对象中
在目标对象的生命周期中有多个点可以进行织入
编译期切面在目标类编译时被织入这种方法需要特殊的编译器AspectJ 的织入编译器就是以这种方式织入切面的类加载期切面在目标类加载到 JVM 时被织入这种方式需要特殊的类加载器它可以在目标类被引入应用之前增强该目标类的字节码AspectJ5 的加载时织入 load-time weaving. LTW就支持以这种方式织入切面运行期切面在应用运行的某一时刻被织入一般情况下在织入切面时AOP容器会为目标对象动态创建一个代理对象Spring AOP 就是以这种方式织入切面的
6.2 JDK 动态代理实现
JDK 动态代理就是依靠反射来实现的
//动态代理使⽤JDK提供的apiInvocationHandler、Proxy实现此种⽅式实现要求被
代理类必须实现接⼝
public class PayServiceJDKInvocationHandler implements InvocationHandler {//⽬标对象即就是被代理对象private Object target;public PayServiceJDKInvocationHandler( Object target) {this.target target;}//proxy代理对象Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//1.安全检查System.out.println(安全检查);//2.记录⽇志System.out.println(记录⽇志);//3.时间统计开始System.out.println(记录开始时间);//通过反射调⽤被代理类的⽅法Object retVal method.invoke(target, args);//4.时间统计结束System.out.println(记录结束时间);return retVal;}public static void main(String[] args) {PayService target new AliPayService();//⽅法调⽤处理器InvocationHandler handler new PayServiceJDKInvocationHandler(target);//创建⼀个代理类通过被代理类、被代理实现的接⼝、⽅法调⽤处理器来创建PayService proxy (PayService) Proxy.newProxyInstance(target.getClass().getClassLoader(),new Class[]{PayService.class},handler);proxy.pay();}
}6.3 CGLIB 动态代理实现
public class PayServiceCGLIBInterceptor implements MethodInterceptor {//被代理对象private Object target;public PayServiceCGLIBInterceptor(Object target){this.target target;}Overridepublic Object intercept(Object o, Method method, Object[] args, MethodProxymethodProxy)throws Throwable {//1.安全检查System.out.println(安全检查);//2.记录⽇志System.out.println(记录⽇志);//3.时间统计开始System.out.println(记录开始时间);//通过cglib的代理⽅法调⽤Object retVal methodProxy.invoke(target, args);//4.时间统计结束System.out.println(记录结束时间);return retVal;}public static void main(String[] args) {PayService target new AliPayService();PayService proxy (PayService) Enhancer.create(target.getClass(),new PayServiceCGLIBInterceptor(target));proxy.pay();}
}6.4 JDK 和 CGLIB 实现的区别
JDK 实现要求被代理类必须实现接口之后是通过 InvocationHander 及 Proxy在运行时动态的在内存中生成了代理对象该代理对象是通过实现同样的接口实现类似静态代理接口实现的方式只是该代理类是在运行期时动态的织入统一的业务逻辑字节码来完成的CGLIB 实现被代理类可以不实现接口是通过继承被代理类在运行时动态的生成代理类对象这种方式实现方式效率高