SpringAOP原理
一、AOP概念解释
1.什么是AOP?
“横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块, 并将其命名为"Aspect”,即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共 同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未 来的可操作性和可维护性。
使用"横切"技术,AOP 把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流 程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生 在核心关注点的多处,而各处基本相似,比如权限认证、日志、事物。AOP 的作用在于分离系统 中的各种关注点,将核心关注点和横切关注点分离开来
2.AOP主要应用场景:
- Authentication 权限
- Caching 缓存
- Context passing 内容传递
- Error handling 错误处理
- Lazy loading 懒加载
- Debugging 调试
- logging, tracing, profiling and monitoring 记录跟踪 优化 校准
- Performance optimization 性能优化 9
- Persistence 持久化
- Resource pooling 资源池
- Synchronization 同步
- Transactions 事务
3.AOP核心概念
1、切面(aspect):类是对物体特征的抽象,切面就是对横切关注点的抽象
2、横切关注点:对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点。
3、连接点(joinpoint):被拦截到的点,因为 Spring 只支持方法类型的连接点,所以在 Spring 中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器
4、切入点(pointcut):对连接点进行拦截的定义
5、通知(advice):所谓通知指的就是指拦截到连接点之后要执行的代码,通知分为前置、后置、 异常、最终、环绕通知五类
6、目标对象:代理的目标对象
7、织入(weave):将切面应用到目标对象并导致代理对象创建的过程
8、引入(introduction):在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法 或字段。
AOP通知类型
SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice
4.AOP两种代理方式
Spring 提供了两种方式来生成代理对象: JDKProxy 和 Cglib,具体使用哪种方式生成由 AopProxyFactory 根据 AdvisedSupport 对象的配置来决定。默认的策略是如果目标类是接口, 则使用JDK动态代理技术,否则使用Cglib来生成代理。
JDK动态接口代理
1.JDK 动态代理主要涉及到 java.lang.reflect 包中的两个类:Proxy 和 InvocationHandler。 InvocationHandler是一个接口,通过实现该接口定义横切逻辑,并通过反射机制调用目标类 的代码,动态将横切逻辑和业务逻辑编制在一起。Proxy 利用 InvocationHandler 动态创建 一个符合某一接口的实例,生成目标类的代理对象。
CGLib 动态代理
2.CGLib全称为Code Generation Library,是一个强大的高性能,高质量的代码生成类库, 可以在运行期扩展 Java 类与实现 Java 接口,CGLib 封装了 asm,可以再运行期动态生成新 的 class。和 JDK 动态代理相比较:JDK 创建代理有一个限制,就是只能为接口创建代理实例, 而对于没有通过接口定义业务方法的类,则可以通过CGLib创建动态代理。
二.搭建环境
-
导入spring相关jar包
-
使用maven环境
-
导入需使用依赖
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
xml配置文件引入aop约束:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>
三.实现AOP的三种方法
方式一:使用Spring的API接口
<!--方式一:使用原生Spring API接口-->
<!--配置aop:需要导入aop的约束-->
<aop:config>
<!--切入点expression:表达式,execution(要执行的位置!)-->
<aop:pointcut id="pointcut" expression="execution(* com.nb.service.UserServiceImpl.*(..))"/>
<!--执行环绕增加 把log类切入到UserServiceImpl的方法里-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
代码实现:
UserService接口:
public interface UserService {
public void add();
public void delete();
public void update();
public void query();
}
UserServiceImpl实现类:
public class UserServiceImpl implements UserService{
@Override
public void add() {
System.out.println("增加一个用户");
}
@Override
public void delete() {
System.out.println("删除一个用户");
}
@Override
public void update() {
System.out.println("修改 一个用户");
}
@Override
public void query() {
System.out.println("查询一个用户");
}
}
Log前置日志切入:
//前置日志
public class Log implements MethodBeforeAdvice {
//method:要执行的目标对象的方法
//args:参数
//target:目标对象
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了");
}
}
AfterLog后置日志切入:
//后置日志
public class AfterLog implements AfterReturningAdvice {
//returnValue:返回值
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("执行了"+method.getName()+"方法,返回结果为"+returnValue);
}
}
测试:
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//动态代理代理的是接口
UserService userService = (UserService) context.getBean("userService");
userService.add();
}
}
第二种:自定义实现AOP【切面定义】:
<!--方式二:自定义类-->
<bean id="diy" class="com.bjwl.diy.DiyPointCut"/>
<aop:config>
<!--自定义切面:aspect ref: 要引用的类-->
<aop:aspect ref="diy">
<!--切入点-->
<aop:pointcut id="point" expression="execution(* com.bjwl.service.StudentServiceImpl.*(..))"/>
<!--通知-->
<aop:before pointcut-ref="point" method="before"/>
<aop:before pointcut-ref="point" method="after"/>
</aop:aspect>
</aop:config>
//自定义切面
public class DiyPointCut {
public void before(){
System.out.println("----方法执行前----");
}
public void after(){
System.out.println("----方法执行后----");
}
}
测试:
public class MyTest {
public static void main(String[] args) {
// ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//动态代理代理的是接口
// UserService userService = (UserService) context.getBean("userService");
// userService.add();
ApplicationContext context = new ClassPathXmlApplicationContext("test.xml");
StudentService student = (StudentService) context.getBean("student");
student.add();
}
}
第三种:注解实现AOP:
<!--方式三-->
<bean id="anno" class="com.bjwl.diy.AnnotationPointCut"/>
<!--开启注解支持 JDK(默认):proxy-target-class="false"
cglib:proxy-target-class="true"
-->
<aop:aspectj-autoproxy/>
//注解实现AOP
@Aspect//标注这个类是一个切面
public class AnnotationPointCut {
@Before("execution(* com.bjwl.service.StudentServiceImpl.*(..))")
public void before(){
System.out.println("方法执行前");
}
@After("execution(* com.bjwl.service.StudentServiceImpl.*(..))")
public void after(){
System.out.println("方法执行后");
}
//再环绕增强中,可以给定参数,代表我们要获取处理切入的点
@Around("execution(* com.bjwl.service.StudentServiceImpl.*(..))")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕前");
//执行方法
Object proceed = joinPoint.proceed();
Signature signature = joinPoint.getSignature();//获得签名,类的信息
System.out.println("signature = " + signature);
System.out.println("环绕后");
}
}