zoukankan      html  css  js  c++  java
  • 设计模式之代理模式

    代理模式

    代理模式是一种设计模式,提供了对目标对象额外的访问方式,即通过代理对象访问目标对象,这样可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能

    代理模式UML类图

    一个典型的代理模式通常有三个角色,这里称之为代理三要素:共同接口、真实对象、代理对象

    代理模式分为静态代理和动态代理两种实现方式,其中动态代理有JDK动态代理和CGLIB动态代理两种实现方式。

    静态代理

    例子

    接口:IUserDAO

    package cn.chengh.proxy;
    
    public interface IUserDAO {
        
        void doAction();
    }

    实现类:UserDAOImpl

    package cn.chengh.proxy;
    
    public class UserDAOImpl implements IUserDAO {
    
        @Override
        public void doAction() {
            System.out.println("搞事情...");
        }
    
    }

    代理类:UserDAOProxy

    package cn.chengh.proxy;
    
    public class UserDAOProxy implements IUserDAO {
    
        private IUserDAO target;
        
        public UserDAOProxy(IUserDAO target) {
            this.target = target;
        }
        
        @Override
        public void doAction() {
            System.out.println("搞事情前...");
            target.doAction();
            System.out.println("搞完事情后...");
        }
    
    }

    测试

    package cn.chengh.proxy;
    
    public class MyTest {
        public static void main(String[] args) {
            // 目标对象
            IUserDAO target = new UserDAOImpl();
            // 代理对象
            UserDAOProxy proxy = new UserDAOProxy(target);
            // 代理对象执行代理操作
            proxy.doAction();
        }
    }

    上述静态代理的实现方式可以很容易看出静态代理的优缺点

    优点:1、在不修改目标对象的前提下扩展目标对象的功能。2、实现起来简单,容易理解。

    缺点:一个代理对象代理一个真实对象,当多个真实对象需要被代理时需要创建多个代理对象,这显然会产生过多的代理类。

    JDK动态代理

    动态代理利用了JDK API,动态地在内存中构建代理对象,从而实现对目标对象的代理功能。动态代理又被称为JDK代理或接口代理。

    注:JDK动态代理要求目标对象必须实现接口,否则不能使用动态代理。

    JDK中生成代理对象主要涉及的类和方法:

    java.lang.reflect Proxy类,使用的方法:

    public static Object newProxyInstance(ClassLoader loader,
                                              Class<?>[] interfaces,
                                              InvocationHandler h)

    这三个参数的含义:

    ClassLoader loader:目标对象的类加载器

    Class<?>[] interfaces:目标对象实现的接口

    InvocationHandler h:事件处理器,代理对象的具体代理操作

    java.lang.reflect InvocationHandler接口,使用的方法:

    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable;

    在invoke实现代理类的具体代理操作。

    例子

    接口:IUserDAO

    package cn.chengh.jdkproxy;
    
    public interface IUserDAO {
        void doAction();
    }

    实现类:UserDAOImpl

    package cn.chengh.jdkproxy;
    
    public class UserDAOImpl implements IUserDAO {
    
        @Override
        public void doAction() {
            System.out.println("搞事情...");        
        }
    
    }

    代理工厂类:ProxyFactory,用来生成代理对象

    package cn.chengh.jdkproxy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class ProxyFactory {
        
        private Object target;
        
        public ProxyFactory(Object target) {
            this.target = target;
        }
        
        // 此方法用来生成代理对象
        public Object getProxyInstance() {
            return Proxy.newProxyInstance(target.getClass().getClassLoader(), 
                    target.getClass().getInterfaces(), 
                    new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    System.out.println("搞事情前...");
                    // 执行目标对象方法
                    method.invoke(target, args);
                    System.out.println("搞事情后...");
                    return null;
                }
            });
        }
    }

    测试

    package cn.chengh.jdkproxy;
    
    public class MyTest {
    
        public static void main(String[] args) {
            // 目标对象
            IUserDAO target = new UserDAOImpl();  
            // 得到代理对象
            IUserDAO proxy = (IUserDAO) new ProxyFactory(target).getProxyInstance();  
            // 代理对象执行代理操作
            proxy.doAction(); 
        }
    }

    JDK动态代理的优缺点

    优点:动态代理对象是在运行时动态生成的,相比静态代理,不需要编写大量的代理类。

    缺点:JDK动态代理要求目标对象必须实现接口,否则不能使用JDK动态代理。

    CGLIB动态代理

    CGLIB(CODE GENERLIZE LIBRARY)代理是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的所有方法,所以该类或方法不能声明称final的

    cglib特点

    • JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口。如果想代理没有实现接口的类,就可以使用CGLIB实现。
    • CGLIB是一个强大的高性能的代码生成包,它可以在运行期扩展Java类与实现Java接口。
    • 它广泛的被许多AOP的框架使用,例如Spring AOP和dynaop,为他们提供方法的interception(拦截)。
    • CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。

    例子

    cglib动态代理需要引入cglib-full-2.0.2.jar包

    目标类:UserDAO

    package cn.chengh.cglibproxy;
    
    public class UserDAO {
        
        public void doAction() {
            System.out.println("搞事情...");
        }
    }

    代理工厂类:ProxyFactory,用来生成代理对象

    package cn.chengh.cglibproxy;
    
    import java.lang.reflect.Method;
    
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    public class ProxyFactory implements MethodInterceptor {
        
        private Object target;
        
        public ProxyFactory(Object target) {
            this.target = target;
        }
    
        public Object getProxyInstance() {
            // 工具类
            Enhancer enhancer = new Enhancer();
            // 设置父类
            enhancer.setSuperclass(target.getClass());
            // 设置回调函数
            enhancer.setCallback(this);
            // 创建子类对象代理
            return enhancer.create();
        }
    
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            System.out.println("搞事情前...");
            method.invoke(target, args);
            System.out.println("搞事情后...");
            return null;
        }
    }

    测试

    package cn.chengh.cglibproxy;
    
    public class MyTest {
        public static void main(String[] args) {
            UserDAO target = new UserDAO();
            UserDAO proxy = (UserDAO) new ProxyFactory(target).getProxyInstance();
            proxy.doAction();
        }
    }

    动态代理的应用

    Spring框架中aop就是使用了jdk动态代理和cglib动态代理。如果目标对象没有实现接口,则默认会采用CGLIB代理。如果目标对象实现了接口,可以强制使用CGLIB实现代理。

  • 相关阅读:
    android: 建立和断开a2dp link 相关方法总结
    Java: volatile和synchronized的区别 (转)
    shell 将变量当命令执行的几种方式
    tcp 自连接(主机的端口和自己建立连接)
    sed使用
    海尔风冷冰箱冷冻室最底层结冰 问题解决
    k8s 传参数的两种方式一种是 环境变量 拼接 另一种说是yaml传参到配置文件的表示怀疑要验证????????????????????????????
    k8s 集群中的etcd故障解决
    dockerfile
    使用VIM/VI给文件加密和解密
  • 原文地址:https://www.cnblogs.com/binaway/p/9754775.html
Copyright © 2011-2022 走看看