设计模式(十)代理模式

本文详细介绍了Java中的代理模式,包括静态代理和动态代理。动态代理分为JDK代理和CGLIB代理,分别展示了如何通过它们实现方法的增强,如添加日志和计时功能。JDK代理基于接口实现,CGLIB则是通过继承被代理类。此外,还提及了Instrument类加载器在动态代理中的作用。
摘要由CSDN通过智能技术生成

代理模式

Proxy

  • 动态代理
  • 静态代理

解释

类似于一个代理商的概念,代理主要的作用就是在不妨碍被代理方法执行的同时,添加一些逻辑。

比如:

  • 加一个日志的打印
  • 记录执行时间

类图

在这里插入图片描述

生成&执行流程图

在这里插入图片描述

代码

  • 静态代理
public class Tank implements Movable {

    /**
     * 模拟坦克移动了一段儿时间
     */
    @Override
    public void move() {
        System.out.println("Tank moving claclacla...");
        try {
            Thread.sleep(new Random().nextInt(10000));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {

        Tank t = new Tank();
        TankTimeProxy ttp = new TankTimeProxy(t);
        TankLogProxy tlp = new TankLogProxy(ttp);
        tlp.move();
    }
}

class TankTimeProxy implements Movable {
    Movable m;

    public TankTimeProxy(Movable m) {
        this.m = m;
    }

    @Override
    public void move() {
        long start = System.currentTimeMillis();
        m.move();
        long end = System.currentTimeMillis();
        System.out.println(end - start);
    }
}

class TankLogProxy implements Movable {
    Movable m;

    public TankLogProxy(Movable m) {
        this.m = m;
    }

    @Override
    public void move() {
        System.out.println("start moving...");
        m.move();
        long end = System.currentTimeMillis();
        System.out.println("stopped!");
    }
}

interface Movable {
    void move();
}
  • 动态代理之JDK代理
public static void main(String[] args) {
  Tank tank = new Tank();

  //reflection 通过二进制字节码分析类的属性和方法

  Movable m = (Movable)Proxy.newProxyInstance(Tank.class.getClassLoader(),
    new Class[]{Movable.class}, //tank.class.getInterfaces()
    new TimeProxy(tank)
  );

  m.move();
}


class TimeProxy implements InvocationHandler {
    Movable m;

    public TimeProxy(Movable m) {
        this.m = m;
    }

    public void before() {
        System.out.println("method start..");
    }

    public void after() {
        System.out.println("method stop..");
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object o = method.invoke(m, args);
        after();
        return o;
    }

}
  • JDK动态代理生成的类
// 1.修改配置,保存动态代理生成的类,1.8之后
System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles","true");
// 1.8以及1.8版本之间的配置方法
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");


// 2.动态生成的代理类代码
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 void move() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
  	//省略toString、hashCode、equals代码

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("com.test.testJdkProxy.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());
        }
    }
}

  • 动态代理之Cglib

    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 TimeMethodInterceptor implements MethodInterceptor {
    
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    
            System.out.println(o.getClass().getSuperclass().getName());
            System.out.println("before");
            Object result = null;
            result = methodProxy.invokeSuper(o, objects);
            System.out.println("after");
            return result;
        }
    }
    
    class Tank {
        public void move() {
            System.out.println("Tank moving claclacla...");
            try {
                Thread.sleep(new Random().nextInt(10000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    • 如果类是final的则不可生成代理类
      • 但是asm可以修改…
      • cglib底层也是使用的asm
  • 动态代理之 Instrument

    • class文件load到内存中拦截,实现逻辑修改class

知识点

代理方式

  • 实现同样的接口,实现类中添加被代理的对象,在实际方法中添加自己的逻辑并执行原方法

    class TankTimeProxy implements Movable {
    
        Tank tank;
    
        public TankTimeProxy(Tank tank) {
            this.tank = tank;
        }
    
        @Override
        public void move() {
            long start = System.currentTimeMillis();
            tank.move();
            long end = System.currentTimeMillis();
            System.out.println(end - start);
        }
    }
    

    但是这样的实现是有问题的。

    • 如果想要多个代理嵌套怎么办?(传递的对象是类,代理类和原对象没有实际关系)
      • 所以实际应用中,传递的是接口(代理类和原类都实现了接口)
  • 通过继承方式

    • 扩展性不好。**cglib ** code generator library
  • 通过类加载进内存前拦截,自定义class内容。

    • 实现难度大。 Instrument

动态代理

如果需要一个代理可以代理Object并且不影响之前方法的使用,那么就需要引入动态代理。

JDK动态代理

通过实现相同接口来进行代理,运行期间动态生成代理类,通过invokeHandler接口的实现逻辑进行代理逻辑的执行。

参数问题

三个参数 第一个参数是代理对象( P r o x y Proxy Proxy,第二个参数是method,第三个参数是args。

代理类生成原理

asm 直接操作字节码(二进制)

Cglib动态代理
  • 使用继承,继承代理类,调用父类方法实现代理。
  • 不可代理final的类
  • 底层原理也是asm

类加载器

BootstrapClassLoader

ExtClassLoader

AppClassLoader

Jdk动态代理时传递的第一个参数一般为被代理类的classLoader,防止不同classLoader不能加载到内存

父类加载器可以加载,同级别兄弟加载器不可以。

Spring的Aop就是使用的动态代理

  • 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-4.2.xsd">

    <!--

    <bean id="d" class="com.mashibing.Driver"></bean>
    <bean id="tank" class="com.mashibing.Tank">
        <property name="driver" ref="d"></property>
    </bean>

    -->

    <bean id="tank" class="com.mashibing.dp.spring.v1.Tank"/>
    <bean id="timeProxy" class="com.mashibing.dp.spring.v1.TimeProxy"/>

    <aop:config>
        <aop:aspect id="time" ref="timeProxy">
            <aop:pointcut id="onmove" expression="execution(void com.mashibing.dp.spring.v1.Tank.move())"/>
            <aop:before method="before" pointcut-ref="onmove"/>
            <aop:after method="after" pointcut-ref="onmove"/>
        </aop:aspect>
    </aop:config>

</beans>
  • 注解
@Aspect
public class TimeProxy {

    @Before("execution (void com.mashibing.dp.spring.v2.Tank.move())")
    public void before() {
        System.out.println("method start.." + System.currentTimeMillis());
    }

    @After("execution (void com.mashibing.dp.spring.v2.Tank.move())")
    public void after() {
        System.out.println("method stop.." + System.currentTimeMillis());
    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值