代理模式的一切

代理模式的背景#

假如我们有一个用户实现接口,如UserService,和实现类UserServiceImpl。现在想在UserServiceImpl的某些方法前后打印参数日志,可以选择的方式有:

静态代理:#

  • 继承:写一个子类继承这个UserServiceImpl,然后方法前后加上日志功能。但是如果要再实现另外一个增强需求,就需要再次继承。或者对多个接口的方法同时进行增强,就要链式继承。长此以往产生类爆炸。

  • 聚合装饰模式。实现同一个接口UserService,装饰器的构造方法传入一个同样的类,进行包装。在包装方法里面进行前后的增强,再去调用父类。[Java的IO流使用了大量的装饰模式]。和上面的代理很像,但是装饰模式只能装饰一个特定的对象,在构造方法里面传进来,实现同样的接口进行增强。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    /**
    * 装饰模式增强,实现相同接口
    */
    public class UserServiceDeractor implements UserService {
    UserService userService;

    /**
    * 构造方法传入同样的对象进行包装
    */
    public UserServiceDeractor(UserService userService){
    this.userService = userService;
    }

    @Override
    public User getUser(String name) {
    System.out.println("--------------装饰模式增强,传入参数"+name);
    return userService.getUser(name);
    }
    }
  • 缺点:实现起来简单,但是代理类会比较多,比较复杂。

动态代理#

基于以上的静态代理,会产生大量的class,如何改进?最浅显的想法就是

程序拼装这一段动态代理的java文件代码—>然后生成class字节码—>然后加载到项目中—>创建对象并使用。

人肉朴素思想的手动代理#

基于以上思想我们试着实现一个山寨版

先来一个函数接口,用于动态封装我们的代理增强功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

/**
* 动态代理接口
*/
@FunctionalInterface
public interface MyInvocationHandler {

/**
* 代理增强
* @param proxy 代理类
* @param method 代理方法
* @param target 目标包装对象
* @param args 参数
* @return
* @throws Exception
*/
public Object invoke(Object proxy, Method method,Object target, Object... args) throws Exception;
}

如果需要对某个对象进行增强,就写一个Handler接口的实现,然后在invoke方法中增强。

将这个invoke传入到我们下面的ProxyUtil中去创建一个代理对象:

  • public static <T> T getProxy(Object target, MyInvocationHandler handler, Class<T> ... interfaces) throws Exception 有三个参数,目标对象、增强实现(或者Lambda)、要实现的接口列表。
  • 这个方法会把所有target的public方法进行重新生成java,每个方法里面调用handler.invoke进行增强
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
/**
* 人工模拟动态代理, 不用任何第三方jar,也不用JDK
*/
public class ProxyUtil {

static JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();

/**
* 需要生成一个代理对象,必然要:
* 1.先得到java代码
* 2.然后把java代码变成class
* 3.然后把class变成对象
*
* 我们动态实现那个装饰模式的静态代理
* @param <T>
*/
public static <T> T getProxy(Object target, MyInvocationHandler handler, Class<T> ... interfaces) throws Exception {
//Class clazz = target.getClass();
if(interfaces == null || interfaces.length==0){
System.out.println("该对象没有实现接口,无法代理!");
return null;
}
//Class interfaceClazz = clazz.getInterfaces()[0];
System.out.println("即将针对接口"+interfaces+"进行动态代理...");
// 拼装java类
StringBuffer java = new StringBuffer();
String proxyPackage = "com.sam.proxy";
java.append("package "+proxyPackage+";\n\n");
// 引入接口
java.append(Arrays.stream(interfaces).map(c->"import "+c.getName()).collect(Collectors.joining(";\n"))+";\n");
java.append(
"import "+target.getClass().getName()+";\n" +
"import java.util.Arrays;\n" +
"import "+MyInvocationHandler.class.getName()+";\n\n" + // import MyInvocationHandler
"// TODO 这个类是由ProxyUtil自动生成的\n"+
// 接口$Myproxy 此处也可以使用
"public class MyProxyOf"+target.getClass().getSimpleName()+" implements "+ Arrays.stream(interfaces).map(Class::getSimpleName).collect(Collectors.joining(","))+" {\n" +
// private 接口 target
" private "+target.getClass().getSimpleName()+" target;\n" +
" private "+MyInvocationHandler.class.getSimpleName()+" handler;\n\n" +
// 构造方法包装
" public MyProxyOf"+target.getClass().getSimpleName()+"("+target.getClass().getSimpleName()+" target, "+MyInvocationHandler.class.getSimpleName()+" h){\n" +
" this.target = target;\n" +
" this.handler = h;\n" +
" }\n");
// 不用在每个方法处理,使用动态处理
for(Method method : target.getClass().getDeclaredMethods()){
// public com.xx.xx.User getUser(java.lang.String
java.append("\n\t@Override\n\tpublic "+method.getReturnType().getName()+" "+method.getName()+"(");

// String name){
List<String> params = IntStream.range(0,method.getParameterTypes().length).mapToObj(i->method.getParameterTypes()[i].getName() +" var"+i).collect(Collectors.toList());
java.append(String.join(", ",params));// java.lang.String var1, java.lang.Integer var2
java.append(")");
if(method.getExceptionTypes().length > 0){
java.append("thorws "+ Arrays.stream(method.getExceptionTypes()).map(Class::getName).collect(Collectors.joining(", ")));
}
java.append("{\n");
// 开始调用invoke或者lambda进行代理增强!
java.append("\t\tSystem.out.println(\"代理对象中即将调用invoke.....\");\n");
// 调用包装类target的方法,进行增强
java.append("\t\ttry{\n");
if(method.getParameterTypes().length == 0){
java.append("\t\t\t"+(method.getReturnType()==void.class?"":("return ("+method.getReturnType().getName()+")"))+"handler.invoke(this, target.getClass().getMethod(\""+method.getName()+"\"), target);\n");
}else{
List<String>vars = IntStream.range(0,method.getParameterTypes().length).mapToObj(i->"var"+i).collect(Collectors.toList());
List<String>paramClazz = Arrays.stream(method.getParameterTypes()).map(c->c.getName()+".class").collect(Collectors.toList());
java.append("\t\t\tClass[] paramClazz = new Class[]{"+String.join(",",paramClazz)+"};\n");
java.append("\t\t\t"+(method.getReturnType()==void.class?"":("return ("+method.getReturnType().getName()+")"))+"handler.invoke(this, target.getClass().getMethod(\""+method.getName()+"\", paramClazz), target, "+String.join(",",vars)+");\n");
}

java.append("\t\t}catch(Exception ex){\n");
//method.getExceptionTypes()
if(method.getExceptionTypes().length > 0){
java.append("\t\tList<Class> methodExs = Arrays.asList("+ String.join(",",Arrays.stream(method.getExceptionTypes()).map(c->c.getName()+".class").collect(Collectors.toList()))+");\n");
java.append("\t\t\tif(methodExs.contains(ex.getClass())){throw ex;}\n");
}
java.append("\t\t\tex.printStackTrace();\n");
if(method.getReturnType() != void.class){ // 异常时候返回null
java.append("\t\t\treturn null;\n");
}
java.append("\t\t}\n"); // 结束catch
java.append("\t}\n");// 结束方法
}
java.append("}\n");
//System.out.println(java);

// 落盘
String filePath = System.getProperty("user.dir")+"/src/main/java/"+proxyPackage.replaceAll("\\.","/");
String fileprefix = "MyProxyOf"+target.getClass().getSimpleName();
File dir = new File(filePath);
if(!dir.exists()){
dir.mkdirs();
}
File javaFile = new File(filePath + "/" +fileprefix +".java");
if(javaFile.exists()){
javaFile.delete();
}
FileWriter fw = new FileWriter(javaFile);
fw.write(java.toString());
fw.flush();
fw.close();
boolean result = compilerJavaFile(filePath + "/" + fileprefix +".java",System.getProperty("user.dir")+"/target/classes/");
if(result){
// 因为上一步编译到了当前工程的target中,在classpath里面,所以可以Class.forName
// TODO 如果是线上编译到一个类似/tmp目录,这里需要使用URLClassloader去LoadClass加载进来才行
Class tClass = Class.forName(proxyPackage+".MyProxyOf"+target.getClass().getSimpleName());
// 找到装饰模式的那个构造方法,传入装饰器包装的原始对象
return (T) tClass.getConstructor(target.getClass(),MyInvocationHandler.class).newInstance(target,handler);
}
return null;
}


/**
* 动态编译java文件到class字节码,需要把JDK/lib/tools.jar加入到环境变量中
* @param sourceFileInputPath
* @param classFileOutputPath
* @return
*/
public static boolean compilerJavaFile(String sourceFileInputPath, String classFileOutputPath) {
System.out.println("sourceFileInputPath="+sourceFileInputPath);
System.out.println("classFileOutputPath="+classFileOutputPath);
try{
// 设置编译选项,配置class文件输出路径
System.out.println("输出到:"+classFileOutputPath);
Iterable<String> options = Arrays.asList("-d", classFileOutputPath);
StandardJavaFileManager fileManager = javaCompiler.getStandardFileManager(null, null, null);
Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjectsFromFiles(Arrays.asList(new File(sourceFileInputPath)));
boolean flag = javaCompiler.getTask(null, fileManager, null, options, null, compilationUnits).call();
if(flag){
System.out.println("动态编译成功!");
}
return flag;
}catch (Exception ex){
ex.printStackTrace();
if(ex instanceof ClassNotFoundException){
System.out.println("动态编译失败!");
System.out.println("把JDK/lib/tools.jar加入到环境变量中");
}

return false;
}

}

public static void main(String[] args) throws Exception{
UserService instance = getProxy(new UserServiceImpl(), (proxy,method,target,params)->{ System.out.println("在lambda中的增强代理.....");return method.invoke(target,params); }, UserService.class);
System.out.println("-----------------开始运行");
instance.addUser(new User("wangwu",20));
}
}

生成效果:

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// TODO 这个类是由ProxyUtil自动生成的
public class MyProxyOfUserServiceImpl implements UserService {
private UserServiceImpl target;
private MyInvocationHandler handler;

public MyProxyOfUserServiceImpl(UserServiceImpl target, MyInvocationHandler h){
this.target = target;
this.handler = h;
}

@Override
public com.sam.bootdemo.model.User getUser(java.lang.String var0){
System.out.println("代理对象中即将调用invoke.....");
try{
Class[] paramClazz = new Class[]{java.lang.String.class};
return (com.sam.bootdemo.model.User)handler.invoke(this, target.getClass().getMethod("getUser", paramClazz), target, var0);
}catch(Exception ex){
ex.printStackTrace();
return null;
}
}

@Override
public void addUser(com.sam.bootdemo.model.User var0){
System.out.println("代理对象中即将调用invoke.....");
try{
Class[] paramClazz = new Class[]{com.sam.bootdemo.model.User.class};
handler.invoke(this, target.getClass().getMethod("addUser", paramClazz), target, var0);
}catch(Exception ex){
ex.printStackTrace();
}
}

@Override
public void initService(){
System.out.println("代理对象中即将调用invoke.....");
try{
handler.invoke(this, target.getClass().getMethod("initService"), target);
}catch(Exception ex){
ex.printStackTrace();
}
}
}

可以正常运行并输出:

1
2
3
4
5
6
7
8
动态编译成功!
-----------------开始运行
代理对象中即将调用invoke.....
在lambda中的增强代理.....
UserServiceImpl假装addUser:User(userName=wangwu, age=20)

Process finished with exit code 0

JDK 动态代理#

JDK动态代理使用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
// 实现一个增强器,来拦截我们包装target的方法,进行业务增强
public class UserServiceInvocationHandler implements InvocationHandler {
private Object target;

public UserServiceInvocationHandler(Object target){
this.target = target;
}

/**
* 代理方法的增强器
* 调用代理对象的业务对象的时候会来执行这个方法
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("-------UserServiceInvocationHandler.invoke代理增强");
// 此处进行拦截增强
return method.invoke(target, args); // 执行target的真正业务逻辑
}
}

测试JDK的动态代理:

1
2
3
4
5
6
7
8
9
System.out.println("===================JDKProxy InvocationHandler=========================");
// jdk动态代理
// 为啥要classloader?因为JVM启动的时候已经加载了project的所有class。
// 但是项目运行过程中动态生成了calss,所以要传入classloader去加载这个class。
// 为啥不是URLClassLoader去远程加载?因为JDK动态代理产生的项目是在classpath下的
// 传入classloader、接口、和要增强的InvocationHandler
UserService service3 = (UserService) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),
new Class[]{UserService.class}, new UserServiceInvocationHandler(new UserServiceImpl()));
service3.getUser("小马");

原理:

  • Class使用来描述一个类的,看起来是废话。但是很重要,仔细体会。

  • Class对象如 Class userClazz = Clazz.forname("com.xxx.User")就可以拿到User类的详细信息,包括属性、方法、构造方法等等。

  • 一个java文件--->编译成class文件--->解析成byte[]到JVM中--->构建为类对象Class----->newInstance变成实例

  • 判断两个对象是否相等,首先判断类加载器是否同一个,不是的话就不相等。这块动态代理判断了。传进去的接口列表使用用户的ClassLoader先加载一遍forName的结果,看看和传进来的是否相同。

  • 默认生成的代理类在com.sun.proxy这个包名下如com.sun.proxy.$Proxy0,除非有interface不是public的,会生成到这个interface同包名下(否则无法外部implements访问到)。

1
2
//java.lang.reflect.Proxy中获取到包名后生成class字节流的方法,使用了native方法生成
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);

结果

JDK上面那一步动态生成的类,我们反编译后看一眼:

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108

import com.sam.bootdemo.model.User;
import com.sam.bootdemo.service.UserService;
import java.io.FileNotFoundException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import java.sql.SQLException;

// 继承了JDK的Proxy,所以不能继承我们的目标对象,只能是实现接口
public class UserServiceProxy extends Proxy implements UserService {
private static Method m1;
private static Method m3;
private static Method m5;
private static Method m2;
private static Method m4;
private static Method m0;

// 反射获取了我们UserService接口中需要覆盖的方法,同时反射拿到要覆盖的hashCode、equals、toString方法
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m3 = Class.forName("com.sam.bootdemo.service.UserService").getMethod("initService");
m5 = Class.forName("com.sam.bootdemo.service.UserService").getMethod("addUser", Class.forName("com.sam.bootdemo.model.User"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m4 = Class.forName("com.sam.bootdemo.service.UserService").getMethod("getUser", Class.forName("java.lang.String"));
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}

// 构造方法传入了我们写的增强类InvocationHandler,塞到父类Proxy中了
// 这个handler里面有包装我们的原始userServiceImpl对象
public UserServiceProxy(InvocationHandler var1) throws {
super(var1);
}

// 因为实现了同样的UserService接口,这里代理实现
public final User getUser(String var1) throws {
try {
// 调用了invocationHandler的invoke方法,传入 代理对象、method、参数。(但是缺少真正的target,target在invocationhandler中,也就是我们要增强的userServiceImpl对象)
return (User)super.h.invoke(this, m4, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}

// 同上
public final void initService() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}

// 同上
public final void addUser(User var1) throws FileNotFoundException, SQLException {
try {
super.h.invoke(this, m5, new Object[]{var1});
} catch (RuntimeException | FileNotFoundException | SQLException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}

// toString、equals、hashCode也调用了invocationHandler的invoke方法。
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}

public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}

public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}

}

可以发现JDK生成代理类的逻辑和我们之前手动山寨版的很像。只是JDK是调用native方法直接生成字节流数组。我们是拼装java,再动态编译为class的。

虽然是native方法,但JDK也是通过反射生成的,他反射读取了我们接口中的方法列表,逐个实现,然后生成到class流里的。

缺点: 必须要有接口,才能生成动态代理。如果对象没有接口就无法进行代理。

JDK动态代理为什是接口不是继承?#

因为Java是单继承的,JDK底层源码已经继承了proxy对象, 里面存放了invocationHandler(invocationHandler里面包装了我们的目标对象)。所以不能再继承我们的目标对象。

只能去和目标对象实现相同的接口,包装一下,具有相同行为。

CGlib#

使用基于继承的方式,使用ASM进行字节码操作完成代理增强。都是直接操作字节码和JDK比起来性能差异不大。来看看Spring使用CGlib对类的代理:

这里的核心作用:

这个类使用CGlib来生成@Configuration类的子类,使得每一个@Bean修饰的方法都会被子类重写,除非容器来调用,否则在其他地方来调用这些@Bean方法的时候不会创建对象(会破坏单例性),而是返回一个容器内已有的Bean的引用。

CGlib的核心代码:

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
27
/*
* CHlib创建子类的核心逻辑
* Creates a new CGLIB {@link Enhancer} instance.
*/
private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) {
// 一个增强器,是CGlib核心接口ClassGenerator的实现
Enhancer enhancer = new Enhancer();
// 设置父类,就是当前@Configuration修饰的类
enhancer.setSuperclass(configSuperClass);
// 给代理类实现一个实际上是BeanFactoryAware接口,用来注入$$beanFactory
enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class});
// 是否实现Factory接口,一般false
enhancer.setUseFactory(false);
// 生成的beanName策略 xxxBySpringCGLIB
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
// 自定义CGlib生成策略,里面声明了一个public 的 $$beanFactory用来存放aware注入的bf
enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
// 设置一个方法调用的过滤器,塞进去了两个:
//new BeanMethodInterceptor():
//拦截@Bean方法,&beanName去BF里面拿一下,判断是否是FactoryBean,是的话对FB里面的getObject进行代理,非inCreation调用时返回引用。容器调用的时候inCreation,才真的getObject
// 非FactoryBean的代理了@Bean注解方法,非inCreation调用时返回引用。容器调用的时候inCreation,才真的new
//new BeanFactoryAwareMethodInterceptor(),
// 代理setBF方法,如果找到$$beanFactory,就在setBF方法里面给bf设置到$$beanFactory上
enhancer.setCallbackFilter(CALLBACK_FILTER);
enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
return enhancer;
}

完整部分参见下面:

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
package org.springframework.context.annotation;


/**
* 这个类使用CGlib来生成@Configuration类的子类,使得每一个@Bean修饰的方法都会被子类重写,除非容器来调用,否则在其他地方来调用这些@Bean方法的时候不会创建对象(会破坏单例性),而是返回一个容器内已有的Bean的引用。
* Enhances {@link Configuration} classes by generating a CGLIB subclass which
* interacts with the Spring container to respect bean scoping semantics for
* {@code @Bean} methods. Each such {@code @Bean} method will be overridden in
* the generated subclass, only delegating to the actual {@code @Bean} method
* implementation if the container actually requests the construction of a new
* instance. Otherwise, a call to such an {@code @Bean} method serves as a
* reference back to the container, obtaining the corresponding bean by name.
*
* @author Chris Beams
* @author Juergen Hoeller
* @since 3.0
* @see #enhance
* @see ConfigurationClassPostProcessor
*/
class ConfigurationClassEnhancer {

// The callbacks to use. Note that these callbacks must be stateless.
private static final Callback[] CALLBACKS = new Callback[] {
new BeanMethodInterceptor(),
new BeanFactoryAwareMethodInterceptor(),
NoOp.INSTANCE
};

private static final ConditionalCallbackFilter CALLBACK_FILTER = new ConditionalCallbackFilter(CALLBACKS);

// 子类里面增强一个BF的属性
private static final String BEAN_FACTORY_FIELD = "$$beanFactory";


private static final Log logger = LogFactory.getLog(ConfigurationClassEnhancer.class);

private static final SpringObjenesis objenesis = new SpringObjenesis();


/**
外面ConfigurationClassPostProcessor调用的入口
* Loads the specified class and generates a CGLIB subclass of it equipped with
* container-aware callbacks capable of respecting scoping and other bean semantics.
* @return the enhanced subclass
*/
public Class<?> enhance(Class<?> configClass, @Nullable ClassLoader classLoader) {
if (EnhancedConfiguration.class.isAssignableFrom(configClass)) { // 这里判断是否EnhancedConfiguration接口。这是一个空接口,是BFAware的子接口,
if (logger.isDebugEnabled()) {
logger.debug(String.format("Ignoring request to enhance %s as it has " +
"already been enhanced. This usually indicates that more than one " +
"ConfigurationClassPostProcessor has been registered (e.g. via " +
"<context:annotation-config>). This is harmless, but you may " +
"want check your configuration and remove one CCPP if possible",
configClass.getName()));
}
return configClass;
}
// 否则,真正开始进行增强
Class<?> enhancedClass = createClass(newEnhancer(configClass, classLoader));
if (logger.isTraceEnabled()) {
logger.trace(String.format("Successfully enhanced %s; enhanced class name is: %s",
configClass.getName(), enhancedClass.getName()));
}
return enhancedClass;
}

/*
* CHlib创建子类的核心逻辑
* Creates a new CGLIB {@link Enhancer} instance.
*/
private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) {
// 一个增强器,是CGlib核心接口ClassGenerator的实现
Enhancer enhancer = new Enhancer();
// 设置父类,就是当前@Configuration修饰的类
enhancer.setSuperclass(configSuperClass);
// 给代理类实现一个实际上是BeanFactoryAware接口,用来注入$$beanFactory
enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class});
// 是否实现Factory接口,一般false
enhancer.setUseFactory(false);
// 生成的beanName策略 xxxBySpringCGLIB
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
// 自定义CGlib生成策略,里面声明了一个public 的 $$beanFactory用来存放aware注入的bf
enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
// 设置一个方法调用的过滤器,塞进去了两个:
//new BeanMethodInterceptor():
//拦截@Bean方法,&beanName去BF里面拿一下,判断是否是FactoryBean,是的话对FB里面的getObject进行代理,非inCreation调用时返回引用。容器调用的时候inCreation,才真的getObject
// 非FactoryBean的代理了@Bean注解方法,非inCreation调用时返回引用。容器调用的时候inCreation,才真的new
//new BeanFactoryAwareMethodInterceptor(),
// 代理setBF方法,如果找到$$beanFactory,就在setBF方法里面给bf设置到$$beanFactory上
enhancer.setCallbackFilter(CALLBACK_FILTER);
enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
return enhancer;
}

/**
* Uses enhancer to generate a subclass of superclass,
* ensuring that callbacks are registered for the new subclass.
*/
private Class<?> createClass(Enhancer enhancer) {
Class<?> subclass = enhancer.createClass();
// Registering callbacks statically (as opposed to thread-local)
// is critical for usage in an OSGi environment (SPR-5932)...
Enhancer.registerStaticCallbacks(subclass, CALLBACKS);
return subclass;
}


/**
空接口,只是一个BFAware的子接口,用来标记当前类可以注入BF
* Marker interface to be implemented by all @Configuration CGLIB subclasses.
* Facilitates idempotent behavior for {@link ConfigurationClassEnhancer#enhance}
* through checking to see if candidate classes are already assignable to it, e.g.
* have already been enhanced.
* <p>Also extends {@link BeanFactoryAware}, as all enhanced {@code @Configuration}
* classes require access to the {@link BeanFactory} that created them.
* <p>Note that this interface is intended for framework-internal use only, however
* must remain public in order to allow access to subclasses generated from other
* packages (i.e. user code).
*/
public interface EnhancedConfiguration extends BeanFactoryAware {
}


/**
* Conditional {@link Callback}.
* @see ConditionalCallbackFilter
*/
private interface ConditionalCallback extends Callback {

boolean isMatch(Method candidateMethod);
}


/**
* A {@link CallbackFilter} that works by interrogating {@link Callback Callbacks} in the order
* that they are defined via {@link ConditionalCallback}.
*/
private static class ConditionalCallbackFilter implements CallbackFilter {

private final Callback[] callbacks;

private final Class<?>[] callbackTypes;

public ConditionalCallbackFilter(Callback[] callbacks) {
this.callbacks = callbacks;
this.callbackTypes = new Class<?>[callbacks.length];
for (int i = 0; i < callbacks.length; i++) {
this.callbackTypes[i] = callbacks[i].getClass();
}
}

@Override
public int accept(Method method) {
for (int i = 0; i < this.callbacks.length; i++) {
Callback callback = this.callbacks[i];
if (!(callback instanceof ConditionalCallback) || ((ConditionalCallback) callback).isMatch(method)) {
return i;
}
}
throw new IllegalStateException("No callback available for method " + method.getName());
}

public Class<?>[] getCallbackTypes() {
return this.callbackTypes;
}
}


/**
* Custom extension of CGLIB's DefaultGeneratorStrategy, introducing a {@link BeanFactory} field.
* Also exposes the application ClassLoader as thread context ClassLoader for the time of
* class generation (in order for ASM to pick it up when doing common superclass resolution).
*/
private static class BeanFactoryAwareGeneratorStrategy extends
ClassLoaderAwareGeneratorStrategy {

public BeanFactoryAwareGeneratorStrategy(@Nullable ClassLoader classLoader) {
super(classLoader);
}

@Override
protected ClassGenerator transform(ClassGenerator cg) throws Exception {
ClassEmitterTransformer transformer = new ClassEmitterTransformer() {
@Override
public void end_class() {
// 给代理类塞进去一个public的$$beanFactory属性,用来BeanFactoryAware接口的setBF的时候拦截注入BF
declare_field(Constants.ACC_PUBLIC, BEAN_FACTORY_FIELD, Type.getType(BeanFactory.class), null);
super.end_class();
}
};
return new TransformingClassGenerator(cg, transformer);
}

}


/**
* Intercepts the invocation of any {@link BeanFactoryAware#setBeanFactory(BeanFactory)} on
* {@code @Configuration} class instances for the purpose of recording the {@link BeanFactory}.
* @see EnhancedConfiguration
*/
private static class BeanFactoryAwareMethodInterceptor implements MethodInterceptor, ConditionalCallback {

@Override
@Nullable
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
Field field = ReflectionUtils.findField(obj.getClass(), BEAN_FACTORY_FIELD);
Assert.state(field != null, "Unable to find generated BeanFactory field");
field.set(obj, args[0]);

// Does the actual (non-CGLIB) superclass implement BeanFactoryAware?
// If so, call its setBeanFactory() method. If not, just exit.
if (BeanFactoryAware.class.isAssignableFrom(ClassUtils.getUserClass(obj.getClass().getSuperclass()))) {
return proxy.invokeSuper(obj, args);
}
return null;
}

@Override
public boolean isMatch(Method candidateMethod) {
// 匹配是否是setBeanFactory方法
return isSetBeanFactory(candidateMethod);
}

public static boolean isSetBeanFactory(Method candidateMethod) {
return (candidateMethod.getName().equals("setBeanFactory") &&
candidateMethod.getParameterCount() == 1 &&
BeanFactory.class == candidateMethod.getParameterTypes()[0] &&
BeanFactoryAware.class.isAssignableFrom(candidateMethod.getDeclaringClass()));
}
}


/**
* Intercepts the invocation of any {@link Bean}-annotated methods in order to ensure proper
* handling of bean semantics such as scoping and AOP proxying.
* @see Bean
* @see ConfigurationClassEnhancer
*/
private static class BeanMethodInterceptor implements MethodInterceptor, ConditionalCallback {

/**
* Enhance a {@link Bean @Bean} method to check the supplied BeanFactory for the
* existence of this bean object.
* @throws Throwable as a catch-all for any exception that may be thrown when invoking the
* super implementation of the proxied method i.e., the actual {@code @Bean} method
*/
@Override
@Nullable
public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
MethodProxy cglibMethodProxy) throws Throwable {

ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);
String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod);

// Determine whether this bean is a scoped-proxy
if (BeanAnnotationHelper.isScopedProxy(beanMethod)) {
String scopedBeanName = ScopedProxyCreator.getTargetBeanName(beanName);
if (beanFactory.isCurrentlyInCreation(scopedBeanName)) {
beanName = scopedBeanName;
}
}

// To handle the case of an inter-bean method reference, we must explicitly check the
// container for already cached instances.

// 首先检查是不是FactoryBean,是的话就创建一个子类并拦截getObject()方法,调用缓存的对象。
// First, check to see if the requested bean is a FactoryBean. If so, create a subclass
// proxy that intercepts calls to getObject() and returns any cached bean instance.
// This ensures that the semantics of calling a FactoryBean from within @Bean methods
// is the same as that of referring to a FactoryBean within XML. See SPR-6602.
// 如果是一个FactoryBean,而且&beanName可以拿到,就去处理FactoryBean
if (factoryContainsBean(beanFactory, BeanFactory.FACTORY_BEAN_PREFIX + beanName) &&
factoryContainsBean(beanFactory, beanName)) {
Object factoryBean = beanFactory.getBean(BeanFactory.FACTORY_BEAN_PREFIX + beanName);
if (factoryBean instanceof ScopedProxyFactoryBean) {
// Scoped proxy factory beans are a special case and should not be further proxied
}
else {
// It is a candidate FactoryBean - go ahead with enhancement
return enhanceFactoryBean(factoryBean, beanMethod.getReturnType(), beanFactory, beanName);
}
}

// BF在调用的时候,需要真的去创建
if (isCurrentlyInvokedFactoryMethod(beanMethod)) {
// The factory is calling the bean method in order to instantiate and register the bean
// (i.e. via a getBean() call) -> invoke the super implementation of the method to actually
// create the bean instance.
if (logger.isInfoEnabled() &&
BeanFactoryPostProcessor.class.isAssignableFrom(beanMethod.getReturnType())) {
logger.info(String.format("@Bean method %s.%s is non-static and returns an object " +
"assignable to Spring's BeanFactoryPostProcessor interface. This will " +
"result in a failure to process annotations such as @Autowired, " +
"@Resource and @PostConstruct within the method's declaring " +
"@Configuration class. Add the 'static' modifier to this method to avoid " +
"these container lifecycle issues; see @Bean javadoc for complete details.",
beanMethod.getDeclaringClass().getSimpleName(), beanMethod.getName()));
}
return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
}

return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
}

// 拿到bean的引用
private Object resolveBeanReference(Method beanMethod, Object[] beanMethodArgs,
ConfigurableBeanFactory beanFactory, String beanName) {

// The user (i.e. not the factory) is requesting this bean through a call to
// the bean method, direct or indirect. The bean may have already been marked
// as 'in creation' in certain autowiring scenarios; if so, temporarily set
// the in-creation status to false in order to avoid an exception.
boolean alreadyInCreation = beanFactory.isCurrentlyInCreation(beanName);
try {
if (alreadyInCreation) {
beanFactory.setCurrentlyInCreation(beanName, false);
}
boolean useArgs = !ObjectUtils.isEmpty(beanMethodArgs);
if (useArgs && beanFactory.isSingleton(beanName)) {
// Stubbed null arguments just for reference purposes,
// expecting them to be autowired for regular singleton references?
// A safe assumption since @Bean singleton arguments cannot be optional...
for (Object arg : beanMethodArgs) {
if (arg == null) {
useArgs = false;
break;
}
}
}
Object beanInstance = (useArgs ? beanFactory.getBean(beanName, beanMethodArgs) :
beanFactory.getBean(beanName));
if (!ClassUtils.isAssignableValue(beanMethod.getReturnType(), beanInstance)) {
// Detect package-protected NullBean instance through equals(null) check
if (beanInstance.equals(null)) {
if (logger.isDebugEnabled()) {
logger.debug(String.format("@Bean method %s.%s called as bean reference " +
"for type [%s] returned null bean; resolving to null value.",
beanMethod.getDeclaringClass().getSimpleName(), beanMethod.getName(),
beanMethod.getReturnType().getName()));
}
beanInstance = null;
}
else {
String msg = String.format("@Bean method %s.%s called as bean reference " +
"for type [%s] but overridden by non-compatible bean instance of type [%s].",
beanMethod.getDeclaringClass().getSimpleName(), beanMethod.getName(),
beanMethod.getReturnType().getName(), beanInstance.getClass().getName());
try {
BeanDefinition beanDefinition = beanFactory.getMergedBeanDefinition(beanName);
msg += " Overriding bean of same name declared in: " + beanDefinition.getResourceDescription();
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore - simply no detailed message then.
}
throw new IllegalStateException(msg);
}
}
Method currentlyInvoked = SimpleInstantiationStrategy.getCurrentlyInvokedFactoryMethod();
if (currentlyInvoked != null) {
String outerBeanName = BeanAnnotationHelper.determineBeanNameFor(currentlyInvoked);
beanFactory.registerDependentBean(beanName, outerBeanName);
}
return beanInstance;
}
finally {
if (alreadyInCreation) {
beanFactory.setCurrentlyInCreation(beanName, true);
}
}
}

@Override
public boolean isMatch(Method candidateMethod) {
return (candidateMethod.getDeclaringClass() != Object.class &&
!BeanFactoryAwareMethodInterceptor.isSetBeanFactory(candidateMethod) &&
BeanAnnotationHelper.isBeanAnnotated(candidateMethod));
}

private ConfigurableBeanFactory getBeanFactory(Object enhancedConfigInstance) {
Field field = ReflectionUtils.findField(enhancedConfigInstance.getClass(), BEAN_FACTORY_FIELD);
Assert.state(field != null, "Unable to find generated bean factory field");
Object beanFactory = ReflectionUtils.getField(field, enhancedConfigInstance);
Assert.state(beanFactory != null, "BeanFactory has not been injected into @Configuration class");
Assert.state(beanFactory instanceof ConfigurableBeanFactory,
"Injected BeanFactory is not a ConfigurableBeanFactory");
return (ConfigurableBeanFactory) beanFactory;
}

/**
* Check the BeanFactory to see whether the bean named <var>beanName</var> already
* exists. Accounts for the fact that the requested bean may be "in creation", i.e.:
* we're in the middle of servicing the initial request for this bean. From an enhanced
* factory method's perspective, this means that the bean does not actually yet exist,
* and that it is now our job to create it for the first time by executing the logic
* in the corresponding factory method.
* <p>Said another way, this check repurposes
* {@link ConfigurableBeanFactory#isCurrentlyInCreation(String)} to determine whether
* the container is calling this method or the user is calling this method.
* @param beanName name of bean to check for
* @return whether <var>beanName</var> already exists in the factory
*/
private boolean factoryContainsBean(ConfigurableBeanFactory beanFactory, String beanName) {
return (beanFactory.containsBean(beanName) && !beanFactory.isCurrentlyInCreation(beanName));
}

/**
* Check whether the given method corresponds to the container's currently invoked
* factory method. Compares method name and parameter types only in order to work
* around a potential problem with covariant return types (currently only known
* to happen on Groovy classes).
*/
private boolean isCurrentlyInvokedFactoryMethod(Method method) {
Method currentlyInvoked = SimpleInstantiationStrategy.getCurrentlyInvokedFactoryMethod();
return (currentlyInvoked != null && method.getName().equals(currentlyInvoked.getName()) &&
Arrays.equals(method.getParameterTypes(), currentlyInvoked.getParameterTypes()));
}

/**
* Create a subclass proxy that intercepts calls to getObject(), delegating to the current BeanFactory
* instead of creating a new instance. These proxies are created only when calling a FactoryBean from
* within a Bean method, allowing for proper scoping semantics even when working against the FactoryBean
* instance directly. If a FactoryBean instance is fetched through the container via &-dereferencing,
* it will not be proxied. This too is aligned with the way XML configuration works.
*/
private Object enhanceFactoryBean(final Object factoryBean, Class<?> exposedType,
final ConfigurableBeanFactory beanFactory, final String beanName) {

try {
Class<?> clazz = factoryBean.getClass();
boolean finalClass = Modifier.isFinal(clazz.getModifiers());
boolean finalMethod = Modifier.isFinal(clazz.getMethod("getObject").getModifiers());
if (finalClass || finalMethod) {
if (exposedType.isInterface()) {
if (logger.isTraceEnabled()) {
logger.trace("Creating interface proxy for FactoryBean '" + beanName + "' of type [" +
clazz.getName() + "] for use within another @Bean method because its " +
(finalClass ? "implementation class" : "getObject() method") +
" is final: Otherwise a getObject() call would not be routed to the factory.");
}
return createInterfaceProxyForFactoryBean(factoryBean, exposedType, beanFactory, beanName);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Unable to proxy FactoryBean '" + beanName + "' of type [" +
clazz.getName() + "] for use within another @Bean method because its " +
(finalClass ? "implementation class" : "getObject() method") +
" is final: A getObject() call will NOT be routed to the factory. " +
"Consider declaring the return type as a FactoryBean interface.");
}
return factoryBean;
}
}
}
catch (NoSuchMethodException ex) {
// No getObject() method -> shouldn't happen, but as long as nobody is trying to call it...
}

return createCglibProxyForFactoryBean(factoryBean, beanFactory, beanName);
}

private Object createInterfaceProxyForFactoryBean(final Object factoryBean, Class<?> interfaceType,
final ConfigurableBeanFactory beanFactory, final String beanName) {

return Proxy.newProxyInstance(
factoryBean.getClass().getClassLoader(), new Class<?>[] {interfaceType},
(proxy, method, args) -> {
if (method.getName().equals("getObject") && args == null) {
return beanFactory.getBean(beanName);
}
return ReflectionUtils.invokeMethod(method, factoryBean, args);
});
}

private Object createCglibProxyForFactoryBean(final Object factoryBean,
final ConfigurableBeanFactory beanFactory, final String beanName) {

Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(factoryBean.getClass());
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
enhancer.setCallbackType(MethodInterceptor.class);

// Ideally create enhanced FactoryBean proxy without constructor side effects,
// analogous to AOP proxy creation in ObjenesisCglibAopProxy...
Class<?> fbClass = enhancer.createClass();
Object fbProxy = null;

if (objenesis.isWorthTrying()) {
try {
fbProxy = objenesis.newInstance(fbClass, enhancer.getUseCache());
}
catch (ObjenesisException ex) {
logger.debug("Unable to instantiate enhanced FactoryBean using Objenesis, " +
"falling back to regular construction", ex);
}
}

if (fbProxy == null) {
try {
fbProxy = ReflectionUtils.accessibleConstructor(fbClass).newInstance();
}
catch (Throwable ex) {
throw new IllegalStateException("Unable to instantiate enhanced FactoryBean using Objenesis, " +
"and regular FactoryBean instantiation via default constructor fails as well", ex);
}
}

((Factory) fbProxy).setCallback(0, (MethodInterceptor) (obj, method, args, proxy) -> {
if (method.getName().equals("getObject") && args.length == 0) {
return beanFactory.getBean(beanName);
}
return proxy.invoke(factoryBean, args);
});

return fbProxy;
}
}
}

Javassist#

也可以实现字节码增强的代理,使用不太多。