【spring基础】AOP概念与动态代理详解
一、代理模式
代理模式的英文叫做Proxy或Surrogate,中文都可译为”代理“,所谓代理,就是一个人或者一个机构代表另一个人或者另一个机构采取行动。在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
以简单模拟事务的执行过程说明各种代理区别
1.1 静态代理
由程序员创建或由特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
public interface PersonDao { void savePerson();
}
public class PersonDaoImpl implements PersonDao { @Override
public void savePerson() {
System.out.println("save person");
}
}
public class Transaction { void beginTransaction(){
System.out.println("begin Transaction");
} void commit(){
System.out.println("commit");
}
}
接下来编写静态代理类---实现PersonDao接口
/**
* 静态代理类
* @author qjc
*/
public class PersonDaoProxy implements PersonDao{ PersonDao personDao;
Transaction transaction; public PersonDaoProxy(PersonDao personDao, Transaction transaction) {
this.personDao = personDao;
this.transaction = transaction;
} @Override
public void savePerson() {
this.transaction.beginTransaction();
this.personDao.savePerson();
this.transaction.commit();
}
}
测试
/**
* 测试静态代理
* @author qjc
*/
public class TestPersonProxy { @Test
public void testSave(){
PersonDao personDao = new PersonDaoImpl();
Transaction transaction = new Transaction();
PersonDaoProxy proxy = new PersonDaoProxy(personDao, transaction); proxy.savePerson();
}
}
总结:
1、静态代理模式并没有做到事务的重用
2、假设dao有100个类,100个proxy,接口中有多少方法,在proxy层就得实现多少方法,有多少方法就要开启和提交多少事务
3、如果一个proxy实现了多个接口,如果其中的一个接口发生变化(添加了一个方法),那么proxy也要做相应改变
1.2 JDK动态代理
动态代理类:在程序运行时,运用反射机制动态创建而成。
JDK的动态代理必须具备四个条件:1、目标接口 2、目标类 3、拦截器 4、代理类
使用上个例子的PersonDao接口、PersonDaoImpl类及Transaction类
编写拦截器
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method; /**
* 拦截器
* 1、目标类导入进来
* 2、事物导入进来
* 3、invoke完成:开启事务、调用目标对象的方法、事务提交
*
* @author qjc
*/
public class Interceptor implements InvocationHandler { private Object target; // 目标类
private Transaction transaction; public Interceptor(Object target, Transaction transaction) {
this.target = target;
this.transaction = transaction;
} /**
* @param proxy 目标对象的代理类实例
* @param method 对应于在代理实例上调用接口方法的Method实例
* @param args 传入到代理实例上方法参数值的对象数组
* @return 方法的返回值,没有返回值是null
* @throws Throwable
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
String methodName = method.getName();
if ("savePerson".equals(methodName)
|| "deletePerson".equals(methodName)
|| "updatePerson".equals(methodName)) { this.transaction.beginTransaction(); // 开启事务
method.invoke(target); // 调用目标方法
this.transaction.commit(); // 提交事务 } else {
method.invoke(target);
}
return null;
}
}
测试
/**
* 测试jdk动态代理
* @author qjc
*/
public class TestJDKProxy { @Test
public void testSave(){
/**
* 1、创建一个目标对象
* 2、创建一个事务
* 3、创建一个拦截器
* 4、动态产生一个代理对象
*/
Object target = new PersonDaoImpl();
Transaction transaction = new Transaction();
Interceptor interceptor = new Interceptor(target, transaction);
/**
* 参数一:设置代码使用的类加载器,一般采用跟目标类相同的类加载器
* 参数二:设置代理类实现的接口,跟目标类使用相同的接口
* 参数三:设置回调对象,当代理对象的方法被调用时,会调用该参数指定对象的invoke方法
*/
PersonDao personDao = (PersonDao) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
interceptor);
personDao.savePerson();
}
}
总结:
1、因为利用JDKProxy生成的代理类实现了接口,所以目标类中所有的方法在代理类中都有。
2、生成的代理类的所有的方法都拦截了目标类的所有的方法。而拦截器中invoke方法的内容正好就是代理类的各个方法的组成体。
3、利用JDKProxy方式必须有接口的存在。
4、invoke方法中的三个参数可以访问目标类的被调用方法的API、被调用方法的参数、被调用方法的返回类型。
缺点:
1、在拦截器中除了能调用目标对象的目标方法以外,功能是比较单一的,在这个例子中只能处理事务
2、拦截器中的invoke方法的if判断语句在真实的开发环境下是不靠谱的,因为一旦方法很多if语句需要写很多。
1.3 CGLIB动态代理
使用上个例子的PersonDaoImpl类和Transaction类(不用接口)
编写拦截器类
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy; /**
* CGLIB代理 拦截器
* @author qjc
*/
public class Interceptor implements MethodInterceptor { private Object target; // 代理的目标类
private Transaction transaction; public Interceptor(Object target, Transaction transaction) {
this.target = target;
this.transaction = transaction;
} /**
* 创建目标对象的代理对象
*
* @return
*/
public Object createProxy() {
// 代码增强
Enhancer enhancer = new Enhancer(); // 该类用于生成代理对象
enhancer.setCallback(this); // 参数为拦截器
enhancer.setSuperclass(target.getClass());// 设置父类
return enhancer.create(); // 创建代理对象
} /**
* @param obj 目标对象代理类的实例
* @param method 代理实例上 调用父类方法的Method实例
* @param args 传入到代理实例上方法参数值的对象数组
* @param methodProxy 使用它调用父类的方法
* @return
* @throws Throwable
*/
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
this.transaction.beginTransaction();
method.invoke(target);
this.transaction.commit();
return null;
}
}
测试
/**
* 测试cglib动态代理
* 通过cglib产生的代理对象,代理类是目标类的子类
* @author qjc
*/
public class TestCglibProxy { @Test
public void testSave(){ Object target = new PersonDaoImpl();
Transaction transaction = new Transaction();
Interceptor interceptor = new Interceptor(target, transaction); PersonDaoImpl personDaoImpl = (PersonDaoImpl) interceptor.createProxy();
personDaoImpl.savePerson();
}
}
总结:
1、CGlib是一个强大的,高性能,高质量的Code生成类库。它可以在运行期扩展Java类与实现Java接口。
2、用CGlib生成代理类是目标类的子类。
3、用CGlib生成 代理类不需要接口
4、用CGLib生成的代理类重写了父类的各个方法。
5、拦截器中的intercept方法内容正好就是代理类中的方法体
CGLIB和JDK动态代理区别:
JDK:
目标类和代理类实现了共同的接口
拦截器必须实现InvocationHandler接口,而这个接口中invoke方法体的内容就是代理对象方法体的内容
CGLIB:
目标类 是代理类的父类
拦截器必须实现MethodInterceptor接口,而接口中的intercept方法就是代理类的方法体,使用字节码增强机制创建代理对象的.
二、面向切面编程
OOP(面向对象编程):封装、继承、多态、抽象
封装,对代码进行基本的管理、模块化的管理。每个类可能都有自己的职能,出了问题就是论事找人就行了。从修改角度讲,直接修改代码可能有风险,这不是个长远之计,最自然的是从类型封装变化。但是新的类型和旧的体系之间怎么去融合,所以说需要在类与类之间建立一种血缘关系。那么这就是继承的需求,通过继承就可以发现这些类之间是有关联的,它们之间是有父子关系的。然后在继承基础之上多态起决定性的特征。所以说一般认为面向对象最核心的特征,其实是多态。前面几个都是在做铺垫的。多态才是它最核心的特征。子类中通过重写方法,代表了扩展这个层面的东西,而它能融入老的体系中能够正常工作,这是重用这个层面的东西,新的方法、旧的体系、扩展和重用。
AOP(面向切面编程):
面向切面编程,是一种通过预编译方式运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术.
OOP与AOP区别:
OOP:针对业务处理过程的实体及其属性和行为进行抽象封装,以获得更加清楚的逻辑单元划分。
AOP:针对业务处理过程中的横切逻辑 进行提取,它所面对的是处理过程中的某个步骤或者阶段,以获得逻辑过程中各部分之间低耦合的隔离效果。这两种设计思想在目标上有着本质的差异。AOP做到了代码块的重用。
spring AOP代理机制:
1、若目标对象实现了若干接口,spring使用JDK的java.lang.reflect.Proxy类代理。
优点:因为有接口,所以使系统更加松耦合
缺点:为每一个目标类创建接口
2、若目标对象没有实现任何接口,spring使用CGLIB库生成目标对象的子类。
优点:因为代理类与目标类是继承关系,所以不需要有接口的存在。
缺点:因为没有使用接口,所以系统的耦合性没有使用JDK的动态代理好。
使用第一个例子的 PersonDao接口、PersonDaoImpl类和Transaction类
编写spring配置
<bean id="personDao" class="cn.qjc.aop.xml.PersonDaoImpl"></bean>
<bean id="transaction" class="cn.qjc.aop.xml.Transaction"></bean> <aop:config>
<!-- 切入点表达式 确定目标类 -->
<aop:pointcut expression="execution(* cn.qjc.aop.xml.PersonDaoImpl.*(..))" id="perform"/> <!-- ref指向对象就是切面 -->
<aop:aspect ref="transaction">
<aop:before method="beginTransaction" pointcut-ref="perform"/>
<aop:after-returning method="commit" pointcut-ref="perform"/>
</aop:aspect>
</aop:config> </beans>
测试
/**
* 测试spring动态代理
* @author qjc
*/
public class TransactionTest { @Test
public void testSave(){
ApplicationContext context = new ClassPathXmlApplicationContext("cn/qjc/aop/xml/applicationContext.xml");
PersonDao personDao = (PersonDao) context.getBean("personDao");
personDao.savePerson();
}
}
spring AOP原理
1、当spring容器启动的时候,加载两个bean,对像个bean进行实例化
2、当spring容器对配置文件解析到<aop:config>的时候,把切入点表达式解析出来,按照切入点表达式匹配spring容器内容的bean
3、如果匹配成功,则为该bean创建代理对象
4、当客户端利用context.getBean获取一个对象时,如果该对象有代理对象,则返回代理对象,如果没有代理对象,则返回对象本身
【spring基础】AOP概念与动态代理详解的更多相关文章
- 基于SpringBoot实现AOP+jdk/CGlib动态代理详解
动态代理是一种设计模式.在Spring中,有俩种方式可以实现动态代理--JDK动态代理和CGLIB动态代理. JDK动态代理 首先定义一个人的接口: public interface Person { ...
- SpringBoot27 JDK动态代理详解、获取指定的类类型、动态注册Bean、接口调用框架
1 JDK动态代理详解 静态代理.JDK动态代理.Cglib动态代理的简单实现方式和区别请参见我的另外一篇博文. 1.1 JDK代理的基本步骤 >通过实现InvocationHandler接口来 ...
- 新秀学习SSH(十四)——Spring集装箱AOP其原理——动态代理
之前写了一篇文章IOC该博客--<Spring容器IOC解析及简单实现>,今天再来聊聊AOP.大家都知道Spring的两大特性是IOC和AOP. IOC负责将对象动态的注入到容器,从而达到 ...
- java之Spring(AOP)前奏-动态代理设计模式(下)
在上一章我们看到了,新增的三种类都能实现对原始功能类进行添加功能的事务处理,这三种类就是一个代理. 但是这种代理是写死的,怎样实现对任意接口添加自定义的代理呢? 我们先来看一下之前的代理实现: pub ...
- JDK、CGlib动态代理详解
Java动态代理之JDK实现和CGlib实现(简单易懂) 一 JDK和CGLIB动态代理原理 1.JDK动态代理 利用拦截器(拦截器必须实现InvocationHanlder)加上反射机制生 ...
- Spring_AOP动态代理详解(转)
在学习Spring的时候,我们知道Spring主要有两大思想,一个是IoC,另一个就是AOP,对于IoC,依赖注入就不用多说了,而对于Spring的核心AOP来说,我们不但要知道怎么通过AOP来满足的 ...
- JDK动态代理、CGLIB动态代理详解
Spring的AOP其就是通过动态代理的机制实现的,所以理解动态代理尤其重要. 动态代理比静态代理的好处: 1.一个动态代理类可以实现多个业务接口.静态代理的一个代理类只能对一个业务接口的实现类进行包 ...
- JDK动态代理详解
JDK动态代理是代理模式的一种,且只能代理接口.spring也有动态代理,称为CGLib,现在主要来看一下JDK动态代理是如何实现的? 一.介绍 JDK动态代理是有JDK提供的工具类Proxy实现的, ...
- JDK动态代理详解-依赖接口
0. 原理分析 a). 自定义实现InvocationHandler类,实现代理类执行时的invoke方法 b). 使用Proxy.newProxyInstance生成接口的代理类(入参还包括Invo ...
随机推荐
- 三、内存管理单元---MMU
3.1 MMU介绍 3.1.1 MMU 特性 内存管理单元(Memory Management Unit)简称MMU,它负责虚拟地址到物理地址的映射,并提供硬件机制的内存访问权限检查.现在的多用户多进 ...
- android 生成、pull解析xml文件
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools= ...
- 属性动画QPropertyAnimation
属性动画QPropertyAnimation 改变大小.颜色或位置是动画中的常见操作,而QPropertyAnimation类可以修改控件的属性值 大小改变动画: import sys from Py ...
- SpringBoot集成Spring Security(授权与认证)
⒈添加starter依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifact ...
- ES系列十七、logback+ELK日志搭建
一.ELK应用场景 在复杂的企业应用服务群中,记录日志方式多种多样,并且不易归档以及提供日志监控的机制.无论是开发人员还是运维人员都无法准确的定位服务.服务器上面出现的种种问题,也没有高效搜索日志内容 ...
- freeswitch用户整合(使用mysql数据库的用户表)
转:freeswitch用户整合(使用mysql数据库的用户表) freeswitch是一款强大的voip服务器,可以语音和视频.但是它默认是采用/directory文件夹下的xml来配置用户的,对于 ...
- PYTHON-绑定方法 反射 内置函数
'''绑定方法类中定义函数分为了两大类: 1. 绑定方法 特殊之处: 绑定给谁就应该由谁来调用,谁来调用就会将谁当做第一个参数自动传入 如何用: 绑定给对象的方法: 在类中定义函数没有被任何装饰器修饰 ...
- 一次TIME_WAIT和CLOSE_WAIT故障和解决办法
昨天解决了一个curl调用错误导致的服务器异常,具体过程如下: 里头的分析过程有提到,通过查看服务器网络状态检测到服务器有大量的CLOSE_WAIT的状态. 在服务器的日常维护过程中,会经常用到下面的 ...
- vue+element之多表单验证
方法一:利用promise var p1=new Promise(function(resolve, reject) { this.$refs[form1].validate((valid) => ...
- Linux VMware tools安装步骤
Linux VMware tools安装步骤: 1.安装环境介绍 #虚拟机版本:VMware-workstation-full-10 #linux分发版本:CentOS-6.4-i386-LiveCD ...