设计模式-结构型-代理模式

概述

  • 当某对象要去访问目标对象时,不能或不适合直接访问目标对象时,可以创建一个代理对象去反问目标对象。

JAVA中的代理

  • 静态代理:在编译时期生成。
  • 动态代理:在运行时期动态生成,可分为JDK代理(接口代理)和CGLib代理(继承代理)。

角色

  • 抽象对象:规范,需要实现的业务方法。
  • 具体对象:实现规范的对象,实现了具体的业务。
  • 代理对象:对具体对象进行代理,可增强或控制具体对象。

静态代理

实现

public class Test {
    public static void main(String[] args) {
        ProxyCattle proxyCattle = new ProxyCattle();
        proxyCattle.sell();
    }
}

// 抽象对象,票
interface Ticket {
    void sell();
}
// 具体对象,演唱会
class VocalConcert implements Ticket {
    @Override
    public void sell() {
        System.out.println("演唱会门票");
    }
}
// 代理对象,黄牛
class ProxyCattle implements Ticket {
    private VocalConcert vocalConcert = new VocalConcert();
    @Override
    public void sell() {
        System.out.println("黄牛服务费");
        vocalConcert.sell();
    }
}

动态代理

JDK动态代理

实现

public class Test{
    public static void main(String[] args) {
        TicketProxyFactory ticketProxyFactory = new TicketProxyFactory(new VocalConcert());
        Ticket proxyInstance = ticketProxyFactory.getProxyInstance();
        proxyInstance.sell();
    }
}

// 抽象对象,票
interface Ticket {
    void sell();
}

// 具体对象,演唱会
class VocalConcert implements Ticket {
    @Override
    public void sell() {
        System.out.println("演唱会门票");
    }
}

// JDK 动态代理 (接口代理)
class TicketProxyFactory {

    // 目标对象 (VocalConcert 可以改成Object,就是说可以代理所有的类。哈哈)
    public VocalConcert vocalConcert;

    public TicketProxyFactory(VocalConcert vocalConcert) {
        this.vocalConcert = vocalConcert;
    }
	// 运行时,调用,动态生成代理类
    public Ticket getProxyInstance() {

        /**
         * ClassLoader loader,  类加载器,JVM加载代理类使用
         * Class<?>[] interfaces, 代理对象实现的接口(JDK 动态代理通过接口进行代理)
         * InvocationHandler h  代理对象需要进行的处理
         * @return
         */
        Ticket ticketProxy = (Ticket) Proxy.newProxyInstance(
                vocalConcert.getClass().getClassLoader(),
                vocalConcert.getClass().getInterfaces(),
                new InvocationHandler() {
                    /**
                     *
                     * @param proxy 代理对象 其实就是返回的 ticketProxy
                     * @param method 目标对象接口中的方法
                     * @param args 目标对象中接口方法的实际参数
                     * @return
                     * @throws Throwable
                     */
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("前处理");
                        // 通过目标对象和参数执行方法,返回invoke
                        Object invoke = method.invoke(vocalConcert, args);
                        System.out.println("后处理");
                        return invoke;
                    }
                }
        );
        return ticketProxy;
    }

JDK动态代理原理

  • 可通过阿里巴巴提供的Java诊断工具Arthas(阿尔萨)查看内存中的代理类结构
  • 当调用sell()方法时,调用的是代理类($Proxy)的sell()方法
  • 代理类继承Proxy类,代理类sell()方法会去调用Proxy子实现类InvocationHandler()的invoke()方法
  • 有接口,有方法,其实就可以通过反射来获取目标对象的 方法(Method method),方法参数(Objects[] args)
  • 在invoke(Object proxy, Method method, Object[] args)方法中通过反射,可去调用目标对象的sell()方法(可进行前处理,后处理)
原理简化版
// JVM中的代理对象
public final class $Proxy0 extends Proxy implements Ticket {
	private static Method m3;
	public static $Proxy0(InvocationHandler invocationHandler) {
		super(invocationHandler);
	}
	static {
		m3 = Class.forName("com.it.proxy.Ticket").getMethod("sell", new Class[0]);
	}
	public final void sell() {
		this.h.invoke(this, m3, null); // 多态,子实现类重写了
		return;
	}
}
// Proxy类
class Proxy {
	protected InvocationHandler h;
	protected Proxy(InvocationHandler h) {
        Objects.requireNonNull(h);
        this.h = h;
    }
}

CGLib动态代理

  • 是第三方提供的,需引入jar包

依赖引入:

	<dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.2.4</version>
    </dependency>

实现

public class Test {
    public static void main(String[] args) {
        CGLibProxyFactory cgLibProxyFactory = new CGLibProxyFactory();
        VocalConcert proxyInstance = cgLibProxyFactory.getProxyInstance();
        proxyInstance.sell();
    }
}

// 目标对象,演唱会
class VocalConcert {
    public void sell() {
        System.out.println("演唱会门票");
    }
}

class CGLibProxyFactory implements MethodInterceptor {

    public VocalConcert getProxyInstance() {
        // 增强类
        Enhancer enhancer = new Enhancer();
        // 要增强的类(增强为父类)(同理,这里也可以改成Object.class 玩)
        enhancer.setSuperclass(VocalConcert.class);
        // 怎么增强,做些什么(回调函数,这里使用callback的子类:MethodInterceptor)
        enhancer.setCallback(this);
        // 返回增强类(代理类)
        VocalConcert vocalConcert = (VocalConcert) enhancer.create();
        return vocalConcert;
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("前处理");
        // 调用的是代理对象父类的方法
        Object invoke = methodProxy.invokeSuper(o, objects);
        System.out.println("后处理");
        return invoke;
    }
}

代理模式对比

  • 静态代理使用较少,接口中的每个方法都要处理,如果接口扩展,实现类和代理类都需要重写新方法。
  • 有接口的话,建议使用JDK动态代理,对所有方法进行代理。
  • 没有接口,可使用CGLib动态代理,对所有方法进行代理(因为是继承代理,所有不能对final类型的类和方法进行代理)。

优缺点

优点:

  • 可以对目标对象增强或隔绝保护。客户端访问代理对象,而不能直接访问目标对象。
  • 让目标对象和客户端分离,减低耦合性。

缺点:

  • 增加系统的复杂度。

使用场景

  • 数据库事务
  • 权限控制

改动测试-JDK动态代理

public class Test{

    public static void main(String[] args) {
        TicketProxyFactory ticketProxyFactory = new TicketProxyFactory(new VocalConcert());
        TicketProxyFactory ticketProxyFactory2 = new TicketProxyFactory(new VocalConcert());
		// 相同的目标对象,不同的代理对象  false
        System.out.println(ticketProxyFactory.getProxyInstance() == ticketProxyFactory.getProxyInstance());
        // 相同的目标对象,不同的代理对象 sell()返回的对象相同  ture (目标对象相同,sell()获取的对象也就相同)
        System.out.println(ticketProxyFactory.getProxyInstance().sell() == ticketProxyFactory.getProxyInstance().sell());
        // 不同的目标对象,不同的代理对象 sell()返回的对象不同  ture (目标对象不同,sell()获取的对象也就不同)
        System.out.println(ticketProxyFactory.getProxyInstance().sell() == ticketProxyFactory2.getProxyInstance().sell());
    }
}

// 抽象对象,票
interface Ticket {
    Test2222 sell();
}

// 具体对象
class VocalConcert implements Ticket {
    Test2222 test2222 = new Test2222();
    @Override
    public Test2222 sell() {
        return test2222;
    }
}

// JDK 动态代理 (接口代理)
class TicketProxyFactory {

    // 目标对象
    public VocalConcert vocalConcert;

    public TicketProxyFactory(VocalConcert vocalConcert) {
        this.vocalConcert = vocalConcert;
    }

    public Ticket getProxyInstance() {

        Ticket ticketProxy = (Ticket) Proxy.newProxyInstance(
                vocalConcert.getClass().getClassLoader(),
                vocalConcert.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        Object invoke = method.invoke(vocalConcert, args);
                        return invoke;
                    }
                }
        );
        return ticketProxy;
    }
}

class Test2222{

}

改动测试-CGLib动态代理

public class Test {
    public static void main(String[] args) {
        CGLibProxyFactory cgLibProxyFactory = new CGLibProxyFactory();
        // 生成不同的代理对象  false
        System.out.println(cgLibProxyFactory.getProxyInstance() == cgLibProxyFactory.getProxyInstance());
        // 生成不同的代理对象 sell()返回的也不同 false (因为是继承代码,调用的是父类的sell()方法,因此不同)
        System.out.println(cgLibProxyFactory.getProxyInstance().sell() == cgLibProxyFactory.getProxyInstance().sell());
    }
}

// 目标对象
class VocalConcert {
    Test333 test333 = new Test333();
    public Test333 sell() {
        return test333;
    }
}

class CGLibProxyFactory implements MethodInterceptor {

    public VocalConcert getProxyInstance() {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(VocalConcert.class);
        enhancer.setCallback(this);
        VocalConcert vocalConcert = (VocalConcert) enhancer.create();
        return vocalConcert;
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        Object invoke = methodProxy.invokeSuper(o, objects);
        return invoke;
    }
}

class Test333{

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值