java中三种代理方式

一、概述

先举个例子吧,我们大家都知道近些年来微信朋友圈的微商代理异常火爆,微商代理简单地说就是代替厂家卖商品,厂家“委托”代理着为其销售商品。关于微商代理,首先我们从他们那里买东西时通常不知道背后的厂家究竟是谁,也就是说,“委托者”对我们来说是隐秘的;其次,微商代理者主要以朋友圈的人为目标客户,这就相当于为厂家做了一次对客户群体的“过滤”。我们把微商代理者厂家进一步抽象,前者可抽象为代理类,后者可抽象为委托类(被代理类)。
在Java中分为静态代理动态代理。动态代理又分为jdk代理cglb代理。

目的

动态代理技术就是用来产生一个目标对象的代理对象

意义

代理对象的存在的价值主要用于拦截对真实事务对象的访问。代理对象必须具有和目标对象(真实业务对象)相同的方法。

二、静态代理

如果代理类在程序运行前就已经存在,则称这种代理方式为静态代理。在使用静态代理时,需要定义接口或者父类,被代理对象和代理对象要一起实现相同的接口或者继承同一个父类。
例如:
模拟一个代理商代理实际厂家卖酒的动作,定义一个卖酒的接口Sale,然后目标对象(被代理的对象,即实际厂家)实现这个接口的方法。此时使用静态代理的话,则代理对象(代理商)也要实现这个接口的方法;在实际调用卖酒的方法时要通过调用代理对象中的方法来调用目标对象的方法。

代码实现:
接口
/**
 * 接口
 */
public interface Sale {
    void sale();
}
目标对象

/**
 * 接口实现
 * 目标对象
 * 被代理对象
 */
public class RealyFactory implements Sale{
    @Override
    public void sale() {
        System.out.println("我是真正卖酒的");
    }
}
代理对象
/**
 * 代理类的实现
 */
public class Proxy implements Sale{ 
    private RealyFactory man;
    
    public Proxy(RealyFactory man) {
        this.man = man;
    }
    
    @Override
    public void sale() {
        System.out.println("我是代理卖酒的");
        man.sale();//调用被代理对象的方法
    }
}
测试
public static void main(String[] args) {
    RealyFactory man=new RealyFactory();
    Proxy proxy=new Proxy(man);
    proxy.sale();
}
运行结果
我是代理卖酒的
我是真正卖酒的
Process finished with exit code 0
静态代理总结
优点

可以在不修改目标对象功能前提下,对目标对象功能进行扩展

缺点
  1. 当接口增加方法时,目标对象和代理对象都需要维护
  2. 如果目标类的方法过多,在给代理类中加业务时会造成代码冗余

三、动态代理

代理类在程序运行时创建的代理方式称为动态代理。
在Java动态代理机制中有一个重要的类java.lang.reflect.Proxy(class),还有一个重要的接口java.lang.reflect.InvocationHandler(interface)

Proxy

这个类是用来动态的创建一个代理对象。我们通常用这个类的newProxyInstance方法

public static Object newProxyInstance(
											ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h
                                      )
        					               throws IllegalArgumentException
参数详解
  • loader: 一个ClassLoader对象,定义了由哪个ClassLoader对象对生成的代理对象进行加载。
  • interfaces: 一个Interface对象的数组,表示的是给将要代理的代理对象提供一组什么接口。
  • h: 一个InvocationHandler对象,表示动态代理对象在调用方法时关联到哪个InvocationHandler对象。
返回结果

一个代理对象的实例

InvozationHandler

每个动态代理类都必须要实现InvocationHandler接口,每个代理类的实例都关联着一个handler;当我们通过代理对象来调用一个方法时其实是通过InvocationHandler这个接口的invoke方法进行调用。

 public Object invoke(Object proxy, Method method, Object[] args)
参数解释
  • prosy: 指被代理的目标对象
  • method: 指我们要调用的目标对象的方法
  • args: 指目标对象被调用的方法所需要的参数值
返回结果

目标对象被调用的方法的返回值

JDK代理(接口代理)

JDK1.3开始支持动态代理,代理对象的生成是通过JDK提供的java.lang.reflect.Proxy类来实现的,可通过它的newProxyInstance来获得代理对象;对于代理接口的实际处理是通过java.lang.reflect.InvocationHandeler接口中的invoke方法完成的。具体使用如下

代码实现
需要动态代理的接口
/**
 * @author FengTianHao
 * @version 1.0
 * @since 2018/11/29 19:25
 * 需要动态代理的接口
 */
public interface Subject {
    public String inrtroduce(String name);
}
被代理的真实对象
/**
 * @author FengTianHao
 * @version 1.0
 * @since 2018/11/29 19:49
 * 被代理的真实对象
 */
public class RealSubject implements Subject {
    @Override
    public String inrtroduce(String name) {
        System.out.println("My name is :"+name);
        return name;
    }
}
调用处理器实现类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * @author FengTianHao
 * @version 1.0
 * @since 2018/11/29 19:30
 * 调用处理器实现类
 * 每次生成动态代理对象时都需要为其指定一个实现了InvocationHandler接口的调用器处理对象
 */
public class InvocationHandlerIml implements InvocationHandler {
    /**
     * 这个是我们要代理的真实对象
     */
    private Object subject;
    /**
     * 构造方法,给我们要代理的真实对象赋值
     *
     *@param subject
     *@return
     */
    public InvocationHandlerIml(Object subject)
    {
        this.subject=subject;
    }
    /**
     * 该方法负责处理动态代理类上的所有方法调用
     *
     *@param proxy 代理类实例
     *@param method 被调用的方法对象
     *@param args 被调用方法所需要的参数值
     *@return 被调用方法的返回值
     */

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //在调用真实对象的方法前我们可以写一写自己的业务
        System.out.println("调用前的业务");
        System.out.println("Method:"+method);
        //当代理对象调用真实对象的方法时,会自动跳转到代理对象所关联的handler对象的invoke方法进行调用
        Object result=method.invoke(subject,args);
        //在调用真实对象的方法后我们可以增加需要的业务
        System.out.println("调用后的业务");
        return result;
    }
}

测试类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

/**
 * @author FengTianHao
 * @version 1.0
 * @since 2018/11/29 19:45
 */
public class DynamicProxyTest {
    public static void main(String[]args)
    {
        //被代理的真实对象
        RealSubject realSubject=new RealSubject();
        /**
         * InvocationHandlerIml实现了InvocationHandler接口
         * 把要代理的对象传进去,代理对象调用某个方法时是通过真实对象来调用的
         */
        InvocationHandler invocationHandler=new InvocationHandlerIml(realSubject);
        //获得加载代理对象的ClassLoader对象
        ClassLoader classLoader=realSubject.getClass().getClassLoader();
        //获得被代理对象的接口
        Class[]interfaces=realSubject.getClass().getInterfaces();
        //生成动态代理对象
        Subject subject=(Subject)Proxy.newProxyInstance(classLoader,interfaces,invocationHandler);
        //进行动态代理调用被代理对象的方法
        String name=subject.inrtroduce("冯天浩");

    }

}
运行结果

在这里插入图片描述

JDK代理小结
  • 与静态代理相比,JDK代理中代理对象不需要实现接口,而目标对象必须要实现接口
  • 经典应用在Spring aop的实现

Cglib代理

上面的静态代理和动态代理都要求目标对象是实现一个接口的目标对象,但是有时候目标对象只是一个单独的对象,并没有实现任何接口,这个时候就可以使用以目标对象子类的方式实现代理,这种代理就叫做:Cglib代理
Cglib代理,也叫子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展:

  1. JDk的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口,如果想代理没有实现接口的类,就可以使用Cglib代理。
  2. Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类和实现java接口。它广泛的被许多AOP的框架使用,例如Spring和synaop,为它们提供方法的interception(拦截)。
  3. Cglib包的底层是通过使用一个小而块的字节码处理框架ASM来转换字节码并生成新的类,
Cglib子类代理实现

1.需要引入cglib的jar文件pring-core-3.2.5.jar
2.在内存中动态构建子类
3.代理的类不能为final
4.目标对象的方法为final/static不会被拦截

代码实现
目标对象
/**
 * 目标对象没有实现任何接口
 */
public class Sale {
    void sale()
    {
        System.out.println("我是真实卖酒的");
    }
}
代理对象
代理对象
/**
 * 代理对象
 */
public class ProxyFactory implements MethodInterceptor {
    //被代理的目标对象
    private Object target;
    //给目标对象赋值
    public ProxyFactory(Object target) {
        this.target = target;
    }
    //获得代理对象
    public Object getProxyInstance()
    {
    //代理类对象是由Enhancer类创建的。Enhancer是CGLIB的字节码增强器,可以很方便的对类进行拓展
        Enhancer en=new Enhancer();
        //设置父类
        en.setSuperclass(target.getClass());
        en.setCallback(this);
        //通过字节码技术创建创建子类实例
        return en.create();
    }
    //当子类调用方法时会被该方法拦截并通过该方法转而调用父类的方法
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    //可以扩展业务
        System.out.println("开始事务");
        Object result=method.invoke(target,objects);
        System.out.println("提交事务");
        return result;
    }
测试类
public class Test{
public static void main(String[] args) {
    Sale target=new Sale();
    Sale proxy=(Sale)new ProxyFactory(target).getProxyInstance();
    proxy.sale();
}
}
注意

在Spring的aop编程中:

  • 如果加入容器的目标对象有实现接口,用jdk代理,如果目标对象没有实现接口则用Cglib代理。

动态代理总结

  • Java动态代理内部是通过java反射机制来实现的。
  • 主要用来做方法功能的扩展,通过动态代理让你可以在不修改源码的情况下,对原方法的功能进扩展,具体应用的话,比如可以添加调用日志,做事务控制等。
  • 动态代理是设计模式当中代理模式的一种。

参考链接
https://blog.csdn.net/andyzhaojianhui/article/details/72833114
https://www.cnblogs.com/cenyu/p/6289209.html
http://www.cnblogs.com/xiaoluo501395377/p/3383130.html
https://www.cnblogs.com/huhx/p/dynamicTheoryAdvance.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值