美文网首页我爱编程
设计模式-代理模式(九)

设计模式-代理模式(九)

作者: 巨子联盟 | 来源:发表于2018-05-23 20:07 被阅读0次

    代理模式:
    给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用
    面对抽象编程,不要面对具体编程,没事就造个类,把真正的业务逻辑放到某一个类里面,一个类实现一个功能,然后找个类组装一下,这个类就是代理类,欧了...

    类图如下:


    代理模式.png
    • 与装饰模式的区别
      装饰模式用意是为了加强所装饰类的功能,而代理模式是为了对代理对象加以控制.

    • java实现代理模式的方式
      实现方法很多种,大概分:
    1. 静态代理
    2. JDK动态代理
    3. Cglib代理
    4. javassist代理

    1. 静态代理代码示例:
    1. 老套路,先定一个接口,说明我要干啥
    /**
     * FileName :UserService
     * Author :zengzhijun
     * Date : 2018/5/7 10:04
     * Description:
     */
    package com.byedbl.proxy.staticmodel;
    
    public interface UserService {
    
        int save();
    
        int delete();
    }
    
    
    1. 第二步就是实现这个接口
    /**
     * FileName :UserServiceImpl
     * Author :zengzhijun
     * Date : 2018/5/7 10:06
     * Description:
     */
    package com.byedbl.proxy.staticmodel.impl;
    
    
    import com.byedbl.proxy.staticmodel.UserService;
    
    public class UserServiceImpl implements UserService {
        @Override
        public int save() {
            System.out.println(UserServiceImpl.class.getName() +" saveing...");
            return 0;
        }
    
        @Override
        public int delete() {
            System.out.println(UserServiceImpl.class.getName()+" delete...");
            return 0;
        }
    }
    
    
    1. 建一个代理类,代理UserServiceImpl
    /**
     * FileName :UserServiceProxy
     * Author :zengzhijun
     * Date : 2018/5/7 10:08
     * Description:代理类
     */
    package com.byedbl.proxy.staticmodel;
    
    
    import com.byedbl.proxy.staticmodel.impl.UserServiceImpl;
    
    public class UserServiceProxy implements UserService{
        private UserService userService = new UserServiceImpl();
        @Override
        public int save() {
            System.out.println(UserServiceProxy.class.getName()+" save start...");
            userService.save();
            System.out.println(UserServiceProxy.class.getName()+" save end...");
            return 0;
        }
    
        @Override
        public int delete() {
            System.out.println(UserServiceProxy.class.getName()+" delete start...");
            userService.delete();
            System.out.println(UserServiceProxy.class.getName()+" delete end...");
            return 0;
        }
    }
    
    
    1. 客户端用代理类
    package com.byedbl.proxy.staticmodel;
    
    import org.junit.Test;
    
    public class UserServiceProxyTest {
    
        /**
         * <pre>
         * 缺点:代理对象UserServiceProxy需要与目标对象UserServiceImpl实现一样的接口,所以会有很多代理类,类太多.同时,一旦接口增加方法,目标对象与代理对象都要维护
         * @author : zengzhijun
         * @date : 2018/5/7 10:15
         **/
        @Test
        public void save() {
    
            UserServiceProxy proxy = new UserServiceProxy();
            proxy.save();
        }
    }
    
    

    2. JDK动态代理代码示例
    1. 创建JDK要的代理工厂,
      关键代码Proxy.newProxyInstance,传入3个参数,一个是被代理类的类加载器ClassLoader,一个是被代理类需要代理的接口,另一个就是 InvocationHandler实现类
    /**
     * FileName :ProxyFactory
     * Author :zengzhijun
     * Date : 2018/5/7 10:21
     * Description:代理工厂
     */
    package com.byedbl.proxy.dynamic;
    
    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(proxy.getClass());
                            System.out.println("开始事务2");
                            //执行目标对象方法
                            Object returnValue = method.invoke(target, args);
                            System.out.println("提交事务2");
                            return returnValue;
                        }
                    }
            );
        }
    }
    
    
    1. 客户端用法
    package com.byedbl.proxy.dynamic;
    
    import com.byedbl.proxy.staticmodel.UserService;
    import com.byedbl.proxy.staticmodel.impl.UserServiceImpl;
    import org.junit.Test;
    import org.springframework.util.ClassUtils;
    
    public class ProxyFactoryTest {
    
        /**
         * 代理对象不需要实现接口,但是目标对象一定要实现接口,否则不能用动态代理
         * @author : zengzhijun
         * @date : 2018/5/7 10:43
         **/
        @Test
        public void getProxyInstance() {
            // 目标对象
            UserService target = new UserServiceImpl();
            // 【原始的类型】
            System.out.println("原始的类型:"+target.getClass());
    
            // 给目标对象,创建代理对象
            UserService proxy = (UserService) new ProxyFactory(target).getProxyInstance();
            // class $Proxy0   内存中动态生成的代理对象
            System.out.println("内存中动态生成的代理对象:"+proxy.getClass());
    
            // 执行方法   【代理对象】
            proxy.save();
            proxy.delete();
            System.out.println(ClassUtils.getDescriptiveType(proxy));
        }
    
    }
    
    

    3. Cglib动态代理代码示例

    导入jar包,直接导入Spring aop的包就有了

    1. 创建需要代理的对象
    /**
     * FileName :UserServiceDao
     * Author :zengzhijun
     * Date : 2018/5/7 10:49
     * Description:
     */
    package com.byedbl.proxy.cglib;
    
    public class UserServiceDao {
    
        public void save() {
            System.out.println(UserServiceDao.class.getName()+" saving");
        }
    }
    
    
    1. 创建代理工厂
    /**
     * FileName :CglibProxyFactory
     * Author :zengzhijun
     * Date : 2018/5/7 10:50
     * Description:Cglib代理工厂
     */
    package com.byedbl.proxy.cglib;
    
    import org.springframework.cglib.proxy.Enhancer;
    import org.springframework.cglib.proxy.MethodInterceptor;
    import org.springframework.cglib.proxy.MethodProxy;
    
    import java.lang.reflect.Method;
    
    public class CglibProxyFactory implements MethodInterceptor {
        //维护目标对象
        private Object target;
    
        public CglibProxyFactory(Object target) {
            this.target = target;
        }
    
        /**
         *给目标对象创建一个代理对象
         **/
        public Object getProxyInstance(){
            //1.工具类
            Enhancer en = new Enhancer();
            //2.设置父类
            en.setSuperclass(target.getClass());
            //3.设置回调函数
            en.setCallback(this);
            //4.创建子类(代理对象)
            return en.create();
    
        }
    
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
    
            System.out.println("开始事务1...");
    
            //执行目标对象的方法
            Object returnValue = method.invoke(target, args);
    
            System.out.println("提交事务1...");
    
            return returnValue;
        }
    }
    
    
    1. 客户端用法
    package com.byedbl.proxy.cglib;
    
    import org.junit.Test;
    
    public class ProxyFactoryTest {
    
    
        @Test
        public void intercept() {
            //目标对象
            UserServiceDao target = new UserServiceDao();
    
            //代理对象
            UserServiceDao proxy = (UserServiceDao)new CglibProxyFactory(target).getProxyInstance();
    
            //执行代理对象的方法
            proxy.save();
        }
    }
    
    

    Cglib就没有JDK代理的必须要有接口的限制,强大了一点...


    4. javassist代理示例

    javassist 这个直接该字节码的方式来实现,有点小暴力

    1. 加入依赖
            <!--https://github.com/jboss-javassist/javassist/releases/tag/rel_3_20_0_ga-->
            <dependency>
                <groupId>org.javassist</groupId>
                <artifactId>javassist</artifactId>
                <version>3.20.0-GA</version>
            </dependency>
    
    1. 创建一个要代理的类
    /**
     * FileName :JavassistClass
     * Author :zengzhijun
     * Date : 2018/5/7 15:09
     * Description:
     */
    package com.byedbl.proxy.javassist;
    
    public class JavassistClass {
        private String name = "default";
    
        public JavassistClass() {
            name = "me";
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public void execute() {
            System.out.println(name);
            System.out.println("execute ok");
        }
    }
    
    
    1. 修改字节码,实现代理效果
    /**
     * FileName :JavassistLearn1
     * Author :zengzhijun
     * Date : 2018/5/7 15:07
     * Description:
     */
    package com.byedbl.proxy.javassist;
    import javassist.util.proxy.MethodFilter;
    import javassist.util.proxy.MethodHandler;
    import javassist.util.proxy.ProxyFactory;
    
    import java.lang.reflect.Method;
    
    public class JavassistLearn1 {
    
    
    
        public static void main(String[] args) throws Exception{
            ProxyFactory factory=new ProxyFactory();
            //设置父类,ProxyFactory将会动态生成一个类,继承该父类
            factory.setSuperclass(JavassistClass.class);
            //设置过滤器,判断哪些方法调用需要被拦截
            factory.setFilter(new MethodFilter() {
                @Override
                public boolean isHandled(Method m) {
                    if(m.getName().equals("getName")){
                        return true;
                    }
                    return false;
                }
            });
            //设置拦截处理
            factory.setHandler(new MethodHandler() {
                @Override
                public Object invoke(Object self, Method thisMethod, Method proceed,
                                     Object[] args) throws Throwable {
                    //拦截后前置处理,改写name属性的内容
                    //实际情况可根据需求修改
                    JavassistClass o=(JavassistClass) self;
                    o.setName("haha");
                    return proceed.invoke(self, args);
                }
            });
    
            Class<?> c=factory.createClass();
            JavassistClass object=(JavassistClass) c.newInstance();
            System.out.println(object.getName());//输出haha
    
        }
    }
    
    

    SpringAop的实现方式也是利用JDK动态代理和Cglib两种方式来处理的.在此就略了...

    相关文章

      网友评论

        本文标题:设计模式-代理模式(九)

        本文链接:https://www.haomeiwen.com/subject/ggnrjftx.html