Java动态代理

一、什么是动态代理

动态代理就是在程序运行期间,创建目标对象的代理对象,在代理对象中生成目标对接方法的增强方法,在运行期间,对目标对象中的方法动态拦截去执行代理对象中的增强方法

二、动态代理的使用场景

  • 记录日志(调用方法后记录日志)

  • 监控性能(统计方法运行时间)

  • 权限控制(调用方法前校验是否有权限)

  • 事务管理(调用方法前开启事务,调用方法后提交关闭事务 )

  • 缓存优化(第一次调用查询数据库,将查询结果放入内存对象, 第二次调用,直接从内存对象返回,不需要查询数据库 )

三、动态代理实现方式

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,其三个参数的含义如下:

  1. proxy: 动态生成的代理对象。翻阅的大部分资料都没有对这个参数进一步解释,有的还解释成被代理的对象(这明明是动态生成的代理对象。)当使用动态生成的代理对象调用其实现的某个方法时,会将此代理对象的引用作为第一个参数传递给invoke方法。目前看到的例子,都没有在invoke方法中使用这个参数,用处不是很大的样子。

  2. method: 通过代理对象调用的方法。

  3. args: 调用方法需要的参数。

用户需要自己实现这个接口。实现这个接口的类,一般要包含一个委托对象的target引用,通过这个委托对象的引用才可以最终调用到实际的实现方法。

Proxy类的newProxyInstance方法用来生成代理对象。该方法的三个参数含义如下:

  1. loader: 类加载器,一般有是自定义的类加载器。

  2. interfaces: 该代理对象要实现的接口。JDK动态代理对象和目标对象要实现同样的接口。

  3. 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方法的四个参数的含义如下:

  1. obj: 代理类对象

  2. method: 被代理的类中的方法

  3. args: 调用方法需要的参数

  4. 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;
	}
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值