Java代理

Posted 王晓东

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java代理相关的知识,希望对你有一定的参考价值。

 

例子:

我们大家都知道微商代理,简单地说就是代替厂家卖商品,厂家“委托”代理为其销售商品。关于微商代理,首先我们从他们那里买东西时通常不知道背后的厂家究竟是谁,也就是说,“委托者”对我们来说是不可见的;

 

静态代理:

代理类在程序运行前就已经存在(通常都是我们在Java代码中定义的)

 

代码实例:

 

厂家: 委托类(被代理类)
微商代理: 代理类

 

通常情况下,静态代理中的代理类和委托类会实现同一接口或是派生自相同的父类。

下面我们用Vendor类代表生产厂家,BusinessAgent类代表微商代理,来介绍下静态代理的简单实现

委托类和代理类都实现了Sell接口

1、Sell接口的定义如下:

package First;

public interface Sell {

    void sell();
    
    void ad();
    
}

2、Vendor类的定义如下:

package First;
/*
 * 委托类:厂家
 */
public class Vendor  implements Sell{

    @Override
    public void sell() {
        System.out.println("sell method"); 
    }

    @Override
    public void ad() {
        System.out.println("ad method");
    }

    
    
}

 

3、代理类BusinessAgent的定义如下:

package First;
/*
 * 代理类:微商
 */
public class BusinessAgent implements Sell {

    private Vendor vendor;
    
    public BusinessAgent(Vendor vendor) {
        this.vendor = vendor;
    }
    
    @Override
    public void sell() {
        vendor.sell();
    }

    @Override
    public void ad() {
        vendor.ad();
    }
    

}

 

从BusinessAgent类的定义我们可以了解到,静态代理可以通过聚合来实现,让代理类持有一个委托类的引用即可。

 

下面我们考虑一下这个需求:

给Vendor类增加一个过滤功能,只卖货给大学生。

 

通过静态代理,我们无需修改Vendor类的代码就可以实现,只需在BusinessAgent类中的sell方法中添加一个判断方法即可,如下所示:

 

4、增加功能版BusinessAgent:

package First;
/*
 * 代理类:微商
 */
public class BusinessAgent implements Sell {

    private Vendor vendor;
    
    public BusinessAgent(Vendor vendor) {
        this.vendor = vendor;
    }
    
    @Override
    public void sell() {
        if(isCollegeStudent()){
            vendor.sell();
        }
        
    }

    @Override
    public void ad() {
        vendor.ad();
    }
    
    public boolean isCollegeStudent(){
        //为了好理解,这里直接返回True
        return true;
    }

}

 

 

添加一个判断是否是大学生的方法:isCollegeStudent(),只需在代理类BusinessAgent类中判断,无需修改委托类Vendor

 

  • 优点一:可以隐藏委托类的实现;
  • 优点二:可以实现客户与委托类间的解耦,在不修改委托类代码的情况下能够做一些额外的处理。


静态代理的局限在于运行前必须编写好代理类

 

 

 

动态代理:

代理类在程序运行时创建的代理方式被成为动态代理。(是不是想到了反射)

 

也就是说,这种情况下,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。

 

例子:

假设我们要实现这样一个需求:

在执行委托类中的方法之前输出“before”,在执行完毕后输出“after”。

我们还是以上面例子中的Vendor类作为委托类,BusinessAgent类作为代理类来进行介绍。

 

首先我们来使用静态代理来实现这一需求,相关代码如下:

 

package First;
/*
 * 代理类:微商
 */
public class BusinessAgent implements Sell {

    //引用委托类
    private Vendor vendor;
    
    public BusinessAgent(Vendor vendor) {
        this.vendor = vendor;
    }
    
    @Override
    public void sell() {
        System.out.println("before");
        vendor.sell();
        System.out.println("after");
    }

    @Override
    public void ad() {
        System.out.println("before");
        vendor.ad();
        System.out.println("after");
    }

}

 

从以上代码中我们可以了解到,通过静态代理实现我们的需求需要我们在每个方法中都添加相应的逻辑,

这里只存在两个方法所以工作量还不算大,假如Sell接口中包含上百个方法呢?这时候使用静态代理就会编写许多冗余代码。

 

 

通过使用动态代理,我们可以做一个“统一指示”,从而对所有代理类的方法进行统一处理,而不用逐一修改每个方法。

下面我们来具体介绍下如何使用动态代理方式实现我们的需求:

 

一、使用动态代理

 

 

 

 

(1)委托类的定义

package First;
/*
 * 委托类:厂家
 */
public class Vendor  implements Sell{

    @Override
    public void sell() {
        System.out.println("sell method"); 
    }

    @Override
    public void ad() {
        System.out.println("ad method");
    }

    
    
}

 

(2)中介类

上面我们提到过,中介类必须实现InvocationHandler接口,作为调用处理器”拦截“对代理类方法的调用。中介类的定义如下:

package First;
/*
 * 中介类:实现类
 */
import java.lang.reflect.Method;

public class DynamicProxy implements InvocationHandler{

    private Object obj;//obj为委托类对象
    
    public DynamicProxy(Object obj){
        this.obj = obj;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before");
        Object result = method.invoke(obj, args);
        
        System.out.println("before");
        return result;
    }

    
    
}

 

 

InvocationHandler接口(java.lang.reflect包下)

 

这个接口的定义如下:

public interface InvocationHandler { 
  Object invoke(Object proxy, Method method, Object[] args);
}

  

  从InvocationHandler这个名称我们就可以知道,实现了这个接口的中介类用做“调用处理器”。

  当我们调用代理类对象的方法时,这个“调用”会转送到invoke方法中,代理类对象作为proxy参数传入,参数method标识了我们具体调用的是代理类的哪个方法,args为这个方法的参数。这样一来,我们对代理类中的所有方法的调用都会变为对invoke的调用,

  这样我们可以在invoke方法中添加统一的处理逻辑(也可以根据method参数对不同的代理类方法做不同的处理)。因此我们只需在中介类的invoke方法实现中输出“before”,然后调用委托类的invoke方法,再输出“after”。下面我们来一步一步具体实现它。

 

(3)动态生成代理类

package First;
/*
 * 动态生成代理类
 */
import java.lang.reflect.Proxy;

public class Main {

    public static void main(String[] args) {
        
        DynamicProxy inter = new DynamicProxy(new Vendor());
        
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        
        Sell sell = (Sell)(Proxy.newProxyInstance(Sell.class.getClassLoader(), new Class[] {Sell.class}, inter));
        sell.sell(); 
        sell.ad(); 
    }
    
}

 

 

我们运行一下,看看我们的动态代理是否能正常工作。我这里运行后的输出为:

before
sell method
after
before
ad method
after

说明我们的动态代理确实奏效了。

 

在以上代码中,我们调用Proxy类的newProxyInstance方法来获取一个代理类实例。这个代理类实现了我们指定的接口并且会把方法调用分发到指定的调用处理器。

这个方法的声明如下:

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

  

方法的三个参数含义分别如下:

  • loader:定义了代理类的ClassLoder;
  • interfaces:代理类实现的接口列表
  • h:调用处理器,也就是我们上面定义的实现了InvocationHandler接口的类实例

     

 

  

参考:

http://www.jianshu.com/p/cbd58642fc08

 



以上是关于Java代理的主要内容,如果未能解决你的问题,请参考以下文章

scrapy按顺序启动多个爬虫代码片段(python3)

Java RMI地址解析问题

java代码在片段活动中不起作用

java 代码片段【JAVA】

# Java 常用代码片段

# Java 常用代码片段