Spring AOP简介与底层实现机制——动态代理
AOP简介
AOP (Aspect Oriented Programing) 称为:面向切面编程,它是一种编程思想。AOP 是 OOP(面向对象编程 Object Oriented Programming)的思想延续
AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码的编写方式(例如性能监视、事务管理、安全检查、缓存、日志记录等)
AOP核心思想
基于代理思想,对原来目标对象,创建代理对象,在不修改原对象代码情况下,通过代理对象,调用增强功能的代码,从而对原有业务方法进行增强
切面:需要代理一些方法和增强代码
AOP的应用场景
场景一:记录日志
场景二:监控方法运行时间 (监控性能)
场景三: 权限控制
场景四: 缓存优化 (第一次调用查询数据库,将查询结果放入内存对象, 第二次调用, 直接从内存对象返回,不需要查询数据库 )
场景五: 事务管理 (调用方法前开启事务, 调用方法后提交或者回滚、关闭事务 )
Spring AOP编程两种方式
方式一:Spring AOP使用纯Java实现,不需要专门的编译过程和类加载器,在运行期通过代理方式向目标类植入增强代码(编程复杂,不推荐)
方式二:Spring 2.0 之后支持第三方 AOP框架(AspectJ ),实现另一种 AOP编程 (推荐)
AOP编程相关术语
1.Aspect(切面): 是通知和切入点的结合,通知和切入点共同定义了关于切面的全部内容---它的功能、在何时和何地完成其功能
2.joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点.
3.Pointcut(切入点):所谓切入点是指我们要对哪些joinpoint进行拦截的定义.通知定义了切面的”什么”和”何时”,切入点就定义了”何地”.
4.Advice(通知、增强):所谓通知是指拦截到joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)
5.Target(目标对象):代理的目标对象
6.Weaving(织入):是指把切面应用到目标对象来创建新的代理对象的过程.切面在指定的连接点织入到目标对象
7.Introduction(引入)(不要求掌握):在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field.
AOP编程底层实现机制
AOP 就是要对目标进行代理对象的创建, Spring AOP是基于动态代理的,分别基于两种动态代理机制: JDK动态代理和CGLIB动态代理
方式一:JDK动态代理
JDK动态代理,针对目标对象的接口进行代理 ,动态生成接口的实现类 (必须有接口)
过程要点
1.必须对接口生成代理
2.采用Proxy对象,通过newProxyInstance方法为目标创建代理对象。
该方法接收三个参数 :
(1)目标对象类加载器
(2)目标对象实现的接口
(3)代理后的处理程序InvocationHandler
3.实现InvocationHandler 接口中 invoke方法,在目标对象每个方法调用时,都会执行invoke
service层
//接口(表示代理的目标接口)
public interface ICustomerService {
//保存
void save();
//查询
int find();
}
//实现层
public class CustomerServiceImpl implements ICustomerService{
@Override
public void save() {
System.out.println("客户保存了。。。。。");
}
@Override
public int find() {
System.out.println("客户查询数量了。。。。。");
return 100;
}
}
JDK动态代理工厂
//专门用来生成jdk的动态代理对象的-通用
public class JdkProxyFactory{
//target目标对象
private Object target;
//注入target目标对象
public JdkProxyFactory(Object target) {
this.target = target;
}
public Object getProxyObject(){
/**
* 参数1:目标对象的类加载器
* 参数2:目标对象实现的接口
* 参数3:回调方法对象
*/
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
new InvocationHandler(){
public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {
//如果是保存的方法,执行记录日志操作
if(method.getName().equals("save")){
System.out.println("增强代码:写日志了。。。");
}
//目标对象原来的方法执行
Object object = method.invoke(target, args);//调用目标对象的某个方法,并且返回目标对象
return object;
}
});
}
}
测试方法
//目标:使用动态代理,对原来的方法进行功能增强,而无需更改原来的代码。
//JDK动态代理:基于接口的(对象的类型,必须实现接口!)
@Test
public void testJdkProxy(){
//target(目标对象)
ICustomerService target = new CustomerServiceImpl();
//实例化注入目标对象
JdkProxyFactory jdkProxyFactory = new JdkProxyFactory(target);
//获取 Object代理对象:基于目标对象类型的接口的类型的子类型的对象
//必需使用接口对象去强转
ICustomerService proxy = (ICustomerService)jdkProxyFactory.getProxyObject();
//调用目标对象的方法
proxy.save();
System.out.println("————————————————————");
proxy.find();
}

注意
JDK动态代理产生的对象不再是原对象
- 错误:
CustomerServiceImpl proxy = (CustomerServiceImpl)jdkProxyFactory.getProxyObject();
- 正确
ICustomerService proxy = (ICustomerService)jdkProxyFactory.getProxyObject();

方式二:Cglib动态代理
Cglib的引入为了解决类的直接代理问题(生成代理子类),不需要接口也可以代理
该代理方式需要相应的jar包,但不需要导入。因为Spring core包已经包含cglib ,而且同时包含了cglib 依赖的asm的包(动态字节码的操作类库)
//没有接口的类
public class ProductService {
public void save() {
System.out.println("商品保存了。。。。。");
}
public int find() {
System.out.println("商品查询数量了。。。。。");
return 99;
}
}
使用cglib代理
//cglib动态代理工厂:用来生成cglib代理对象
public class CglibProxyFactory implements MethodInterceptor{
private Object target;
//注入代理对象
public CglibProxyFactory(Object target) {
this.target = target;
}
//获取代理对象
public Object getProxyObject(){
//1.代理对象生成器(工厂思想)
Enhancer enhancer = new Enhancer();
// 类加载器
enhancer.setClassLoader(target.getClass().getClassLoader());
//2.在增强器上设置两个属性
//设置要生成代理对象的目标对象:生成的目标对象类型的子类型
enhancer.setSuperclass(target.getClass());
//设置回调方法
enhancer.setCallback(this);
//3.创建获取对象
return enhancer.create();
}
//回调方法(代理对象的方法)
/**
* 参数1:代理对象
* 参数2:目标对象的方法对象
* 参数3:目标对象的方法的参数的值
* 参数4:代理对象的方法对象
*/
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//如果是保存的方法,执行记录日志操作
if(method.getName().equals("save")){
System.out.println("增强代码:写日志了。。。");
}
//目标对象原来的方法执行
//调用目标对象的某个方法,并且返回目标对象
Object object = method.invoke(target, args);
return object;
}
}
测试方法
//cglib动态代理:可以基于类(无需实现接口)生成代理对象
@Test
public void testCglibProxy(){
//target目标:
ProductService target = new ProductService();
//代理工厂对象,注入目标
CglibProxyFactory cglibProxyFactory = new CglibProxyFactory(target);
//获取proxy
//代理对象,其实是目标对象类型的子类型
ProductService proxy = (ProductService)cglibProxyFactory.getProxyObject();
//调用代理对象的方法
proxy.save();
System.out.println("—————————————————————");
proxy.find();
}

总结
spring在运行期,生成动态代理对象,不需要特殊的编译器
Spring AOP 优先对接口进行代理 (使用Jdk动态代理)如果目标对象没有实现任何接口,才会对类进行代理 (使用cglib动态代理)
需要注意的
1.对接口创建代理优于对类创建代理,因为会产生更加松耦合的系统,所以spring默认是使用JDK代理。对类代理是让遗留系统或无法实现接口的第三方类库同样可以得到通知,这种方式应该是备用方案
2.标记为final的方法不能够被通知。spring是为目标类产生子类。任何需要被通知的方法都被复写,将通知织入。final方法是不允许重写的
3.spring只支持方法连接点:不提供属性接入点,spring的观点是属性拦截破坏了封装。面向对象的概念是对象自己处理工作,其他对象只能通过方法调用的得到的结果
Spring AOP简介与底层实现机制——动态代理的更多相关文章
- Spring AOP中的JDK和CGLib动态代理哪个效率更高?
一.背景 今天有小伙伴面试的时候被问到:Spring AOP中JDK 和 CGLib动态代理哪个效率更高? 二.基本概念 首先,我们知道Spring AOP的底层实现有两种方式:一种是JDK动态代理, ...
- Spring AOP 5种通知与java动态代理
接口,要求为每个方法前后添加日志 @Component("arithmeticCalculator") public class ArithmeticCalculatorImpl ...
- Spring AOP中的JDK和CGLIB动态代理
Spring在将Advice织入目标对象的Joinpoint是在运行时动态进行的.它采用的方式可能有两种,即JDK动态代理与CGLIB代理.Spring会根据具体的情况在两者之间切换. 实际情况如下: ...
- java面试题之spring aop中jdk和cglib哪个动态代理的性能更好?
在jdk6和jdk7的时候,jdk比cglib要慢: 在jdk8的时候,jdk性能得到提升比cglib要快很多: 结论出自:https://www.cnblogs.com/xuliugen/p/104 ...
- Spring学习(二)—— java的动态代理机制
在学习Spring的时候,我们知道Spring主要有两大思想,一个是IoC,另一个就是AOP,对于IoC,依赖注入就不用多说了,而对于Spring的核心AOP来说,我们不但要知道怎么通过AOP来满足的 ...
- Spring AOP动态代理实现,解决Spring Boot中无法正常启用JDK动态代理的问题
Spring AOP底层的动态代理实现有两种方式:一种是JDK动态代理,另一种是CGLib动态代理. JDK动态代理 JDK 1.3版本以后提供了动态代理,允许开发者在运行期创建接口的代理实例,而且只 ...
- Spring AOP 简介
Spring AOP 简介 如果说 IoC 是 Spring 的核心,那么面向切面编程就是 Spring 最为重要的功能之一了,在数据库事务中切面编程被广泛使用. AOP 即 Aspect Orien ...
- Spring AOP 简介(三)
Spring AOP 简介 如果说 IoC 是 Spring 的核心,那么面向切面编程就是 Spring 最为重要的功能之一了,在数据库事务中切面编程被广泛使用. AOP 即 Aspect Orien ...
- Spring框架系列(11) - Spring AOP实现原理详解之Cglib代理实现
我们在前文中已经介绍了SpringAOP的切面实现和创建动态代理的过程,那么动态代理是如何工作的呢?本文主要介绍Cglib动态代理的案例和SpringAOP实现的原理.@pdai Spring框架系列 ...
随机推荐
- 关于数论分块里r=sum/(sum/l)的证明!
今天的模拟赛里T2要使用到数论分块,里面有一个重要的坎就是关于r=sum/(sum/l)的证明,网上关于这道题的题解里都没有关于这个的证明,那么我就来填补一下: 在以下的文章里,我都会使用lo(x)表 ...
- python 类内部装饰器的实现 与 参数解构学习
学习了函数的装饰器的写法,然后想到如果要在类中初始化或获取信息时能用装饰器做过滤和验证应该怎么写呢, 在网上查了下相关信息,感觉这样也是可以的,不知道会不会有什么问题class Ctj(): clas ...
- Linux下mysql 多实例安装配置
首先我们要清楚什么是多实例?所谓多实例就是用多个配置文件来启动多个不同端口的进程,以不同的端口的形式为外提供服务.明白了多实例 我们下面的操作和配置就一目了然了首先我们要安装一套基础的应用程序,也就是 ...
- Linux centos5.6版本下mysql5.6主从环境安装配置
MySQL数据库支持数据库的主从复制功能,因此在集群方面具有其独特的优势,国内外大型网站架构体系中,均采用了MySQL的主从数据库配置来实现查询负载.数据库热备等功能.本人在此将如何配置实现做了个简单 ...
- linux引导之grub2
先了解下什么是Bootloader 以下是百度百科释意 在嵌入式操作系统中,BootLoader是在操作系统内核运行之前运行.可以初始化硬件设备.建立内存空间映射图,从而将系统的软硬件环境带到一个合适 ...
- Ios第三方FMDB使用说明
SQLite (http://www.sqlite.org/docs.html) 是一个轻量级的关系数据库.iOS SDK很早就支持了SQLite,在使用时,只需要加入 libsqlite3.dyli ...
- 网站搭建-windows 系统 本地 网站搭建 - IIS
上一章有提到IIS安装,现在打开它: 点击浏览,如果没有启动的话,先点击启动. ip先选好,第一个吧,本机的(IIS自己提供了初始网页的东西). 然后可以自己去https://www.freemoba ...
- MyBatis两种传参方式的区别
$与#的区别 select * from T_PRINT_LAYOUT where D_RECID = ${recId} 最后生成的SQL为: select * from T_PRINT_LAYOUT ...
- Arduino 配置 ESP8266环境
Arduino 配置 ESP8266环境 将 http://arduino.esp8266.com/stable/package_esp8266com_index.json 添加到 [附加开发板管理器 ...
- pat 1023 Have Fun with Numbers(20 分)
1023 Have Fun with Numbers(20 分) Notice that the number 123456789 is a 9-digit number consisting exa ...