引言:AOP是什么?
在软件业,AOP(Aspect Oriented Programming)为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。
主要意思是我们的传统架构至上而下,后期想要从中间切入写代码添加功能,比如日志、权限、事务等,这时候我们就需要写使用代理模式写,而springaop就是专门把这一块写代码的地方给抽象了出来称为切面,从而解决的代码混乱的问题,它的底层实现也是基于代理模式(动态代理)完成的。
利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
实现步骤:将程序中重复的代码抽取出来(例如一个方法),使其成为一个切面,在需要使用某个方法的时候,通过动态代理
在不修改源码的基础上,利用该切面实现对该方法的增强。
AOP的优势:
1.减少重复代码,对代码中重复部分进行抽取
2.提高开发效率,简化代码量
3.维护方便,修改方法的增强部分,不用修改已有方法
AOP相关术语
- Joinpoint(连接点),业务层中所有的方法
- Pointcut(切入点),业务层中需要增强的方法
- Advice(通知/增强),指拦截到 Joinpoint 之后所要做的事情就是通知。通知的类型有:前置通知,后置通知,异常通知,最终通知,环绕通知
- Introduction(引介),引介是一种特殊的通知在不修改类代码的前提下, Introduction 可以在运行期为类动态地添加一些方法或 Field。
- Target(目标对象),代理的目标对象
- Weaving(织入),是指把增强应用到目标对象来创建新的代理对象的过程。spring 采用动态代理织入,而 AspectJ 采用编译期织入和类装载期织入
- Proxy(代理),一个类被 AOP 织入增强后,就产生一个结果代理类
- Aspect(切面),是切入点和通知(引介)的结合
AOP开发步骤:
-
编写核心业务代码。
-
抽取公共代码,制作成通知。
-
在配置文件中,声明切入点与通知间的关系,即切面。
SpringAOP的主要应用场景:
- 日志记录
2.权限验证(SpringSecurity有使用)
3.事务控制(调用方法前开启事务, 调用方法后提交关闭事务 )
4.效率检查(检测方法运行时间)
5.数据源代理(seata里面,获取到数据源连接执行的sql)
6.缓存优化 (第一次调用查询数据库,将查询结果放入内存对象, 第二次调用, 直接从内存对象返回,不需要查询数据库 )
JDK动态代理是通过java.lang.reflect.Proxy 类来实现的,我们可以调用Proxy类的newProxyInstance()方法来创建代理对象。
对于使用业务接口的类,Spring默认会使用JDK动态代理来实现AOP。
接下来的案例演示 Spring 中 JDK 动态代理的实现过程。
实现步骤:
- 在src目录下创建两个包【com.jxust.jdk包和com.jxust.aspect包】
- 在com.jxust.jdk包下,创建接口UserDao,在该接口中编写添加和删除方法。
- 在com.jxust.jdk包下,创建serDao接口的实现类UserDaoImpl。
- 在com.jxust.aspect包下,创建切面类MyAspect,在该类中定义一个模拟权限检查的方法和一个模拟日志记录的方法,这俩个方法就表示切面中的通知。
- 在com.jxust.jdk包下,创建代理类JdkProxy,该类需要实现 InvocationHandler 接口,并编写代理类方法。在代理方法中,需要通过Proxy类实现动态代理。
- 在com.jxust.jdk包下,创建测试类JdkTest。
步骤二:
public interface UserDao {
public void addUser();
public void deleteUser();
}
步骤三:
public class UserDaoImpl implements UserDao {
public void addUser() {
System.out.println("添加用户");
}
public void deleteUser() {
System.out.println("删除用户");
}
}
步骤四:
//切面类:可以存在多个通知Advice(即增强的方法)
public class MyAspect {
public void check_Permissions() {
System.out.println("模拟检查权限...");
}
public void log() {
System.out.println("模拟记录日志...");
}
}
步骤五:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import com.maxian.aspect.MyAspect;
/**
* JDK代理类
*/
public class JdkProxy implements InvocationHandler {
// 声明目标类接口
private UserDao userDao;
// 创建代理方法
public Object createProxy(UserDao userDao) {
this.userDao = userDao;
// 1.类加载器
ClassLoader classLoader = JdkProxy.class.getClassLoader();
// 2.被代理对象实现的所有接口
Class[] clazz = userDao.getClass().getInterfaces();
// 3.使用代理类,进行增强,返回的是代理后的对象
return Proxy.newProxyInstance(classLoader, clazz, this);
//newProxyInstance()方法中包含三个参数,其中第1个参数是当前类的类加载器,第2个参数表示的是被代理对象实现的所有接口,第3个参数this代表的就是代理了JdkProxy本身。
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 声明切面
MyAspect myAspect = new MyAspect();
// 前增强
myAspect.check_Permissions();
// 在目标类上调用方法,并传入参数
Object obj = method.invoke(userDao, args);
// 后增强
myAspect.log();
return obj;
}
}
步骤六:
public class JdkTest {
public static void main(String[] args) {
// 创建代理对象
JdkProxy jdkProxy = new JdkProxy();
// 创建目标对象
UserDao userDao = new UserDaoImpl();
// 从代理对象中获取增强后的目标对象
UserDao userDao1 = (UserDao) jdkProxy.createProxy(userDao);
// 执行方法
userDao1.addUser();
userDao1.deleteUser();
}
}