Web技术 —— Spring中的AOP
Posted Putarmor
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Web技术 —— Spring中的AOP相关的知识,希望对你有一定的参考价值。
AOP
AOP(Aspect-Oriented Programming):面向切片编程,这是一种编程的思想与编程语言的种类无关,接下来我们主要学习Spring中的AOP。
常见的切面业务有:
1.响应统一的数据格式
2.统一的异常处理
3.统一的日志记录,如跟踪用户访问信息
4.用户统一会话管理、权限管理
5.方法执行时间计算
6.事务的管理
。。。
AOP技术优点:
- 解决的最大问题就是对横切业务的统一管理;
- 横切代码的高度复用,和业务代码相互独立;满足了设计上的高内聚低耦合,系统具有更好的扩展性和可维护性。
案例分析
模拟后端的支付业务:
当需要添加日志记录,安全检查和时间统计功能时怎么办呢?
`最简单暴力的方式就是在原类中添加相应的业务,①这会导致类中有大量的重复代码,②对原代码具有较大的侵入性。
对于问题①,可以把公共部分提出模板或者公共的父类方法,然后让调用的类实现公共模版或者继承的公共的父类。
对于问题②,要求实现类统一继承,这会对实现类的代码造成侵入性,因此在不改变实现类代码的基础上可以通过设计模式
来实现。
代理设计模式
静态代理
分为静态代理和动态代理
- 被代理类:原始类不进行任何修改,在创建和使用时,不再使用原始的被代理类,而是设计一个原始类的代理类;
- 代理类:基于被代理类,构造一个代理类,使用时也是使用代理类
静态代理设计模式(了解):该设计模式采用两种手段,继承的方式或者聚合+接口的方式
采用继承模式:通过静态代理类去继承原来的类
采用接口模式:
和被代理类一样都实现同一个接口;
在该类构造方法中传入被代理的对象;
动态代理
在class代码运行时期,动态地织入字节码。Spring中的AOP,主要基于两种方式:JDK和CGLIB,这两种方式的代理目标都是被代理类中的方法,在运行期,动态地织入字节码生成代理类。
JDK:Proxy代理对象
CGLIB:是Java的动态代理框架,主要作用就是根据目标类和方法,动态生成代理类;而动态代理框架几乎都是依赖字节码框架(ASM,Javassist等)实现的,字节码框架是直接操作class字节码的框架,它可以加载已有的字节码文件信息,修改部分信息,或者动态生成一个class;
JDK实现(了解):实现InvocationHandler接口重写方法调用处理器,再通过Proxy来创建代理类。
CGLIB:和JDK流程差不多。。。
Spring AOP大部分可以通过CGLIB实现动态代理,当一个类是final class时就必须使用JDK Proxy实现,因为final修饰的类无法被继承。
Spring中的AOP
Spring AOP由spring-aop,spring-aspects和spring-instrument三个模块组成。
特点
特点:Spring AOP建立在动态代理的基础上,因此Spring对AOP的支持局限于方法拦截,不能实现属性拦截;但是对于AOP而言,既可以实现属性拦截,也可以实现方法拦截。
Spring AOP的AspectJ支持
SpringAOP只提供了AspectJ的注解语法支持,没有真实使用AspectJ的编译器,在运行时还是基于单纯的AOP;也就是说加入spring-aspects依赖包,只是可以用AspectJ的语法,运行时还是基于spring-aop依赖包的动态实现。
AOP术语
描述切面的常用术语有:通知(advice)、切点(pointcut)、连接点(joinpoint)
切面:由切点和通知组成,它既包含了横切逻辑的定义,也包括了连接点的定义;SpringAOP就是负责实施切面的框架,它将切面所定义的横切逻辑织入到切面所指定的连接点中。
连接点:指在应用执行过程中能够插入切面(Aspect)的一个点。
切点:指通知(advice)所要织入的具体位置。
**通知:**总共有5个
- 前置通知:使用@before,通知方法会在目标方法调用之前执行
- 后置通知:使用@after,通知方法会在目标方法返回后或者抛出异常后调用
- 返回之后通知:使用@AfterReturning,通知方法会在目标方法返回后调用
- 抛异常后通知:使用@AfterThrowing,通知方法会在目标方法抛出异常后调用
- 环绕通知:使用@Around,通知包裹了被通知的方法,在被通知的方法通知之前和调用之后执行自定义的行为。
举例:高速公路上的出入口都称为连接点,而某一个我要出的出入口称为切点。
SpringAOP示例
1.先引入spring-aop依赖
2.定义切面(规则)
3.添加通知
被代理类:
package com.example.demo.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/user")
public class UserController {
@RequestMapping("/login")
public int login(String username, String password){
System.out.println("进入了login方法。。。");
return 1;
}
@RequestMapping("/register")
public int register(String username, String password){
System.out.println("进入了register方法。。。");
return 1;
}
}
package com.example.demo.pointcut;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class UserPointcut {
//1.定义切面规则
@Pointcut("execution(* com.example.demo.controller.UserController.*(..))")
public void pointcut(){
}
//2.添加通知
@Before("pointcut()")
public void doBefore(){
System.out.println("执行了前置通知");
}
@After("pointcut()")
public void doAfter(){
System.out.println("执行了后置通知");
}
@AfterReturning("pointcut()")
public void doReturning(){
System.out.println("执行了返回结果之前的通知方法");
}
@AfterThrowing("pointcut()")
public void doThrowing(){
System.out.println("执行了抛出异常之前的通知方法");
}
@Around("pointcut()")
public Object doAround(ProceedingJoinPoint joinPoint){
System.out.println("进入了环绕通知:方法执行之前");
Object ret = null;
try {
//执行被代理的方法
ret = joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("执行了环绕通知:方法执行之后");
return ret;
}
}
当我们访问user/login路由后:
Spring的AOP和拦截器的区别:
拦截器的拦截粒度比较粗,而Spring的AOP拦截粒度更细。
织入
织入是把切面应用到目标对象并创建新的代理对象的过程,切面在指定的连接点被织入到目标对象中,在目标对象的生命周期里有多个点可以织入。
织入时机:类加载时期、编译期、运行期。
目标对象
目标对象(Target Object):被一个或者多个切面所通知的对象,也把它叫做被通知的对象,因为SpringAOP是通过运行时代理实现的,所以这个对象永远是一个被代理的(Proxied)对象。
以上是关于Web技术 —— Spring中的AOP的主要内容,如果未能解决你的问题,请参考以下文章