动手写web框架(2): 类加载器

自定义一个类加载器,用于加载基础包名下的所有类,比如使用了某注解的类,实现了某接口的类,或者继承了某父类的所有子类等。

自定义注解

一个Web框架需要用到一些自定义的注解。

Action
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package org.smart4j.framework.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Action 方法注解
* Created by Roger on 2016/11/23.
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Action {
String value();
}

Controller
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package org.smart4j.framework.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 控制器注解
* Created by Roger on 2016/11/23.
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {
}
Inject
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package org.smart4j.framework.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 依赖注入注解
* Created by Roger on 2016/11/23.
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Inject {
}
Service
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package org.smart4j.framework.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 服务类注解
* Created by Roger on 2016/11/23.
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Service {
}

自定义类加载器

ClassHelper通过一个Set来维护基础包下的所有类,并提供Bean类、Service、Controller或者相应的注解类的获取方式。

ClassHelper
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
package org.smart4j.framework.helper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.smart4j.framework.annotation.Controller;
import org.smart4j.framework.annotation.Service;
import org.smart4j.framework.util.ClassUtil;
import java.lang.annotation.Annotation;
import java.util.HashSet;
import java.util.Set;
/**
* Created by Roger on 2016/11/23.
*/
public final class ClassHelper {
private static final Logger LOGGER = LoggerFactory.getLogger(ClassHelper.class);
/**
* 定义类集合(用于存放所加载的类)
*/
private static final Set<Class<?>> CLASS_SET;
static {
String basePackage = ConfigHelper.getAppBasePackage();
CLASS_SET = ClassUtil.getClassSet(basePackage);
}
/**
* 获取应用包下的所有类
* @return
*/
public static Set<Class<?>> getClassSet(){
return CLASS_SET;
}
/**
* 获取应用包下的所有service类
* @return
*/
public static Set<Class<?>> getServiceClassSet(){
return getClassSetByAnnotation(Service.class);
}
/**
* 获取应用包下的所有controller类
* @return
*/
public static Set<Class<?>> getControllerClassSet(){
return getClassSetByAnnotation(Controller.class);
}
/**
* 获取应用包下的所有Bean
* @return
*/
public static Set<Class<?>> getBeanClassSet(){
Set<Class<?>> classSet = new HashSet<>();
classSet.addAll(getServiceClassSet());
classSet.addAll(getControllerClassSet());
return classSet;
}
/**
* 获取应用包下带有某注解的所有类
* @param annotationClass
* @return
*/
public static Set<Class<?>> getClassSetByAnnotation(Class<? extends Annotation> annotationClass){
Set<Class<?>> classSet = new HashSet<>();
for (Class<?> cls: CLASS_SET){
if (cls.isAnnotationPresent(annotationClass)){
classSet.add(cls);
}
}
return classSet;
}
/**
* 获取应用包下某父类(或接口)的所有子类(或实现类)
* @param superCls
* @return
*/
public static Set<Class<?>> getClassSetBySuper(Class<?> superCls){
Set<Class<?>> classSet = new HashSet<>();
for (Class<?> cls: CLASS_SET){
if (superCls.isAssignableFrom(cls) && !superCls.equals(cls)){
classSet.add(cls);
}
}
return classSet;
}
}

ClassUtil工具类,提供与类操作相关的方法,比如加载类、获取指定包名下的所有类。

ClassUtil
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
package org.smart4j.framework.util;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
/**
* 类操作工具类
* Created by Roger on 2016/11/23.
*/
public final class ClassUtil {
private static final Logger LOGGER = LoggerFactory.getLogger(ClassUtil.class);
/**
* 获取类加载器
* @return
*/
public static ClassLoader getClassLoader(){
return Thread.currentThread().getContextClassLoader();
}
/**
* 加载类
* @param className
* @param isInitialized
* @return
*/
public static Class<?> loadClass(String className, boolean isInitialized){
Class<?> cls;
try {
cls = Class.forName(className, isInitialized, getClassLoader());
} catch (ClassNotFoundException e) {
LOGGER.error("load class failure", e);
throw new RuntimeException(e);
}
return cls;
}
/**
* 获取指定包名下的所有类
* @param packageName eg: com.roger.smart4j
* @return
*/
public static Set<Class<?>> getClassSet(String packageName){
Set<Class<?>> classSet = new HashSet<>();
try {
Enumeration<URL> urls = getClassLoader().getResources(packageName.replace(".", "/"));
while (urls.hasMoreElements()){
URL url = urls.nextElement();
if (url != null){
String protocol = url.getProtocol();
if (protocol.equals("file")){
String packagePath = url.getPath().replaceAll("%20", " ");
addClass(classSet, packagePath, packageName);
}else if (protocol.equals("jar")){
JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
if (jarURLConnection != null){
JarFile jarFile = jarURLConnection.getJarFile();
if (jarFile != null){
Enumeration<JarEntry> jarEntries = jarFile.entries();
while (jarEntries.hasMoreElements()){
JarEntry jarEntry = jarEntries.nextElement();
String jarEntryName = jarEntry.getName();
if (jarEntryName.endsWith(".class")){
String className = jarEntryName.substring(0, jarEntryName.lastIndexOf(".")).replaceAll("/", ".");
doAddClass(classSet, className);
}
}
}
}
}
}
}
} catch (IOException e) {
LOGGER.error("get class set failure", e);
throw new RuntimeException(e);
}
return classSet;
}
private static void addClass(Set<Class<?>> classSet, String packagePath, String packageName){
File[] files = new File(packagePath).listFiles(new FileFilter() {
@Override
public boolean accept(File file) {
return ((file.isFile()) && file.getName().endsWith(".class")) || file.isDirectory();
}
});
for (File file: files){
String fileName = file.getName();
if (file.isFile()){
String className = fileName.substring(0, fileName.lastIndexOf("."));
if (StringUtils.isNotEmpty(packageName)){
className = packageName + "." + className;
}
doAddClass(classSet, className);
}else {
String subPackagePath = fileName;
if (StringUtils.isNotEmpty(packagePath)){
subPackagePath = packagePath + "/" + subPackagePath;
}
String subPackageName = fileName;
if (StringUtils.isNotEmpty(packageName)){
subPackageName = packageName + "." + subPackageName;
}
addClass(classSet, subPackagePath, subPackageName);
}
}
}
private static void doAddClass(Set<Class<?>> classSet, String className){
Class<?> cls = loadClass(className, false);
classSet.add(cls);
}
}