java设计模式-代理模式

代理模式:

代理模式还是比较好理解,就是委托别人做事情,比如我们要租房子,委托中介去找房子,这就是代理模式。代理模式分为静态代理模式和动态代理模式。

静态代理:

package com.example.designmode.proxy;

/**
 * <h3>design-mode</h3>
 * <p>定义消费者接口,查找指定区域,指定价格以下的房子</p>
 *
 * @author : ZhangYuJie
 * @date : 2022-03-27 14:38
 **/

public interface ICustomer {
    /**
     * 查找房子,指定区域,指定价格
     */
    String findHouse(String location, int price);
}

/**
 * 真正的找房消费者,所以只关注找到这个目标
 **/
class ZhangCustomer implements ICustomer {
    @Override
    public String findHouse(String location, int price) {
        System.out.println("找到了位于[" + location + "],价格" + price + "以下的房子");
        return "找到了位于[" + location + "],价格" + price + "以下的房子";
    }
}

/**
 * 找房子中介类,找到合适房子再反馈给指消费者
 **/
class AgencyProxy implements ICustomer {
    private ICustomer coustomer;

    public AgencyProxy(ICustomer coustomer) {
        this.coustomer = coustomer;
    }


    @Override
    public String findHouse(String location, int price) {
        String result = "没有符合条件的房子";
        before();
        if (null != coustomer) {
            result = coustomer.findHouse(location, price);
        }
        after();
        return result;
    }

    private void before() {
        System.out.println("开始找房子");
    }

    private void after() {
        System.out.println("结束找房子");
    }
}

/***场景类*/
class Client {
    public static void main(String[] args) {
        ICustomer customer = new ZhangCustomer();
        ICustomer proxy = new AgencyProxy(customer);
        String result = proxy.findHouse("钓鱼岛附近", 2000);
    }
}


输出结果:
开始找房子

找到了位于[钓鱼岛附近],价格2000以下的房子

结束找房子

JDK动态代理:

动态代理比较复杂一点,相对于静态代理的指定代理对象,动态代理是在运行时候才知道实际代理对象。动态代理应用比较广泛,我们用的最多的框架Spring中 AOP就采用了动态代理。我们来把上面的代码改造成动态代理。

package com.example.designmode.proxy;

import org.springframework.cglib.proxy.InvocationHandler;
import org.springframework.cglib.proxy.Proxy;

import java.lang.reflect.Method;

/**
 * <h3>design-mode</h3>
 * <p>JDK动态代理</p>
 *
 * @author : ZhangYuJie
 * @date : 2022-03-27 14:45
 **/
public class MyInvocationHandler implements InvocationHandler {
    private Object object;

    public MyInvocationHandler(Object object) {
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object result= method.invoke(object,args);
        after();
        return result;
    }
    private void before(){
        System.out.println("开始找房子");
    }
    private void after(){
        System.out.println("结束找房子");
    }

}
/**
 * 真正的找房消费者,所以只关注找到这个目标
 **/
class Zhang1Customer implements ICustomer {
    @Override
    public String findHouse(String location, int price) {
        System.out.println("找到了位于[" + location + "],价格" + price + "以下的房子");
        return "找到了位于[" + location + "],价格" + price + "以下的房子";
    }
}
 class Client1 {
    public static void main(String[] args) {
        // 获取动态代理对象。
        ICustomer customer = (ICustomer) Proxy.newProxyInstance(Client.class.getClassLoader()
                , new Class[]{ICustomer.class}
                , new MyInvocationHandler(new ZhangCustomer()));
        customer.findHouse("钓鱼岛附近", 2000);
    }
}


输出结果:

开始找房子

找到了位于[钓鱼岛附近],价格2000以下的房子

结束找房子


动态代理要求代理的类必须要有接口,同时实现InvocationHandler的接口,实现接口的invoke方法即可,在invoke方法内可以在真正执行方法的前后添加想做的事情,比如说记录日志,通知消息等等,这就是AOP的思想,在不改变原有业务代码的情况下,添加功能。但是jdk动态代理的缺点就是代理的类必须要有接口才行,为了弥补这个缺点,出现了一款不需要接口就能被代理的第三方库,CGLIB库。

CGLIB动态代理:

CGLIB是一个强大的、高性能的代码生成库。它被广泛使用在基于代理的AOP框架(例如Spring AOP和dynaop)提供方法拦截。在实现内部,CGLIB库使用了ASM这一个轻量但高性能的字节码操作框架来转化字节码,产生新类。

CGLIB的使用:

既然是第三方库,我们需要添加maven依赖

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


CGLIB也和jdk动态代理一样,需要设置回调方法,在JDK动态代理中我们要实现 InvocationHandler,而在CGLIB中我们需要实现net.sf.cglib.proxy.MethodInterceptor接口作为回调接口。我们先看代码

package com.example.designmode.proxy;

/**
 * <h3>design-mode</h3>
 * <p></p>
 *
 * @author : ZhangYuJie
 * @date : 2022-03-27 14:51
 **/

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 *  cglib动态代理
 *
 * @author ZhangYuJie
 **/
public class MyMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        before();
        Object result = methodProxy.invokeSuper(o, objects);
        after();
        return result;
    }

    private void before() {
        System.out.println("开始找房子");
    }

    private void after() {
        System.out.println("结束找房子");
    }
}

/**
 * 真正的找房消费者,所以只关注找到这个目标
 **/
class Zhang2Customer implements ICustomer {
    @Override
    public String findHouse(String location, int price) {
        System.out.println("找到了位于[" + location + "],价格" + price + "以下的房子");
        return "找到了位于[" + location + "],价格" + price + "以下的房子";
    }
}

class Client2 {
    public static void main(String[] args) {
        //创建回调实例
        MyMethodInterceptor interceptor = new MyMethodInterceptor();
        //CGLIB创建实例
        Enhancer enhancer = new Enhancer();
        //设置需要代理的类
        enhancer.setSuperclass(Zhang2Customer.class);
        //设置回调类
        enhancer.setCallback(interceptor);
        //获取代理对象
        Zhang2Customer customer = (Zhang2Customer) enhancer.create();
        //执行方法
        customer.findHouse("钓鱼岛", 1000);
    }
}


输出结果:
开始找房子
找到了位于[钓鱼岛],价格1000以下的房子
结束找房子


这里CGLIB实现结果和JDK动态代理完全一样。上面实现回调方法的时候需要注意,推荐使用methodProxy.invokeSuper(o,objects);,这里调用的是CJLIB库的方法,如果使用method.invoke(o,args);需要注意的是,这里使用的就是JDK的动态代理了,同时invoke的object必须是传入的代理实例,而不是方法中形参object,否则会导致死循环调用。同时考虑到性能,还是建议使用第一种调用方式。
CGLIB库还提供了很多其他的特性,比如回调方法过滤等等。

代理模式的优点与缺点

优点

1.职责清晰

真实的角色实现实际的业务逻辑,不用关心其他非核心的业务逻辑。业务是业务,辅助功能是辅助功能,职责非常清晰。比如要实现日志功能,不用耦合在实际的业务代码中,只要做一个代理即可。

2.良好的扩展性

由于核心的业务逻辑已经封装好了,后面要增强业务功能,也可以使用代理模式代理增加功能即可。

缺点
1.类的臃肿

对于静态代理来说,如果过多的使用静态代理会带来类臃肿。一般会在接口协议转换中使用比较多代理模式。
2.复杂性
对于动态代理来说,设计到回调方法的实现,特别是CGLIB中的使用,还是带来了一定的复杂性。

3.性能

对于动态代理来说,都是运行期进行字节码操作,所以还是带来了一些性能损耗,但是这不能作为不使用动态代理的理由,任何东西都是有两面性的。

代理模式的使用场景

1.方法增强;比如增加日志,事务等功能。
2.远程RPC调用;现在很多分布式系统RPC调用都是采用了代理模式。
3.协议等的转换;比如需要适用另外一套协议接口,会使用代理模式,先转换为老协议,然后再调用实际的类去执行。
4.懒加载;有些框架会在开始的时候使用代理类来替换实际类,等到真正要使用该类的时候才进行加载,从而达到了懒加载的效果。

以上示例代码地址

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值