代理介绍和使用

  1. 云栖社区>
  2. 博客>
  3. 正文

代理介绍和使用

醉意人间 2014-10-12 23:16:00 浏览529
代理的核心就是拦截方法调用,并在需要的时候执行匹配某方法的通知链。 

和CGLIB不同的是,JDK代理只能代理接口,不能代理类。 

使用JDK代理时,如何处理一个特定的方法调用的决定是在程序运行时做出的,也就是在每次方法被调用时。使用CGLIB代理可以边开这种处理方法,CGLIB会在运行中随时为代理创建新类的字节码,并尽可能的重用已经生成的类的字节码。 


Spring1.2:
将事务代理工厂[TransactionProxyFactoryBean] 或 自动代理拦截器[BeanNameAutoProxyCreator]
的 proxyTargetClass 属性,设置为true,则使用CGLIB代理,此属性默认为false,使用JDK动态代理.
以下引用 Spring Framework reference 2.0.5:
Spring2.0:
Spring AOP部分使用JDK动态代理或者CGLIB来为目标对象创建代理。(建议尽量使用JDK的动态代理)
如果被代理的目标对象实现了至少一个接口,则会使用JDK动态代理。所有该目标类型实现的接口都将被代理。若该目标对象没有实现任何接口,则创建一个CGLIB代理。

如果你希望强制使用CGLIB代理,(例如:希望代理目标对象的所有方法,而不只是实现自接口的方法)那也可以。但是需要考虑以下问题:

无法通知(advise)Final 方法,因为他们不能被覆写。 
你需要将CGLIB 2二进制发行包放在classpath下面,与之相较JDK本身就提供了动态代理 
强制使用CGLIB代理需要将 |aop:config| 的 proxy-target-class 属性设为true:
|aop:config proxy-target-class="true"|
...
|/aop:config|

当需要使用CGLIB代理和@AspectJ自动代理支持,请按照如下的方式设置 |aop:aspectj-autoproxy| 的 proxy-target-class 属性:
|aop:aspectj-autoproxy proxy-target-class="true"/|

而实际使用的过程中才会发现细节问题的差别,The devil is in the detail.JDK动态代理:其代理对象必须是某个接口的实现,它是通过在运行期间创建一个接口的实现类来完成对目标对象的代理。
CGLIB代理:实现原理类似于JDK动态代理,只是它在运行期间生成的代理对象是针对目标类扩展的子类。CGLIB是高效的代码生成包,底层是依靠ASM(开源的java字节码编辑类库)操作字节码实现的,性能比JDK强。
Spring是依靠什么来判断采用哪种代理策略来生成AOP代理呢?以下代码就是Spring的判断逻辑  
    //org.springframework.aop.framework.DefaultAopProxyFactory   
    //参数AdvisedSupport 是Spring AOP配置相关类   
    public AopProxy createAopProxy(AdvisedSupport advisedSupport)   
            throws AopConfigException {   
        //在此判断使用JDK动态代理还是CGLIB代理   
        if (advisedSupport.isOptimize() || advisedSupport.isProxyTargetClass()   
                || hasNoUserSuppliedProxyInterfaces(advisedSupport)) {   
            if (!cglibAvailable) {   
                thrownew AopConfigException(   
                        "Cannot proxy target class because CGLIB2 is not available. "  
                                +"Add CGLIB to the class path or specify proxy interfaces.");   
              }   
            return CglibProxyFactory.createCglibProxy(advisedSupport);   
          } else {   
            returnnew JdkDynamicAopProxy(advisedSupport);   
          }   
      }  
advisedSupport.isOptimize()与advisedSupport.isProxyTargetClass()默认返回都是false,所以在默认情况下目标对象有没有实现接口决定着Spring采取的策略,当然可以设置advisedSupport.isOptimize()或者advisedSupport.isProxyTargetClass()返回为true,这样无论目标对象有没有实现接口Spring都会选择使用CGLIB代理。

所以在默认情况下,如果一个目标对象如果实现了接口Spring则会选择JDK动态代理策略动态的创建一个接口实现类(动态代理类)来代理目标对象,可以通俗的理解这个动态代理类是目标对象的另外一个版本,所以这两者之间在强制转换的时候会抛出java.lang.ClassCastException。而所以在默认情况下,如果目标对象没有实现任何接口,Spring会选择CGLIB代理, 其生成的动态代理对象是目标类的子类。

上说的是默认情况下,也可以手动配置一些选项使Spring采用CGLIB代理。 
org.springframework.transaction.interceptor.TransactionProxyFactoryBean是org.springframework.aop.framework. ProxyConfig的子类,所以可以参照ProxyConfig里的一些设置如下所示,将optimize和proxyTargetClass任意一个设置为true都可以强制Spring采用CGLIB代理。
如果当需要使用CGLIB代理和@AspectJ自动代理支持,请按照如下的方式设置 |aop:aspectj-autoproxy| 的 proxy-target-class 属性: 
|aop:aspectj-autoproxy proxy-target-class="true"/|
这样使用CGLIB代理也就不会出现前面提到的ClassCastException问题了,也可以在性能上有所提高,关键是对于代理对象是否继承接口可以统一使用。




两种类型AOP:静态AOP和动态AOP。 

静态代理: 
代理对象与被代理对象必须实现同一个接口。 
demo:
view plaincopy to clipboardprint?
package cn.partner4java.proxy.staticproxy;  
  
  
/** 
* 静态代理,统一接口 
* @author partner4java 
* 
*/  
public interface IHello {  
    /** 
     * 可以带来的统一方法 
     * @param name 
     */  
    public void hello(String name);  
}  
view plaincopy to clipboardprint?
package cn.partner4java.proxy.staticproxy;  
  
/** 
* 被代理的对象,需要借助代理对象加入日志 
* @author partner4java 
* 
*/  
public class HelloSpeaker implements IHello {  
  
    public void hello(String name) {  
        System.out.println("Hello " + name);  
    }  
  
}  
view plaincopy to clipboardprint?
package cn.partner4java.proxy.staticproxy;  
  
/** 
* 代理对象,给被代理对象添加日志 
*/  
public class HelloProxy implements IHello {  
      
    private IHello iHello;  
  
    public HelloProxy(IHello iHello) {  
        super();  
        this.iHello = iHello;  
    }  
  
  
    public void hello(String name) {  
        System.out.println("记录日志");  
        iHello.hello(name);  
    }  
  
}  
view plaincopy to clipboardprint?
package cn.partner4java.proxy.staticproxy;  
  
/** 
* 调用 
* @author partner4java 
* 
*/  
public class ProxyDemo {  
  
    public static void main(String[] args) {  
        IHello iHello = new HelloProxy(new HelloSpeaker());  
        iHello.hello("long");  
    }  
  
}  

动态代理: 
动态代理区别于静态带来实现的地方在于织入过程是在运行时动态进行的。自己实现一般实现java.lang.reflect.InvocationHandler接口。 
例子:
view plaincopy to clipboardprint?
package cn.partner4java.proxy.dynamicproxy;  
  
  
public interface IHello {  
    public void hello(String name);  
}  
view plaincopy to clipboardprint?
package cn.partner4java.proxy.dynamicproxy;  
  
/** 
* 被代理的对象,需要借助代理对象加入日志 
* @author partner4java 
* 
*/  
public class HelloSpeaker implements IHello {  
  
    public void hello(String name) {  
        System.out.println("Hello " + name);  
    }  
  
}  
view plaincopy to clipboardprint?
package cn.partner4java.proxy.dynamicproxy;  
  
import java.lang.reflect.InvocationHandler;  
import java.lang.reflect.Method;  
import java.lang.reflect.Proxy;  
  
/** 
* 动态代理对象 
* @author partner4java 
* 
*/  
public class LogHandler implements InvocationHandler {  
  
    private Object delegate;  
      
    public Object bind(Object delegate){  
        this.delegate = delegate;  
        return Proxy.newProxyInstance(delegate.getClass().getClassLoader(),   
                delegate.getClass().getInterfaces(), this);  
    }  
    /** 
     * 代理对象,这里面还可以改变原有的方法 
     */  
    public Object invoke(Object proxy, Method method, Object[] args)  
            throws Throwable {  
        Object result = null;  
        try {  
            System.out.println("添加日志");  
            result = method.invoke(delegate, args);  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
          
        return null;  
    }  
  
}  
view plaincopy to clipboardprint?
package cn.partner4java.proxy.dynamicproxy;  
  
/** 
* 测试 
* @author partner4java 
* 
*/  
public class ProxyDemo {  
    public static void main(String[] args) {  
        LogHandler logHandler = new LogHandler();  
        IHello iHello = (IHello) logHandler.bind(new HelloSpeaker());  
        iHello.hello("long");  
    }  
}  


------------------------------------------------------------------ 


利用ProxyFactory连接CGLIB简单实现AOP: 
加入包aopalliance.jar\cglib-nodep-2.1_3.jar 
demo:
view plaincopy to clipboardprint?
package cn.partner4java.proxy.proxyfactory;  
  
/** 
* 被代理的对象 
* @author partner4java 
* 
*/  
public class MessageWriter {  
    public void writeMessage(){  
        System.out.println("world!");  
    }  
}  
view plaincopy to clipboardprint?
package cn.partner4java.proxy.proxyfactory;  
  
import org.aopalliance.intercept.MethodInterceptor;  
import org.aopalliance.intercept.MethodInvocation;  
  
/** 
* 装饰者<br/> 
* MethodInterceptor接口是对方法调用连接点实现包围通知的AOP联盟标准接口 
* @author partner4java 
* 
*/  
public class MessageDecorator implements MethodInterceptor{  
  
    public Object invoke(MethodInvocation invocation) throws Throwable {  
        System.out.print("Hello ");  
        Object retVal = invocation.proceed();  
        return retVal;  
    }  
  
}  
view plaincopy to clipboardprint?
package cn.partner4java.proxy.proxyfactory;  
  
import org.springframework.aop.framework.ProxyFactory;  
  
/** 
* 调用组装 
* 这里最重要的部分是我们使用ProxyFactory来创建一个目标对象代理,同时织入通知  
* @author partner4java 
* 
*/  
public class HelloWorldWeaver {  
  
    public static void main(String[] args) {  
        //目标  
        MessageWriter target = new MessageWriter();  
          
        //create the proxy  
        ProxyFactory proxyFactory = new ProxyFactory();  
          
        proxyFactory.addAdvice(new MessageDecorator());  
        proxyFactory.setTarget(target);  
          
        //获取返回被代理的目标  
        MessageWriter proxy = (MessageWriter) proxyFactory.getProxy();  
          
        target.writeMessage();  
        System.out.println("---");  
        proxy.writeMessage();  
//      后台打印:  
//      world!  
//      ---  
//      World world!  
    }  
  
}  

来自:http://blog.csdn.net/partner4java/article/details/7048879