spring基础概念AOP与动态代理理解

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

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();
mit();
}
}
测试
/**
* 测试静态代理
* @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 ng.reflect.InvocationHandler;
import ng.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); // 调⽤⽬标⽅法
mit(); // 提交事务
} 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);
mit();
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的ng.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获取⼀个对象时,如果该对象有代理对象,则返回代理对象,如果没有代理对象,则返回对象本⾝
以上就是本⽂的全部内容,希望对⼤家的学习有所帮助,也希望⼤家多多⽀持。

相关文档
最新文档