设计模式 ☞ 结构型模式之代理模式

2021-01-07 17:02:58 浏览数 (1)

1.1 简介

1.1.1 概述

  代理模式的定义:由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介,代理控制着对于原对象的访问,并允许在将请求提交给对象前后进行一些处理。在现实生活中,买房子找中介帮忙,这时中介就是代理对象;在软件设计中,使用代理模式的例子也很多,例如,要访问的远程对象比较大,如视频或大图像等,下载要花很多时间。还有因为安全原因需要屏蔽客户端直接访问真实对象,如某单位的内部数据库等。

1.1.2 优缺点

优点:  ① 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;  ② 代理对象可以扩展目标对象的功能;  ③ 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度,增加了程序的可扩展性

缺点:  ① 代理模式会造成系统设计中类的数量增加  ② 在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢;  ③ 增加了系统的复杂度;

1.2 案例

1.2.1 静态代理

  静态代理在使用时,需要定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或者是继承相同父类。静态代理优点是在不修改目标对象的功能前提下,能通过代理对象对目标功能扩展;缺点是因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类。一旦接口增加方法,目标对象与代理对象都要维护。

代码语言:javascript复制
/**
 * @author Demo_Null
 * @version 1.0
 * @date 2021/1/6
 */
public interface IServer {
    void method();
}
代码语言:javascript复制
/**
 * @author Demo_Null
 * @version 1.0
 * @date 2021/1/6
 */
public class Server implements IServer {
    @Override
    public void method() {
        System.out.println("静态代理");
    }
}
代码语言:javascript复制
/**
 * @author Demo_Null
 * @version 1.0
 * @date 2021/1/6
 */
public class Proxy implements IServer {

    /**
     * 目标对象,通过接口聚合
     */
    private IServer server;

    /**
     * 构造器
     * @param server
     */
    public Proxy(IServer server) {
        this.server = server;
    }

    @Override
    public void method() {
        System.out.println("代理前置处理");
        server.method();
        System.out.println("代理后置处理");
    }
}
代码语言:javascript复制
/**
 * @author Demo_Null
 * @version 1.0
 * @date 2021/1/6
 */
public class Client {
    public static void main(String[] args) {
        // 创建目标对象
        Server server = new Server();

        // 创建代理对象
        Proxy proxy = new Proxy(server);

        // 通过代理对象,调用目标对象
        proxy.method();
    }
}

1.2.2 动态代理

☞ jdk

  JDK 动态代理中代理对象不需要实现接口,但是目标对象要实现接口,否则不能用动态代理,代理对象的生成,是利用 JDK 的 API,不需要借助第三方就可以动态的在内存中构建代理对象,代理类所在包:java.lang.reflect.Proxy,JDK 实现代理只需要使用 static Object newProxylnstance(ClassLoader loader, Class<?> [] interfaces, InvocationHandler h) 方法。这种代理方式即使在接口中增加方法也不需要修改 Proxy。

代码语言:javascript复制
/**
 * @author Demo_Null
 * @version 1.0
 * @date 2021/1/6
 */
public interface IServer {
    void method();
}
代码语言:javascript复制
/**
 * @author Demo_Null
 * @version 1.0
 * @date 2021/1/6
 */
public class Server implements IServer {
    @Override
    public void method() {
        System.out.println("动态代理");
    }
}
代码语言:javascript复制
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @author Demo_Null
 * @version 1.0
 * @date 2021/1/6
 */
public class ProxyFactory {

    /**
     * 目标对象,通过接口聚合
     */
    private Object server;

    /**
     * 构造器
     * @param server
     */
    public ProxyFactory(Object server) {
        this.server = server;
    }

    public Object getProxyInstance() {

        /**
         * ClassLoader loader: 目标对象的类加载器
         * Class<?> [] interfaces: 目标对象的实现接口,使用泛型确定类型
         * InvocationHandler h: 事件处理,在执行目标方法时,会触发事件处理器并把当前执行的目标方法作为参数传入
         */
        return Proxy.newProxyInstance(server.getClass().getClassLoader(), server.getClass().getInterfaces(), 
        	new InvocationHandler() {
            	@Override
	            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
	                System.out.println("JDK 代理前置处理");
	                Object invoke = method.invoke(server, args);
	                System.out.println("JDK 代理后置处理");
	                return invoke;
	        }
        });
    }
}
代码语言:javascript复制
/**
 * @author Demo_Null
 * @version 1.0
 * @date 2021/1/6
 */
public class Client {
    public static void main(String[] args) {
        // 创建目标对象
        Server server = new Server();

        // 动态创建代理对象
        IServer proxyInstance = (IServer) new ProxyFactory(server).getProxyInstance();

        // 通过代理对象,调用目标方法
        proxyInstance.method();
    }
}
☞ cglib

  静态代理和 JDK 代理模式都要求目标对象是实现一个接口,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候可使用目标对象子类来实现代理,这就是 Cglib 代理也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能扩展,有的也将 Cglib 代理归属到动态代理。Cglib 是一个强大的高性能的代码生成包,它可以在运行期扩展 java 类与实现 java 接口,Cglib 包的底层是通过使用字节码处理框架 ASM 来转换字节码并生成新的类。它广泛的被许多 AOP 的框架使用,例如 Spring AOP 实现方法拦截。

代码语言:javascript复制
<dependency>
	<groupId>cglib</groupId>
	<artifactId>cglib</artifactId>
	<version>3.1</version>
</dependency>
代码语言:javascript复制
/**
 * @author Demo_Null
 * @version 1.0
 * @date 2021/1/6
 */
public class Server {
    
    public void method() {
        System.out.println("动态代理");
    }
}
代码语言:javascript复制
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;


/**
 * @author Demo_Null
 * @version 1.0
 * @date 2021/1/6
 */
public class ProxyFactory implements MethodInterceptor {

    /**
     * 目标对象,通过接口聚合
     */
    private Object server;

    /**
     * 构造器, 传入目标对象
     * @param server
     */
    public ProxyFactory(Object server) {
        this.server = server;
    }

    /**
     * 返回一个代理对象
     * @return
     */
    public Object getProxyInstance() {
        // 创建工具类
        Enhancer enhancer = new Enhancer();
        // 设置父类
        enhancer.setSuperclass(server.getClass());
        // 设置回调函数,就是他自己
        enhancer.setCallback(this);
        // 创建子类对象即代理对象
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("cglib 代理前置处理");
        Object invoke = method.invoke(server, objects);
        System.out.println("cglib 代理后置处理");
        return invoke;
    }
}
代码语言:javascript复制
/**
 * @author Demo_Null
 * @version 1.0
 * @date 2021/1/6
 */
public class Client {
    public static void main(String[] args) {
        // 创建目标对象
        Server server = new Server();

        // 创建代理对象
        Server proxyInstance = (Server) new ProxyFactory(server).getProxyInstance();

        // 通过代理对象,调用目标方法
        proxyInstance.method();
    }
}

0 人点赞