什么是代理
代理(Proxy)是一种设计模式, 提供了对目标对象另外的访问方式,即通过代理访问目标对象。 这样好处:可以做到在不修改目标对象的功能前提下,对目标对象的功能进行扩展。*。(扩展目标对象的功能)。
静态代理: 介绍静态代理:通过事先写好代理类,其实现了公共接口和内部维护真实角色,在真实角色的基础上做一些功能扩展,可通过代理类调用真实角色的方法
静态代理中涉及到的角色:
抽象角色:一般使用接口或者抽象类来实现真实角色:被代理的对象代理角色:代理真实角色,做一些附属的 *** 作客户:使用代理角色来进行一些 *** 作 实例
//抽象接口:租房 public interface Rent { public void rent(); } //真实角色:房东-->租房 public class Host implements Rent{ public void rent() { System.out.println("房屋出租"); } } //代理类:中介 public class Proxy implements Rent{ // 接收保存目标对象【真正做事的还是真实角色Host】,因此需要维护Host的引用 Host host; public Proxy(Host host){this.host = host;} public void rent() { seeHouse();//看房 host.rent(); 执行目标对象的方法 fare();//收费 } public void seeHouse(){ System.out.println("带房客看房"); } public void fare(){ System.out.println("收中介费"); } } //客户类:通过代理类执行一系列业务 public class Client { public static void main(String[] args) { // 目标对象 Host host = new Host(); // 代理 Proxy proxy = new Proxy(host); proxy.rent(); // 执行的是,代理的方法 } }静态代理优缺点
优点:
可以使真实角色更加纯粹,不在去关注一些公共的事情
公共的业务由代理来完成,完成业务的分工
公共业务发生扩展时更加集中方便
缺点:
如果接口改了,代理的也要跟着改
因为代理对象,需要与目标对象实现一样的接口,所以会有很多代理类,增加了代码量
在编译期就将代理类和目标对象确立下来,不利于程序的扩展。
动态代理 介绍动态代理:代理类编写时不指定被代理的类是谁,代理类是动态生成的,通过java反射机制,在运行时获取某个目标类的接口,并创建代理类
动态代理分为两类:基于接口的动态代理;基于类的动态代理
基于接口的动态代理—JDK API动态代理基于类的动态代理–cglib动态代理 JDK API 动态代理:
被代理类和代理类都实现同一套接口,代理类先看代理的是谁,运行时把代理类加载进来,被代理类实现的接口代理类也实现了,从而可对目标对象的方法进行调用和扩展
两个重要的类:**Proxy类:**他是所有动态代理类的超类,调用它的静态方法newProxyInstance()可以生成目标对象的代理对象
+ public static Object newProxyInstance(ClassLoader loader,Class>[] interfaces,InvocationHandler h) - 参数一:生成代理对象使用哪个类装载器【一般我们使用的是代理类的装载器】 - 参数二:要代理的对象,通过接口指定【指定要代理类的接口】 - 参数三:要将方法调用分派到的调用处理程序【可自行实现Invocationhandler接口传入】
InvocationHandler:【调用处理程序】每个代理实例都有一个关联的调用处理程序。 当在代理实例上调用方法时,方法调用被编码并分发给其调用处理程序的invoke方法来执行并返回结果。
InvocationHandler是JDK动态代理的核心,生成的代理对象的方法调用都会委托到InvocationHandler.invoke()方法
+ invoke()是InvocationHandler接口唯一的一个方法 + public Object invoke(Object proxy, Method method, Object[] args) - 参数一:the proxy instance that the method was invoked on (正在被调用的方法的代理实例) - 参数二:instance corresponding to the interface method invoked on the proxy instance.(method对应于调用代理实例上的接口方法的实例) - 参数三args:当前被调用的代理实例方法中的参数
注意:
代理对象拥有目标对象相同的方法【因为参数二指定了对象的接口】用户调用代理对象的什么方法,调用的都是处理器的invoke方法。使用JDK动态代理必须要有接口【参数二需要接口】 实例
//抽象接口和真实角色用的还是上面那个 //代理类 public class JdkProxy implements InvocationHandler { // 接收保存目标对象 private Object target; public void setRent(Rent rent){this.target = rent;} //生成代理类 public Object getJdkProxt(){return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this); // } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { seeHouse(); //核心:本质利用反射实现 Object result = method.invoke(target, args); fare(); return result; } public void seeHouse(){ System.out.println("带房客看房"); } public void fare(){ System.out.println("收中介费"); } } //上述代码还可以换一种写法,不用实现InvocationHandler接口,直接在newInstace()中传入InvocationHandler的匿名类匿名对象来绑定调用处理程序 public class JdkProxy2 { Object target; public void setRent(Rent rent){this.target = rent;} public Object getJdkProxt(){return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this); } public Object getProxy(){ return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { seeHouse(); Object result = method.invoke(target, args); fare(); return result; } }) } public void seeHouse(){ System.out.println("带房客看房"); } public void fare(){ System.out.println("收中介费"); } } //客户端 public class Client { public static void main(String[] args) { Host host = new Host(); JdkProxy jdkProxy = new JdkProxy(); jdkProxy.setRent(host); Rent proxy = (Rent)jdkProxy.getProxt(); proxy.rent(); } }cglib动态代理 介绍
JDK动态代理是必须实现接口才能进行代理,如果一个类没实现接口,就不能使用Proxy动态代理。此时可以使用CGLIB(Code Generation Library)动态代理,来实现增强。
cglib【code generator bibrary】,代码生成器,可以动态的生成字节码对象,即生成目标类的子类,通过调用父类(目标类)的方法实现,在调用父类方法时再代理中进行增强
cglib实现动态代理的步骤:
生成一个字节码对象(空的)
设置字节码对象的父类是目标对象
查找目标类上的所有非final的public类型的方法定义;
将符合条件的方法定义转换成字节码;
通过生成字节码对象进行回调方法,在回调的过程中,追加功能代码(方法功能的扩展)
将组成的字节码转换成相应的代理的class对象;
实现MethodInterceptor接口,用来处理对代理类上所有方法的请求
**Enhance类:**生成动态子类以启用方法拦截。动态生成的子类将重写父类的非final方法,并回调到用户定义的拦截器实现的钩子
**MethodInterceptor:**方法拦截器
Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) 参数一:由CGLib生成的代理类实例 参数二:在代理类上调用的被代理方法的引用 参数三:被调用的方法的参数 参数四:要被触发的父类的方法的引用
使用CGLIB的前提:
1)如果类是抽象类,只能调用已实现的方法,如果调用抽象方法的时候会报错
2)类不能是final类,否则会报错
//目标类 public class Host{ public void rent() { System.out.println("房屋出租"); } } public class CglibProxy implements MethodInterceptor { private Object target;//需要代理的目标对象 public Object getCglibProxy(Object objectTarget){ //为目标对象target赋值 this.target = objectTarget; Enhancer enhancer = new Enhancer(); //设置父类,因为Cglib是针对指定的类生成一个子类,所以需要指定父类 enhancer.setSuperclass(objectTarget.getClass()); // 设置回调 enhancer.setCallback(this); //创建并返回代理对象 return enhancer.create(); } public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { seeHouse(); Object result = methodProxy.invoke(target, args); fare(); return result; } public void seeHouse(){ System.out.println("带房客看房"); } public void fare(){ System.out.println("收中介费"); } }JDK动态代理与CGLIB对比
1)
JDK动态代理:基于Java反射机制实现,必须要实现了接口的业务类才生成代理对象,在调用具体方法前调用InvokeHandler的invoke()处理。
CGLIB动态代理:利用asm开源包,将代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
2)
如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
如果目标对象实现了接口,可以强制使用CGLIB实现AOP
如何强制使用CGLIB实现AOP?
(1)添加CGLIB库,SPRING_HOME/cglib/*.jar
(2)在spring配置文件中加入
如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换
3)
JDK动态代理只能对实现了接口的类生成代理,而不能针对类CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法。因为是继承,所以该类或方法最好不要声明成final
4)
JDK Proxy的优势:
最小化依赖关系、代码实现简单、简化开发和维护、JDK原生支持,比CGLIB更加可靠,随JDK版本平滑升级。而字节码类库通常需要进行更新以保证在新版Java上能够使用。
CGLIB的优势:
无需实现接口,达到代理类无侵入,只 *** 作关心的类,而不必为其他相关类增加工作量。高性能。
动态代理优缺点优点:
可以使真实角色更加纯粹,不在去关注一些公共的事情公共的业务由代理来完成,完成业务的分工公共业务发生扩展时更加集中方便可以代理多个类,一个动态代理类代理的是一个接口,只要实现了该接口的类,他都可以代理 Spring AOP使用的代理方式
默认使用 JDK 动态代理,这样便可以代理所有的接口类型
如果目标对象没有实现任何接口,则默认是CGLIB的代理方式
可强制使用CGLIB,指定proxy-target-class = “true” 或者 基于注解@EnableAspectJAutoProxy(proxyTargetClass = true)
org.springframework.aop.framework.ProxyProcessorSupport
org.springframework.aop.framework.DefaultAopProxyFactory
spring会根据对当前类的所有接口进行判断,如果没有满足的接口,则设置proxyTargetClass为true,如果有则将接口记录下来方便之后使用JDK代理方式时创建代理类使用。
在createAopProxy()中,config.isProxyTargetClass() 为true,则会进入第一个if分支里面。在这个分支里面,如果bean它本身是一个interface或者是本身已经是一个代理对象,那么就会创建JDK代理,否则会使用CGLIB的方式创建代理对象。
参考文档:https://blog.csdn.net/w449226544/article/details/103365446
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)