美文网首页
Proxy代理模式

Proxy代理模式

作者: silence_J | 来源:发表于2020-04-14 11:10 被阅读0次

    静态代理

    各种代理类可以组合起来使用

    import java.util.Random;
    
    /**
     * 不改变源代码,记录坦克移动时间
     * 使用代理模式
     * 静态代理
     */
    public class Tank implements Movable {
    
        /**
         * 模拟坦克移动了一段时间
         */
        @Override
        public void move() {
            System.out.println("Tank is moving ......");
            try {
                // 模拟移动了0~10秒
                Thread.sleep(new Random().nextInt(10000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        public static void main(String[] args) {
            // 任意组合使用
            new TankLogProxy(
                    new TankTimeProxy(
                            new Tank()
                    )
            ).move();
        }
    
    }
    
    // 代理人与代理对象实现共同的接口
    interface Movable{
        void move();
    }
    
    // 代理类
    
    /**
     * 计算时间
     */
    class TankTimeProxy implements Movable {
    
        Movable movable; // 聚合
    
        public TankTimeProxy(Movable movable) {
            this.movable = movable;
        }
    
        // 代理 实现了Movable接口类的 move()方法,并加入时间记录功能
        @Override
        public void move() {
            long start = System.currentTimeMillis();
            movable.move();
            long end = System.currentTimeMillis();
            System.out.println("[TankTimeProxy]--->"+(end-start));
        }
    }
    
    /**
     * 打印日志
     */
    class TankLogProxy implements Movable {
    
        Movable movable; // 聚合
    
        public TankLogProxy(Movable movable) {
            this.movable = movable;
        }
    
        // 代理 实现了Movable接口类的 move()方法,并加入打印日志功能
        @Override
        public void move() {
            System.out.println("[TankLogProxy]---> start moving......");
            movable.move();
            long end = System.currentTimeMillis();
            System.out.println("[TankLogProxy]---> stopped!");
        }
    
    }
    

    动态代理

    jdk实现

    • 问题:如果有不止一个方法需要被代理?
    • 若想让LogProxy可以重用,不仅可以代理Tank,还可以代理其它类型 ( 打印日志,时间计算是很多类都要做的事 ),这时怎么做?
    • 分离代理行为与被代理对象
    • 使用jdk的动态代理,内部是用ASM实现的
    • jdk反射生成代理,被代理对象必须实现某个接口,这是由Proxy的内部实现决定的
    /**
     * 问题:如果有不止一个方法需要被代理?
     * 若想让LogProxy可以重用,不仅可以代理Tank,还可以代理其它类型
     * ( 打印日志,时间计算是很多类都要做的事 ),这时怎么做?
     * 分离代理行为与被代理对象
     * 使用jdk的动态代理
     * jdk反射生成代理,被代理对象必须实现某个接口,这是由Proxy的内部实现决定的
     */
    public class Tank implements Movable {
    
        /**
         * 模拟坦克移动了一段时间
         */
        @Override
        public void move() {
            System.out.println("Tank is moving ......");
            try {
                // 模拟移动了0~10秒
                Thread.sleep(new Random().nextInt(10000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        public static void main(String[] args) {
    
            Tank tank = new Tank();
    
            // 将jdk11 生成的代理类保存成文件
            System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");
    
            // 反射 通过二进制字节码分析类的属性和方法
            Movable proxy = (Movable) Proxy.newProxyInstance(Tank.class.getClassLoader(),
                    new Class[]{Movable.class},
                    new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            System.out.println("method:"+method.getName()+"   start...");
                            Object o = method.invoke(tank, args);
                            System.out.println("method:"+method.getName()+"   end!");
                            return o;
                        }
                    });
            movable.move();
        }
    }
    
    // 代理人与代理对象实现共同的接口
    interface Movable{
        void move();
    }
    

    jdk11下执行
    System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");
    可以将生成的代理类以文件形式保存。
    以上代码中调用 Proxy.newProxyInstance方法会根据所传参数动态生成代理类。生成的代理类反编译后如下:

    final class $Proxy0 extends Proxy implements Movable {
        private static Method m1;
        private static Method m3;
        private static Method m2;
        private static Method m0;
    
        public $Proxy0(InvocationHandler var1) throws  {
            super(var1);
        }
    
        public final boolean equals(Object var1) throws  {
            try {
                return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
            } catch (RuntimeException | Error var3) {
                throw var3;
            } catch (Throwable var4) {
                throw new UndeclaredThrowableException(var4);
            }
        }
    
        public final void move() throws  {
            try {
                super.h.invoke(this, m3, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
        public final String toString() throws  {
            try {
                return (String)super.h.invoke(this, m2, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
        public final int hashCode() throws  {
            try {
                return (Integer)super.h.invoke(this, m0, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
        static {
            try {
                m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
                m3 = Class.forName("com.jiangxb.proxy.v2.Movable").getMethod("move");
                m2 = Class.forName("java.lang.Object").getMethod("toString");
                m0 = Class.forName("java.lang.Object").getMethod("hashCode");
            } catch (NoSuchMethodException var2) {
                throw new NoSuchMethodError(var2.getMessage());
            } catch (ClassNotFoundException var3) {
                throw new NoClassDefFoundError(var3.getMessage());
            }
        }
    }
    

    该代理类默认继承Proxy类并实现了 之前第二个参数传入的Movable接口。代理类中除了有Object类的几个方法外还有 在Movable接口中定义的move()方法
    move()中:

    super.h.invoke(this, m3, (Object[])null);
    

    调用父类Proxy的成员变量h的invoke方法,看下Proxy中:

    protected InvocationHandler h;   // 所定义的成员变量是一个接口
    

    这是一个接口,接口中定义了一个抽象方法:

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

    根据多态调用在匿名内部类中被重写的invoke方法:

    new InvocationHandler() {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("method:"+method.getName()+"   start...");
            Object o = method.invoke(tank, args); // 反射调用
            System.out.println("method:"+method.getName()+"   end!");
            return o;
        }
    }
    

    cglib实现

    cglib实现动态代理不需要接口
    需要导入依赖:

    <!-- https://mvnrepository.com/artifact/cglib/cglib -->
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.2.12</version>
     </dependency>
    

    实现:

    public class Main {
        public static void main(String[] args) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(Tank.class);
            enhancer.setCallback(new TimeMethodInterceptor());
            Tank tank = (Tank) enhancer.create();
            tank.move();
        }
    }
    
    class  Tank {
        /**
         * 模拟坦克移动了一段时间
         */
        public void move() {
            System.out.println("Tank is moving ......");
            try {
                // 模拟移动了0~10秒
                Thread.sleep(new Random().nextInt(10000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    class  TimeMethodInterceptor implements MethodInterceptor{
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            System.out.println("before");
            Object result = null;
            result = methodProxy.invokeSuper(o, objects);
            System.out.println("after");
            return result;
        }
    }
    

    Spring AOP

    IOC+AOP
    bean工厂+灵活装配+动态行为拼接(织入)使Spring极为灵活
    使用AOP代理,导入依赖:

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.2.3.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.9.4</version>
    </dependency>
    

    Spring配置文件中进行aop配置:

    <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns="http://www.springframework.org/schema/beans"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
                              http://www.springframework.org/schema/beans/spring-beans.xsd
                              http://www.springframework.org/schema/aop
                              http://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <!-- 实体类 -->
        <bean id="tank" class="com.jiangxb.proxy.v4.Tank"/>
        <!-- 切面类 -->
        <bean id="timeProxy" class="com.jiangxb.proxy.v4.TimeProxy"/>
        
        <aop:config>
            <aop:aspect id="time" ref="timeProxy">
                <!-- 切点 -->
                <aop:pointcut id="onmove" expression="execution(void com.jiangxb.proxy.v4.Tank.move())"/>
                <aop:before method="before" pointcut-ref="onmove"/>
                <aop:after method="after" pointcut-ref="onmove"/>
            </aop:aspect>
        </aop:config>
    
    </beans>
    

    Main:

    public class Main {
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("app.xml");
            Tank tank = (Tank) context.getBean("tank");
            tank.move();
        }
    }
    /**
     * 输出为:
     * 
     * before
     * Tank is moving ......
     * after
     */
    
    • 除此外还可用更简单的注解配置AOP:
      app.xml:开启自动代理
    <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns="http://www.springframework.org/schema/beans"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
                              http://www.springframework.org/schema/beans/spring-beans.xsd
                              http://www.springframework.org/schema/aop
                              http://www.springframework.org/schema/aop/spring-aop.xsd">
        <!-- 开启自动代理 -->
        <aop:aspectj-autoproxy/>
    
        <!-- 实体类 -->
        <bean id="tank" class="com.jiangxb.proxy.v4.Tank"/>
        <!-- 切面类 -->
        <bean id="timeProxy" class="com.jiangxb.proxy.v4.TimeProxy"/>
    
    </beans>
    

    在切面类中添加注解及切入点表达式

    @Aspect
    public class TimeProxy {
        
        @Before("execution(void com.jiangxb.proxy.v4.Tank.move())")
        public void before () {
            System.out.println("before");
        }
        
        @After("execution(void com.jiangxb.proxy.v4.Tank.move())")
        public void after () {
            System.out.println("after");
        }
    }
    

    相关文章

      网友评论

          本文标题:Proxy代理模式

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