代理模式是在不修改原有代码逻辑的情况下,对原有代码逻辑增强的一种方式,要了解什么是动态代理,首先要知道什么是静态代理。
一、静态代理
假设一种场景,一个汽车类(Car)有一个启动方法(run),我在不修改业务逻辑的情况下,我想知道启动汽车花了多长时间该如何实现呢?
公共的汽车接口
/**
* 机动车接口
*
*/
public interface Auto {
void run() throws Exception;
}
小汽车实现类
/**
* 机动车接口
*
*/
public class Car implements Auto {
public void run() throws Exception {
//模拟方法执行过程
Thread.sleep(new Random().nextInt(10));
System.out.println("Car run");
}
}
通过代理类实现对原有方法的增强,代理类实现相同的接口,并将被代理类指派给代理类的属性对象。
/**
* 代理类
*/
public class AutoProxy implements Auto {
Auto auto;
public AutoProxy(Auto auto) {
this.auto = auto;
}
@Override
public void run() throws Exception {
long start = System.currentTimeMillis();
auto.run();
long end = System.currentTimeMillis();
System.out.println("启动时间:" + (end - start));
}
}
运行代理类
public class Main {
public static void main(String[] args) throws Exception {
AutoProxy proxy = new AutoProxy(new Car());
proxy.run();
}
}
Car run
启动时间:1
静态代理的几个要素:
接口:Auto
被代理类:Car
代理类:AutoProxy,实现相同接口,对原实现类进行增强
静态代理的特点是在程序编译期就已经编译好了,即代理类在运行之前就已经存在了。假如随着业务的发展,不光有小汽车,还出现了公交车(Bus),警车(Police Car)等等,如果用静态代理,需要为每个被代理类实现一个代理类,那有没有更优雅的方式呢?答案就是动态代理。
二、动态代理
动态代理模式下,代理类实现InvocationHandler接口,被代理的业务无任何侵入,即可实现对一组接口实现类的增强。
代理类
/**
* 动态代理
*/
public class DynamicAutoProxy implements InvocationHandler {
Auto auto;
public Object bind(Auto auto) {
this.auto = auto;
//生成代理对象
//第一个参数为被代理类的类加载器
//第二个参数为被代理类的接口
//第三个参数为代理类实例,这里传入this
return Proxy.newProxyInstance(auto.getClass().getClassLoader(), auto.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long start = System.currentTimeMillis();
//反射调用实际的方法
Object object = method.invoke(auto, args);
long end = System.currentTimeMillis();
System.out.println("启动时间:" + (end - start));
return object;
}
}
动态代理类的几个要素:
1、被代理对象(Car、Bus)
2、执行者对象(DynamicAutoProxy)
3、代理对象,在执行者对象中通过Proxy.newProxyInstance生成的代理对象。
运行代理类
public class Main {
public static void main(String[] args) throws Exception {
DynamicAutoProxy proxy = new DynamicAutoProxy();
Auto car = (Auto) proxy.bind(new Car());
car.run();
Auto bus = (Auto) proxy.bind(new Bus());
bus.run();
}
}
Car run
启动时间:1
Bus run
启动时间:5
代理对象Proxy是如何生成的呢,跟踪下源码可以看出,只是对创建动态代理类的过程进行了封装。
@CallerSensitive
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();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
// 通过类加载器生成指定代理类
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
// 实例化一个Proxy代理对象
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
动态代理本质上是在代码运行的过程中动态的创建了Proxy代理对象。
jdk动态代理的前提是被代理类实现了接口,如果没有实现接口又将如何代理呢,cglib动态代理就是为了解决这个问题。
springboot中通过@EnableAspectJAutoProxy与@Aspect注解实现动态代理,实际上@SpringBootApplication会自动开启@EnableAspectJAutoProxy,不需要手工指定。
可通过下面代码跟踪
@SpringBootApplication->@EnableAutoConfiguration->spring.factories->@AopAutoConfiguration->@EnableAspectJAutoProxy
public @interface EnableAspectJAutoProxy {
// true: 使用cglib代理,false:使用jdk动态代理,默认为false
boolean proxyTargetClass() default false;
//是否通过aop框架暴露代理对象
boolean exposeProxy() default false;
}