代理模式

统筹java,我们可以总结出来java一共大概有四种代理模式.
那么什么叫做代理哪?

定义:为其他对象提供一种代理以控制对这个对象的访问

可能有一些人还是不太懂这是什么逻辑?

我们可以进行大白话的分析:

我们上班的目的是为了生活,赚钱养家,可是如果我们上班了,但是公司不给发工资那么会怎么样? 我们需要去讨要工资,但是我们对法律一般都是不太懂的,我们为了要工资这个时候我们需要去申请劳动仲裁,此时劳动局就会派遣一位代理律师负责你的仲裁事宜,
此时就使用了代理模式. 此时的代理律师会代理你的全权事宜.

我们使用代理能解决什么问题呢?

  • 远程代理 :为位于两个不同地址空间对象的访问提供了一种实现机制,可以将一些消耗资源较多的对象和操作移至性能更好的计算机上,提高系统的整体运行效率。
  • 虚拟代理:通过一个消耗资源较少的对象来代表一个消耗资源较多的对象,可以在一定程度上节省系统的运行开销。
  • 缓冲代理:为某一个操作的结果提供临时的缓存存储空间,以便在后续使用中能够共享这些结果,优化系统性能,缩短执行时间。
  • 保护代理:可以控制对一个对象的访问权限,为不同用户提供不同级别的使用权限。
    智能引用:要为一个对象的访问(引用)提供一些额外的操作时可以使用

静态代理

静态代理是指预先确定了代理与被代理者的关系,例如我们的代理律师小小Z是在开庭前就确定的了。那映射到编程领域的话,就是指代理类与被代理类的依赖关系在编译期间就确定了。下面就是代码实现:

  1. 首先我们需要一个向劳动局申请的接口
//此时的接口为我们讨要工资的一个接口
public interface FindMoney {
    //下面我们定义方法
    void submit(String proof);//向劳动局提出劳动仲裁
    void defend();//代理律师为我们进行辩护
}

2.下面我们写一个类 小小Z

//创建小小Z类并实现讨要血汗钱的接口
public class XiaoXiaoZ implements FindMoney{
    public void submit(String proof) {
        System.out.println("小小Z想请劳动局为小小Z进行仲裁!"+"看这是什么? "+proof);
    }

    public void defend() {
        System.out.println("代理律师为小小Z进行讨薪!");
    }
}


3.创建律师类并实现讨要工资接口

public class Lawyer implements FindMoney {
    //引入要代理的对象
    XiaoXiaoZ xiaoXiaoZ;
    public Lawyer(XiaoXiaoZ xiaoXiaoZ) {
        this.xiaoXiaoZ=xiaoXiaoZ;
    }
    @Override
    public void submit(String proof) {
        xiaoXiaoZ.submit(proof);
    }

    @Override
    public void defend() {
        xiaoXiaoZ.defend();
    }
}

4.创建代理工厂类

//创建代理工厂类
public class ProxyFactory {
    public static FindMoney getProxy(){
        return new Lawyer(new XiaoXiaoZ());
    }
}

5.进行测试,并查看结果

public class demo1 {
    public static void main(String[] args) {
        ProxyFactory.getProxy().submit("给你证据!");
        ProxyFactory.getProxy().defend();
    }
}
输出结果:
小小Z想请劳动局为小小Z进行仲裁!看这是什么? 给你证据!
代理律师为小小Z进行讨薪!

从上面代码中我们可以看出来,此时代理律师类中的引入是固定的:

 //引入要代理的对象
    XiaoXiaoZ xiaoXiaoZ;

由于它是写死的,此时的这种行为成为静态代理.

那么什么是动态代理哪?

动态代理本质上仍然是代理,情况与上面介绍的完全一样,只是代理与被代理人的关系是动态确定的,例如小小Z的同事大大Z开庭前没有确定她的代理律师,而是在开庭当天当庭选择了一个律师,映射到编程领域为这个关系是在运行时确定的。

那既然动态代理没有为我们增强代理方面的任何功能,那我们为什么还要用动态代理呢,静态代理不是挺好的吗?凡是动态确定的东西大概都具有灵活性,强扩展的优势。上面的例子中如果牛翠花也使用静态代理的话,那么就需要再添加两个类。一个是大大Z诉讼类,一个是大大Z的代理律师类,还的在代理静态工厂中添加一个方法。而如果使用动态代理的话,就只需要生成一个诉讼类就可以了,全程只需要一个代理律师类,因为我们可以动态的将很多人的案子交给这个律师来处理。

Jdk动态代理实现

在java的动态代理机制中,有两个重要的类或接口,一个是InvocationHandler接口、另一个则是 Proxy类,这个类和接口是实现我们动态代理所必须用到的。

InvocationHandler接口是给动态代理类实现的,负责处理被代理对象的操作的,而Proxy是用来创建动态代理类实例对象的,因为只有得到了这个对象我们才能调用那些需要代理的方法。
下面我们来看一下代码?
1.创建大大Z的讨薪类

public class DaDaZ implements FindMoney {
    @Override
    public void submit(String proof) {
        System.out.println("我是大大Z,我想要讨薪"+"这是我的证据  "+proof);
    }

    @Override
    public void defend() {
        System.out.println("代理律师快到碗里来!");
    }
}

2.创建动态代理律师类

//动态代理类
public class DTProxyLawyer implements InvocationHandler {


    private Object target;//被代理的对象
    public DTProxyLawyer(Object obj){
        this.target=obj;
    }

    //执行方法
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("案件进展:"+method.getName());
        Object result=method.invoke(target,args);
        return result;
    }
}

3.创建工厂类

//创建代理工厂类
public class ProxyFactory {
    public static FindMoney getProxy(){
        return new Lawyer(new XiaoXiaoZ());
    }
    //动态代理生成
    public static Object getDynProxy(Object target) {
        InvocationHandler handler = new DTProxyLawyer(target);
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), handler);
    }

}

4.执行方法

public class demo1 {
    public static void main(String[] args) {
//        ProxyFactory.getProxy().submit("给你证据!");
        ProxyFactory.getProxy().defend();

        FindMoney dynProxy = (FindMoney) ProxyFactory.getDynProxy(new DaDaZ());
        dynProxy.submit("工资账单");
        dynProxy.defend();

    }
}
输出结果: 
案件进展:submit
我是大大Z,我想要讨薪这是我的证据  工资账单
案件进展:defend
代理律师快到碗里来!
JDK动态代理实现的原理

首先Jdk的动态代理实现方法是依赖于接口的,首先使用接口来定义好操作的规范。然后通过Proxy类产生的代理对象调用被代理对象的操作,而这个操作又被分发给InvocationHandler接口的 invoke方法具体执行

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;

此方法的参数含义如下

  • proxy:代表动态代理对象
  • method:代表正在执行的方法
  • args:代表当前执行方法传入的实参
  • 返回值:表示当前执行方法的返回值

例如上面DaDaZ案例中,我们使用Proxy类的newProxyInstance()方法生成的代理对象proxy去调用了proxy.submit(“工资账单”);操作,那么系统就会将此方法分发给invoke().其中proxy对象的类是系统帮我们动态生产的,其实现了我们的业务接口FindMoney。

cgLib的动态代理实现

由于JDK只能针对实现了接口的类做动态代理,而不能对没有实现接口的类做动态代理,所以cgLib横空出世!CGLib(Code Generation Library)是一个强大、高性能的Code生成类库,它可以在程序运行期间动态扩展类或接口,它的底层是使用java字节码操作框架ASM实现。

1 引入cgLib 库
cglib-nodep-3.2.6.jar:使用nodep包不需要关联asm的jar包,jar包内部包含asm的类.

2 定义业务类,被代理的类没有实现任何接口

public class Frank {
   public void submit(String proof) {
       System.out.println(String.format("老板欠薪跑路,证据如下:%s",proof));
   }
   public void defend() {
       System.out.println(String.format("铁证如山,%s还Frank血汗钱","大大Z"));
   }
}

3 定义拦截器,在调用目标方法时,CGLib会回调MethodInterceptor接口方法拦截,来实现你自己的代理逻辑,类似于JDK中的InvocationHandler接口。

public class cgLibDynProxyLawyer implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] params, MethodProxy methodProxy) throws Throwable {
        if (method.getName().equals("submit"))
            System.out.println("案件提交成功,证据如下:"+ Arrays.asList(params));
        Object result = methodProxy.invokeSuper(o, params);
        return result;
    }
}

4定义动态代理工厂,生成动态代理

public class ProxyFactory {
    public static Object getGcLibDynProxy(Object target){
        Enhancer enhancer=new Enhancer();
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(new cgLibDynProxyLawyer());
        Object targetProxy= enhancer.create();
        return targetProxy;
    }
}

5客户端调用

public static void main(String[] args) {
        Frank cProxy= (Frank) ProxyFactory.getGcLibDynProxy(new Frank());
        cProxy.submit("工资流水在此");
        cProxy.defend();
    }

可见,通过cgLib对没有实现任何接口的类做了动态代理,达到了和前面一样的效果。这里只是简单的讲解了一些cgLib的使用方式,有兴趣的可以进一步了解其比较高级的功能,例如回调过滤器(CallbackFilter)等。

cgLib的动态代理原理

CGLIB原理:动态生成一个要代理类的子类,子类重写要代理的类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。它比使用java反射的JDK动态代理要快。

CGLIB底层:使用字节码处理框架ASM,来转换字节码并生成新的类。不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。

CGLIB缺点:对于final方法,无法进行代理。

动态代理在AOP中的应用

什么是AOP? 维基百科上如是说:

定义:

In computing, aspect-oriented programming (AOP) is a programming paradigm that aims to increase modularity by allowing the separation of cross-cutting concerns.

AOP是一种编程范式,其目标是通过隔离切面耦合来增加程序的模块化。

首先声明,AOP是OOP的补充,其地位及其重要性远不及OOP,总体来说OOP面向名词领域而AOP面向动词领域,例如对一个人的设计肯定是使用OOP,例如这个人有手,脚,眼睛瞪属性。而对这个人上厕所这个动作就会涉及到AOP,例如上厕所前的先确定一下拿没拿手纸等。要理解AOP就首先要理解什么是切面耦合(cross-cutting concerns)。例如有这样一个需求,要求为一个程序中所有方法名称以test开头的方法打印一句log,这个行为就是一个典型的cross-cutting场景。首先这个打印log和业务毫无关系,然后其处于分散在整个程序当中的各个模块,如果按照我们原始的方法开发,一旦后期需求变动将是及其繁琐的。所以我们就需要将这个切面耦合封装隔离,不要将其混入业务代码当中。

例如在小小Z的案子中,我们希望在案子起诉后打印一句成功的log,如果不使用代理的话,我们是需要将log写在相应的业务逻辑里面的,例如小小Z类里面的submit()方法中。使用了动态代理后,我们只需要在InvocationHandler 里面的invoke()方法中写就可以了,不会侵入业务代码当中,在以后的维护过程中对业务毫无影响,这是我们希望看到的。

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    if (method.getName().equals("submit"))
           System.out.println("案件提交成功,证据如下:"+ Arrays.asList(args));
    Object result=method.invoke(target,args);
    return result;
}

所以AOP主要可以用于:日志记录,性能统计,安全控制,事务处理,异常处理等场景下。

总结
静态代理比动态代理更符合OOP原则,在日常开发中使用也较多。动态代理在开发框架时使用较多,例如大名鼎鼎的Spring。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值