java动态代理--proxy&cglib

时间:2021-04-19 16:47:26

大纲

  1. 代理
  2. proxy
  3. cglib
  4. 小结

一、代理

为什么要用代理?其实就是希望不修改对象的情况下,增强对象。

静态代理:

  • 静态代理模式,需要代理类和目标类实现同一接口,代理类的方法调用目标类的方法,调用方法的前后实现需要增强的逻辑。
  • 静态代理有一个问题就是,每个代理类和目标类一一对应,需要代理的类多的情况,需要大量的代理类,难以维护。

动态代理:

  • 动态代理就是运行时动态生成的类,并不是在编译时期。
  • 动态代理有两种不同的方式,一种是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);
}

四、小结:

  1. Proxy需要代理类实现接口,底层为反射。
  2. Cglib代理对象不能被final修饰,底层是字节码操作。
  3. spring会根据目标类是否实现接口的情况,切换动态代理的模式,也可以通过配置强制使用cglib。