浅谈Java的代理模式

概念:代理模式是一种设计模式,是为了在不变动目标对象(类)源码的情况下,进行目标对象现有功能的拓展。保证不修改原有类的同时(即满足对扩展开放,对修改关闭的原则),对原有类增加一些功能实现。

1、静态代理(代理类需要自己写)

  1. 定义:静态代理就是在编译时就确定了代理类与被代理类的关系。因为在编译阶段已经确认了代理关系。

  2. 实现思路:定义一个接口 ,定义一个目标类  和一个代理类 同时实现 这个接口,在代理类中持有目标类对象,并对目标类对象的方法进行一些拓展功能的实现。代码如下:

  3. 代码
public interface Eat  
{
	public void eat();
}

//定义一个目标对象,功能是提示开始吃晚饭了
public class EatDinner implements Eat{
    public void eat(){
        System.out.println("It's time to eat dinner .");
    }
}

//但是现在想拓展这个吃晚饭的功能了,
//拓展为,先饭前洗手,再吃晚饭
//不想改变EatDinner类源码的情况下怎么实现呢?写一个代理类,这个类包含EatDinner类,
//并进行目标类功能拓展。也就是说,将EatDinner类对象注入到代理类中,然后调用拓展后的功能方法
public class EatProcxy implements Eat{
    private EatDinner eatDinner;
    public EatProcxy(EatDinner eatDinner){
        this.eatDinner = eatDinner;
    }
    public void eat(){
        System.out.println("饭前洗手");
        System.out.println("It's time for dinner");
    }
    
    public static void main (String[] args) {
        EatDinner eatDinner = new EatDinner();
        EatProcxy eatProcxy = new EatProcxy(eatDinner);
        System.out.println(eatDinner.eat());
        System.out.println(eatProcxy.eat());
    }
}

这种模式只是将目标对象中的方法覆盖了感觉,像是重新写了一个方法。。。假装是拓展了类方法的功能,实际上并没有包含目标对象的方法,也就不算代理模式了。一旦接口改动了,所有实现了该接口的代理类也需要改动。维护成本太高。

2、动态代理

  1. 定义 :代理类在程序运行时创建的代理方式被称为动态代理。动态代理的代理类并不需要在Java代码中定义,而是在运行时动态生成的。能解决静态代理中维护代理类的成本。
  2. Java的动态代理主要有两种,即JDK代理和Cglib代理

         2.1 JDK代理

               JDK动态代理,写法很固定了,主要需要一个代理类,一个接口(InvocationHandler ),所有的代理类都需要实现InvocationHandler 接口。通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态的将横切逻辑和业务逻辑编织在一起。

/**
 *  动态代理之JDK代理
 */
public class JDKProxy implements InvocationHandler {

    // 目标对象
    private Object object;

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

    //InvocationHandler 接口唯一的实现方法
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("-----------JDKProxy before------------");
        Object invoke = method.invoke(object, args);
        System.out.println("-----------JDKProxy after------------");
        return invoke;
    }

    //测试方法
    public static void main(String[] args) {

        // 被代理的类,目标类
        EatDinner realSubject = new EatDinner();
        // 代理类
        JDKProxy jdkProxy = new JDKProxy(realSubject);
        // 生成代理对象
        Subject subject = (Subject) 
        Proxy.newProxyInstance(RealSubject.class.getClassLoader(), new Class[]{Subject.class}, jdkProxy);
        subject.doSomething();
    }

}

实现JDK动态代理,调用Proxy类的静态方法newProxyInstance即可,该方法会返回代理类对象

static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )

接收的三个参数依次为:

loader:类加载器,一般使用被代理类的类加载器,当然使用接口的类加载器也是可以的

InvocationHandler:表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了

h:就是我们上面所说的InvocationHandler接口具体实现对象

invoke方法:

Object invoke(Object proxy, Method method, Object[] args) throws Throwable
三个参数的具体意思:
proxy:生成的代理类对象
method:被代理类的某个具体方法
arg0:实现方法的具体参数

可以看出静态代理和JDK代理有一个共同的缺点,就是目标对象必须实现一个或多个接口,如果没有,则可以使用Cglib代理

    2.2 Cglib动态代理

使用cglib实现动态代理,并不要求委托类必须实现接口,底层采用asm字节码生成框架生成代理类的字节码,我们需要导入 cglib-nodep-3.2.6.jar 包。由于Spring的核心包中已经包括了Cglib功能,所以也可以直接引入spring-core-3.2.5.jar。目标类不能为final、目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法

//定义一个目标类,不用实现接口
public class EatDinner{
    public void eat(){
        System.out.println("It's time to eat dinner .");
    }
}

//代理类CglibProxy实现MethodInterceptor接口并重写intercept方法
public class EatProcxy implements MethodInterceptor{
    private EatDinner eatDinner;
    public EatProcxy(EatDinner eatDinner){
        this.eatDinner = eatDinner;
    }
    //给目标对象创建一个代理对象
    public Object getProxyInstance(){
        //1.工具类
        Enhancer en = new  Enhancer();
        //2.设置父类
        en.setSuperclass(eatDinner.getClass());
        //3.设置回调函数
        en.setCallback(this);
        //4.创建子类(代理对象)
        return en.create();
        
    }
    
    @Override
    public Object intercept(Object obj, Method method, Object[]args, MethodProxy proxy) throws Throwable {
        //扩展功能  
        。。。。。
        //执行目标对象的方法
        Object returnValue = method.invoke(eatDinner, args);
        return returnValue;
    }
    public static void main (String[] args) {
        //目标对象
        EatDinner target = new EatDinner();
        //代理对象
        EatDinner proxy = (EatDinner)new EatProcxy(target).getProxyInstance();
        //执行代理对象的方法
        proxy.eat();
    }
}

总结:三种代理模式各有优缺点和相应的适用范围,主要看目标对象是否实现了接口。以Spring框架所选择的代理模式举例

在Spring的AOP编程中:
如果加入容器的目标对象有实现接口,用JDK代理
如果目标对象没有实现接口,用Cglib代理

参考链接:https://www.cnblogs.com/boboxing/p/8126046.html

https://www.jianshu.com/p/4539e6d9f337

补充:程序设计的单一性原则,讲究类的功能单一,这样在该类改动的可能性才会更小。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值