您的位置:首页 > 编程语言 > Java开发

Java动态代理

2016-07-28 17:08 429 查看
以前做动态代理都是通过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));
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: