静态代理
之前通过1+N的问题实践了下静态代理。但静态代理有其局限性。代理类需要实现目标接口,而如果目标接口有所改变,代理类和目标实现均需要作出改变,不是很灵活。
动态代理
那如何实现动态代理呢?我们可以通过java内建的动态代理功能或者通过cglib(code generation library)来实现。需要注意的是,java的动态代理只能对接口进行代理,需要对类进行代理只能使用cglib。
java的动态代理
// 定义接口
public interface IPerson
{
void say();
}
public class Person implements IPerson
{
@Override
public void say() {
System.out.println("我是个人");
}
}
public class ProxyForPerson implements InvocationHandler
{
IPerson person = null;
// 关联代理类和接口
public IPerson getProxyInterface(IPerson person) {
this.person = person;
return (IPerson) Proxy.newProxyInstance(person.getClass()
.getClassLoader(),
person.getClass()
.getInterfaces(),
this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if("say".equals(method.getName())){
method.invoke(person, args);
}
return null;
}
}
// 客户端调用
public class Client
{
public static void main(String[] args) {
Person person = new Person();
ProxyForPerson proxy = new ProxyForPerson();
// 获得代理类
IPerson iperson = proxy.getProxyInterface(person);
iperson.say();
}
}
cglib实现动态代理
注意使用cglib,需要引入cglib的jar包。同时注意jar名称为cglib-nodep-xxx.jar,而不是cglib-xxx.jar。如果添加引用后者,会如下报错:
Caused by: java.lang.ClassNotFoundException: org.objectweb.asm.Type
public class CglibProxy implements MethodInterceptor
{
private Enhancer enhancer = new Enhancer();
public Object getProxy(Class clazz) {
// 设置需要创建子类的类
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
// 通过字节码技术动态创建子类实例
return enhancer.create();
}
// 实现MethodInterceptor接口方法
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("前置代理");
// 通过代理类调用父类中的方法
Object result = proxy.invokeSuper(obj, args);
System.out.println("后置代理");
return result;
}
}
//客户端调用
public class ClientForCglib
{
public static void main(String[] args) {
CglibProxy proxy = new CglibProxy();
Person proxyson = (Person)proxy.getProxy(Person.class);
proxyson.say();
}
}
后记
CGLib创建的动态代理对象时的性能比JDK创建的动态代理对象的性能高不少,但是CGLib在创建代理对象时所花费的时间却比JDK多得多,所以前者适用于创建单例对象,因为无需频繁创建对象。反之,使用JDK方式要更为合适一些。同时,由于CGLib由于是采用动态创建子类的方法,对于final方法,无法进行代理。