Skip to content

Commit caac0b5

Browse files
wanghbxxxxbeiwei30
authored andcommitted
refactor javassist compiler: extract class CtClassBuilder (apache#3424)
* refactor JavassistCompiler * rename variable names * reformat code * refactor: prepend modifier of constructor, field and method outside the JavassistClassInfo * add null for ClassUtils.getSimpleClassName * rename JavassistClassInfo to CtClassBuilder
1 parent 7a53a1b commit caac0b5

File tree

4 files changed

+222
-68
lines changed

4 files changed

+222
-68
lines changed

dubbo-common/src/main/java/org/apache/dubbo/common/compiler/support/ClassUtils.java

+12
Original file line numberDiff line numberDiff line change
@@ -431,5 +431,17 @@ public static <K, V> Map<K, V> toMap(Map.Entry<K, V>[] entries) {
431431
}
432432
return map;
433433
}
434+
435+
/**
436+
* get simple class name from qualified class name
437+
*/
438+
public static String getSimpleClassName(String qualifiedName) {
439+
if (null == qualifiedName) {
440+
return null;
441+
}
442+
443+
int i = qualifiedName.lastIndexOf('.');
444+
return i < 0 ? qualifiedName : qualifiedName.substring(i + 1);
445+
}
434446

435447
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.dubbo.common.compiler.support;
18+
19+
import java.util.ArrayList;
20+
import java.util.HashMap;
21+
import java.util.List;
22+
import java.util.Map;
23+
24+
import javassist.CannotCompileException;
25+
import javassist.ClassPool;
26+
import javassist.CtClass;
27+
import javassist.CtField;
28+
import javassist.CtNewConstructor;
29+
import javassist.CtNewMethod;
30+
import javassist.LoaderClassPath;
31+
import javassist.NotFoundException;
32+
33+
/**
34+
* CtClassBuilder is builder for CtClass
35+
* <p>
36+
* contains all the information, including:
37+
* <p>
38+
* class name, imported packages, super class name, implemented interfaces, constructors, fields, methods.
39+
*/
40+
public class CtClassBuilder {
41+
42+
private String className;
43+
44+
private String superClassName = "java.lang.Object";
45+
46+
private List<String> imports = new ArrayList<>();
47+
48+
private Map<String, String> fullNames = new HashMap<>();
49+
50+
private List<String> ifaces = new ArrayList<>();
51+
52+
private List<String> constructors = new ArrayList<>();
53+
54+
private List<String> fields = new ArrayList<>();
55+
56+
private List<String> methods = new ArrayList<>();
57+
58+
public String getClassName() {
59+
return className;
60+
}
61+
62+
public void setClassName(String className) {
63+
this.className = className;
64+
}
65+
66+
public String getSuperClassName() {
67+
return superClassName;
68+
}
69+
70+
public void setSuperClassName(String superClassName) {
71+
this.superClassName = getQualifiedClassName(superClassName);
72+
}
73+
74+
public List<String> getImports() {
75+
return imports;
76+
}
77+
78+
public void addImports(String pkg) {
79+
int pi = pkg.lastIndexOf('.');
80+
if (pi > 0) {
81+
String pkgName = pkg.substring(0, pi);
82+
this.imports.add(pkgName);
83+
if (!pkg.endsWith(".*")) {
84+
fullNames.put(pkg.substring(pi + 1), pkg);
85+
}
86+
}
87+
}
88+
89+
public List<String> getInterfaces() {
90+
return ifaces;
91+
}
92+
93+
public void addInterface(String iface) {
94+
this.ifaces.add(getQualifiedClassName(iface));
95+
}
96+
97+
public List<String> getConstructors() {
98+
return constructors;
99+
}
100+
101+
public void addConstructor(String constructor) {
102+
this.constructors.add(constructor);
103+
}
104+
105+
public List<String> getFields() {
106+
return fields;
107+
}
108+
109+
public void addField(String field) {
110+
this.fields.add(field);
111+
}
112+
113+
public List<String> getMethods() {
114+
return methods;
115+
}
116+
117+
public void addMethod(String method) {
118+
this.methods.add(method);
119+
}
120+
121+
/**
122+
* get full qualified class name
123+
*
124+
* @param className super class name, maybe qualified or not
125+
*/
126+
protected String getQualifiedClassName(String className) {
127+
if (className.contains(".")) {
128+
return className;
129+
}
130+
131+
if (fullNames.containsKey(className)) {
132+
return fullNames.get(className);
133+
}
134+
135+
return ClassUtils.forName(imports.toArray(new String[0]), className).getName();
136+
}
137+
138+
/**
139+
* build CtClass object
140+
*/
141+
public CtClass build(ClassLoader classLoader) throws NotFoundException, CannotCompileException {
142+
ClassPool pool = new ClassPool(true);
143+
pool.appendClassPath(new LoaderClassPath(classLoader));
144+
145+
// create class
146+
CtClass ctClass = pool.makeClass(className, pool.get(superClassName));
147+
148+
// add imported packages
149+
imports.stream().forEach(pool::importPackage);
150+
151+
// add implemented interfaces
152+
for (String iface : ifaces) {
153+
ctClass.addInterface(pool.get(iface));
154+
}
155+
156+
// add constructors
157+
for (String constructor : constructors) {
158+
ctClass.addConstructor(CtNewConstructor.make(constructor, ctClass));
159+
}
160+
161+
// add fields
162+
for (String field : fields) {
163+
ctClass.addField(CtField.make(field, ctClass));
164+
}
165+
166+
// add methods
167+
for (String method : methods) {
168+
ctClass.addMethod(CtNewMethod.make(method, ctClass));
169+
}
170+
171+
return ctClass;
172+
}
173+
174+
}

dubbo-common/src/main/java/org/apache/dubbo/common/compiler/support/JavassistCompiler.java

+29-68
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,9 @@
1818

1919
import org.apache.dubbo.common.utils.ClassHelper;
2020

21-
import javassist.ClassPool;
2221
import javassist.CtClass;
23-
import javassist.CtField;
24-
import javassist.CtNewConstructor;
25-
import javassist.CtNewMethod;
26-
import javassist.LoaderClassPath;
2722

28-
import java.util.ArrayList;
29-
import java.util.HashMap;
30-
import java.util.List;
31-
import java.util.Map;
23+
import java.util.Arrays;
3224
import java.util.regex.Matcher;
3325
import java.util.regex.Pattern;
3426

@@ -49,77 +41,46 @@ public class JavassistCompiler extends AbstractCompiler {
4941

5042
@Override
5143
public Class<?> doCompile(String name, String source) throws Throwable {
52-
int i = name.lastIndexOf('.');
53-
String className = i < 0 ? name : name.substring(i + 1);
54-
ClassPool pool = new ClassPool(true);
55-
pool.appendClassPath(new LoaderClassPath(ClassHelper.getCallerClassLoader(getClass())));
44+
CtClassBuilder builder = new CtClassBuilder();
45+
builder.setClassName(name);
46+
47+
// process imported classes
5648
Matcher matcher = IMPORT_PATTERN.matcher(source);
57-
List<String> importPackages = new ArrayList<String>();
58-
Map<String, String> fullNames = new HashMap<String, String>();
5949
while (matcher.find()) {
60-
String pkg = matcher.group(1);
61-
if (pkg.endsWith(".*")) {
62-
String pkgName = pkg.substring(0, pkg.length() - 2);
63-
pool.importPackage(pkgName);
64-
importPackages.add(pkgName);
65-
} else {
66-
int pi = pkg.lastIndexOf('.');
67-
if (pi > 0) {
68-
String pkgName = pkg.substring(0, pi);
69-
pool.importPackage(pkgName);
70-
importPackages.add(pkgName);
71-
fullNames.put(pkg.substring(pi + 1), pkg);
72-
}
73-
}
50+
builder.addImports(matcher.group(1).trim());
7451
}
75-
String[] packages = importPackages.toArray(new String[0]);
52+
53+
// process extended super class
7654
matcher = EXTENDS_PATTERN.matcher(source);
77-
CtClass cls;
7855
if (matcher.find()) {
79-
String extend = matcher.group(1).trim();
80-
String extendClass;
81-
if (extend.contains(".")) {
82-
extendClass = extend;
83-
} else if (fullNames.containsKey(extend)) {
84-
extendClass = fullNames.get(extend);
85-
} else {
86-
extendClass = ClassUtils.forName(packages, extend).getName();
87-
}
88-
cls = pool.makeClass(name, pool.get(extendClass));
89-
} else {
90-
cls = pool.makeClass(name);
56+
builder.setSuperClassName(matcher.group(1).trim());
9157
}
58+
59+
// process implemented interfaces
9260
matcher = IMPLEMENTS_PATTERN.matcher(source);
9361
if (matcher.find()) {
9462
String[] ifaces = matcher.group(1).trim().split("\\,");
95-
for (String iface : ifaces) {
96-
iface = iface.trim();
97-
String ifaceClass;
98-
if (iface.contains(".")) {
99-
ifaceClass = iface;
100-
} else if (fullNames.containsKey(iface)) {
101-
ifaceClass = fullNames.get(iface);
102-
} else {
103-
ifaceClass = ClassUtils.forName(packages, iface).getName();
104-
}
105-
cls.addInterface(pool.get(ifaceClass));
106-
}
63+
Arrays.stream(ifaces).forEach(i -> builder.addInterface(i.trim()));
10764
}
108-
String body = source.substring(source.indexOf("{") + 1, source.length() - 1);
65+
66+
// process constructors, fields, methods
67+
String body = source.substring(source.indexOf('{') + 1, source.length() - 1);
10968
String[] methods = METHODS_PATTERN.split(body);
110-
for (String method : methods) {
111-
method = method.trim();
112-
if (method.length() > 0) {
113-
if (method.startsWith(className)) {
114-
cls.addConstructor(CtNewConstructor.make("public " + method, cls));
115-
} else if (FIELD_PATTERN.matcher(method).matches()) {
116-
cls.addField(CtField.make("private " + method, cls));
117-
} else {
118-
cls.addMethod(CtNewMethod.make("public " + method, cls));
119-
}
69+
String className = ClassUtils.getSimpleClassName(name);
70+
Arrays.stream(methods).map(String::trim).filter(m -> !m.isEmpty()).forEach(method-> {
71+
if (method.startsWith(className)) {
72+
builder.addConstructor("public " + method);
73+
} else if (FIELD_PATTERN.matcher(method).matches()) {
74+
builder.addField("private " + method);
75+
} else {
76+
builder.addMethod("public " + method);
12077
}
121-
}
122-
return cls.toClass(ClassHelper.getCallerClassLoader(getClass()), JavassistCompiler.class.getProtectionDomain());
78+
});
79+
80+
// compile
81+
ClassLoader classLoader = ClassHelper.getCallerClassLoader(getClass());
82+
CtClass cls = builder.build(classLoader);
83+
return cls.toClass(classLoader, JavassistCompiler.class.getProtectionDomain());
12384
}
12485

12586
}

dubbo-common/src/test/java/org/apache/dubbo/common/compiler/support/ClassUtilsTest.java

+7
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,13 @@ public void testGetGenericClass() {
146146
public void testGetSizeMethod() {
147147
Assertions.assertEquals("getLength()", ClassUtils.getSizeMethod(GenericClass3.class));
148148
}
149+
150+
@Test
151+
public void testGetSimpleClassName() {
152+
Assertions.assertNull(ClassUtils.getSimpleClassName(null));
153+
Assertions.assertEquals("Map", ClassUtils.getSimpleClassName(Map.class.getName()));
154+
Assertions.assertEquals("Map", ClassUtils.getSimpleClassName(Map.class.getSimpleName()));
155+
}
149156

150157
private interface GenericInterface<T> {
151158
}

0 commit comments

Comments
 (0)