0%

Java

基础

java hashmap

并发编程

volatile关键字

concurrenthashmap

框架

dubbo面试题

动态代理

gdk

利用拦截器(拦截器必须实现InvocationHanlder)加上反射机制生成一个实现代理接口的匿名类,

在调用具体方法前调用InvokeHandler来处理。

JDK动态代理主要涉及两个类:java.lang.reflect.Proxy 和 java.lang.reflect.InvocationHandler

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Main {
public static void main(String[] args) {
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method);
if (method.getName().equals("morning")) {
System.out.println("Good morning, " + args[0]);
}
return null;
}
};
Hello hello = (Hello) Proxy.newProxyInstance(
Hello.class.getClassLoader(), // 传入ClassLoader
new Class[] { Hello.class }, // 传入要实现的接口
handler); // 传入处理调用方法的InvocationHandler
hello.morning("Bob");
}
}

interface Hello {
void morning(String name);
}

输出:

public abstract void Hello.morning(java.lang.String)

Good morning, Bob

流程:

  1. 定义一个InvocationHandler实例,它负责实现接口的方法调用;
  2. 通过Proxy.newProxyInstance()创建interface实例,它需要3个参数:
    1. 使用的ClassLoader,通常就是接口类的ClassLoader;
    2. 需要实现的接口数组,至少需要传入一个接口进去;
    3. 用来处理接口方法调用的InvocationHandler实例。
  3. 将返回的Object强制转型为接口。

cglib

利用ASM开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

何时使用JDK还是CGLIB?

  1. 如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP。
  2. 如果目标对象实现了接口,可以强制使用CGLIB实现AOP。
  3. 如果目标对象没有实现了接口,必须采用CGLIB库,Spring会自动在JDK动态代理和CGLIB之间转换。

JDK动态代理和CGLIB字节码生成的区别?

  1. JDK动态代理只能对实现了接口的类生成代理,而不能针对类。
  2. CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,
    并覆盖其中方法实现增强,但是因为采用的是继承,所以该类或方法最好不要声明成final,
    对于final类或方法,是无法继承的。

Spring如何选择用JDK还是CGLIB?

  1. 当Bean实现接口时,Spring就会用JDK的动态代理。
  2. 当Bean没有实现接口时,Spring使用CGlib是实现。
  3. 可以强制使用CGlib(在spring配置中加入<aop:aspectj-autoproxy proxy-target-/>)。