Skip to content

Commit eacf162

Browse files
committed
feat: use custom classloader instead of plugin classloader
1 parent cdb0a6b commit eacf162

File tree

8 files changed

+206
-136
lines changed

8 files changed

+206
-136
lines changed
Lines changed: 39 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,19 @@
11
package io.github.linyimin.plugin.compile;
22

33
import com.intellij.compiler.impl.ProjectCompileScope;
4-
import com.intellij.ide.plugins.cl.PluginClassLoader;
54
import com.intellij.openapi.compiler.CompilerManager;
65
import com.intellij.openapi.project.Project;
76
import com.intellij.openapi.roots.OrderEnumerator;
87
import com.intellij.openapi.roots.ProjectRootManager;
98
import com.intellij.openapi.ui.Messages;
109
import com.intellij.openapi.util.io.FileUtil;
1110
import com.intellij.openapi.vfs.VirtualFile;
12-
import com.intellij.util.lang.UrlClassLoader;
13-
import io.github.linyimin.plugin.dom.Constant;
1411
import io.github.linyimin.plugin.utils.JavaUtils;
1512
import org.apache.commons.collections.CollectionUtils;
13+
import org.apache.commons.lang3.StringUtils;
1614

1715
import java.io.File;
18-
import java.lang.reflect.Field;
16+
import java.io.IOException;
1917
import java.net.MalformedURLException;
2018
import java.net.URL;
2119
import java.util.*;
@@ -31,7 +29,7 @@ public class MybatisPojoCompile {
3129

3230
// TODO: 需要优化成只编译Mybatis需要的类
3331

34-
public static UrlClassLoader classLoader;
32+
public static ProjectLoader classLoader;
3533
public static List<String> preDependencies;
3634

3735
public static void compile(Project project) {
@@ -47,15 +45,7 @@ public static void setClassLoader(Project project) {
4745

4846
List<String> dependencies = getProjectDependencies(project);
4947

50-
List<URL> urls = new ArrayList<>();
51-
for (String path : dependencies) {
52-
try {
53-
urls.add(new File(FileUtil.toSystemIndependentName(path)).toURI().toURL());
54-
}
55-
catch (MalformedURLException e) {
56-
Messages.showErrorDialog(e.getMessage(), "MalformedURLException");
57-
}
58-
}
48+
List<URL> urls = pathToURL(dependencies);
5949

6050
if (Objects.nonNull(classLoader)) {
6151
changeLoaderUrls(preDependencies, dependencies);
@@ -65,6 +55,30 @@ public static void setClassLoader(Project project) {
6555
preDependencies = dependencies;
6656
}
6757

58+
private static List<URL> pathToURL(List<String> paths) {
59+
List<URL> urls = new ArrayList<>();
60+
61+
try {
62+
for (String path : paths) {
63+
urls.add(new File(FileUtil.toSystemIndependentName(path)).toURI().toURL());
64+
}
65+
66+
// 添加本地获取消息类
67+
URL url = MybatisPojoCompile.class.getClassLoader().getResource("io/github/linyimin/plugin/utils/MybatisSqlUtils.class");
68+
if (Objects.nonNull(url)) {
69+
String fileName = MybatisPojoCompile.class.getClassLoader().getResource("io/github/linyimin/plugin/utils/MybatisSqlUtils.class").getFile();
70+
71+
fileName = StringUtils.substring(fileName, 0, fileName.indexOf("jar!") + 3);
72+
73+
urls.add(new URL(fileName));
74+
}
75+
76+
} catch (MalformedURLException e) {
77+
Messages.showErrorDialog(e.getMessage(), "MalformedURLException");
78+
}
79+
80+
return urls;
81+
}
6882
private static List<String> getProjectDependencies(Project project) {
6983

7084
Set<String> mapperDependencies = JavaUtils.getAllDependenciesRecursive(project);
@@ -75,13 +89,7 @@ private static List<String> getProjectDependencies(Project project) {
7589
.runtimeOnly()
7690
.withoutSdk()
7791
.getPathsList()
78-
.getPathList()
79-
.stream()
80-
.filter(path -> path.contains(Constant.MYBATIS_LOGGING_LOG4J)
81-
|| path.contains(Constant.MYBATIS_LOGGING_SLF4J)
82-
|| isProjectModule(project, path))
83-
.collect(Collectors.toList());
84-
92+
.getPathList();
8593

8694
list.addAll(mapperDependencies);
8795

@@ -99,49 +107,10 @@ private static boolean isProjectModule(Project project, String path) {
99107
}
100108

101109
private static void createProjectLoader(List<URL> urls) {
102-
classLoader = UrlClassLoader.build().urls(urls).parent(ClassLoader.getSystemClassLoader()).get();
103-
attachPluginParentLoader(classLoader);
104-
}
105110

106-
private static void attachPluginParentLoader(UrlClassLoader classLoader) {
107-
PluginClassLoader pluginClassLoader = (PluginClassLoader) MybatisPojoCompile.class.getClassLoader();
108-
109-
Class<? extends PluginClassLoader> pluginClassLoaderClass = pluginClassLoader.getClass();
110-
111-
Field field = null;
112-
113-
try {
114-
field = pluginClassLoaderClass.getDeclaredField(Constant.PLUGIN_CLASS_LOADER_PARENTS);
115-
} catch (NoSuchFieldException ignored) {
116-
}
117-
118-
try {
119-
field = pluginClassLoaderClass.getDeclaredField(Constant.PLUGIN_CLASS_LOADER_MY_PARENTS);
120-
} catch (NoSuchFieldException ignored) {
121-
}
122-
123-
if (Objects.isNull(field)) {
124-
Messages.showInfoMessage("Unsupported this version", Constant.APPLICATION_NAME);
125-
return;
126-
}
127-
128-
try {
129-
field.setAccessible(true);
130-
131-
ClassLoader[] parents = (ClassLoader[]) field.get(pluginClassLoader);
132-
if (Objects.isNull(parents)) {
133-
parents = new ClassLoader[] {classLoader};
134-
field.set(pluginClassLoader, parents);
135-
} else {
136-
ClassLoader[] newParents = new ClassLoader[parents.length + 1];
137-
newParents[parents.length] = classLoader;
138-
System.arraycopy(parents, 0, newParents, 0, parents.length);
139-
field.set(pluginClassLoader, newParents);
140-
}
141-
} catch (IllegalAccessException e) {
142-
e.printStackTrace();
143-
}
111+
URL[] urlArr = urls.toArray(new URL[0]);
144112

113+
classLoader = new ProjectLoader(urlArr, MybatisPojoCompile.class.getClassLoader());
145114
}
146115

147116
private static void changeLoaderUrls(List<String> preDependencies, List<String> dependencies) {
@@ -156,15 +125,15 @@ private static void changeLoaderUrls(List<String> preDependencies, List<String>
156125
return;
157126
}
158127

159-
for (String path : list) {
160-
try {
161-
URL url = new File(FileUtil.toSystemIndependentName(path)).toURI().toURL();
162-
classLoader.addURL(url);
163-
}
164-
catch (MalformedURLException e) {
165-
Messages.showErrorDialog(e.getMessage(), "MalformedURLException");
166-
}
128+
try {
129+
classLoader.close();
130+
} catch (IOException e) {
131+
throw new RuntimeException(e);
167132
}
133+
134+
List<URL> urls = pathToURL(dependencies);
135+
classLoader = new ProjectLoader(urls.toArray(new URL[0]), MybatisPojoCompile.class.getClassLoader());
136+
168137
}
169138

170139
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package io.github.linyimin.plugin.compile;
2+
3+
import java.net.URL;
4+
import java.net.URLClassLoader;
5+
import java.util.Map;
6+
import java.util.Objects;
7+
import java.util.concurrent.ConcurrentHashMap;
8+
9+
/**
10+
* @author banzhe
11+
* @date 2022/07/27 16:12
12+
**/
13+
public class ProjectLoader extends URLClassLoader {
14+
15+
16+
private final Map<String /* class qualifier */, Class<?> /*loaded class*/> loadedClassMap = new ConcurrentHashMap<>();
17+
18+
19+
public ProjectLoader(URL[] urls, ClassLoader parent) {
20+
super(urls, parent);
21+
}
22+
23+
private Class<?> findLoadedClass0(String name) {
24+
return loadedClassMap.get(name);
25+
}
26+
27+
@Override
28+
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
29+
30+
Class<?> clazz = findLoadedClass0(name);
31+
32+
if (Objects.nonNull(clazz)) {
33+
return clazz;
34+
}
35+
36+
clazz = findLoadedClass(name);
37+
38+
if (Objects.nonNull(clazz)) {
39+
return clazz;
40+
}
41+
42+
try {
43+
clazz = findClass(name);
44+
if (Objects.nonNull(clazz)) {
45+
if (resolve) {
46+
resolveClass(clazz);
47+
}
48+
loadedClassMap.put(name, clazz);
49+
return clazz;
50+
}
51+
} catch (ClassNotFoundException | SecurityException ignored) {
52+
53+
}
54+
55+
return super.loadClass(name, resolve);
56+
}
57+
}

src/main/java/io/github/linyimin/plugin/service/SqlParamGenerateService.java

Lines changed: 61 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,12 @@
1818
import io.github.linyimin.plugin.service.model.MybatisSqlConfiguration;
1919
import io.github.linyimin.plugin.utils.JavaUtils;
2020
import io.github.linyimin.plugin.utils.MapperDomUtils;
21-
import io.github.linyimin.plugin.utils.MybatisSqlUtils;
2221
import org.apache.commons.collections.MapUtils;
2322
import org.apache.commons.lang3.StringUtils;
23+
24+
import java.lang.reflect.Method;
2425
import java.util.*;
26+
import java.util.stream.Collectors;
2527

2628
/**
2729
* @author yiminlin
@@ -42,59 +44,65 @@ public String generateSql(Project project, String methodQualifiedName, String pa
4244
return StringUtils.EMPTY;
4345
}
4446

45-
String mybatisConfig = getMybatisConfiguration(project, psiMethods.get(0));
47+
List<String> mybatisConfigs = getMybatisConfigurations(project, psiMethods.get(0));
4648

47-
boolean isNeedCompile = checkNeedCompile(mybatisConfig, methodQualifiedName, params);
49+
boolean isNeedCompile = checkNeedCompile(mybatisConfigs, methodQualifiedName, params);
4850

4951
if (isNeedCompile) {
5052
MybatisPojoCompile.compile(project);
5153
}
5254

53-
return MybatisSqlUtils.getSql(mybatisConfig, methodQualifiedName, params, false);
55+
return getSql(mybatisConfigs, methodQualifiedName, params);
5456

5557
}
5658

57-
private boolean checkNeedCompile(String mybatisConfig, String methodQualifiedName, String params) {
59+
private boolean checkNeedCompile(List<String> mybatisConfigs, String methodQualifiedName, String params) {
5860
if (Objects.isNull(MybatisPojoCompile.classLoader)) {
59-
return true;
61+
return true;
6062
}
6163

6264
try {
63-
MybatisSqlUtils.getSql(mybatisConfig, methodQualifiedName, params, true);
64-
} catch (Exception | NoClassDefFoundError e) {
65+
getSql(mybatisConfigs, methodQualifiedName, params);
66+
} catch (Throwable e) {
6567
return true;
6668
}
6769

6870
return false;
6971
}
7072

71-
private String getMybatisConfiguration(Project project, PsiMethod psiMethod) {
73+
private List<String> getMybatisConfigurations(Project project, PsiMethod psiMethod) {
7274

73-
MybatisConfiguration mybatisConfiguration = MapperDomUtils.findConfiguration(project, psiMethod);
74-
if (Objects.isNull(mybatisConfiguration)) {
75+
List<MybatisConfiguration> mybatisConfigurations = MapperDomUtils.findConfiguration(project, psiMethod);
76+
if (org.apache.commons.collections.CollectionUtils.isEmpty(mybatisConfigurations)) {
7577
Messages.showInfoMessage("Mybatis配置文件不存在", Constant.APPLICATION_NAME);
76-
return StringUtils.EMPTY;
78+
return Collections.emptyList();
7779
}
7880

79-
if (Objects.isNull(mybatisConfiguration.getParent().getXmlElement())) {
81+
mybatisConfigurations = mybatisConfigurations.stream().filter(mybatisConfiguration -> Objects.nonNull(mybatisConfiguration.getParent().getXmlElement())).collect(Collectors.toList());
82+
83+
if (org.apache.commons.collections.CollectionUtils.isEmpty(mybatisConfigurations)) {
8084
Messages.showInfoMessage("Mybatis配置文件错误", Constant.APPLICATION_NAME);
81-
return StringUtils.EMPTY;
85+
return Collections.emptyList();
8286
}
8387

84-
String mybatisConfig = mybatisConfiguration.getParent().getXmlElement().getText();
88+
return mybatisConfigurations.stream().map(mybatisConfiguration -> {
89+
String mybatisConfig = mybatisConfiguration.getParent().getXmlElement().getText();
8590

86-
// 不处理plugins
87-
XmlElement xmlElement = mybatisConfiguration.getXmlElement();
91+
// 不处理plugins
92+
XmlElement xmlElement = mybatisConfiguration.getXmlElement();
8893

89-
if (Objects.nonNull(xmlElement) && xmlElement instanceof XmlTag) {
90-
XmlTag[] tags = ((XmlTag) xmlElement).findSubTags("plugins");
91-
if (tags.length > 0) {
92-
String plugins = tags[0].getText();
93-
mybatisConfig = mybatisConfig.replace(plugins, StringUtils.EMPTY);
94+
if (Objects.nonNull(xmlElement) && xmlElement instanceof XmlTag) {
95+
XmlTag[] tags = ((XmlTag) xmlElement).findSubTags("plugins");
96+
if (tags.length > 0) {
97+
String plugins = tags[0].getText();
98+
mybatisConfig = mybatisConfig.replace(plugins, StringUtils.EMPTY);
99+
}
94100
}
95-
}
96101

97-
return mybatisConfig;
102+
return mybatisConfig;
103+
}).collect(Collectors.toList());
104+
105+
98106
}
99107

100108

@@ -179,7 +187,7 @@ private String parseParamNameTypeList(List<ParamNameType> paramNameTypes) {
179187

180188
// 数组或者列表
181189
if (isArray(type)) {
182-
param.put(name, Lists.newArrayList(param.get(name)));
190+
param.put(name, Lists.newArrayList(param.get(name)));
183191
}
184192

185193
params.putAll(param);
@@ -369,4 +377,32 @@ public ParamNameType(String name, PsiClass psiClass, PsiType psiType, boolean is
369377
}
370378
}
371379

380+
private String getSql(List<String> mybatisConfigurations, String qualifiedMethod, String params) {
381+
382+
ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader();
383+
384+
try {
385+
Thread.currentThread().setContextClassLoader(MybatisPojoCompile.classLoader);
386+
Class<?> clazz = MybatisPojoCompile.classLoader.loadClass("io.github.linyimin.plugin.utils.MybatisSqlUtils");
387+
Object object = clazz.newInstance();
388+
Method method = clazz.getDeclaredMethod("getSql", String.class, String.class, String.class);
389+
390+
int length = mybatisConfigurations.size();
391+
for (int i = 0; i < length - 1; i++) {
392+
try {
393+
return (String) method.invoke(object, mybatisConfigurations.get(i), qualifiedMethod, params);
394+
} catch (Throwable ignored) {
395+
396+
}
397+
}
398+
399+
return (String) method.invoke(object, mybatisConfigurations.get(length - 1), qualifiedMethod, params);
400+
} catch (Exception e) {
401+
throw new RuntimeException(e);
402+
} finally {
403+
Thread.currentThread().setContextClassLoader(currentClassLoader);
404+
}
405+
406+
}
407+
372408
}

src/main/java/io/github/linyimin/plugin/utils/JavaUtils.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ public static Set<String> getAllDependenciesRecursive(Project project) {
153153
List<PsiClass> psiClassList = namespaces
154154
.stream()
155155
.map(namespace -> JavaUtils.findClazz(project, namespace))
156+
.filter(Objects::nonNull)
156157
.collect(Collectors.toList());
157158

158159
Set<PsiClass> allDependencies = new HashSet<>();

0 commit comments

Comments
 (0)