- 反射
- 反射可以在运行时分析类以及执行类中方法的能力。可以通过反射,获取任意一个类的所有属性和方法,还可以操作属性或执行调用这些方法。
- 使用场景:动态代理;注解使用;AOP等
- 代理
- 概念
使用代理对象来代替对真实对象的访问,这样就可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能。比如:在目标对象的某个方法执行前增加一些自定义的功能。分为静态代理和动态代理两种实现。 - 静态代理
JVM层面理解,在编译期,就将接口、实现类、代理类等,实现为一个个的.class文件。需要对每个目标类都单独实现一个代理类。AspectJ。
- 实现步骤:
- 定义一个接口及其实现类;
- 创建一个代理类,同样实现该接口;
- 将目标对象注入进代理类,然后在代理类的对应方法调用目标类中的对应方法。(本质是通过代理类屏蔽对目标对象的访问,并且可以在目标方法执行前后扩展功能)
- 动态代理
- 从JVM角度来说,动态代理是在运行期,动态生成类字节码,并加载到JVM中。
- 不需要针对每个目标类都单独创建一个代理类,且不需要必须实现接口,就可以直接代理实现类(CGLIB 动态代理机制);
- JDK 动态代理
- JDK动态代理主要涉及java.lang.reflect包下的两个类:Proxy和InvocationHandler。其中InvocationHandler是一个接口,可以通过实现该接口定义横切逻辑 ,并通过反射机制调用目标类的代码,动态地横切逻辑和业务逻辑编织在一起。
- 只提供了基于接口的动态代理,不支持类的代理。只能代理实现了接口的类。
- 动态生成的代理类,已经继承了Proxy类,所以不能再继承其他类。
- 创建动态代理对象: Proxy.newProxyInstance(xx1,xx2,xx3); 方法获取某个类的代理对象。
- 必须实现InvocationHandler 接口来自定义处理逻辑,当动态代理对象调用一个方法时,其方法的调用会被转发到实现InvocationHandler 接口类的 invoke 方法来调用。动态地将横切逻辑和业务编织在一起。
- 实现步骤
- 定义一个接口及其实现类;
- 自定义类并实现 InvacationHandler 接口,并重写 invoke 方法。在 invoke 方法中,调用原生方法(被代理类的方法),并自定义一些处理逻辑;
- 通过Proxy.newProxyInstance() 方法创建代理对象。
public final class $Proxy0 extends Proxy implements Interface{
public $Proxy0(InvocationHandler paramInvocationHandler) {
super(paramInvocationHandler);
}
public final void sayHello(String paramString){
try {
this.h.invoke(this, m3, new Object[] { paramString });
return;
} catch (Error|RuntimeException localError) {
throw localError;
}
catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
}
}
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException {
}
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args);
}
CGLIB (Code Generation library)动态代理
CGLIB是一个基于ASM的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成。
CGLIB 是通过继承的方法实现的动态代理。在运行时动态的生成一个继承目标对象的代理类(子类对象),并重写覆盖其中特定方法并添加增强代理,然后创建代理对象实例。
MethodInterceptor 接口和 Enhancer 类是核心
实现步骤
自定义一个类;
自定义类并实现 MethodInterceptor 接口并重写 intercept 方法,intercept 用于拦截增强被代理类的方法,和JDK 动态代理中的invoke方法类似。
public interface MethodInterceptor extends Callback {
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable;
}
public class DebugMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("before method " + method.getName());
Object object = methodProxy.invokeSuper(o, args);
System.out.println("after method " + method.getName()); return object;
}
}
通过Enhancer 类的 create() 创建代理类。
public static Object getProxy(class<?> clazz) {
Enhancer enhancer = new Enhancer();
enhancer.setClassLoader(clazz.getClassLoader())
enhancer.setSuperclass(clazz);
enhancer.setCallback(new DebugMethodInterceptor);
enhancer.create();
}
- JDK和CGLIB动态代理区别
- JDK动态代理是基于接口的代理,必须要有接口;
- CGLIB动态代理是通过字节码底层继承要代理类来实现。因为被final修饰的类无法进行代理;
- 如果被代理的对象是实现类,Spring默认使用JDK动态代理来完成;如果被代理的对象不是实现类,Spring会强制使用CGLIB来实现动态代理。