• java代理的深入浅出(二)-CGLIB


    java代理的深入浅出(二)-CGLIB

    1.基本原理

    CGLIB的原理就是生成一个要代理类的子类,子类重写要代理的类的所有不是final的方法。在子类中拦截所有父类方法的调用,拦截下来交给设置的MethodInterceptor去执行。 由于是采用继承来实现的代理,所以不能对final修饰的类进行代理,其它都可以代理。

    子类的生成它采用了非常底层的字节码技术(ASM节码处理框架),转换字节码来完成。它要求必须对JVM内部结构包括class文件的格式和指令集都很熟悉。

    代理一个类一般会动态生成三个类

    第一个是生成的新子类,对原始类里每一个方法都会在动态的子类里生成一个对应的MethodProxy。

    另外两个动态生成的FastClass类,一个是针对应原始类的方法生成方法索引表,一个针对应新生成子类的方法生成方法索引表。这2个额外生成的类主要作用在于当我们调用一个方法时,不通过反射来调用,而是通过类似于数组下标的方式来定位方法,直接进行类方法的执行。因此CGLib创建的动态代理对象性能比JDK创建的动态代理对象的性能高不少,但是CGLib在创建代理对象时所花费的时间却比JDK多得多,所以对于单例的对象,因为无需频繁创建对象,用CGLib合适,反之,使用JDK方式要更为合适一些。

    2.核心类

    cglib主要包含4个概念,BeanGenerator、Enhancer、MethodInterceptor、LazyLoader、Dispatcher。
    beangenerator主要用于动态生成一个类的子类,可以给子类动态添加一些成员变量,自动生成Getter、Setter方法。缺点是它只能生成含默认构造函数的子类。

    BeanGenerator gen = BeanGenerator();
    gen.setSuperclass(SuperClass.class);
    gen.addProperty("name", String.class);
    Class subClazz = (Class)gen.createClass();
    SuperClass obj = (SuperClass)gen.create();
    

    enhancer用于实现某个方法调用的aop功能。enhancer生成对象会包含很多cglib自动添加进去的属性,所以最后生成的对象会比较大。

    MethodInterceptor

    public class MethodInterceptorImpl implements MethodInterceptor{
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
    return proxy.invokeSuper(obj, args);
    }
    }

    //代理invoke方法
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(clazz);
    enhancer.setCallbacks(new Callback[]{NoOp.INSTANCE, new MethodInterceptorImpl()});
    enhancer.setCallbackFilter(new CallbackFilter() {
    @Override
    public int accept(Method method) {
    //只拦截Algorithm接口定义的invoke方法
    if(method.getName().equals("invoke"))
    return 1;
    return 0;
    }
    });
    enhancer.setUseFactory(false);
    //new 出对象
    Object proxy = enhancer.create();

    lazyLoader是cglib的代理延迟初始化接口。当接口方法被第一次调用时,才确定实际要访问的对象。

    public class ConcreteClassLazyLoader implements LazyLoader{
    
        public class PropertyBean {
            private String propertyName;  
            public String getPropertyName() {
                return propertyName;
            }
            public void setPropertyName(String propertyName) {
                this.propertyName = propertyName;
            }
        }
    
        @Override
        public Object loadObject() throws Exception {  
            System.out.println("LazyLoader loadObject() ...");  
            PropertyBean bean=new PropertyBean();  
            bean.setPropertyName("lazy-load object propertyName!");  
            return bean;  
        }
    
        public static void main(String[] args){
            Enhancer enhancer=new Enhancer();  
            enhancer.setSuperclass(PropertyBean.class);  
            PropertyBean propertyBean = (PropertyBean)enhancer.create(PropertyBean.class,new ConcreteClassLazyLoader());  
    
            //此处会回调loadObject
            System.out.println(propertyBean.getPropertyName());
    
            System.out.println("after...");  
            //之后不再回调loadObejct,直接访问第一次返回的对象 
            System.out.println(propertyBean.getPropertyName());  
        }
    }
    

    Dispatcher功能与LazyLoader相同,只是dispatcher每次都会被回调。

    public class ConcreteDispatcher implements Dispatcher{
    
        public class PropertyBean {
            private String propertyName;  
            public String getPropertyName() {
                return propertyName;
            }
            public void setPropertyName(String propertyName) {
                this.propertyName = propertyName;
            }
        }
    
        @Override
        public Object loadObject() throws Exception {  
            System.out.println("Dispatcher loadObject() ...");  
            PropertyBean bean=new PropertyBean();  
            bean.setPropertyName("Dispatcher object propertyName!");  
            return bean;  
        }
    
        public static void main(String[] args){
            Enhancer enhancer=new Enhancer();  
            enhancer.setSuperclass(PropertyBean.class);  
            PropertyBean propertyBean = (PropertyBean)enhancer.create(PropertyBean.class,new ConcreteDispatcher());  
    
            //此处会回调loadObject
            System.out.println(propertyBean.getPropertyName());
    
            System.out.println("after...");  
            //每次都回调loadObejct
            System.out.println(propertyBean.getPropertyName());  
        }
    }
    

    3.示例

    目标类 CrudServiceImpl

    public class CrudServiceImpl implements CrudService {
        @Override
        public Long create(String content) {
            System.out.println("create......");
            return 1L;
        }
    
        @Override
        public List<String> retrieve(String condition) {
            System.out.println("retrieve......");
            return null;
        }
    
        @Override
        public boolean update(Long id, String content) {
            System.out.println("update......");
            return true;
        }
    
        @Override
        public boolean delete(Long id) {
            System.out.println("delete......");
            return false;
        }
    }
    

    拦截处理类

    public class MyMethodIntercepter implements MethodInterceptor{
    
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            System.out.println("brfore1 cglib invoke");
            Object result = proxy.invokeSuper(obj, args);
            System.out.println("after2 cglib invoke");
            return result;
        }
    }
    

    回调过滤类

    public class ConcreteClassCallbackFilter implements CallbackFilter {
        @Override
        public int accept(Method method) {
            if(method.getName().equals("create")){
                return 1;
            }
            return 0;
        }
    }
    

    生成动态代理类

    public class TestCglib {
        public static void main(String[] args){
            Enhancer enhancer = new Enhancer(); 
            enhancer.setSuperclass(CrudServiceImpl.class); 
            enhancer.setCallbacks(new Callback[]{NoOp.INSTANCE,new MyMethodIntercepter()});
            enhancer.setCallbackFilter(new ConcreteClassCallbackFilter()); 
            CrudService proxy = (CrudService)enhancer.create();
            System.out.println("proxy class name>>>>>"+proxy.getClass().getName());
            System.out.println(proxy.create("ddd"));
            System.out.println("==============================");
            System.out.println(proxy.delete(1L));
        }
    }
    

    反编译动态代理类

    用agent(premain)将加载类导出到指定目录下,加下面的代码打包到jar里,增加MANIFEST.INF描述内容

    Can-Redefine-Classes: true
    Premain-Class: com.longchao.agent.CustomAgent
    

    在程序启动的vm参数里加agent参数,-javaagent:D:/work/code/middleware/study/agent.jar

    public class CustomAgent implements ClassFileTransformer {
    
        public static void premain(String agentArgs, Instrumentation inst) {
            inst.addTransformer(new CustomAgent());
        }
    
        public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
            if (!className.startsWith("java") && !className.startsWith("sun")) {
                // 既不是java也不是sun开头的
                // 导出代码
                int lastIndexOf = className.lastIndexOf("/") + 1;
                String fileName = className.substring(lastIndexOf) + ".class";
                exportClazzToFile("d:/aop/", fileName, classfileBuffer);
                System.out.println(className + " --> EXPORTED Succeess!");
            }
            return classfileBuffer;
        }
    
        /**
         * @param dirPath  目录以/结尾,且必须存在
         * @param fileName
         * @param data
         */
        private void exportClazzToFile(String dirPath, String fileName, byte[] data) {
            try {
                File file = new File(dirPath + fileName);
                if (!file.exists()) {
                    file.createNewFile();
                }
                FileOutputStream fos = new FileOutputStream(file);
                fos.write(data);
                fos.close();
            } catch (Exception e) {
                System.out.println("exception occured while doing some file operation");
                e.printStackTrace();
            }
        }
    }
    

    4.总结

    BeanGenerator适合给子类加成员变量
    MethodInterceptor 适合做方法拦截
    LazyLoader、Dispatcher适合做对象路由

    ps:代码的地址:https://github.com/zhulongchao/proxy.git

  • 相关阅读:
    TMD 这个写笔记的号,盗了有意思吗
    类成员的指针必须NULL化,否则是乱七八糟的东西
    超前引用不可使用类名来定义变量和函数的变量参数,只可用来定义引用或者指针。
    XP下,移动窗口产生重影的问题
    生成ico格式图标
    设置窗口的z-order总是在最底部
    关于windows的锁定状态
    使用Layered Window遇到的一些问题及解决方法
    转-使用wifi调试程序
    URL的格式
  • 原文地址:https://www.cnblogs.com/zhulongchao/p/5516258.html
Copyright © 2020-2023  润新知