• Java动态代理


    以前做动态代理都是通过Java的动态代理,今天看公司代码,公司的是使用Javassist来实现的代理,于是又去了解了一下动态代理的东西。

    动态代理主要有三种实现方式:

    1、 Jdk原生的动态代理

     Jdk动态代理要求被代理的对象必须实现一个接口,没有接口的条件下可以使用cglib

    参考代码:

    package cn.edu.knowledge.proxy;
    
    public interface Person {
        
        void sayHello();
    }
    package cn.edu.knowledge.proxy;
    
    public class Student implements Person{
    
        @Override
        public void sayHello() {
            
            System.out.println("I am student");
        }
    
    }
    package cn.edu.knowledge.proxy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class JDKProxy implements InvocationHandler {
        private Object target;
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args)
                throws Throwable {
            System.out.println("Before say hello");
    
            Object result = method.invoke(target, args);
    
            System.out.println("After say hello");
    
            return result;
        }
        
        public Object getProxy(Object target){
            this.target = target;
            
            return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
        }
        
        public static void main(String[] args) {
            Person p = new Student();
            JDKProxy proxy = new JDKProxy();
            Person p2 = (Person) proxy.getProxy(p);
            p2.sayHello();
        }    
    
    }

    2、 动态字节码生成

    使用动态字节码生成技术实现 AOP 原理是在运行期间目标字节码加载后,生成目标类 的子类,将切面逻辑加入到子类中,所以使用 Cglib 实现 AOP 不需要基于接口。

    本节介绍如何使用 Cglib 来实现动态字节码技术。Cglib 是一个强大的,高性能的 Code 生 成类库,它可以在运行期间扩展 Java 类和实现 Java 接口,它封装了 Asm,所以使用 Cglib 前 需要引入 Asm 的 jar。 清单七:使用CGLib实现AOP

    package my.test;
    
    import java.lang.reflect.Method;
    
    import net.sf.cglib.proxy.Enhancer;
    
    import net.sf.cglib.proxy.MethodInterceptor;
    
    import net.sf.cglib.proxy.MethodProxy;
    
    public class CglibProxyTest implements MethodInterceptor {
    
        private CglibProxyTest() {
    
        }
    
        public static <T extends Test> Test newProxyInstance(Class<T> targetInstanceClazz){
    
            Enhancer enhancer = new Enhancer();
    
            enhancer.setSuperclass(targetInstanceClazz);
    
            enhancer.setCallback(new CglibProxyTest());
    
            return (Test) enhancer.create();
    
        }
    
        public Object intercept(Object obj, Method method, Object[] args,
    
                MethodProxy proxy) throws Throwable {
    
            return proxy.invokeSuper(obj, args);
    
        }
    
    }

    3、 自定义类加载器

    如果我们实现了一个自定义类加载器,在类加载到 JVM 之前直接修改某些类的方法, 并将切入逻辑织入到这个方法里,然后将修改后的字节码文件交给虚拟机运行,那岂不是更 直接。 

    Javassist 是一个编辑字节码的框架,可以让你很简单地操作字节码。它可以 在运行期定义或修改 Class。使用 Javassist 实现 AOP 的原理是在字节码加载前直 接修改需要切入的方法。这比使用 Cglib 实现 AOP 更加高效,并且没太多限制

    package cn.edu.knowledge.proxy;
    
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    
    import javassist.CannotCompileException;
    import javassist.ClassPool;
    import javassist.CtClass;
    import javassist.CtConstructor;
    import javassist.CtField;
    import javassist.CtField.Initializer;
    import javassist.CtNewMethod;
    import javassist.Modifier;
    import javassist.NotFoundException;
    
    public class JavassistTest {
    
        public static void main(String[] args) throws CannotCompileException,
                NotFoundException, SecurityException, NoSuchMethodException,
                InstantiationException, IllegalAccessException,
                ClassNotFoundException, IllegalArgumentException,
                InvocationTargetException {
            ClassPool pool = ClassPool.getDefault();
            CtClass cls = pool.makeClass("cn.edu.knowledge.TestClass");
    
            // 添加私有成员name及其getter、setter方法
            CtField param = new CtField(pool.get("java.lang.String"), "name", cls);
            param.setModifiers(Modifier.PRIVATE);
            cls.addMethod(CtNewMethod.setter("setName", param));
            cls.addMethod(CtNewMethod.getter("getName", param));
            cls.addField(param, Initializer.constant(""));
    
            // 添加无参的构造体
            CtConstructor cons = new CtConstructor(new CtClass[] {}, cls);
            cons.setBody("{name = "Brant";}");
            cls.addConstructor(cons);
    
            // 添加有参的构造体
            cons = new CtConstructor(
                    new CtClass[] { pool.get("java.lang.String") }, cls);
            cons.setBody("{$0.name = $1;}");
            cls.addConstructor(cons);
    
            // 打印创建类的类名
            System.out.println("类名:"+cls.toClass());
    
            // 通过反射创建无参的实例,并调用getName方法
            Object o = Class.forName("cn.edu.knowledge.TestClass").newInstance();
            Method getter = o.getClass().getMethod("getName");
            System.out.println("name: "+getter.invoke(o));
    
            // 调用其setName方法
            Method setter = o.getClass().getMethod("setName",
                    new Class[] { String.class });
            setter.invoke(o, "Adam");
            System.out.println("name: " + getter.invoke(o));
    
            // 通过反射创建有参的实例,并调用getName方法
            o = Class.forName("cn.edu.knowledge.TestClass")
                    .getConstructor(String.class).newInstance("Liu Jian");
            getter = o.getClass().getMethod("getName");
            System.out.println("name: "+ getter.invoke(o));
        }
    
    }
  • 相关阅读:
    jichu
    scanf
    数位dp
    hdu 5667
    华东交通大学2018年ACM“双基”程序设计竞赛 K
    华东交通大学2018年ACM“双基”程序设计竞赛 D
    map
    次小生成树
    set
    c++11之为什么C++11引入了std::ref?
  • 原文地址:https://www.cnblogs.com/googlemeoften/p/5715499.html
Copyright © 2020-2023  润新知