代理模式(静态代理和动态代理)

一、代理模式

代理模式是为某一个对象(委托类)提供一个代理(代理类),用来控制对这个对象的访问。委托类和代理类有一个共同的父类或父接口,代理类会对请求做预处理、过滤,将请求分配给指定的对象。

生活中的常见代理有:租房中介、婚庆公司等。

代理类的两个设计原则:

  • 代理类与委托类具有相似的行为
  • 代理类增强委托类的行为

常用的代理模式:

  • 静态代理
  • 动态代理

二、静态代理

1.代理三要素 (以结婚为例)
  • 有共同的行为(结婚)- 定义接口
  • 目标角色/真实角色(新人)- 实现接口
  • 代理角色(婚庆公司)- 实现接口 增强用户行为
2.代码

(1)有共同的行为(结婚)- 定义接口

public interface Marry {
    void toMarry();
}

(2)目标角色/真实角色(新人)- 实现接口

public class You implements Marry{
    @Override
    public void toMarry() {
        System.out.println("我结婚了");
    }
}

(3)代理角色(婚庆公司)- 实现接口 增强用户行为

public class MarryProxy implements Marry{

    private Marry target;

    public MarryProxy(Marry target) {
        this.target = target;
    }

    @Override
    public void toMarry() {
        before();
        target.toMarry();
        after();
    }

    private void after() {
        System.out.println("百年好合");
    }

    private void before() {
        System.out.println("婚礼现场布置中");
    }
}

(4)测试

public static void main(String[] args) {
        You you = new You();
        MarryProxy marryProxy = new MarryProxy(you);
        marryProxy.toMarry();
    }

结果如下:
在这里插入图片描述

3.静态代理特点
  • 目标角色固定
  • 在应用程序之前就得知目标角色
  • 代理对象会增强目标对象的行为
  • 有可能存在多个代理,产生“类爆炸”

三、动态代理

可以根据需要,通过反射机制在程序运行期,动态的为目标对象创建代理对象。

动态代理的两种实现方式:

  • JDK动态代理
  • CGLIB动态代理

动态代理的特点:

  • 目标对象不固定
  • 在程序运行时,动态创建目标对象
  • 代理对象会增强目标对象的行为

四、JDK动态代理

1.实现过程
public class JdkHandler implements InvocationHandler {

    private Object target;

    public JdkHandler(Object target) {
        this.target = target;
    }

    //得到代理对象
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("方法执行前");
        Object object = method.invoke(target, args);
        System.out.println("方法执行后");
        return object;
    }
}

(1)定义一个动态代理类,该类去实现InvocationHandler这个接口。
(2)在该类中定义一个方法,使用Proxy.newProxyInstance得到代理对象,newProxyInstance有三个参数,如下:

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
  • ClassLoader loader:该动态代理类的类加载器
  • Class<?>[] interfaces:目标对象的接口数组
  • InvocationHandler h:传入InvocationHandler 接口的实现类,在这里填入的是该动态代理类,因为该动态代理类去实现了InvocationHandler 这个接口。

(3)重写InvocationHandler中的invoke方法,调用目标对象的方法并且增强目标对 象的行为。invoke方法同样有三个参数,如下:

public Object invoke(Object proxy, Method method, Object[] args) 
  • Object proxy:调用该方法的代理实例
  • Method method:目标对象的方法
  • Object[] args:目标对象的方法所需要的参数

(4)测试:

public static void main(String[] args) {
        You you = new You();
        JdkHandler jdkHandler = new JdkHandler(you);
        Marry proxy = (Marry)jdkHandler.getProxy();
        proxy.toMarry();
    }

结果:
在这里插入图片描述
以上结果说明,当执行到proxy.toMarry()这行代码时,底层会自动去调用动态代理类JdkHandler 中的invoke方法。

2.执行流程

在这里插入图片描述

五、CGLIB动态代理

JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能使用JDK的动态代理。
cglib是针对类来实现代理的,它的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。

cglib动态的原理如下图:
在这里插入图片描述

可以看到,每次调用代理类的方法都会被拦截器所拦截,在拦截器中才是调用目标类的该方法的逻辑。

1.引入依赖
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>
2.实现过程
public class CglibProxy implements MethodInterceptor {

    private Object target;

    public CglibProxy(Object target) {
        this.target = target;
    }

    public Object getProxy(){
        Enhancer enhancer = new Enhancer();
        //设置目标类成为父类
        enhancer.setSuperclass(target.getClass());
        //设置回调 回调对象为本身
        enhancer.setCallback(this);
        //生成代理类对象并返回
        return enhancer.create();
    }
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

        System.out.println("方法执行前");
        Object object = methodProxy.invoke(target, objects);
        System.out.println("方法执行后");
        return object;
    }
}

(1)定义一个类去实现MethodInterceptor 接口,并且重写intercept拦截器方法。
(2)在类中定义一个方法getProxy,在方法中设置:让目标类成为该动态代理类的父类,并且设置回调对象为本身。最后通过create方法返回生成的代理对象。
(3)在intercept拦截器方法调用目标类的方法,并且增强行为目标类的行为或写上一些逻辑。
(4)测试:

public static void main(String[] args) {
        You you = new You();
        CglibProxy cglibProxy = new CglibProxy(you);
        You proxy = (You) cglibProxy.getProxy();
        proxy.toMarry();
    }

结果:
在这里插入图片描述

以上结果说明,每次动态代理对象调用目标对象的方法时,都会被拦截器拦截,然后运行拦截器中的代码。

六、JDK代理和CGLIB代理的区别

  • JDK动态代理实现接口,Cglib动态代理继承思想
  • JDK动态代理(目标对象存在接口时)执行效率高于Cglib
  • 如果目标对象有接口实现,选择JDK代理,如果没有接口实现选择Cglib代理
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值