当前位置 : 主页 > 编程语言 > java >

设计模式——代理模式

来源:互联网 收集:自由互联 发布时间:2022-06-23
模式介绍 代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间

模式介绍

  • 代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
  • 当一个复杂对象的多份副本须存在时,代理模式可以结合享元模式以减少存储器用量。典型作法是创建一个复杂对象及多个代理者,每个代理者会引用到原本的复杂对象。而作用在代理者的运算会转送到原本对象。一旦所有的代理者都不存在时,复杂对象会被移除。
  • 代理模式有不同的形式,主要有三种静态代理、动态代理(JDK 代理、接口代理)和Cglib 代理(可以在内存动态的创建对象,而不需要实现接口,他是 属于动态代理的范畴)。

UML类图

image.png

类图解析:

Subject:通过接口或抽象类声明真实角色实现的业务方法。

Proxy:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。

RealSubject:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。

静态代理

背景介绍:老师生病了,需要另一名老师帮其代课

image.png

ITeacherDao接口类

public interface ITeacherDao { /** * 老师上课 */ void teach(); }

TeacherDao实现类

public class TeacherDao implements ITeacherDao { @Override public void teach() { System.out.println("开始上课..."); } }

TeacherDaoProxy代理类

public class TeacherDaoProxy implements ITeacherDao{ // 被代理对象 private ITeacherDao iTeacherDao; public TeacherDaoProxy(ITeacherDao iTeacherDao) { this.iTeacherDao = iTeacherDao; } public TeacherDaoProxy() { try { iTeacherDao =(ITeacherDao) this.getClass().getClassLoader().loadClass("com.xy.staticproxy.TeacherDao").newInstance(); } catch (Exception e) { System.err.println(e.getMessage()); } } /** * 上课前 */ public void preTeach() { System.out.println("你们老师生病了, 我来代一节课..."); } @Override public void teach() { preTeach(); iTeacherDao.teach(); afterTeach(); } /** * 上课后 */ private void afterTeach() { System.out.println("下课了, 同学们有缘再见..."); } }

Client测试类

public class Client { public static void main(String[] args) { // 生病的老师, 被代理对象 ITeacherDao patientTeacher = new TeacherDao(); // 新老师, 代理对象 //ITeacherDao proxyTeacher = new TeacherDaoProxy(patientTeacher); TeacherDaoProxy proxyTeacher = new TeacherDaoProxy(); // 老师代课 proxyTeacher.teach(); } }

实现效果:

image.png

静态代理优缺点

  • 优点:在不修改目标对象的功能前提下,能通过代理对象对目标功能扩展。
  • 缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类一旦接口增加方法,目标对象与代理对象都要维护

动态代理

动态代理模式的基本介绍

  • 代理对象,不需要实现接口,但是目标对象要实现接口,否则不能用动态代理。
  • 代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象动态代理也叫做: JDK代理、接口代理。

image.png

ITeacherDao接口类

public interface ITeacherDao { /** * 老师上课 */ void teach(); }

TeacherDao实现类

public class TeacherDao implements ITeacherDao { @Override public void teach() { System.out.println("开始上课..."); } }

ProxyFactory代理类

public class ProxyFactory{ // 目标对象 private Object target; // 构造器,对target进行初始化 public ProxyFactory(Object target) { this.target = target; } /** * 方法执行前 */ public void preMethod() { System.out.println("方法执行前..."); } /** * 方法执行后 */ private void afterMethod() { System.out.println("方法执行后..."); } // 给目标对象生成代理对象 public Object getProxyInstance() { /** * ClassLoader loader, 指定类加载器 * Class<?>[] interfaces,目标对象(被代理对象),使用泛型方式确认类型 * InvocationHandler h 事件处理执行目标对象的方法时,会触发时间处理器的方法 * 会把当前执行的目标对象方法作为参数传入 */ return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), (proxy, method, args) -> { preMethod(); // 反射机制调用目标对象方法 Object invoke = method.invoke(target, args); afterMethod(); return invoke; }); } }

Test用于测试动态代理效果

public interface Test { void test(); }

Client测试类

public class Client { public static void main(String[] args) { // 被代理对象 TeacherDao target = new TeacherDao(); // 创建代理对象 ITeacherDao proxyITeacher = (ITeacherDao)new ProxyFactory(target).getProxyInstance(); proxyITeacher.teach(); System.out.println("-------------"); Test test = (Test)new ProxyFactory((Test) () -> System.out.println("this is a test") ).getProxyInstance(); test.test(); } }

测试结果:image.png

cglib代理

Cglib代理模式的基本介绍

  • 静态代理和JDK代理模式都要求目标对象是实现一个接口,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候可使用目标对象子类来实现代理,即Cglib代理。

  • Cglib代理也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能扩展,有些书也将Cglib代理归属到动态代理。

  • Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口.它广泛的被许多AOP的框架使用,例如Spring AOP,实现方法拦截。

  • 在AOP编程中如何选择代理模式:

    • 目标对象需要实现接口,用JDK代理
    • 目标对象不需要实现接口,用Cglib代理
  • Cglib包的底层是通过使用字节码处理框架ASM来转换字节码并生成新的类

image.png

导入cglib依赖jar包

image.png`ProxyFactoryd代理工厂

public class ProxyFactory implements MethodInterceptor { // 维护一个目标对象 private Object target; public ProxyFactory(Object target) { this.target = target; } /** * 返回一个target代理对象 * @return */ public Object getProxyInstance() { // 1、创建一个工具类 Enhancer enhancer = new Enhancer(); // 2、设置父类 enhancer.setSuperclass(target.getClass()); // 3、设置回调函数 enhancer.setCallback(this); // 4、返回子类,即代理对象 return enhancer.create(); } /** * 方法执行前 */ public void preMethod() { System.out.println("方法执行前..."); } /** * 方法执行后 */ private void afterMethod() { System.out.println("方法执行后..."); } // 重写拦截方法,调用目标方法 @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { preMethod(); // 使用反射 //Object value = method.invoke(target, objects); // 使用fastClass Object value = methodProxy.invokeSuper(o, objects); afterMethod(); return value; } }

TeacherDao和Test(被代理类)

public class TeacherDao { public void teach() { System.out.println("老师授课中..."); } } class Test{ public void test() { System.out.println("this is a teat..."); } }

Client测试类

public class Client { public static void main(String[] args) { // 被代理类 TeacherDao target = new TeacherDao(); // 创建对象代理 TeacherDao proxyTeacher =(TeacherDao) new ProxyFactory(target).getProxyInstance(); // 执行方法 proxyTeacher.teach(); System.out.println("----------"); // 测试类 ((Test)new ProxyFactory(new Test()).getProxyInstance()).test(); } }

测试结果:

image.png

几种常见的代理模式介绍——几种变体

  • 防火墙代理:内网通过代理穿透防火墙,实现对公网的访问。
  • 缓存代理:比如:当请求图片文件等资源时,先到缓存代理取,如果取到资源则ok,如果取不到资源,再到公网或者数据库取,然后缓存。
  • 远程代理:远程对象的本地代表,通过它可以把远程对象当本地对象来调用。远程代理通过网络和真正的远程对象沟通信息。
  • 同步代理:主要使用在多线程编程中,完成多线程间同步工作
上一篇:玩转基因组浏览器之tdf文件
下一篇:没有了
网友评论