java动态代理--proxy&cglib
大纲
- 代理
- proxy
- cglib
- 小结
一、代理
为什么要用代理?其实就是希望不修改对象的情况下,增强对象。
静态代理:
- 静态代理模式,需要代理类和目标类实现同一接口,代理类的方法调用目标类的方法,调用方法的前后实现需要增强的逻辑。
- 静态代理有一个问题就是,每个代理类和目标类一一对应,需要代理的类多的情况,需要大量的代理类,难以维护。
动态代理:
- 动态代理就是运行时动态生成的类,并不是在编译时期。
- 动态代理有两种不同的方式,一种是jdk反射包下的的Prxoy,一种是cglib。
二、Proxy
Proxy生成代理对象需要目标对象实现一至少一个接口。
Proxy通过反射实现动态代理。
生成代理对象需要调用Proxy中newProxyInstance方法。
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
loader-目标对象的classloader
interfaces-目标对象实现的接口
InvocationHandler-处理器,当目标对象接口中的方法被调用时处理器中invoke方法会被调用从而实现动态代理
在看下InvocationHandler
public interface InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; }
proxy-代理对象
method-被调用的方法
args-调用方法时传入的参数
invoke返回值为代理方法的返回值
测试:
接口:
public interface Speak { String say(String content); }
目标类:
import lombok.Data;
@Data public class Person implements Speak{ private String name; private int age; public String say(String content){ System.out.println("hi"+name+age+"content:"+content); return "say return"; } }
代理工厂:
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class ProxyFactory{ //维护一个目标对象 private Object target; public ProxyFactory(Object target) { this.target = target; } //生成代理对象 public Object getProxyInstance() { return Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("before method:"+method.getName()); //执行目标对象方法,返回return值。 Object returnValue = method.invoke(target, args); System.out.println("end method:"+method.getName()); return returnValue; } }); } }
代理对象在代理方法调用前后打了一行字。
public static void main(String[] args) { Person p = new Person(); p.setAge(11); p.setName("xx"); ProxyFactory factory = new ProxyFactory(p); Object proxyInstance = factory.getProxyInstance(); Speak speak = (Speak) proxyInstance; String returnValue = speak.say("haha"); System.out.println("returnValue:"+returnValue); }
三、cglib
静态代理和都必须实现接口,而cglib没有这个限制,cglib通过字节码操作动态生成子类,因此目标类不能被final修饰。
与proxy类似的我们也需要复写一个处理器
public interface MethodInterceptor extends Callback { Object (Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable; }
proxy-代理对象
method-被调用的方法
methodProxy-代理方法(具体本人不是特别清楚)
intercept返回值为代理方法的返回值
测试:
重写Person不需要实现接口
import lombok.Data; @Data public class Person{ private String name; private int age; public String say(String content){ System.out.println("hi"+name+age+"content:"+content); return "say return"; } }
代理工厂:
import org.assertj.core.internal.cglib.proxy.Enhancer; import org.assertj.core.internal.cglib.proxy.MethodInterceptor; import org.assertj.core.internal.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class CgProxyFactory<T> { //维护目标对象 private T target; public CgProxyFactory(T target) { this.target = target; } //获取代理 public T getProxyInstance() { Enhancer en = new Enhancer(); en.setSuperclass(this.target.getClass()); //设置拦截器 en.setCallback(new MethodInterceptor() { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("before method:"+method.getName()); Object returnValue = method.invoke(target, objects); System.out.println("end method:"+method.getName()); return returnValue; } }); return (T) en.create(); } }
和之前的代理对象一样,在代理方法调用前后打了一行字。
public static void main(String[] args) { Person p = new Person(); p.setAge(11); p.setName("xx"); CgProxyFactory<Person> factory = new CgProxyFactory(p); Person proxyInstance = factory.getProxyInstance(); String returnValue = proxyInstance.say("cg"); System.out.println("returnValue:" + returnValue); }
四、小结:
- Proxy需要代理类实现接口,底层为反射。
- Cglib代理对象不能被final修饰,底层是字节码操作。
- spring会根据目标类是否实现接口的情况,切换动态代理的模式,也可以通过配置强制使用cglib。