高碑店地区网站建设,室内设计较好的学校,网址大全浏览器,企业管理顾问东莞网站建设定义
为其他对象提供一种代理以控制对这个对象的访问。在某些情况下#xff0c;一个对象不适合或者不能直接引用另一个对象#xff0c;而代理对象可以在客户端和目标对象之间起到中介的作用。
结构
抽象角色#xff1a;通过接口或抽象类声明真实角色实现的业务方法。
代…定义
为其他对象提供一种代理以控制对这个对象的访问。在某些情况下一个对象不适合或者不能直接引用另一个对象而代理对象可以在客户端和目标对象之间起到中介的作用。
结构
抽象角色通过接口或抽象类声明真实角色实现的业务方法。
代理角色实现抽象角色是真实角色的代理通过真实角色的业务逻辑方法来实现抽象方法并可以附加自己的操作。
真实角色实现抽象角色定义真实角色所要实现的业务逻辑供代理角色调用
优缺点
优点
代理模式能将代理对象与真实被调用的目标对象分离。一定程度上降低了系统的耦合度扩展性好。可以起到保护目标对象的作用。可以对目标对象的功能增强。
缺点
代理模式会造成系统设计中类的数量增加。在客户端和目标对象增加一个代理对象会造成请求处理速度变慢。
应用实例
静态代理
业务描述
有个哥们 想和他对象求婚但是自己没有经验又想浪漫一点于是就找了一个“婚姻策划” 代码实现
package com.beauty.designpatterns.structure;/*** description 代理** author yufengwen* date 2023/2/28 17:45*/
public class ProxyPattern {public static void main(String[] args) {Marriage lover new Lover();Marriage scheme new Scheme(lover);scheme.propose();}}// 求婚策划/*** 接口*/
interface Marriage{/*** 求婚*/void propose();}/*** 具体的对象*/
class Lover implements Marriage{/*** 求婚*/Overridepublic void propose() {System.out.println(嫁给我吧);}
}/*** 代理对象*/
class Scheme implements Marriage{private Marriage marriage;public Scheme(Marriage marriage) {this.marriage marriage;}/*** 求婚*/Overridepublic void propose() {System.out.println(前期策划方案);System.out.println();marriage.propose();System.out.println();System.out.println(策划后期);}
}动态代理
JDK动态代理
JDK动态代理步骤
JDK动态代理分为以下几步
拿到被代理对象的引用并且通过反射获取到它的所有的接口。通过JDK Proxy类重新生成一个新的类同时新的类要实现被代理类所实现的所有的接口。动态生成 Java 代码把新加的业务逻辑方法由一定的逻辑代码去调用。编译新生成的 Java 代码.class。将新生成的Class文件重新加载到 JVM 中运行。
所以说JDK动态代理的核心是通过重写被代理对象所实现的接口中的方法来重新生成代理类来实现的那么假如被代理对象没有实现接口呢那么这时候就需要CGLIB动态代理了。
/*** description* 主体方法 Proxy.newProxyInstanceClassLoader loader,* Class?[] interfaces,* InvocationHandler h* InvocationHandler的实现为 代理的实现** author yufengwen* date 2021/12/6 2:34 下午*/
public class JdkProxy {public static void main(String[] args) {//第一步创建被代理对象Person xm new Xm();// 第二步创建handler,传入真实对象InvocationHandler invocationHandler new MyInvocationHandler(xm);//第三步创建代理对象传入类加载器、接口、handlerfinal Person person (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class[]{Person.class}, invocationHandler);//第四步调用方法person.eat();person.say();}
}/*** 被代理类*/
class Xm implements Person{Overridepublic void eat() {System.out.println(我是小明我在吃东西);}Overridepublic void say() {System.out.println(我是小明我在说话);}
}/**
* 代理接口
*/
interface Person{void eat();void say();
}/**
* 实现InvocationHandler编写具体的代理逻辑
*/
class MyInvocationHandler implements InvocationHandler{private Object target;public MyInvocationHandler(Object target) {this.target target;}Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println(jdk 代理操作前逻辑);Object result method.invoke(target, args);System.out.println(jdk 代理操作后逻辑);return result;}
}测试结果
jdk 代理操作前逻辑
我是小明我在吃东西
jdk 代理操作后逻辑
jdk 代理操作前逻辑
我是小明我在说话
jdk 代理操作后逻辑Process finished with exit code 0cglib动态代理
JDK动态代理是通过重写被代理对象实现的接口中的方法来实现而CGLIB是通过继承被代理对象来实现和JDK动态代理需要实现指定接口一样CGLIB也要求代理对象必须要实现MethodInterceptor接口并重写其唯一的方法intercept。
CGLib采用了非常底层的字节码技术其原理是通过字节码技术为一个类创建子类并在子类中采用方法拦截的技术拦截所有父类方法的调用顺势织入横切逻辑。利用ASM开源包对代理对象类的class文件加载进来通过修改其字节码生成子类来处理
注意因为CGLIB是通过继承目标类来重写其方法来实现的故而如果是final和private方法则无法被重写也就是无法被代理。
/*** description** author yufengwen* date 2021/12/6 3:00 下午*/
public class CglibProxy {public static void main(String[] args) {final CgProxyXiaoMing callback new CgProxy();final XiaoMing proxy callback.getProxy(XiaoMing.class);proxy.eat();}}/*** 实现 MethodInterceptor* 重写 intercept 方法 实现具体的代理逻辑* param T*/
class CgProxyT implements MethodInterceptor{public T getProxy(ClassT clazz){Enhancer enhancer new Enhancer();//设置父类enhancer.setSuperclass(clazz);//设置方法拦截处理器enhancer.setCallback(this);return (T) enhancer.create();}Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println(cglib 代理 前逻辑);System.out.println(method.getName());methodProxy.invokeSuper(o,objects);System.out.println(cglib 代理 后逻辑);return null;}
}测试结果
cglib 代理 前逻辑
eat
我是小明我在吃东西
cglib 代理 后逻辑Process finished with exit code 0CGLIB动态代理实现分析
CGLib动态代理采用了FastClass机制其分别为代理类和被代理类各生成一个FastClass这个FastClass类会为代理类或被代理类的方法分配一个 index(int类型)。这个index当做一个入参FastClass 就可以直接定位要调用的方法直接进行调用这样省去了反射调用所以调用效率比 JDK 动态代理通过反射调用更高。
但是我们看上面的源码也可以明显看到JDK动态代理只生成一个文件而CGLIB生成了三个文件所以生成代理对象的过程会更复杂
JDK和CGLib动态代理对比
JDK 动态代理是实现了被代理对象所实现的接口CGLib是继承了被代理对象。 JDK和CGLib 都是在运行期生成字节码,JDK是直接写Class字节码CGLib 使用 ASM 框架写Class字节码Cglib代理实现更复杂生成代理类的效率比JDK代理低。
JDK 调用代理方法是通过反射机制调用,CGLib 是通过FastClass机制直接调用方法,CGLib 执行效率更高。
原理区别
java动态代理是利用反射机制生成一个实现代理接口的匿名类在调用具体方法前调用InvokeHandler来处理。核心是实现InvocationHandler接口使用invoke()方法进行面向切面的处理调用相应的通知。
而cglib动态代理是利用asm开源包对代理对象类的class文件加载进来通过修改其字节码生成子类来处理。核心是实现MethodInterceptor接口使用intercept()方法进行面向切面的处理调用相应的通知。
1、如果目标对象实现了接口默认情况下会采用JDK的动态代理实现AOP
2、如果目标对象实现了接口可以强制使用CGLIB实现AOP
3、如果目标对象没有实现了接口必须采用CGLIB库spring会自动在JDK动态代理和CGLIB之间转换
性能区别
1、CGLib底层采用ASM字节码生成框架使用字节码技术生成代理类在jdk6之前比使用Java反射效率要高。唯一需要注意的是CGLib不能对声明为final的方法进行代理因为CGLib原理是动态生成被代理类的子类。
2、在jdk6、jdk7、jdk8逐步对JDK动态代理优化之后在调用次数较少的情况下JDK代理效率高于CGLIB代理效率只有当进行大量调用的时候jdk6和jdk7比CGLIB代理效率低一点但是到jdk8的时候jdk代理效率高于CGLIB代理。
各自局限
1、JDK的动态代理机制只能代理实现了接口的类而不能实现接口的类就不能实现JDK的动态代理。
2、cglib是针对类来实现代理的他的原理是对指定的目标类生成一个子类并覆盖其中方法实现增强但因为采用的是继承所以不能对final修饰的类进行代理。
类型机制回调方式适用场景效率JDK动态代理委托机制代理类和目标类都实现了同样的接口InvocationHandler持有目标类代理类委托InvocationHandler去调用目标类的原始方法反射目标类是接口类效率瓶颈在反射调用稍慢CGLIB动态代理继承机制代理类继承了目标类并重写了目标方法通过回调函数MethodInterceptor调用父类方法执行原始逻辑通过FastClass方法索引调用非接口类、非final类非final方法第一次调用因为要生成多个Class对象比JDK方式慢。多次调用因为有方法索引比反射快如果方法过多switch case过多其效率还需测试
静态代理和动态的本质区别
静态代理只能通过手动完成代理操作如果被代理类增加新的方法代理类需要同步新增违背开闭原则。动态代理采用在运行时动态生成代码的方式取消了对被代理类的扩展限制遵循开闭原则。若动态代理要对目标类的增强逻辑扩展结合策略模式只需要新增策略类便可完成无需修改代理类的代码。
代理模式和装饰器模式的区别
代理模式代理模式在不改变原始类接口的条件下为原始类定义一个代理类主要目的是控制访问而非加强功能这是它跟装饰器模式最大的不同。
装饰器模式装饰者模式在不改变原始类接口的情况下对原始类功能进行增强并且支持多个装饰器的嵌套使用。
个人感觉两者最主要的区别在于代理模式增加的代理逻辑 不是为了增强本身的功能****装饰器模式主要就是为了增强原始类本身的逻辑
主要应用场景
代理模式常用在业务系统中开发一些非功能性需求比如监控、统计、鉴权、限流、事务、幂等、日志。我们将这些附加功能与业务功能解耦放到代理类统一处理让程序员只需要关注业务方面的开发。除此之外代理模式还可以用在 RPC、缓存等应用场景中。
参考文章 设计模式之美 https://juejin.cn/post/7011357346018361375