Java基础:静态代理和动态代理

转载请注明出处:jiq•钦's technical Blog 


一、静态代理:

假设原来有一个实现了指定接口/抽象类的子类:

class RealSubject implements Subject{  
    public void request(){  
        System.out.print("real request handling\n");  
    }  
} 
现在有两种情况会发生:

1新的代码需要调用Subject接口,但是需要给每个接口加入新代码(比如日志记录、权限控制等)

2旧的代码已经使用了Subject接口,需要不改动现有代码情况下加入新的代码(比如日志记录,权限控制)

 

对于这这两种情况,最直接的想法就是重新写一个代理类,同样实现Subject接口,针对RealSubject的所有接口进行封装,加入新的代码:

/************************************************ 
 * 静态代理,封装(控制)request方法的访问  
 ************************************************/  
class StaticProxySubject implements Subject{  
    private RealSubject realSubject;    //组合被代理的对象  
    public void request(){  
        //do some thing else...  
        if(realSubject == null){  
            realSubject = new RealSubject();  
        }  
        realSubject.request();          //真正的request调用  
        //do some thing else...  
    }     

这里需要注意,第二种情况即旧代码要在不改动的情况下使用新建的这个静态代理类,在写代码时需要满足两点:

1面向Subject接口编程;

2创建Subject实例时采用工厂模式,通过读取配置文件决定反射哪个类的实例;

这样在创建一个新的静态代理时,才可以通过修改配置文件无缝集成到现有代码,修改行为而无需改动现有代码。

//通过静态代理来访问对象  
Subject proxy = new StaticProxySubject();  
proxy.request(); 

二、面向方面编程(AOP)

其实上面所说的第二种情况就是实现面向方面的编程的方式之一(将切面代码至于原始方法外面),在不改动现有代码的情况下,为所有业务横向插入公共逻辑(常见的如安全、事务控制、日志等),将交叉业务模块化。

 

三、动态代理

很明显静态代理有个大问题,就是一旦原始目标类接口过多,要为每个接口进行静态代理封装,那代价也很大。

因此Java中提出一个动态代理的概念,动态代理需要实现Java中的InvocationHandler接口:

/*********************************************************** 
 * 动态代理. java.lang.reflect包中有自己的代理支持,利用这个包 
 * 你可以在运行时动态创建一个代理类,实现一个或者多个接口,并将 
 * 方法的调用转发到你所指定的类。  
 * 因为实际的代理类是在运行时创建的,所以我们称这个技术为动态代理。 
 * ***********************************************************/  
class DynamicProxySubject implements InvocationHandler{  
    private Object realSubject;         //组合被代理的对象  
      
    public DynamicProxySubject(Object realSubject){  
        this.realSubject = realSubject;  
    }  
      
    @Override  
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
        //do some thing else...  
        return method.invoke(realSubject, args);  //调用对象realSubject的method方法
        //do some thing else...  
    }  
}  

然后通过 Java Proxy 类提供的静态方法newProxyInstance () 可以创建一个动态代理实例:  

RealSubject real = new RealSubject();  
Subject subject = (Subject) Proxy.newProxyInstance(  
                real.getClass().getClassLoader(),   //定义代理类的类加载器   
                real.getClass().getInterfaces(),    //代理类要实现的接口列表  
                new DynamicProxySubject(real));     //指派方法调用的调用处理程序  
subject.request();  

1其中最后一个参数是动态代理类对象,组合了其真正要代理的类。

2倒数第二个参数需要传递要代理的真正的类的接口信息(Class信息),即Subject接口信息,这样返回的代理就是Subject类型的。

 

这样每次调用返回的Subject类型代理对象的接口时,都会转换为针对动态代理类中invoke方法的调用,在invoke方法中又调用真正代理的类的方法,我们可以将横切代码(日志、权限控制等)至于invoke方法中实现AOP

 

四、动态代理原理

关键在于创建动态代理的newProxyInstance方法: 

RealSubject real = new RealSubject();  
Subject subject = (Subject) Proxy.newProxyInstance(  
                real.getClass().getClassLoader(),   //定义代理类的类加载器   
                real.getClass().getInterfaces(),    //代理类要实现的接口列表  
                new DynamicProxySubject(real));     //指派方法调用的调用处理程序  
subject.request();  

倒数第二个参数是要代理的类的接口(Subject)的Class对象。第三个参数是动态代理类。

newProxyInstance方法返回一个自动生成类对象,该类实现了Subject接口,同时继承自Java Proxy类: 

class Proxy{ 
    InvocationHandler h=null; 
    protected Proxy(InvocationHandler h) { 
        this.h = h; 
    } 
    ... 
}

这个自动生成的类为什么能够实现Subject接口,答案是反射机制。

通过反射可以知道该接口所有方法,然后重写这些方法,简单地调用父类Proxy的动态代理属性的invoke方法即可。

publicfinal void request() { 
        try { 
            super.h.invoke(this,m3, null); 
            return; 
        } catch (Error e) { 
        } catch (Throwable throwable) { 
            throw newUndeclaredThrowableException(throwable); 
        } 
 
    }

由此可见,返回的代理对象向上转换为 Subject 类型,调用其方法 ( 自动生成 ) 时,在内部转换为针对动态代理类的 invoke 方法的调用。

你可能感兴趣的:(Java基础:静态代理和动态代理)