Cglib动态代理

        在上一文中介绍了一下jdk动态代理及实现原理,但是其有一个致命缺陷是,只能对接口进行代理。如果要代理的类为一个普通类、没有接口,则Java动态代理则毫无用武之地。CGLIB(Code Generation Library)是一个开源项目,是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。Hibernate支持它来实现PO(Persistent Object 持久化对象)字节码的动态生成。

Cglib原理:

        CGLIB是一个强大的高性能的代码生成包,它广泛的被许多AOP的框架使用,例如Spring AOP,EasyMock和jMock是通过使用模仿(mock)对象来测试java代码的包。它们都通过使用CGLIB来为那些没有接口的类创建模仿(mock)对象。CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。除了CGLIB包,脚本语言例如Groovy和BeanShell,也是使用ASM来生成java的字节码。当然不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。

应用场景(引入相应的包 )

<!-- 需要引入jar包 cglib -->
<dependency>
    <groupId>cglib</groupId>
     <artifactId>cglib</artifactId>
     <version>2.2.2</version>
</dependency>

CGLIB中重要常用的类Enhancer

        Enhancer可能是CGLIB中最常用的一个类,和Java1.3动态代理中引入的Proxy类差不多(如果对Proxy不懂,可以参考这里)。和Proxy不同的是,Enhancer既能够代理普通的class,也能够代理接口。Enhancer创建一个被代理对象的子类并且拦截所有的方法调用(包括从Object中继承的toString和hashCode方法)。Enhancer不能够拦截final方法,例如Object.getClass()方法,这是由于Java final方法语义决定的。基于同样的道理,Enhancer也不能对fianl类进行代理操作。这也是Hibernate为什么不能持久化final class的原因。 

Enhancer:增强通知

package com.cloud.ceres.rnp.cglib;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class Test1 {

    public void fun(){
        System.out.println("本体");
    }

    public static void main(String[] args) {
        //增强器
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Test1.class);
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object obj, Method method, Object[] objects, MethodProxy proxy) throws Throwable {
                System.out.println("前置");
                Object result = proxy.invokeSuper(obj, args);
                System.out.println("后置");
                return result;
            }
        });
        Test1 proxyObj = (Test1) enhancer.create();
        proxyObj.fun();
    }


}

        结果可想而知,一个Enhancer和一个MethodInterceptor来实现对方法的拦截,不用实现接口我也能帮你创建代理出来,只是在在回调方法加增强逻辑,是不是真香!

Enhancer:拦截所用方法,并对其返回统一类型参数

 
package com.cloud.ceres.rnp.cglib;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.FixedValue;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;
public class Test2 {

    public String fun(String str ){
        return "本体";
    }

    public static void main(String[] args) {
        //增强器
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Test2.class);
        enhancer.setCallback(new FixedValue() {
            @Override
            public Object loadObject() throws Exception {
                return "回调";
            }
        });
        Test2 proxy = (Test2) enhancer.create();
        System.out.println(proxy.fun(null)); 
        System.out.println(proxy.toString());
        System.out.println(proxy.getClass());//不会被拦截
        System.out.println(proxy.hashCode());//数字转化字符串异常
    }


}
回调
回调
class com.cloud.ceres.rnp.cglib.Test2$$EnhancerByCGLIB$$9af150ab
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Number

        FixedValue:拦截所有的方法返回相同的值(final修饰的方法除外),从输出我们可以看出来,Enhancer对非final方法test()、toString()、hashCode()进行了拦截,没有对getClass进行拦截。由于hashCode()方法需要返回一个Number,但是我们返回的是一个String,这解释了上面的程序中为什么会抛出异常。

ImmutableBean 创建不可变类

    通过名字就可以知道,不可变的Bean。ImmutableBean允许创建一个原来对象的包装类,这个包装类是不可变的,任何改变底层对象的包装类操作都会抛出IllegalStateException。但是我们可以通过直接操作底层对象来改变包装类对象。这有点类似于Guava中的不可变视图

package com.cloud.ceres.rnp.cglib.immutableBean;

import org.junit.Assert;
import org.springframework.cglib.beans.ImmutableBean;

public class SampleBean {
    private String value;

    public String getValue() {
        return value;
    }

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

    public static void main(String[] args) {
        SampleBean bean = new SampleBean();
        bean.setValue("Hello world");
        SampleBean immutableBean = (SampleBean) ImmutableBean.create(bean); //创建不可变类
        Assert.assertEquals("Hello world",immutableBean.getValue());
        System.out.println(1);
        immutableBean.setValue("Hello cglib"); //直接修改将throw exception
    }
}

BeanGenerator: 生成动态类

cglib提供的一个操作bean的工具,使用它能够在运行时动态的创建一个bean

  @Test
    public void testBeanGenerator() throws Exception{
        BeanGenerator beanGenerator = new BeanGenerator();
        beanGenerator.addProperty("value",String.class);
        Object myBean = beanGenerator.create();
        Method setter = myBean.getClass().getMethod("setValue",String.class);
        setter.invoke(myBean,"Hello cglib");
        Method getter = myBean.getClass().getMethod("getValue");
        Assert.assertEquals("Hello cglib",getter.invoke(myBean));
    }

在上面的代码中,我们使用cglib动态的创建了一个和SampleBean相同的Bean对象,包含一个属性value以及getter、setter方法

BeanMap:将对象转成map

BeanMap类实现了Java Map,将一个bean对象中的所有属性转换为一个String-to-Obejct的Java Map

  @Test
    public void testBeanMap() 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,"password");
        BeanMap map = BeanMap.create(bean);
        Assert.assertEquals("admin", map.get("username"));
        Assert.assertEquals("password", map.get("password"));
    }

        我们使用BeanGenerator生成了一个含有两个属性的Java Bean,对其进行赋值操作后,生成了一个BeanMap对象,通过获取值来进行验证。

 

注意:

  1. 由于CGLIB的大部分类是直接对Java字节码进行操作,这样生成的类会在Java的永久堆中。如果动态代理操作过多,容易造成永久堆满,触发OutOfMemory异常。

CGLIB和Java动态代理的区

  1. Java动态代理只能够对普通类进行代理,不能对接口进行代理(因为所有生成的代理类的父类为Proxy,Java类继承机制不允许多重继承);CGLIB能够代理普通类;
  2. Java动态代理使用Java原生的反射API进行操作,在生成类上比较高效;CGLIB使用ASM框架直接对字节码进行操作,在类的执行过程中比较高效
     

转载至:https://blog.csdn.net/danchu/article/details/70238002

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值