当前位置: 首页 > news >正文

Spring中AOP详解

目录

一、AOP的概念

二、AOP的底层实现原理

2.1 JDK的动态代理

2.1.1 invocationhandler接口

2.1.2 代理对象和原始类实现相同的接口 interfaces

2.1.3 类加载器ClassLoador

2.1.4 编码实现

2.2 Cglib动态代理

2.2.1 Cglib动态代理编码实现

三、AOP如何通过原始对象的id获取到代理对象

3.1 BeanPostProcessor

3.2 编码实现


一、AOP的概念

AOP(Aspect Oriented Programing)即面向切面编程,以切面为基本单位的程序开发,通过切面间的彼此协同,相互调用,完成程序的构建。这里的 切面 = 切入点 + 额外功能,所以我们常说的AOP也就等同于Spring中的动态代理开发!那么什么是切面呢?当在不同的ServiceImpl中,需要添加同一个额外功能的时候,这几个类的方法中所添加的相同额外功能就会由点构成面,所以就将这个称为是切面

二、AOP的底层实现原理

2.1 JDK的动态代理

由于这里是探索AOP底层的实现原理,所以我们这里先摒弃Spring框架。首先我们需要了解代理创建的三个要素(1.原始对象 2.额外功能 3.代理对象和原始对象实现相同的接口),有了这三个要素之后就能创建出一个代理对象,接下来画图分析

首先将这个原始对象创建出来,在添加额外功能和实现相同的接口的时,使用JDK的Proxy类中的newProxyInstance(动态字节码技术)方法来完成。要了解一个类中方法的具体使用,就需要了解这个类中参数的具体含义

2.1.1 invocationhandler接口

这里的invocationhandler接口就是完成额外功能的,实现这个接口时要实现这个invoke方法,提到这个invoke方法是不是就联想到了Spring中的拦截器MethodInterceptor中的invoke方法?其实MethodInterceptor中的invoke方法就是对这一系列的操作进行了封装

invocationHandler接口中的invoke方法有三个参数,其中proxy忽略掉

method:额外功能所增加给的原始方法

args:原始方法的参数

method调用其invoke方法使得原始方法运行起来,那么我们想要添加额外功的就只需要添加在method.invoke的前后即可。这样额外功能的添加就完成了

2.1.2 代理对象和原始类实现相同的接口 interfaces

这里的参数interfaces是获取到原始对象实现的那个接口。通过获取类文件在获取接口来实现

2.1.3 类加载器ClassLoador

在一般创建对象的过程都是通过类加载器将对应的字节码文件加载到JVM,同时类加载器创建类的class对象,进而创建出这个类的对象。其中CL表示类加载器,同时获取这个类加载器也不需要我们担心,每一个类的.class文件都会自动分配一个

但是在创建动态代理类的时候是没有源文件的,它是通过动态字节码技术(Proxy.newProxyInstance)去创建字节码的。由于动态代理技术是直接将字节码文件写入JVM中的,并没有这个类加载器,但是我们又需要使用类加载器去帮我们创建代理类对象,那这个时候怎么办呢?借一个嘛!所以这就是参数中需要一个类加载器的原因

2.1.4 编码实现

创建接口

public interface UserService {void register();boolean login();
}

原始方法实现这个类 

public class UserServiceImpl implements UserService{@Overridepublic void register() {System.out.println("register核心功能正在执行");}@Overridepublic boolean login() {System.out.println("login核心功能正在执行");return false;}
}

添加额外功能 

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;public class TestJDKProxy {public static void main(String[] args) {// 创建原始对象UserService userService = new UserServiceImpl();// 以下是JDK动态代理创建// 实现InvocationHandler接口,为了方便演示采取内部类的方式InvocationHandler handler = new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 原始方法运行Object ret = method.invoke(userService,args);// 在原始方法后面添加额外功能System.out.println("aop底层实现----额外功能添加在原始功能后面----log");return ret;}};// TestJDKProxy.class.getClassLoader()借用一个类加载器,借谁的无所谓// userService.getClass().getInterfaces() 拿到原始类的接口// 使用相同的接口接收代理类UserService userServiceProxy =(UserService) Proxy.newProxyInstance(TestJDKProxy.class.getClassLoader(),userService.getClass().getInterfaces(),handler);// 调用核心方法 观察额外功能是否添加完成userServiceProxy.login();userServiceProxy.register();}
}

至此,JDK的动态代理原理就已经全部分析完了

2.2 Cglib动态代理

首先在开始Cglib动态代理之前,我们在回顾以下JDK动态代理的过程。JDK动态代理类通过与原始类实现同一个接口从而完成额外功能的添加。但是在现实开发的过程中有没有一种可能这个原始类没有实现任何的接口,那这个时候该怎么办呢?这个时候就需要使用Cglib动态代理来完成了

Cglib是怎么完成这个代理类的实现的呢?Cglib是采取了继承的方式来完成代理类的实现的

由于这里的Cglib动态代理的实现与JDK动态代理的实现是高度一致的,这里就只介绍二者的区别了,而不再介绍相同点了

2.2.1 Cglib动态代理编码实现

Cglib动态代理的实现中是通过Enhancer类中的一系列方法来完成的,通过setClassLoder方法去设置类加载器,通过setSuperClass方法设置父类对象,通过setCallback方法设置额外功能,当然这个额外功能也要去实现接口,这里的接口是MethodInterceptor(这个并不是Spring提供的那个接口,而是Cglib包中的接口),最后通过create方法创建动态代理对象

public class UserServiceImpl{public void register() {System.out.println("register核心功能正在执行");}public boolean login() {System.out.println("login核心功能正在执行");return false;}
}
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;import java.lang.reflect.Method;public class TestCglib {public static void main(String[] args) {// 创建原始对象UserServiceImpl userService = new UserServiceImpl();// 以下是Cglib创建动态代理对象Enhancer enhancer = new Enhancer();// TestCglib.class.getClassLoader() 借用的类加载器enhancer.setClassLoader(TestCglib.class.getClassLoader());// userService.getClass() 获取到的父类对象enhancer.setSuperclass(userService.getClass());MethodInterceptor interceptor = new MethodInterceptor() {@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {// 添加额外功能System.out.println("Cglib底层实现------额外功能添加在方法执行前---log");// 原始方法执行Object ret = method.invoke(userService, args);return ret;}};// 设置额外功能enhancer.setCallback(interceptor);// 创建动态代理对象UserServiceImpl userServiceCglib = (UserServiceImpl) enhancer.create();userServiceCglib.register();userServiceCglib.login();}
}

 

三、AOP如何通过原始对象的id获取到代理对象

3.1 BeanPostProcessor

在Spring中提供了一个接口BeanPostProcessor,这个接口是用来加工Spring通过配置文件创建的对象的。通过实现接口中的postProcessorAfterInitialization方法,就可以实现通过原始对象的id值获取到代理对象了(也就是通过这个接口对原始类进行再加工)

3.2 编码实现

首先在Spring的配置文件中创建UserServiceImpl的对象,这里是实现的接口,所以动态代理应该使用JDK动态代理的方式

public class UserServiceImpl implements UserService{@Overridepublic void register() {System.out.println("register核心功能正在执行");}@Overridepublic boolean login() {System.out.println("login核心功能正在执行");return false;}
}
<bean id="userService" class="com.gl.demo.proxy.UserServiceImpl"/>

创建好对象以后,创建一个类实现BeanPostProcessor接口为原始类进行加工,进而将代理类返回给用户

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;public class BeanPostProcessorTest implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {// 在这个方法中对需要添加额外功能的类进行加工// 这里采取JDK动态代理的方式进行加工// BeanPostProcessorTest.class.getClassLoader() 借用的类加载器// bean.getClass().getInterfaces()获取原始类的接口// 实现InvocationHandler接口添加额外功能InvocationHandler handler = new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object ret = method.invoke(bean, args);System.out.println("spring底层实现动态代理----额外功能添加在原始方法后---log");return ret;}};// 将代理类返回给用户而不是原始类return Proxy.newProxyInstance(BeanPostProcessorTest.class.getClassLoader(),bean.getClass().getInterfaces(),handler);}
}

最后将加工的好的代理对象配置在Spring的配置文件中

<bean id="proxyBeanProcessor" class="com.gl.demo.proxy.BeanPostProcessorTest"/>

这时候,用户通过原始类的id值拿到的是代理类而不是原始类了,进而完成了动态代理的过程

public void test4() {ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-config2.xml");UserService userService = (UserService) ctx.getBean("userService");userService.register();userService.login();
}

至此,AOP底层原理就已经全部分析完毕了!以上的工作Spring其实都给我们封装好了,在日后的开发过程中直接使用就可以了,不用这么麻烦!

http://www.lryc.cn/news/206130.html

相关文章:

  • Unity DOTS系列之Filter Baking Output与Prefab In Baking核心分析
  • Matlab读写操作
  • Android 开发技巧:音乐播放器的后台处理【Service、Handler、MediaPlayer】
  • 使用Windows平台的Hyper-V虚拟机安装CentOS7的详细过程
  • 某马机房预约系统 C++项目(二) 完结
  • npm 安装到指定文件夹
  • 自建的离散傅里叶变换matlab程序实现及其与matlab自带函数比较举例
  • Vue图片路径问题(动态引入)
  • 项目部署Linux步骤
  • UG\NX二次开发 在资源栏(左侧面板)中添加按钮
  • Proteus仿真--量程自动切换数字电压表(仿真+程序)
  • ​如何使用ArcGIS Pro制作一张地形图
  • 人工智能三要数之算法Transformer
  • Java ThreadPoolExecutor 线程池
  • 网络协议--IP选路
  • 使用udevil自动挂载U盘或者USB移动硬盘
  • 学习笔记二十二:K8s控制器Replicaset
  • 2023-10-25 精神分析-领悟新技术的错误做法-持续数年的错误做法-记录与分析
  • Arrays 中的 asList()方法
  • 基于自动化工具autox.js的抢票(猫眼)
  • Java架构师内功计算机网络
  • vue 中 mixin 和 mixins 区别
  • reqable(小黄鸟)+雷电抓包安卓APP
  • 高等数学啃书汇总重难点(七)微分方程
  • 阿里云对象存储OSS文件无法预览,Bucket设置了Referer
  • 数字孪生技术:工业数字化转型的引擎
  • 算法刷题-哈希表
  • 2023NOIP A层联测17 黑暗料理
  • 关于nacos的配置获取失败及服务发现问题的排坑记录
  • 【QT】其他常用控件1