Proxy代理模式

2020-04-14  本文已影响0人  silence_J

静态代理

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

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的动态代理
 * 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
 */
<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");
    }
}
上一篇 下一篇

猜你喜欢

热点阅读