cglib 动态代理

cglib代理主要通过对字节码的操作,为对象引入间接级别,以控制对象的访问,cglib相对于java动态代理更加强大,因为JDK动态代理有一个致命的缺陷,如果要代理一个普通类,没有接口,那么java动态代理就没法使用了。

在开始学习之前我们先简单的看一下

public class SampleClass {
    public void test(){         //被代理对象
        System.out.println("hello world");
    }

    public static void main(String[] args) {
        //Enhancer : 增强
        Enhancer enhancer=new Enhancer();                   //创建一个增强因子对象
        enhancer.setSuperclass(SampleClass.class);          //设置代理类
        enhancer.setCallback(new MethodInterceptor() {      //设置代理回调方法
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("befor method run...");
                Object result=methodProxy.invokeSuper(o,objects);
                System.out.println("after method run...");
                return result;
            }
        });

        SampleClass sampleClass= (SampleClass) enhancer.create();
        sampleClass.test();
    }
}

在上面的程序中,我们引入了Enhance和MethodInterceptor

Enhancer可能是CGLIB中最常用的一个类,和JDK动态代理不同的是,Enhancer即能够代理普通的class,也能够代理接口

ENhance创建一个被代理对象的子类并且拦截所有的方法调用(包括从Object中继承的toString和hashCode)Enhancer不能够

拦截final方法。例如Object.getClass()方法,这是由于java的方法语义决定的,基于同样的道理Enhancer也不能对final类

进行代理操作,这也是Hibernate为什么不能持久化final class的原因。

下面我们来分别看看不同的方法,首先是

FIxedValue()

先来看看其例子

public class SampleClass {
    public String test(String input){         //被代理对象
        return "hello world";
    }

    public static void main(String[] args) {
        //Enhancer : 增强
        Enhancer enhancer=new Enhancer();                   //创建一个增强因子对象
        enhancer.setSuperclass(SampleClass.class);          //设置代理类
        enhancer.setCallback(new FixedValue() {
            @Override
            public Object loadObject() throws Exception {
                return "Hello cglib";
            }
        });

        SampleClass sampleClass= (SampleClass) enhancer.create();
        System.out.println(sampleClass.test(null)); //Hello cglib
        System.out.println(sampleClass.toString()); //Hello cglib
        System.out.println(sampleClass.getClass()); //class cglib.SampleClass$$EnhancerByCGLIB$$ceb0270f
        System.out.println(sampleClass.hashCode()); //Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Number
    }
}
从输出中我们可以看到hashCode()抛出了异常,原因在于其期望的是一个int类型的值,而得到的是String类型的值

Enhancer.setSuperclass用来设置父类型,从toString方法中可以看出cglib生成的类为被代理类的一个子类,形如:cglib.SampleClass$$EnhancerByCGLIB$$ceb0270f

Enhancer.createClass() 用来创建字节码,然后用字节码动态的生成增强的对象。(具体用法需要考察)

也可以使用一个 InvocationHandler 作为回调,然后使用invoke来代替直接访问类的方法但是此处必须注意死循环,因为invoke中调用的任何原代理方法,均会重新代理待invoke方法中,如下:

public class SampleClass {
    public String test(String input){
        return "hello world";
    }

    public static void main(String[] args) {
        Enhancer enhancer=new Enhancer();
        enhancer.setSuperclass(SampleClass.class);
        enhancer.setCallback(new InvocationHandler() {
            @Override
            public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
                System.out.println("before....");
                Object result=method.invoke(o, objects);
                System.out.println("after....");
                return result;
            }
        });
        SampleClass sampleClass= (SampleClass) enhancer.create();
        System.out.println(sampleClass.test(null));
    }
}

结果如下:

省略至少1000个before....
before....
before....
before....
before....
before....
before....
before....
before....
before....
before....
before....
before....
before....
before....
Exception in thread "main" net.sf.cglib.proxy.UndeclaredThrowableException: java.lang.reflect.InvocationTargetException-->null
	at cglib.SampleClass$$EnhancerByCGLIB$$12d2f200.test(<generated>)
	at cglib.SampleClass.main(SampleClass.java:32)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)
Caused by: java.lang.reflect.InvocationTargetException
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at cglib.SampleClass$1.invoke(SampleClass.java:25)
	... 7 more
Caused by: net.sf.cglib.proxy.UndeclaredThrowableException: java.lang.reflect.InvocationTargetException-->null
	at cglib.SampleClass$$EnhancerByCGLIB$$12d2f200.test(<generated>)
	... 12 more
Caused by: java.lang.reflect.InvocationTargetException

为了避免死循环,我们可以使用MethodInterceptor

有时我们想针对特定的方法进行拦截,则可以使用CallbackFilter

public class SampleClass {
    public String test(String input){
        return "hello world";
    }

    public static void main(String[] args) {
        Enhancer enhancer=new Enhancer();
        enhancer.setSuperclass(SampleClass.class);
        CallbackHelper callbackHelper=new CallbackHelper(SampleClass.class,new Class[0]) {
            @Override
            protected Object getCallback(Method method) {
               if(method.getDeclaringClass()!=Object.class && method.getReturnType()==String.class){
                   return new MethodInterceptor() {
                       @Override
                       public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                           System.out.println("befor.....");
                           Object result=methodProxy.invokeSuper(o,objects);
                           System.out.println("after.....");
                           return result;
                       }
                   };
               }else{
                   return NoOp.INSTANCE; //NoOp.INSTANCE cglib定义好的不做任何事情的拦截器。 
               }
            }
        };
        enhancer.setCallbackFilter(callbackHelper);
        enhancer.setCallbacks(callbackHelper.getCallbacks());
        SampleClass proxy = (SampleClass) enhancer.create();
        System.out.println(proxy.test(null));
    }
}
上面:首先定义了一个CallbackHelper 该helper返回一个方法拦截器,然后在将CallbackHelper 设置到enhancer中,之后再将CallbackHelper 的回调方法设置到Callbacks中

再来看一下cglib的不可变bean  ImmutableBean

首先定义一个简单bean

public class SampleBean {
    private String value;
    public SampleBean(){}

    public SampleBean(String value){
        this.value=value;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }
}
写一个测试类

public class SampleClass {

    public static void main(String[] args) {
        SampleBean bean=new SampleBean();
        bean.setValue("hello world");
        //immutable : 不可改变的   --> immutableBean 不可修改的bean
        SampleBean immutableBean= (SampleBean) ImmutableBean.create(bean);
        System.out.println(immutableBean.getValue());   //hello world
        bean.setValue("hello world again");
        System.out.println(immutableBean.getValue());   //hello world again
        immutableBean.setValue("hello cglib");          //Exception in thread "main" java.lang.IllegalStateException: Bean is immutable

        /**
         * 通过上面的例子可以看出,Cglib的不可变bean,是指代理的对象不能修改被代理bean中的属性值,
         * 但是被代理对象可以自行修改属性的值。
         */
    }
}

再来看看cglib的生产bean对象  BeanGenerator
例子如下:

public class SampleClass {

    public static void main(String[] args)throws Exception{
        //Generator : 生产
        BeanGenerator beanGenerator=new BeanGenerator();
        beanGenerator.addProperty("value",String.class);    //给bean追加一个value属性
        Object myBean=beanGenerator.create();               //创造该bean
        Method setter=myBean.getClass().getMethod("setValue",String.class);
        setter.invoke(myBean,"hello cglib");
        Method getter=myBean.getClass().getMethod("getValue");
        System.out.println(getter.invoke(myBean));  //hello cglib
        
        /**
         * 对于cglib生产对象这一块不是很理解
         * Method setter=myBean.getClass().getMethod("setValue",String.class)
         * 的意思应该是获取噶bean里面的setValue方法,但是在此之前myBean里面并没有setValue这个方法
         * 莫非是在设置属性的时候就自动的按照javaBean的规范生成了这些方法吗?
         */
    }
}

在来看看cglib的属性复制  BeanCopier 能够将一个bean里面的属性复制到另一个bean中需要保证两个bean具有相同的方法

例子:

public class OtherSampleBean {
    private String value1111;

    public String getValue() {
        return value1111;
    }

    public void setValue(String value) {
        this.value1111 = value;
    }
}

public class SampleBean {
    private String value;
    public SampleBean(){}

    public SampleBean(String value){
        this.value=value;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }
}
public class SampleClass {

    public static void main(String[] args)throws Exception{
        //Copier : 复印机
        BeanCopier copier=BeanCopier.create(SampleBean.class,OtherSampleBean.class,
                false); //设置为true,则使用converter
        SampleBean myBean=new SampleBean();
        myBean.setValue("hello cglib");
        OtherSampleBean otherSampleBean=new OtherSampleBean();
        copier.copy(myBean,otherSampleBean,
                null);//如果上文设置为true,则传入converter指明进行怎么转换
        System.out.println(otherSampleBean.getValue()); //hello cglib

        /**
         * 上面的例子是将myBean里面的value值复制到了otherSampleBean中
         */
    }
}

再来看看 BulkBean

public class SampleClass {

    public static void main(String[] args)throws Exception{
        /**
         *  Bulk : 散装
         *  相对于BeanCopier而言,BulkBean将copy的动作拆分为
         *  getPropertyValues和setPropertyValues两个方法,允许自定义处理属性
         *
         *  使用注意:
         *  1. 避免每次进行BulkBean.create创建对象,一般将其声明为static的
         *  2. 应用场景:针对特定属性的get,set操作,一般适用通过xml配置注入和注出的属性,
         *  运行时才确定处理的Source,Target类,只需要关注属性名即可。
         *  
         *  对于下面的例子,并么有发现有什么应用场景
         */
        BulkBean bulkBean=BulkBean.create(SampleBean.class,
                new String[]{"getValue"},
                new String[]{"setValue"},
                new Class[]{String.class});
        SampleBean bean=new SampleBean();
        bean.setValue("Hello Wolrd");
        Object[] propertyValues=bulkBean.getPropertyValues(bean);
        System.out.println(propertyValues.length);                      //1
        System.out.println(propertyValues[0]);                          //Hello Wolrd
        bulkBean.setPropertyValues(bean,new Object[]{"Hello cglib"});   //因为设置函数可以需要多个值,因而是一个数组
        System.out.println(bean.getValue());                            //Hello cglib
    }
}
再来看看BeanMap 也就是将javaBean转化为map的方法
public class SampleClass {

    public static void main(String[] args)throws Exception{
        BeanGenerator generator=new BeanGenerator();
        generator.addProperty("username",String.class);
        generator.addProperty("password",String.class);
        Object bean= generator.create();
        Method setUsername=bean.getClass().getMethod("setUsername",String.class);
        Method setPassword=bean.getClass().getMethod("setPassword",String.class);
        setUsername.invoke(bean,"admin");
        setPassword.invoke(bean,"pwd");

        //public abstract class BeanMap implements Map      可以看到其实现了Map接口
        BeanMap map=BeanMap.create(bean);
        System.out.println(map.get("username"));    //admin
        System.out.println(map.get("password"));    //pwd
    }
}

再来看看KeyFactory 该类用来动态生成接口的实例,接口只需要包含一个newInstance方法,返回一个Object,KeyFactory为构造出来的实例动态生成Object.equals 和

hashCode(),同时确保相同参数构造出来的实例为单例(必须有一个newInstance方法否则会报错)


public class SampleClass {

    interface SampleKeyFactory{
        Object newInstance(String first,int second);
    }

    public static void main(String[] args)throws Exception{
       SampleKeyFactory keyFactory= (SampleKeyFactory) KeyFactory.create(SampleKeyFactory.class);
        Object key1=keyFactory.newInstance("foo",42);
        Object key2=keyFactory.newInstance("foo",42);
        System.out.println(key1.equals(key2));      //ture
    }
}
再来看看Mixin Minix能够让我们将多个对象整合到一个对象中去,前提是这些对象必须是接口的实现。

public class MinixInterface {
    interface Interface1{
        String first();
    }
    interface Interface2{
        String second();
    }

    class Class1 implements Interface1{
        @Override
        public String first() {
            return "first";
        }
    }

    class Class2 implements Interface2{
        @Override
        public String second() {
            return "second";
        }
    }

    interface MixinInterface extends Interface1,Interface2{

    }

    @Test
    public void testMixin() throws Exception{
        Mixin mixin=Mixin.create(new Class[]{Interface1.class,Interface2.class,MixinInterface.class},new Object[]{new Class1(),new Class2()});
        MixinInterface mixinDelegate= (MixinInterface) mixin;
        System.out.println(mixinDelegate.first());  //first
        System.out.println(mixinDelegate.second()); //second
    }
}

再来看看 String switcher :用来模拟一个String到int类型的Map类型如果在Java7以后的版本中,类似一个switch语句

public class MinixInterface {

    @Test
    public void testStringSwitch() throws Exception{
        String[] strings=new String[]{"one","two"};
        int[] values=new int[]{10,20};
        StringSwitcher stringSwitcher=StringSwitcher.create(strings,values,true);
        System.out.println(stringSwitcher.intValue("one"));//10
        System.out.println(stringSwitcher.intValue("two"));//20
    }
}


再来看看Interface Maker :正如其名字一样 是用来创建接口的

@Test
    public void testInterfaceMarker() throws Exception{
        Signature signature=new Signature("foo", Type.DOUBLE_TYPE,new Type[]{Type.INT_TYPE});
        InterfaceMaker interfaceMaker=new InterfaceMaker();
        interfaceMaker.add(signature,new Type[0]);
        Class iface=interfaceMaker.create();
        System.out.println(iface.getMethods().length);          //1
        System.out.println(iface.getMethods()[0].getName());    //foo
        System.out.println(iface.getMethods()[0].getReturnType());  //double
    }
再来看看 Method delegate:其主要用来对方法进行代理

public class SampleBean {
    private String value;
    public SampleBean(){}

    public SampleBean(String value){
        this.value=value;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }
}
public class MinixInterface {

    interface BeanDelegate{
        String getValueFromDelegate();
    }

    @Test
    public void testMethodDelegate() throws Exception{
        SampleBean bean=new SampleBean();
        bean.setValue("hello cglib");
        /**
         * 关于Method.create的参数说明:
         * 1. 第二个参数为即将被代理的方法
         * 2. 第一个参数必须是一个无参数构造的bean。因此MethodDelegate.create并不是你想象的那么有用
         * 3. 第三个参数为只含有一个方法的接口。当这个接口中的方法被调用的时候,将会调用第一个参数所指向bean的第二个参数方法
         * 缺点:
         * 1. 为每一个代理类创建了一个新的类,这样可能会占用大量的永久代堆内存
         * 2. 你不能代理需要参数的方法
         * 3. 如果你定义的接口中的方法需要参数,那么代理将不会工作,并且也不会抛出异常;如果你的接口中方法需要其他的返回类型,那么将抛出IllegalArgumentException
         */
        BeanDelegate delegate= (BeanDelegate) MethodDelegate.create(bean,"getValue",BeanDelegate.class);
        System.out.println(delegate.getValueFromDelegate());    //hello cglib
    }
}

再来看看多重代理 MulticastDelegate

  1. 多重代理和方法代理差不多,都是将代理类方法的调用委托给被代理类。使用前提是需要一个接口,以及一个类实现了该接口
  2. 通过这种interface的继承关系,我们能够将接口上方法的调用分散给各个实现类上面去。
  3. 多重代理的缺点是接口只能含有一个方法,如果被代理的方法拥有返回值,那么调用代理类的返回值为最后一个添加的被代理类的方法返回值

public interface DelegatationProvider {
    void setValue(String value);
}
public class MinixInterface {

   class SimpleMulticastBean implements DelegatationProvider{

       private String value;

       @Override
       public void setValue(String value) {
           this.value=value;
       }

       public String getValue(){
           return value;
       }
   }

    @Test
    public void testMulticastDelegate() throws Exception{
        MulticastDelegate multicastDelegate = MulticastDelegate.create(DelegatationProvider.class);
        SimpleMulticastBean first = new SimpleMulticastBean();      //实现接口对象1
        SimpleMulticastBean second = new SimpleMulticastBean();     //实现接口对象2
        multicastDelegate = multicastDelegate.add(first);           //加入到多重代理
        multicastDelegate  = multicastDelegate.add(second);         //加入到多重代理

        DelegatationProvider provider = (DelegatationProvider) multicastDelegate;   //代理转化为接口
        provider.setValue("Hello world");                                           //代理中修改值

        System.out.println(first.getValue());   //Hello world                       //被代理对象的值都被修改
        System.out.println(second.getValue());  //Hello world
    }
}

再连看看对构造函数的代理Constructor delegate

为了对构造函数进行代理,我们需要一个接口,这个接口只含有一个Object newInstance(…)方法,用来调用相应的构造函数

public interface SampleBeanConstructorDelegate {
    Object newInstance(String value);
}
被代理类

public class SampleBean {
    private String value;
    public SampleBean(){}

    public SampleBean(String value){
        this.value=value;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }
}
测试类

public class MinixInterface {

    @Test
    public void testConstructorDelegate() throws Exception{
        SampleBeanConstructorDelegate constructorDelegate= (SampleBeanConstructorDelegate)
                ConstructorDelegate.create(SampleBean.class, SampleBeanConstructorDelegate.class);
        SampleBean bean= (SampleBean) constructorDelegate.newInstance("Hello world");
        System.out.println(bean.getClass());    //class cglib.SampleBean
        System.out.println(bean.getValue());    //Hello world
    }
}

最后再来看看fastClass : 作用对Class对象进行特定的处理,比如通过数组保存Method引用,因此FastClass

引出了一个index下标的新概览,比如getIndex(String name,Class[] parameterTypes)就是以前获取method的方法

通过数组存储method constructor等class信息从而将原先的反射调用,转化为class.index的直接调用,从而体现FastClass

测试对象

public class SampleBean {
    private String value;
    public SampleBean(){}

    public SampleBean(String value){
        this.value=value;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }
}

测试类

public class MinixInterface {

    @Test
    public void testFastClass() throws Exception{
        FastClass fastClass=FastClass.create(SampleBean.class);             //通过SampleBean的Class创建fastClass
        FastMethod fastMethod=fastClass.getMethod("getValue",new Class[0]); //由于getValue不需要参数因而传入长度为0的数组对象
        SampleBean bean =new SampleBean();                                  //创建SampleBean对象
        bean.setValue("hello world");                                       //设置对象的值
        System.out.println(fastMethod.invoke(bean,new Object[0]));  //hello world   //调用该对象的getValue方法
    }
}

注:以上文章参考的是一篇博客的内容,由于是很早之前找到的,然后复制到了自己的笔记本上,尝试了好久都没有找到原文,十分感谢源博主的分享。










CGLIB是一个强大的开源代码生成库,它可以在运行时扩展Java类和实现接口。CGLIB动态代理CGLIB库的一个重要特性,它通过生成目标类的子类来实现代理。 在CGLIB动态代理中,我们可以使用`MethodInterceptor`接口来定义代理逻辑。`MethodInterceptor`接口有一个`intercept`方法,该方法在目标方法被调用时被触发。在`intercept`方法中,我们可以编写自定义的逻辑来增强目标方法的功能。 下面是一个使用CGLIB动态代理的示例代码: ```java import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class CglibProxyExample implements MethodInterceptor { public Object createProxy(Object target) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(target.getClass()); enhancer.setCallback(this); return enhancer.create(); } @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { // 在目标方法执行前的逻辑 System.out.println("Before method: " + method.getName()); // 调用目标方法 Object result = proxy.invokeSuper(obj, args); // 在目标方法执行后的逻辑 System.out.println("After method: " + method.getName()); return result; } } ``` 在上述示例中,我们首先创建了一个`CglibProxyExample`类,实现了`MethodInterceptor`接口。然后,我们通过`Enhancer`类创建了一个代理对象,并设置了目标类和代理逻辑。在`intercept`方法中,我们可以在目标方法执行前后添加自定义的逻辑。 使用CGLIB动态代理时,我们可以通过调用`createProxy`方法来创建代理对象。例如: ```java SomeClass target = new SomeClass(); CglibProxyExample proxyExample = new CglibProxyExample(); SomeClass proxy = (SomeClass) proxyExample.createProxy(target); ``` 这样,我们就可以通过`proxy`对象来调用目标类的方法,并在方法执行前后添加自定义的逻辑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值