一、什么是动态代理
动态代理就是在程序运行期间,创建目标对象的代理对象,在代理对象中生成目标对接方法的增强方法,在运行期间,对目标对象中的方法动态拦截去执行代理对象中的增强方法
二、动态代理的使用场景
-
记录日志(调用方法后记录日志)
-
监控性能(统计方法运行时间)
-
权限控制(调用方法前校验是否有权限)
-
事务管理(调用方法前开启事务,调用方法后提交关闭事务 )
-
缓存优化(第一次调用查询数据库,将查询结果放入内存对象, 第二次调用,直接从内存对象返回,不需要查询数据库 )
三、动态代理实现方式
1、JDK动态代理
实现方式
委托机制,代理类和目标类都实现了同样的接口,InvocationHandler持有目标类,代理类委托InvocationHandler去调用目标类的原始方法
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
public class Proxy implements java.io.Serializable{
......
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
InvocationHandler接口只有一个方法invoke,其三个参数的含义如下:
-
proxy: 动态生成的代理对象。翻阅的大部分资料都没有对这个参数进一步解释,有的还解释成被代理的对象(这明明是动态生成的代理对象。)当使用动态生成的代理对象调用其实现的某个方法时,会将此代理对象的引用作为第一个参数传递给invoke方法。目前看到的例子,都没有在invoke方法中使用这个参数,用处不是很大的样子。
-
method: 通过代理对象调用的方法。
-
args: 调用方法需要的参数。
用户需要自己实现这个接口。实现这个接口的类,一般要包含一个委托对象的target引用,通过这个委托对象的引用才可以最终调用到实际的实现方法。
Proxy类的newProxyInstance方法用来生成代理对象。该方法的三个参数含义如下:
-
loader: 类加载器,一般有是自定义的类加载器。
-
interfaces: 该代理对象要实现的接口。JDK动态代理对象和目标对象要实现同样的接口。
-
h: 用户自己写的实现InvocationHandler接口的类的引用。
DEMO
import java.math.BigDecimal;
public interface Transaction{
public void pay(BigDecimal money);
}
import lombok.Builder;
import lombok.Data;
import java.math.BigDecimal;
@Data
@Builder
public class Person implements Transaction{
private String name;
private BigDecimal money;
@Override
public void pay(BigDecimal money) {
System.out.println("支付中");
this.setMoney(getMoney().subtract(money));
}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
@Builder
public class TransactionInvocationHandler implements InvocationHandler {
private Object target;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
System.out.println(((Person)target).getName()+"剩余金额"+((Person) target).getMoney());
Object invoke = method.invoke(target, args);
System.out.println(((Person)target).getName()+"剩余金额"+((Person) target).getMoney());
return invoke;
}
public Object newProxy(Object target){
this.target=target;
return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
}
public class DynamicProxyDemo{
public static void main(String[] args){
Person person1 = Person.builder().name("张三").money(BigDecimal.valueOf(1000)).build();
Transaction proxy1 = (Transaction) TransactionInvocationHandler.builder().build().newProxy(person1);
proxy1.pay(new BigDecimal(100));
}
}
控制台打印
张三剩余金额1000
支付中
张三剩余金额900
2、cglib代理
实现方式
继承机制,代理类继承了目标类并重写了目标方法,通过回调函数MethodInterceptor调用父类方法执行原始逻辑
CGLIB动态代理主要涉及到MethodInterceptor接口和Enhancer类。MethodInterceptor又继承了Callback接口,Callback接口是个标签接口,并未声明任何方法。Enhancer类主要用来生成代理对象,涉及到的主要方法是setSuperClass setCallback create。
public interface MethodInterceptor extends Callback {
Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable;
}
public class Enhancer extends AbstractClassGenerator {
public Enhancer() {
super(SOURCE);
}
public void setSuperclass(Class superclass) {
if (superclass != null && superclass.isInterface()) {
this.setInterfaces(new Class[]{superclass});
this.setContextClass(superclass);
} else if (superclass != null && superclass.equals(Object.class)) {
this.superclass = null;
} else {
this.superclass = superclass;
this.setContextClass(superclass);
}
}
public void setCallback(Callback callback) {
this.setCallbacks(new Callback[]{callback});
}
public Object create() {
this.classOnly = false;
this.argumentTypes = null;
return this.createHelper();
}
}
intercept方法的四个参数的含义如下:
-
obj: 代理类对象
-
method: 被代理的类中的方法
-
args: 调用方法需要的参数
-
proxy: 生成的代理类对方法的“代理引用”
用户需要实现MethodInterceptor接口,实现对方法的拦截。这一点与JDK动态代理中用户需要实现InvocationHandler接口类似。
DEMO
@Builder
@Setter
@Getter
public class Person {
private String name = "100";
private BigDecimal money = BigDecimal.valueOf(100);
public Person() {
}
public Person(String name, BigDecimal money) {
this.name = name;
this.money = money;
}
public void pay(Person person,BigDecimal money) {
System.out.println("支付中");
this.setMoney(this.money.subtract(money));
person.receive(money);
}
public void receive(BigDecimal money) {
System.out.println("收款中");
this.setMoney(this.money.add(money));
}
}
public class PersonInterceptor implements MethodInterceptor {
Object object;
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
Object o;
if(method.getName().equals("pay")){
System.out.println(((Person)object).getName()+"开始支付");
o = proxy.invokeSuper(obj, args);
System.out.println(((Person)object).getName()+"剩余金额"+((Person)object).getMoney());
}else if (method.getName().equals("receive")){
System.out.println(((Person)object).getName()+"开始收款");
o = proxy.invokeSuper(obj, args);
System.out.println(((Person)object).getName()+"剩余金额"+((Person)object).getMoney());
}else {
o = proxy.invokeSuper(obj, args);
}
return o;
}
public <T> T newProxy(T object){
this.object = object;
//实例化一个增强器,也就是cglib中的一个class generator
Enhancer eh = new Enhancer();
//设置目标类
eh.setSuperclass(object.getClass());
// 设置拦截对象
eh.setCallback(this);
// 生成代理类并返回一个实例
Object t = eh.create();
BeanUtils.copyProperties(object,eh.create());
return (T) t;
}
}
public class Test {
public static void main(String[] args) {
Person person1 = Person.builder().name("张三").money(BigDecimal.valueOf(100)).build();
Person person2 = Person.builder().name("赵四").money(BigDecimal.ONE).build();
Person person1Proxy = new PersonInterceptor().newProxy(person1);
Person person2Proxy = new PersonInterceptor().newProxy(person2);
person1Proxy.pay(person2Proxy,BigDecimal.valueOf(100));
}
}
控制台打印:
张三开始支付
支付中
赵四开始收款
收款中
赵四剩余金额1
张三剩余金额100
3、代理方式对比
JDK Proxy的优势:
-
最小化依赖关系,减少依赖意味着简化开发和维护,JDK本身的支持,可能比cglib更加可靠。
-
平滑进行JDK版本升级,而字节码类库通常需要进行更新以保证在新版Java上能够使用。
-
代码实现简单。
-
基于接口实现,底层基于反射
cglib的优势:
-
有的时候调用目标可能不便实现额外接口,从某种角度看,限定调用者实现接口是有些侵入性的实践,类似cglib动态代理就没有这种限制。
-
只操作我们关心的类,而不必为其他相关类增加工作量。
-
高性能。
-
基于继承实现
四、常见问题
1、spring的cglib代理类无法取到被代理对象的public成员属性
Spring使用的是Objenesis+cglib构造代理对象,使用Obienesis在构建生成代理对象的时候不会执行委托类的构造方法,会绕过父类构造直接产生代理类(子类)对象。
下面Spring源码中看出,使用CglibAopProxy类获取代理对象getProxy()时,对增强代理类Enhancer做代理对象配置,但并没有直接创建实例,而是调用ObjenesisCglibAopProxy.createProxyClassAndInstance()方法objenesis.newInstance()创建实例
class CglibAopProxy implements AopProxy, Serializable {
@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
if (logger.isTraceEnabled()) {
logger.trace("Creating CGLIB proxy: " + this.advised.getTargetSource());
}
try {
Class<?> rootClass = this.advised.getTargetClass();
Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");
Class<?> proxySuperClass = rootClass;
if (rootClass.getName().contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) {
proxySuperClass = rootClass.getSuperclass();
Class<?>[] additionalInterfaces = rootClass.getInterfaces();
for (Class<?> additionalInterface : additionalInterfaces) {
this.advised.addInterface(additionalInterface);
}
}
// Validate the class, writing log messages as necessary.
validateClassIfNecessary(proxySuperClass, classLoader);
// Configure CGLIB Enhancer...
Enhancer enhancer = createEnhancer();
if (classLoader != null) {
enhancer.setClassLoader(classLoader);
if (classLoader instanceof SmartClassLoader &&
((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
enhancer.setUseCache(false);
}
}
enhancer.setSuperclass(proxySuperClass);
enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader));
Callback[] callbacks = getCallbacks(rootClass);
Class<?>[] types = new Class<?>[callbacks.length];
for (int x = 0; x < types.length; x++) {
types[x] = callbacks[x].getClass();
}
// fixedInterceptorMap only populated at this point, after getCallbacks call above
enhancer.setCallbackFilter(new ProxyCallbackFilter(
this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
enhancer.setCallbackTypes(types);
// Generate the proxy class and create a proxy instance.
return createProxyClassAndInstance(enhancer, callbacks);
}
catch (CodeGenerationException | IllegalArgumentException ex) {
throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() +
": Common causes of this problem include using a final class or a non-visible class",
ex);
}
catch (Throwable ex) {
// TargetSource.getTarget() failed
throw new AopConfigException("Unexpected AOP exception", ex);
}
}
}
class ObjenesisCglibAopProxy extends CglibAopProxy {
@Override
protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
Class<?> proxyClass = enhancer.createClass();
Object proxyInstance = null;
if (objenesis.isWorthTrying()) {
try {
proxyInstance = objenesis.newInstance(proxyClass, enhancer.getUseCache());
}
catch (Throwable ex) {
logger.debug("Unable to instantiate proxy using Objenesis, " +
"falling back to regular proxy construction", ex);
}
}
if (proxyInstance == null) {
// Regular instantiation via default constructor...
try {
Constructor<?> ctor = (this.constructorArgs != null ?
proxyClass.getDeclaredConstructor(this.constructorArgTypes) :
proxyClass.getDeclaredConstructor());
ReflectionUtils.makeAccessible(ctor);
proxyInstance = (this.constructorArgs != null ?
ctor.newInstance(this.constructorArgs) : ctor.newInstance());
}
catch (Throwable ex) {
throw new AopConfigException("Unable to instantiate proxy using Objenesis, " +
"and regular proxy instantiation via default constructor fails as well", ex);
}
}
((Factory) proxyInstance).setCallbacks(callbacks);
return proxyInstance;
}
}