spring基础概念AOP与动态代理理解
一、代理模式
代理模式的英文叫做Proxy或Surrogate,中文都可译为”代理“,所谓代理,就是一个人或者一个机构代表另一个人或者另一个机构采取行动。在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
以简单模拟事务的执行过程说明各种代理区别
1.1静态代理
由程序员创建或由特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
publicinterfacePersonDao{ voidsavePerson(); }
publicclassPersonDaoImplimplementsPersonDao{ @Override publicvoidsavePerson(){ System.out.println("saveperson"); } }
publicclassTransaction{ voidbeginTransaction(){ System.out.println("beginTransaction"); } voidcommit(){ System.out.println("commit"); } }
接下来编写静态代理类---实现PersonDao接口
/** *静态代理类 *@authorqjc */ publicclassPersonDaoProxyimplementsPersonDao{ PersonDaopersonDao; Transactiontransaction; publicPersonDaoProxy(PersonDaopersonDao,Transactiontransaction){ this.personDao=personDao; this.transaction=transaction; } @Override publicvoidsavePerson(){ this.transaction.beginTransaction(); this.personDao.savePerson(); this.transaction.commit(); } }
测试
/** *测试静态代理 *@authorqjc */ publicclassTestPersonProxy{ @Test publicvoidtestSave(){ PersonDaopersonDao=newPersonDaoImpl(); Transactiontransaction=newTransaction(); PersonDaoProxyproxy=newPersonDaoProxy(personDao,transaction); proxy.savePerson(); } }
总结:
1、静态代理模式并没有做到事务的重用
2、假设dao有100个类,100个proxy,接口中有多少方法,在proxy层就得实现多少方法,有多少方法就要开启和提交多少事务
3、如果一个proxy实现了多个接口,如果其中的一个接口发生变化(添加了一个方法),那么proxy也要做相应改变
1.2JDK动态代理
动态代理类:在程序运行时,运用反射机制动态创建而成。
JDK的动态代理必须具备四个条件:1、目标接口2、目标类3、拦截器4、代理类
使用上个例子的PersonDao接口、PersonDaoImpl类及Transaction类
编写拦截器
importjava.lang.reflect.InvocationHandler; importjava.lang.reflect.Method; /** *拦截器 *1、目标类导入进来 *2、事物导入进来 *3、invoke完成:开启事务、调用目标对象的方法、事务提交 * *@authorqjc */ publicclassInterceptorimplementsInvocationHandler{ privateObjecttarget;//目标类 privateTransactiontransaction; publicInterceptor(Objecttarget,Transactiontransaction){ this.target=target; this.transaction=transaction; } /** *@paramproxy目标对象的代理类实例 *@parammethod对应于在代理实例上调用接口方法的Method实例 *@paramargs传入到代理实例上方法参数值的对象数组 *@return方法的返回值,没有返回值是null *@throwsThrowable */ publicObjectinvoke(Objectproxy,Methodmethod,Object[]args) throwsThrowable{ StringmethodName=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); } returnnull; } }
测试
/** *测试jdk动态代理 *@authorqjc */ publicclassTestJDKProxy{ @Test publicvoidtestSave(){ /** *1、创建一个目标对象 *2、创建一个事务 *3、创建一个拦截器 *4、动态产生一个代理对象 */ Objecttarget=newPersonDaoImpl(); Transactiontransaction=newTransaction(); Interceptorinterceptor=newInterceptor(target,transaction); /** *参数一:设置代码使用的类加载器,一般采用跟目标类相同的类加载器 *参数二:设置代理类实现的接口,跟目标类使用相同的接口 *参数三:设置回调对象,当代理对象的方法被调用时,会调用该参数指定对象的invoke方法 */ PersonDaopersonDao=(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.3CGLIB动态代理
使用上个例子的PersonDaoImpl类和Transaction类(不用接口)
编写拦截器类
importnet.sf.cglib.proxy.Enhancer; importnet.sf.cglib.proxy.MethodInterceptor; importnet.sf.cglib.proxy.MethodProxy; /** *CGLIB代理拦截器 *@authorqjc */ publicclassInterceptorimplementsMethodInterceptor{ privateObjecttarget;//代理的目标类 privateTransactiontransaction; publicInterceptor(Objecttarget,Transactiontransaction){ this.target=target; this.transaction=transaction; } /** *创建目标对象的代理对象 * *@return */ publicObjectcreateProxy(){ //代码增强 Enhancerenhancer=newEnhancer();//该类用于生成代理对象 enhancer.setCallback(this);//参数为拦截器 enhancer.setSuperclass(target.getClass());//设置父类 returnenhancer.create();//创建代理对象 } /** *@paramobj目标对象代理类的实例 *@parammethod代理实例上调用父类方法的Method实例 *@paramargs传入到代理实例上方法参数值的对象数组 *@parammethodProxy使用它调用父类的方法 *@return *@throwsThrowable */ publicObjectintercept(Objectobj,Methodmethod,Object[]args, MethodProxymethodProxy)throwsThrowable{ this.transaction.beginTransaction(); method.invoke(target); this.transaction.commit(); returnnull; } }
测试
/** *测试cglib动态代理 *通过cglib产生的代理对象,代理类是目标类的子类 *@authorqjc */ publicclassTestCglibProxy{ @Test publicvoidtestSave(){ Objecttarget=newPersonDaoImpl(); Transactiontransaction=newTransaction(); Interceptorinterceptor=newInterceptor(target,transaction); PersonDaoImplpersonDaoImpl=(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做到了代码块的重用。
springAOP代理机制:
1、若目标对象实现了若干接口,spring使用JDK的java.lang.reflect.Proxy类代理。
优点:因为有接口,所以使系统更加松耦合
缺点:为每一个目标类创建接口
2、若目标对象没有实现任何接口,spring使用CGLIB库生成目标对象的子类。
优点:因为代理类与目标类是继承关系,所以不需要有接口的存在。
缺点:因为没有使用接口,所以系统的耦合性没有使用JDK的动态代理好。
使用第一个例子的PersonDao接口、PersonDaoImpl类和Transaction类
编写spring配置
<beanid="personDao"class="cn.qjc.aop.xml.PersonDaoImpl"></bean> <beanid="transaction"class="cn.qjc.aop.xml.Transaction"></bean> <aop:config> <!--切入点表达式确定目标类--> <aop:pointcutexpression="execution(*cn.qjc.aop.xml.PersonDaoImpl.*(..))"id="perform"/> <!--ref指向对象就是切面--> <aop:aspectref="transaction"> <aop:beforemethod="beginTransaction"pointcut-ref="perform"/> <aop:after-returningmethod="commit"pointcut-ref="perform"/> </aop:aspect> </aop:config> </beans>
测试
/** *测试spring动态代理 *@authorqjc */ publicclassTransactionTest{ @Test publicvoidtestSave(){ ApplicationContextcontext=newClassPathXmlApplicationContext("cn/qjc/aop/xml/applicationContext.xml"); PersonDaopersonDao=(PersonDao)context.getBean("personDao"); personDao.savePerson(); } }
springAOP原理
1、当spring容器启动的时候,加载两个bean,对像个bean进行实例化
2、当spring容器对配置文件解析到<aop:config>的时候,把切入点表达式解析出来,按照切入点表达式匹配spring容器内容的bean
3、如果匹配成功,则为该bean创建代理对象
4、当客户端利用context.getBean获取一个对象时,如果该对象有代理对象,则返回代理对象,如果没有代理对象,则返回对象本身
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。