前言

spring框架给java工程师们提供了极大的便利,只需要添加相应的配置即可快速搭建项目骨架,将更多重心放在业务逻辑的开发中。spring具有两大核心功能:

  1. 控制反转(IoC,Inversion of Control)

    传统的JAVA开发模式中,当需要一个对象时,我们使用new或者通过getInstance等直接或者间接调用构造方法创建一个对象,而在spring开发模式中,spring容器使用工厂模式为我们创建了所需要的对象,不需要我们自己去创建了,直接调用spring提供的对象就可以了,这就是控制反转。控制反转的好处有:

    1. 使用方便,不用开发者大量使用new
    2. 解耦合,实例对象交给容器管理
  2. 面向切面编程(AOP)

    在面向对象编程(OOP)中,我们将事务纵向抽成一个个的对象,而在面向切面编程中,我们将一个个的对象某些类似的方面横向抽成一个切面,对这个切面进行一些如权限控制,事务管理,日志记录等公用操作处理的过程,就是面向切面编程的思想。

实现ioc

主要流程图

img

加载类文件

首先我们需要加载包下的类文件,包名我们可以手动配置,因此提供配置文件:

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
/**
* @Title
* @Description 提供相关配置项常量
* @Author WolfMan
* @Date 2023/3/4 19:53
* @Email 2370032534@qq.com
*/
public interface ConfigConstant {

/**
* 配置文件名
*/
String CONFIG_FILE = "wolf.properties";

/**
* 数据库驱动
*/
String JDBC_DRIVER = "wolf.framework.jdbc.driver";

/**
* 数据库连接
*/
String JDBC_URL = "wolf.framework.jdbc.url";

/**
* 数据库用户
*/
String JDBC_USERNAME = "wolf.framework.jdbc.username";

/**
* 数据库密码
*/
String JDBC_PASSWORD = "wolf.framework.jdbc.password";

/**
* 项目包名(扫包需要)
*/
String APP_BASE_PACKAGE = "wolf.framework.app.base_package";

/**
* jsp配置路径
*/
String APP_JSP_PATH = "wolf.framework.app.jsp_path";

/**
* 静态资源路径
*/
String APP_ASSET_PATH = "wolf.framework.app.asset_path";

/**
* 文件上传大小限制
*/
String APP_UPLOAD_LIMIT = "wolf.framework.app.upload_limit";

}

提供properties工具类:

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
/**
* @Title
* @Description 属性文件工具类
* @Author WolfMan
* @Date 2023/3/4 20:01
* @Email 2370032534@qq.com
*/
public final class PropsUtil {

private static final Logger LOGGER = LoggerFactory.getLogger(PropsUtil.class);

/**
* 加载属性文件
*
* @param fileName
* @return
*/
public static Properties loadProps(String fileName) {
Properties props = null;
InputStream is = null;
try {
is = ClassUtil.getClassLoader().getResourceAsStream(fileName);
if (is == null) {
throw new FileNotFoundException(fileName + " file is not found");
}
props = new Properties();
props.load(is);
} catch (IOException e) {
LOGGER.error("load properties file failure", e);
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
LOGGER.error("close input stream failure", e);
}
}
}
return props;
}

/**
* 获取String类型的属性值(默认为空字符串)
*
* @param props
* @param key
* @return
*/
public static String getString(Properties props, String key) {
return getString(props, key, "");
}

/**
* 获取String类型的属性值(可指定默认值)
*
* @param props
* @param key
* @param defaultValue
* @return
*/
public static String getString(Properties props, String key, String defaultValue) {
String value = defaultValue;
if (props.contains(key)) {
value = props.getProperty(key);
}
return value;
}

/**
* 获取 int 类型的属性值(默认值为 0)
*/
public static int getInt(Properties props, String key) {
return getInt(props, key, 0);
}

/**
* 获取 int 类型的属性值(可指定默认值)
*
* @param props
* @param key
* @param defaultValue
* @return
*/
public static int getInt(Properties props, String key, int defaultValue) {
int value = defaultValue;
if (props.contains(key)) {
value = CastUtil.castInt(props.getProperty(key));
}
return value;
}

/**
* 获取 boolean 类型属性(默认值为 false)
*/
public static boolean getBoolean(Properties props, String key) {
return getBoolean(props, key, false);
}

/**
* 获取 boolean 类型属性(可指定默认值)
*/
public static boolean getBoolean(Properties props, String key, boolean defaultValue) {
boolean value = defaultValue;
if (props.containsKey(key)) {
value = CastUtil.castBoolean(props.getProperty(key));
}
return value;
}
}

通过配置文件我们可以知道包名,因此也能拿到类文件的地址。接下来提供一个class工具类,主要目的为加载类,返回所有类信息。

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
/**
* @Title
* @Description 类操作工具类
* @Author WolfMan
* @Date 2023/3/4 20:05
* @Email 2370032534@qq.com
*/
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 className
* @return
*/
public static Class<?> loadClass(String className) {
return loadClass(className, true);
}

public static Set<Class<?>> getClassSet(String packageName) {
Set<Class<?>> classSet = new HashSet<Class<?>>();
try {
Enumeration<URL> urls = getClassLoader().getResources(packageName.replace(".", "/"));
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
if (url != null) {
String protocol = url.getProtocol();
if ("file".equals(protocol)) {
String packagePath = url.getPath().replaceAll("%20", " ");
addClass(classSet, packagePath, packageName);
} else if ("jar".equals(protocol)) {
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 (Exception 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 (StringUtil.isNotEmpty(packageName)) {
className = packageName + "." + className;
}
doAddClass(classSet, className);
} else {
String subPackagePath = fileName;
if (StringUtil.isNotEmpty(packagePath)) {
subPackagePath = packagePath + "/" + subPackagePath;
}
String subPackageName = fileName;
if (StringUtil.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);
}


}

生成实例对象

我们已经拿到了类的信息,因此可以实例化类并用map保存起来,实例化的对象就叫bean。并不是所有的类都需要实例化,因此提供ClassHelper,供我们筛选指定的目标,例如带有service注解的类:

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
/**
* @Title
* @Description 类操作助手类
* @Author WolfMan
* @Date 2023/3/4 21:13
* @Email 2370032534@qq.com
*/
public final class ClassHelper {

/**
* 定义类集合(用于存放所加载的类)
*/
private static final Set<Class<?>> CLASS_SET;

static {
String basePackage = ConfigHelper.getAppBasePackage();
CLASS_SET = ClassUtil.getClassSet(basePackage);
}

/**
* 获取应用包名下的所有类
*/
public static Set<Class<?>> getClassSet() {
return CLASS_SET;
}

/**
* 获取应用包名下所有 Service 类
*/
public static Set<Class<?>> getServiceClassSet() {
Set<Class<?>> classSet = new HashSet<Class<?>>();
for (Class<?> cls : CLASS_SET) {
if (cls.isAnnotationPresent(Service.class)) {
classSet.add(cls);
}
}
return classSet;
}

/**
* 获取应用包名下所有 Controller 类
*/
public static Set<Class<?>> getControllerClassSet() {
Set<Class<?>> classSet = new HashSet<Class<?>>();
for (Class<?> cls : CLASS_SET) {
if (cls.isAnnotationPresent(Controller.class)) {
classSet.add(cls);
}
}
return classSet;
}

/**
* 获取应用包名下所有 Bean 类(包括:Service、Controller 等)
*/
public static Set<Class<?>> getBeanClassSet() {
Set<Class<?>> beanClassSet = new HashSet<Class<?>>();
beanClassSet.addAll(getServiceClassSet());
beanClassSet.addAll(getControllerClassSet());
return beanClassSet;
}

/**
* 获取应用包名下某父类(或接口)的所有子类(或实现类)
*/
public static Set<Class<?>> getClassSetBySuper(Class<?> superClass) {
Set<Class<?>> classSet = new HashSet<Class<?>>();
for (Class<?> cls : CLASS_SET) {
if (superClass.isAssignableFrom(cls) && !superClass.equals(cls)) {
classSet.add(cls);
}
}
return classSet;
}

/**
* 获取应用包名下带有某注解的所有类
*/
public static Set<Class<?>> getClassSetByAnnotation(Class<? extends Annotation> annotationClass) {
Set<Class<?>> classSet = new HashSet<Class<?>>();
for (Class<?> cls : CLASS_SET) {
if (cls.isAnnotationPresent(annotationClass)) {
classSet.add(cls);
}
}
return classSet;
}
}
/**
* @Title
* @Description Bean助手类
* @Author WolfMan
* @Date 2023/3/4 21:20
* @Email 2370032534@qq.com
*/
public final class BeanHelper {

private static final Map<Class<?>, Object> BEAN_MAP = new HashMap<Class<?>, Object>();

static {
Set<Class<?>> beanClassSet = ClassHelper.getBeanClassSet();
for (Class<?> beanClass : beanClassSet) {
Object obj = ReflectionUtil.newInstance(beanClass);
BEAN_MAP.put(beanClass, obj);
}
}

/**
* 获取 Bean 映射
*/
public static Map<Class<?>, Object> getBeanMap() {
return BEAN_MAP;
}

/**
* 获取 Bean 实例
*/
@SuppressWarnings("unchecked")
public static <T> T getBean(Class<T> cls) {
if (!BEAN_MAP.containsKey(cls)) {
throw new RuntimeException("can not get bean by class: " + cls);
}
return (T) BEAN_MAP.get(cls);
}

/**
* 设置 Bean 实例
*/
public static void setBean(Class<?> cls, Object obj) {
BEAN_MAP.put(cls, obj);
}

}

依赖注入

上述操作我们已经把注解标注的类都实例化到bean容器中了,接下来考虑类与类的依赖关系,因此实现依赖注入。

依赖注入的主要步骤为:

  1. 获取类
  2. 判断类中是否依赖其他类
  3. 从bean容器中拿到依赖类注入到当前bean中
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
/**
* @Title
* @Description 依赖注入助手类
* @Author WolfMan
* @Date 2023/3/4 21:23
* @Email 2370032534@qq.com
*/
public final class IocHelper {

static {
Map<Class<?>, Object> beanMap = BeanHelper.getBeanMap();
if (CollectionUtil.isNotEmpty(beanMap)) {
for (Map.Entry<Class<?>, Object> beanEntry : beanMap.entrySet()) {
Class<?> beanClass = beanEntry.getKey();
Object beanInstance = beanEntry.getValue();
Field[] beanFields = beanClass.getDeclaredFields();
if (ArrayUtil.isNotEmpty(beanFields)) {
for (Field beanField : beanFields) {
if (beanField.isAnnotationPresent(Inject.class)) {
Class<?> beanFieldClass = beanField.getType();
Object beanFieldInstance = beanMap.get(beanFieldClass);
if (beanFieldInstance != null) {
ReflectionUtil.setField(beanInstance, beanField, beanFieldInstance);
}
}
}
}
}
}
}

}

以上就是简易版的控制反转,能够使用@Service、@Sontroller等注解管理bean,同时使用@Inject注解进行依赖注入

实现aop

主要流程图

img

获取被代理类

为了实现切面编程,首先需要知道的就是哪些是被代理类,为了方便,我们可以使用自定义注解区分。例如被标注了@Logs的类就是我们的代理类,因此在@Aspect中,我们需要设置代理注解的值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* @Title
* @Description 切面注解
* @Author WolfMan
* @Date 2023/3/4 21:11
* @Email 2370032534@qq.com
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Aspect {

/**
* 注解
*/
Class<? extends Annotation> value();

}

根据value()我们即可获取被代理类的注解。

生成代理对象

由于代理对象不止一个,因此我们要获取所有的代理对象,组成一个代理类链,并与被代理类形成映射。最终我们将被代理类信息与代理类实例注入到容器中。

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
/**
* @Title
* @Description 方法拦截助手类
* @Author WolfMan
* @Date 2023/3/4 23:40
* @Email 2370032534@qq.com
*/
public final class AopHelper {

private static final Logger LOGGER = LoggerFactory.getLogger(AopHelper.class);

static {
try {
Map<Class<?>, Set<Class<?>>> proxyMap = createProxyMap();
Map<Class<?>, List<Proxy>> targetMap = createTargetMap(proxyMap);
for (Map.Entry<Class<?>, List<Proxy>> targetEntry : targetMap.entrySet()) {
Class<?> targetClass = targetEntry.getKey();
List<Proxy> proxyList = targetEntry.getValue();
Object proxy = ProxyManager.createProxy(targetClass, proxyList);
BeanHelper.setBean(targetClass, proxy);
}
} catch (Exception e) {
LOGGER.error("aop failure", e);
}
}

private static Map<Class<?>, Set<Class<?>>> createProxyMap() throws Exception {
Map<Class<?>, Set<Class<?>>> proxyMap = new HashMap<Class<?>, Set<Class<?>>>();
addAspectProxy(proxyMap);
addTransactionProxy(proxyMap);
return proxyMap;
}

private static void addAspectProxy(Map<Class<?>, Set<Class<?>>> proxyMap) throws Exception {
Set<Class<?>> proxyClassSet = ClassHelper.getClassSetBySuper(AspectProxy.class);
for (Class<?> proxyClass : proxyClassSet) {
if (proxyClass.isAnnotationPresent(Aspect.class)) {
Aspect aspect = proxyClass.getAnnotation(Aspect.class);
Set<Class<?>> targetClassSet = createTargetClassSet(aspect);
proxyMap.put(proxyClass, targetClassSet);
}
}
}

private static void addTransactionProxy(Map<Class<?>, Set<Class<?>>> proxyMap) {
Set<Class<?>> serviceClassSet = ClassHelper.getClassSetByAnnotation(Service.class);
proxyMap.put(TransactionProxy.class, serviceClassSet);
}

private static Set<Class<?>> createTargetClassSet(Aspect aspect) throws Exception {
Set<Class<?>> targetClassSet = new HashSet<Class<?>>();
Class<? extends Annotation> annotation = aspect.value();
if (annotation != null && !annotation.equals(Aspect.class)) {
targetClassSet.addAll(ClassHelper.getClassSetByAnnotation(annotation));
}
return targetClassSet;
}

private static Map<Class<?>, List<Proxy>> createTargetMap(Map<Class<?>, Set<Class<?>>> proxyMap) throws Exception {
Map<Class<?>, List<Proxy>> targetMap = new HashMap<Class<?>, List<Proxy>>();
for (Map.Entry<Class<?>, Set<Class<?>>> proxyEntry : proxyMap.entrySet()) {
Class<?> proxyClass = proxyEntry.getKey();
Set<Class<?>> targetClassSet = proxyEntry.getValue();
for (Class<?> targetClass : targetClassSet) {
Proxy proxy = (Proxy) proxyClass.newInstance();
if (targetMap.containsKey(targetClass)) {
targetMap.get(targetClass).add(proxy);
} else {
List<Proxy> proxyList = new ArrayList<Proxy>();
proxyList.add(proxy);
targetMap.put(targetClass, proxyList);
}
}
}
return targetMap;
}

}

ProxyManager类生成最终代理类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* @Title
* @Description 代理管理器
* @Author WolfMan
* @Date 2023/3/4 23:44
* @Email 2370032534@qq.com
*/
public class ProxyManager {

@SuppressWarnings("unchecked")
public static <T> T createProxy(final Class<?> targetClass, final List<Proxy> proxyList) {
return (T) Enhancer.create(targetClass, new MethodInterceptor() {
@Override
public Object intercept(Object targetObject, Method targetMethod, Object[] methodParams, MethodProxy methodProxy) throws Throwable {
return new ProxyChain(targetClass, targetObject, targetMethod, methodProxy, methodParams, proxyList).doProxyChain();
}
});
}

}

提供代理模板类

我们需要暴露给用户如何使用aop,因此提供了代理模板类,里面提供了各种钩子函数供用户选择。

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
/**
* @Title
* @Description 切面代理
* @Author WolfMan
* @Date 2023/3/4 23:43
* @Email 2370032534@qq.com
*/
public abstract class AspectProxy implements Proxy {

private static final Logger logger = LoggerFactory.getLogger(AspectProxy.class);

@Override
public final Object doProxy(ProxyChain proxyChain) throws Throwable {
Object result = null;

Class<?> cls = proxyChain.getTargetClass();
Method method = proxyChain.getTargetMethod();
Object[] params = proxyChain.getMethodParams();

begin();
try {
if (intercept(cls, method, params)) {
before(cls, method, params);
result = proxyChain.doProxyChain();
after(cls, method, params, result);
} else {
result = proxyChain.doProxyChain();
}
} catch (Exception e) {
logger.error("proxy failure", e);
error(cls, method, params, e);
throw e;
} finally {
end();
}

return result;
}

public void begin() {
}

public boolean intercept(Class<?> cls, Method method, Object[] params) throws Throwable {
return true;
}

public void before(Class<?> cls, Method method, Object[] params) throws Throwable {
}

public void after(Class<?> cls, Method method, Object[] params, Object result) throws Throwable {
}

public void error(Class<?> cls, Method method, Object[] params, Throwable e) {
}

public void end() {
}

}

其中proxy接口如下:

1
2
3
4
5
6
7
8
9
10
11
12
/**
* @Title
* @Description 代理接口
* @Author WolfMan
* @Date 2023/3/4 23:41
* @Email 2370032534@qq.com
*/
public interface Proxy {

Object doProxy(ProxyChain proxyChain) throws Throwable;

}

值得注意的是AopHelper的初始化顺序,必须在BeanHelper初始化后,IocHelper初始化前初始化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* @Title
* @Description 加载相应的 Helper 类
* @Author WolfMan
* @Date 2023/3/4 23:49
* @Email 2370032534@qq.com
*/
public final class HelperLoader {

public static void init() {
Class<?>[] classList = {
ClassHelper.class,
BeanHelper.class,
AopHelper.class,
IocHelper.class,
ControllerHelper.class
};
for (Class<?> cls : classList) {
ClassUtil.loadClass(cls.getName());
}
}

}

以上就是aop的简单实现,我们可以通过注解@Aspect注解同时继承AspectProxy类来代理目标类,实现不侵入业务的逻辑。

实现dispatcherServlet

主要流程图

转发器实现接受请求并且将请求转发到对应的处理方法中,主要流程如下:

img

封装请求与处理器映射

我们要做的是封装请求与处理方法的映射,唯一标识一个请求的方法是请求路径与请求方法的组合,前面已经将controller加入bean容器中了,只需要解析controller中的Action注解并封装成映射即可。

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
/**
* @Title
* @Description 控制器助手类
* @Author WolfMan
* @Date 2023/3/4 21:22
* @Email 2370032534@qq.com
*/
public final class ControllerHelper {

private static final Map<Request, Handler> ACTION_MAP = new HashMap<Request, Handler>();

static {
Set<Class<?>> controllerClassSet = ClassHelper.getControllerClassSet();
if (CollectionUtil.isNotEmpty(controllerClassSet)) {
for (Class<?> controllerClass : controllerClassSet) {
Method[] methods = controllerClass.getDeclaredMethods();
if (ArrayUtil.isNotEmpty(methods)) {
for (Method method : methods) {
if (method.isAnnotationPresent(Action.class)) {
Action action = method.getAnnotation(Action.class);
String mapping = action.value();
if (mapping.matches("\\w+:/\\w*")) {
String[] array = mapping.split(":");
if (ArrayUtil.isNotEmpty(array) && array.length == 2) {
String requestMethod = array[0];
String requestPath = array[1];
Request request = new Request(requestMethod, requestPath);
Handler handler = new Handler(controllerClass, method);
ACTION_MAP.put(request, handler);
}
}
}
}
}
}
}
}

/**
* 获取 Handler
*/
public static Handler getHandler(String requestMethod, String requestPath) {
Request request = new Request(requestMethod, requestPath);
return ACTION_MAP.get(request);
}

}

封装请求参数

我们需要封装请求参数,并且从ACTION_MAP中获取对应的handler,反射调用即可

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
/**
* @Title
* @Description 请求转发器
* @Author WolfMan
* @Date 2023/3/4 23:50
* @Email 2370032534@qq.com
*/
@WebServlet(urlPatterns = "/*", loadOnStartup = 0)
public class DispatcherServlet extends HttpServlet {


@Override
public void init(ServletConfig servletConfig) throws ServletException {
HelperLoader.init();

ServletContext servletContext = servletConfig.getServletContext();

registerServlet(servletContext);

UploadHelper.init(servletContext);
}

private void registerServlet(ServletContext servletContext) {
ServletRegistration jspServlet = servletContext.getServletRegistration("jsp");
jspServlet.addMapping("/index.jsp");
jspServlet.addMapping(ConfigHelper.getAppJspPath() + "*");

ServletRegistration defaultServlet = servletContext.getServletRegistration("default");
defaultServlet.addMapping("/favicon.ico");
defaultServlet.addMapping(ConfigHelper.getAppAssetPath() + "*");
}

@Override
public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletHelper.init(request, response);
try {
String requestMethod = request.getMethod().toLowerCase();
String requestPath = request.getPathInfo();
Handler handler = ControllerHelper.getHandler(requestMethod, requestPath);
if (handler != null) {
Class<?> controllerClass = handler.getControllerClass();
Object controllerBean = BeanHelper.getBean(controllerClass);

Param param;
if (UploadHelper.isMultipart(request)) {
param = UploadHelper.createParam(request);
} else {
param = RequestHelper.createParam(request);
}

Object result;
Method actionMethod = handler.getActionMethod();
if (param.isEmpty()) {
result = ReflectionUtil.invokeMethod(controllerBean, actionMethod);
} else {
result = ReflectionUtil.invokeMethod(controllerBean, actionMethod, param);
}

if (result instanceof View) {
handleViewResult((View) result, request, response);
} else if (result instanceof Data) {
handleDataResult((Data) result, response);
}
}
} finally {
ServletHelper.destroy();
}
}

}

解析处理器返回值

根据handler返回值判读即可:

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
/**
* @Title
* @Description 请求转发器
* @Author WolfMan
* @Date 2023/3/4 23:50
* @Email 2370032534@qq.com
*/
@WebServlet(urlPatterns = "/*", loadOnStartup = 0)
public class DispatcherServlet extends HttpServlet {

private void handleViewResult(View view, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
String path = view.getPath();
if (StringUtil.isNotEmpty(path)) {
if (path.startsWith("/")) {
response.sendRedirect(request.getContextPath() + path);
} else {
Map<String, Object> model = view.getModel();
for (Map.Entry<String, Object> entry : model.entrySet()) {
request.setAttribute(entry.getKey(), entry.getValue());
}
request.getRequestDispatcher(ConfigHelper.getAppJspPath() + path).forward(request, response);
}
}
}

private void handleDataResult(Data data, HttpServletResponse response) throws IOException {
Object model = data.getModel();
if (model != null) {
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
PrintWriter writer = response.getWriter();
String json = JsonUtil.toJson(model);
writer.write(json);
writer.flush();
writer.close();
}
}

}

转发器的主要思想是拦截所有请求,并获取对应的处理器,解析处理器返回值,转发给客户端。

相关资料

  1. 源码地址:手写简易版spring框架
  2. 参考资料:架构探险从零开始写java web框架