1 代理模式概述
1 生活中的代理案例在这里插入图片描述
- 房屋中介代理 - 客户手中没有房源,找一个中介
- 商品代购 - 代购者可以拿到比较低成本的商品,拥有自己的渠道
2 为什么要使用代理
- 对于消费者而言,可以减少成本, 只需要关心自己需要的商品,不需要去寻找渠道或者是找房源
3 代理模式在java中的应用
- 统一异常处理
- Mybatis 使用了代理
- Spring aop实现原理
- 日志框架
4 概述
- 代理模式(proxy pattern):是23种设计模式中的一种,属于结构性的模式。指一个对象本身不做实际的操作,而是通过其它对象来得到自己想得到的结果
- 意义:目标对象只需要关心自己的实现细节,通过代理对象来实现功能的增强,可以扩展目标对象的功能
- 体现了一个非常重要的编程思想:不能随便修改源码,如果需要修改源码,通过代理模式来扩展。
5 图示
- 图示
2 代理的实现方式
1 java中代理图示
- 图示
- 元素组成 - 接口, 定义行为和规范的- 被代理类,是目标对象- 代理类,做功能增强
2 静态代理
2.1 案例
- 通过代理模式实现事务操作
2.2 实现案例
- 创建domain对象
- 创建service接口来管理规范
- 创建实现类,被代理类
- 创建事务类对象
- 创建代理类对象
- 测试代理类对象
- 运行结果
2.3 存在的问题
- 不利于代码的扩展,比如接口中新添加一个抽象方法时,所有实现类都需要重新实现,否则报错
- 代理对象需要创建很多,这种设计很不方便和麻烦
3 动态代理
1 概述
- 概述:在不改变原有功能代码的前提下,能够动态的实现方法的增强
2 JDK动态代理
2.1 基础准备
- 创建service
packagecom.ff.service;/**
* @author
* @version 1.0
*/publicinterfaceIStudentService{publicvoidsave();Studentquery(Long id);}
- 创建service实现类(需要代理的类)
publicclassIStudentServiceImplimplementsIStudentService{publicvoidsave(){System.out.println("保存学生信息");}publicStudentquery(Long id){System.out.println("查询操作");Student student =newStudent();
student.setAge(18);
student.setName("sy");return student;}}
- 增强类
publicclassDaoTransaction{publicvoidbefore(){System.out.println("开启事务操作");}publicvoidafter(){System.out.println("关闭事务");}}
2.2 实现InvocationHandler接口
- InvocationHandler接口,用来做方法拦截
- 实现接口
publicclassTransactionHandlerimplementsInvocationHandler{//增强类对象privateDaoTransaction transaction;//需要代理的对象privateObject object;publicTransactionHandler(DaoTransaction transaction,Object object){this.transaction = transaction;this.object = object;}publicObjectinvoke(Object proxy,Method method,Object[] args)throwsThrowable{Object ret =null;//判断当前方法是否是save,是才做事务if(method.getName().equals("save")){
transaction.before();
ret = method.invoke(object,args);
transaction.after();}else{
ret = method.invoke(object,args);}return ret;}}
- 分别介绍里面的参数- Proxy:代理实例,可以通过
newProxyInstance
创建代理实例publicstaticObjectnewProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
- - ClassLoader类加载器,直接通过需要代理的类获得就行- Class[]:目标类所实现的所有接口- InvocationHandler:方法拦截处理器,可以在里面实现方法的增强- Method:执行目标方法的,invoke方法执行- args:参数数组
2.3 测试
- 方法
@TestpublicvoidtestSave(){//增强类对象DaoTransaction daoTransaction =newDaoTransaction();//目标执行类IStudentServiceImpl iStudentService =newIStudentServiceImpl();//方法拦截处理器TransactionHandler handler =newTransactionHandler(daoTransaction, iStudentService);//获取代理实例对象IStudentService proxyStudentService =(IStudentService)Proxy.newProxyInstance(IStudentServiceImpl.class.getClassLoader(),IStudentServiceImpl.class.getInterfaces(), handler);
proxyStudentService.save();}
2.4 底层运行原理
- 生成代理类的字节码来学习
/**
* 使用来生成字节码学习使用
*/privatevoidsaveProxyClass(String path)throwsIOException{byte[] $proxy1s =ProxyGenerator.generateProxyClass("$proxy1",IStudentServiceImpl.class.getInterfaces());FileOutputStream fileOutputStream =null;try{
fileOutputStream =newFileOutputStream(newFile(path +"$Proxy1.class"));
fileOutputStream.write($proxy1s);}catch(Exception e){
e.printStackTrace();}finally{if(fileOutputStream !=null){
fileOutputStream.flush();
fileOutputStream.close();}}}
- 生成字节码反编译结果
publicfinalclass $proxy1 extendsProxyimplementsIStudentService{privatestaticMethod m1;privatestaticMethod m4;privatestaticMethod m2;privatestaticMethod m0;privatestaticMethod m3;public $proxy1(InvocationHandler var1)throws{super(var1);}publicfinalbooleanequals(Object var1)throws{try{return(Boolean)super.h.invoke(this, m1,newObject[]{var1});}catch(RuntimeException|Error var3){throw var3;}catch(Throwable var4){thrownewUndeclaredThrowableException(var4);}}publicfinalStudentquery(Long var1)throws{try{return(Student)super.h.invoke(this, m4,newObject[]{var1});}catch(RuntimeException|Error var3){throw var3;}catch(Throwable var4){thrownewUndeclaredThrowableException(var4);}}publicfinalStringtoString()throws{try{return(String)super.h.invoke(this, m2,(Object[])null);}catch(RuntimeException|Error var2){throw var2;}catch(Throwable var3){thrownewUndeclaredThrowableException(var3);}}publicfinalinthashCode()throws{try{return(Integer)super.h.invoke(this, m0,(Object[])null);}catch(RuntimeException|Error var2){throw var2;}catch(Throwable var3){thrownewUndeclaredThrowableException(var3);}}publicfinalvoidsave(Student var1)throws{try{super.h.invoke(this, m3,newObject[]{var1});}catch(RuntimeException|Error var3){throw var3;}catch(Throwable var4){thrownewUndeclaredThrowableException(var4);}}static{try{
m1 =Class.forName("java.lang.Object").getMethod("equals",Class.forName("java.lang.Object"));
m4 =Class.forName("com.ff.service.IStudentService").getMethod("query",Class.forName("java.lang.Long"));
m2 =Class.forName("java.lang.Object").getMethod("toString");
m0 =Class.forName("java.lang.Object").getMethod("hashCode");
m3 =Class.forName("com.ff.service.IStudentService").getMethod("save",Class.forName("com.ff.domain.Student"));}catch(NoSuchMethodException var2){thrownewNoSuchMethodError(var2.getMessage());}catch(ClassNotFoundException var3){thrownewNoClassDefFoundError(var3.getMessage());}}}
- 执行原理图
这里写一个自己的理解,毕竟纠结了几天的东西,感觉终于搞懂了
- 首先我们在我们的主测试程序中通过
Proxy.newProxyInstance()
来进行了一个Proxy类的初始化并返回一个目标类的代理类对象,在这里我们需要注意一点是,我们此时赋值的handler是在其中对Proxy类中的h进行了赋值。 - 而后我们去看它这一步生成的代理类对象,我们可以发现我们自己生成出来的$Proxy和proxyStudentService其实是一个对象!!,而后我们直接用proxyStudentService.save()来执行
- 随后我们必然是执行代理对象的save方法,发现代理对象中其实是
super.h.invoke(this,m3,new Object[]{val})
,细心的你会发现,这里父类的Proxy中的h,在第一步中就被我们自己的handler赋值了,所以我们理所当然他对这个方法拦截器进行了重写,我们当然会执行自己重写的invoke方法 - 程序进入了我们自己的handler之后,我们就执行,这个是就如同静态代理一样,我们的加强类进行了业务上的加强,并将我们的invoke(这里的invoke是我们最初的被代理类对象的)进行融合加强。
- 值得注意的是,我们的代理类和实现类都实现了接口,可以说不实现接口,就没有jdk动态代理。
3 CGLIB动态代理
- jdk动态代理有一个前提,需要代理的类必须实现接口,如果没有实现接口,只能通过CGLIB来实现,其实·就是对于JDK动态代理的一个补充
- 注意: - 类不能被final修饰- 方法不能被final修饰
3.1 基础准备
- 导包
<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>2.2.2</version></dependency>
- 准备需要代理的目标类
publicclassIStudentServiceImplimplementsIStudentService{publicvoidsave(Student student){System.out.println("保存学生信息");}publicStudentquery(Long id){System.out.println("查询操作");Student student =newStudent();
student.setAge(18);
student.setName("sy");return student;}}
publicinterfaceIStudentService{publicvoidsave(Student student);Studentquery(Long id);}
3.2实现方法拦截
- 实现方法拦截
publicclassCglibInterceptorimplementsMethodInterceptor{DaoTransaction transaction;publicCglibInterceptor(DaoTransaction transaction){this.transaction = transaction;}publicObjectintercept(Object o,Method method,Object[] objects,MethodProxy methodProxy)throwsThrowable{//事务增强
transaction.before();Object ret = methodProxy.invokeSuper(o, objects);
transaction.after();return ret;}}
3.3测试
@TestpublicvoidtestSave()throwsIOException{//得到方法拦截器CglibInterceptor interceptor =newCglibInterceptor(newDaoTransaction());//使用CGLIB框架生成目标类的子类(代理类)实现增强Enhancer enhancer =newEnhancer();//设置父类字节码
enhancer.setSuperclass(IStudentServiceImpl.class);//设置拦截处理
enhancer.setCallback(interceptor);IStudentServiceImpl service =(IStudentServiceImpl)enhancer.create();
service.save(newStudent());}
3.4底层运行原理
- 底层运行原理图
- 文字说明: - 通过继承的方式去获取目标对象的方法- 通过传递方法拦截器
MethodInterceptor
实现方法拦截,在这里做具体的增强- 调用生成的代理类对象具体执行重写的save方法,直接去调用方法拦截器里面的intercept方法- 前后加上了增强操作,从而实现了不修改代码实现业务增强
4 总结
代理类型实现机制回调方式适用场景效率JDK动态代理通过实现接口,通过反射获取接口里面的方法,并且自定义
InvocationHandler
接口,实现方法拦截调用invoke方法目标类有接口1.8高于CGLIBCGLIB动态代理继承机制,通过继承重写目标方法,使用MethodInterceptor调用父类的目标方法从而实现代理而实现代理调用interceptor方法不能使用final修饰的类和方法第一次调用生成字节码比较耗时间,多次调用性能还行
5 另一种CGLIB写法
@TestpublicvoidtestSave()throwsIOException{//生成目标代理类System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"D:\\ideaProject\\CGLIB-test-01\\src");//得到方法拦截器finalIStudentServiceImpl service1 =newIStudentServiceImpl();CglibInterceptor interceptor =newCglibInterceptor(newDaoTransaction());//使用CGLIB框架生成目标类的子类(代理类)实现增强Enhancer enhancer =newEnhancer();//设置父类字节码
enhancer.setSuperclass(IStudentServiceImpl.class);//设置拦截处理
enhancer.setCallback(newMethodInterceptor(){publicObjectintercept(Object o,Method method,Object[] objects,MethodProxy methodProxy)throwsThrowable{DaoTransaction daoTransaction =newDaoTransaction();
daoTransaction.before();Object obj =null;
obj = method.invoke(service1,objects);//save//obj = methodProxy.invokeSuper(o,objects);//CGLIB$save$0
daoTransaction.after();return obj;}});IStudentServiceImpl service =(IStudentServiceImpl)enhancer.create();
service.save(newStudent());}
- enhancer.create()来完成的初始化,把我们的父类和拦截器进行了记录缓存。
版权归原作者 不爱笑的良田 所有, 如有侵权,请联系我们删除。