手写 spring mvc 基于注解
author: huifer
前置知识
-
在 spring 中我们会有如下几个注解来帮助我们定义 web-mvc 的语义
- Controller
- Service
- RequestParam
- Autowired
- RequestMapping
- 这些注解相比大家都使用过在这里就不具体展开描述了. 在后面的开发中我们再来细说
配置篇
- web.xml 的配置
-
在 web.xml 中我们需要配置
- servlet-class
- spring的配置(伪)
- url-pattern
- spring 配置这里简化为一个包的扫描路径. component-scan
- 配置详情
- web.xml
HuiFer web application
HuiFer mvc
org.huifer.spring.servlet.v1.HFDispatcherServlet
contextConfigLocation
classpath*:application.properties
1
HuiFer mvc
/*
- application.properties
scanPackage=org.huifer.spring
注解篇
- HFAutowired
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD,
ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface HFAutowired {
String value() default "";
}
- HFController
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface HFController {
}
- HFRequestMapping
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface HFRequestMapping {
String name() default "";
}
- HFRequestParam
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface HFRequestParam {
String name() default "";
}
- HFService
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface HFService {
String name() default "";
}
- 注解篇没什么可以多说的. 只是定义一些语义标记. 接下来就是具体的实现了
servlet 篇
-
开发流程
- 读取web.xml中的配置,读取包扫描路径
- 根据包扫描路径加载类命.
-
实例化类.(service注解)
- 根据名称注入
- 根据类型注入
-
读取 requestMapping 注解
- key: url , value: method
- 执行http请求
- 首先继承
javax.servlet.http.HttpServlet
-
重写的方法
- init
- doPost
- doGet
-
主要部分都在init方法中, 按照行为拆分如下几个方法
- doLoadConfig 读取配置
- doScan 进行包扫描
- instance 实例化
- autowired 注入
- initHandlerMapping url和method映射关系
- 读取配置文件. 通过
getServletConfig
获取 servlet 的配置, 并读取 resource 文件夹中的配置.
/**
* 从 web.xml 读取contextConfigLocation
把 {@code classpath*:application.properties} 载入配置
*/
private void doLoadConfig() {
InputStream resourceAsStream = null;
try {
// 获取servlet的配置
ServletConfig servletConfig = this.getServletConfig();
String configInitParameter = servletConfig.getInitParameter(CONTEXT_CONFIG_LOCATION)
.replace("classpath*:", "");
// 读取配置文件
resourceAsStream = this.getClass().getClassLoader()
.getResourceAsStream(configInitParameter);
SPRING_CONTEXT_CONFIG.load(resourceAsStream);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (resourceAsStream != null) {
try {
resourceAsStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
- 包扫描. 递归的扫描基本路径下的class. 放入类名字列表
/**
* 包扫描
*/
private void doScan(String scanPackage) {
// 类路径
URL resource = this.getClass().getClassLoader()
.getResource("/" + scanPackage.replaceAll("\\.", "/"));
File classPath = new File(resource.getFile());
for (File file : classPath.listFiles()) {
if (file.isDirectory()) {
doScan(scanPackage + "." + file.getName());
}
else {
if (!file.getName().endsWith(".class")) {
continue;
}
else {
String className = (scanPackage + "." + file.getName()).replace(".class", "");
classNameList.add(className);
}
}
}
}
-
实例化.
-
那些类需要实例化. 带有 spring 注解的类需要初始化. 在spring里面是 @Component 这里做例子就直接写死了几个需要初始化的标记接口
- HFController
- HFService
-
实例化后的存储
-
Map 结构毋庸置疑
-
在spring中我们注入有 byName 和 byType. 我们这个简单的ioc容器也会有
- byName 的存储方式: key: beanName value: object
-
通过反射方法
getInterfaces
我们可以获取这个类实现了那些接口. 因此可以直接进行注入- byType 的存储方式: key: interfaceName value:Object
-
-
-
/**
* 实例化
*/
private void instance() {
if (this.classNameList.isEmpty()) {
return;
}
try {
for (String clazz : this.classNameList) {
Class> aClass = Class.forName(clazz);
// 1. 接口的实现类初始化
if (!aClass.isInterface()) {
System.out.println(clazz);
Object o = aClass.newInstance();
// 1. 带有注解的初始化
if (aClass.isAnnotationPresent(HFController.class)) {
IOC_NAME.put(aClass.getSimpleName(), o);
}
else if (aClass.isAnnotationPresent(HFService.class)) {
HFService annotation = aClass.getAnnotation(HFService.class);
// 名字注入
if (annotation.name().equals("")) {
IOC_NAME.put(aClass.getSimpleName(), o);
}
else {
IOC_NAME.put(annotation.name(), o);
}
// 类型注入
Class>[] interfaces = aClass.getInterfaces();
for (Class> anInterface : interfaces) {
if (!IOC_NAME.containsKey(anInterface.getName())) {
IOC_NAME.put(anInterface.getName(), o);
}
}
}
else {
continue;
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
-
自动注入
-
从ico容器中获取实例化的对象并且强制设置属性
- 那些字段需要呗强制设置? 带有 HFAutowired 注解的字段
-
private void autowired() {
if (IOC_NAME.isEmpty()) {
return;
}
for (Entry entry : IOC_NAME.entrySet()) {
try {
String k = entry.getKey();
Object v = entry.getValue();
Field[] declaredFields = v.getClass().getDeclaredFields();
for (Field declaredField : declaredFields) {
// 是否又自动注入的注解
if (declaredField.isAnnotationPresent(HFAutowired.class)) {
HFAutowired annotation = declaredField.getAnnotation(HFAutowired.class);
String beanName = annotation.value().trim();
// byType 获取具体的实现类
if (beanName.equals("")) {
beanName = declaredField.getType().getName();
}
declaredField.setAccessible(true);
declaredField.set(v, IOC_NAME.get(beanName));
}
else {
continue;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
-
绑定url和执行方法
- 先找到 HFController. 再找到 HFRequestMapping 反射获取所有 method 判断method是否有 HFRequestMapping. 最终组装, 类上面的HFRequestMapping和方法上的HFRequestMapping属性值拼接起来就是url
private void initHandlerMapping() {
if (IOC_NAME.isEmpty()) {
return;
}
for (Entry entry : IOC_NAME.entrySet()) {
Class> clazz = entry.getValue().getClass();
if (clazz.isAnnotationPresent(HFController.class)) {
if (clazz.isAnnotationPresent(HFRequestMapping.class)) {
HFRequestMapping annotation = clazz.getAnnotation(HFRequestMapping.class);
String baseUri = annotation.name();
for (Method method : clazz.getMethods()) {
HFRequestMapping annotation1 = method.getAnnotation(HFRequestMapping.class);
if (annotation1 != null) {
String uri = ("/" + baseUri + "/" + annotation1.name()).replaceAll("/+", "/");
HandlerMapping.put(uri, method);
}
}
}
}
}
}
运行时
-
处理请求
- 从请求的url转换为method
-
参数匹配
- 读取method的参数列表,类型列表
- 根据类型我们有两个可以直接设置HttpServletRequest,HttpServletResponse
- 获取参数的注解. HFRequestParam . 从url中获取对应的名称. 放入参数列表.
- method.invoke 执行.
- 获取method的执行结果. response 写出
private void dispath(HttpServletRequest req, HttpServletResponse resp) throws Exception {
String requestURI = req.getRequestURI();
String contextPath = req.getContextPath();
requestURI = requestURI.replaceAll(contextPath, "").replaceAll("/+", "/");
if (!HandlerMapping.containsKey(requestURI)) {
throw new RuntimeException("不存在的url");
}
Method method = HandlerMapping.get(requestURI);
if (method != null) {
// 获取 method 所在的class
String simpleName = method.getDeclaringClass().getSimpleName();
Object o = IOC_NAME.get(simpleName);
// 请求参数
Map parameterMap = req.getParameterMap();
// 参数动态赋值
Class>[] parameterTypes = method.getParameterTypes();
Object[] paramValues = new Object[parameterTypes.length];
for (int i = 0; i < paramValues.length; i++) {
// 获取 controller 中的参数类型
Class> parameterType = parameterTypes[i];
if (parameterType.equals(req.getClass())) {
paramValues[i] = req;
}
else if (parameterType.equals(resp.getClass())) {
paramValues[i] = resp;
}
// todo: 2020/7/25 参数内容的设置
else if (parameterType.equals(String.class)) {
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
for (Annotation a : parameterAnnotations[i]) {
if (a instanceof HFRequestParam) {
String paramName = ((HFRequestParam) a).name();
if (!"".equals(paramName.trim())) {
String value = Arrays.toString(parameterMap.get(paramName))
.replaceAll("\\[|\\]", "").replaceAll("\\s", ",");
paramValues[i] = value;
}
}
}
}
else if (parameterType.equals(Integer.class) || parameterType.equals(int.class)) {
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
for (Annotation a : parameterAnnotations[i]) {
if (a instanceof HFRequestParam) {
String paramName = ((HFRequestParam) a).name();
if (!"".equals(paramName.trim())) {
String value = Arrays.toString(parameterMap.get(paramName))
.replaceAll("\\[|\\]", "").replaceAll("\\s", ",");
paramValues[i] = Integer.valueOf(value);
}
}
}
}
}
Object invoke = method.invoke(o, paramValues);
resp.getWriter().write(invoke.toString());
}
}
完整的类
package org.huifer.spring.servlet.v1;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.huifer.spring.annotation.HFAutowired;
import org.huifer.spring.annotation.HFController;
import org.huifer.spring.annotation.HFRequestMapping;
import org.huifer.spring.annotation.HFRequestParam;
import org.huifer.spring.annotation.HFService;
public class HFDispatcherServlet extends HttpServlet {
public static final String CONTEXT_CONFIG_LOCATION = "contextConfigLocation";
public static final String SCAN_KEY = "scanPackage";
private final Properties SPRING_CONTEXT_CONFIG = new Properties();
private final List classNameList = new ArrayList<>();
private final Map HandlerMapping = new HashMap<>();
/**
*
*/
Map IOC_NAME = new HashMap<>();
@Override
public void init() throws ServletException {
// 配置读取
doLoadConfig();
// 注解扫描.
doScan(this.SPRING_CONTEXT_CONFIG.getProperty(SCAN_KEY));
// 初始化类
instance();
// 依赖注入
autowired();
// 初始化 handlerMapping
initHandlerMapping();
System.out.println();
}
private void initHandlerMapping() {
if (IOC_NAME.isEmpty()) {
return;
}
for (Entry entry : IOC_NAME.entrySet()) {
Class> clazz = entry.getValue().getClass();
if (clazz.isAnnotationPresent(HFController.class)) {
if (clazz.isAnnotationPresent(HFRequestMapping.class)) {
HFRequestMapping annotation = clazz.getAnnotation(HFRequestMapping.class);
String baseUri = annotation.name();
for (Method method : clazz.getMethods()) {
HFRequestMapping annotation1 = method.getAnnotation(HFRequestMapping.class);
if (annotation1 != null) {
String uri = ("/" + baseUri + "/" + annotation1.name()).replaceAll("/+", "/");
HandlerMapping.put(uri, method);
}
}
}
}
}
}
private void autowired() {
if (IOC_NAME.isEmpty()) {
return;
}
for (Entry entry : IOC_NAME.entrySet()) {
try {
String k = entry.getKey();
Object v = entry.getValue();
Field[] declaredFields = v.getClass().getDeclaredFields();
for (Field declaredField : declaredFields) {
// 是否又自动注入的注解
if (declaredField.isAnnotationPresent(HFAutowired.class)) {
HFAutowired annotation = declaredField.getAnnotation(HFAutowired.class);
String beanName = annotation.value().trim();
// byType 获取具体的实现类
if (beanName.equals("")) {
beanName = declaredField.getType().getName();
}
declaredField.setAccessible(true);
declaredField.set(v, IOC_NAME.get(beanName));
}
else {
continue;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 实例化
*/
private void instance() {
if (this.classNameList.isEmpty()) {
return;
}
try {
for (String clazz : this.classNameList) {
Class> aClass = Class.forName(clazz);
// 1. 接口的实现类初始化
if (!aClass.isInterface()) {
System.out.println(clazz);
Object o = aClass.newInstance();
// 1. 带有注解的初始化
if (aClass.isAnnotationPresent(HFController.class)) {
IOC_NAME.put(aClass.getSimpleName(), o);
}
else if (aClass.isAnnotationPresent(HFService.class)) {
HFService annotation = aClass.getAnnotation(HFService.class);
// 名字注入
if (annotation.name().equals("")) {
IOC_NAME.put(aClass.getSimpleName(), o);
}
else {
IOC_NAME.put(annotation.name(), o);
}
// 类型注入
Class>[] interfaces = aClass.getInterfaces();
for (Class> anInterface : interfaces) {
if (!IOC_NAME.containsKey(anInterface.getName())) {
IOC_NAME.put(anInterface.getName(), o);
}
}
}
else {
continue;
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 包扫描
*/
private void doScan(String scanPackage) {
// 类路径
URL resource = this.getClass().getClassLoader()
.getResource("/" + scanPackage.replaceAll("\\.", "/"));
File classPath = new File(resource.getFile());
for (File file : classPath.listFiles()) {
if (file.isDirectory()) {
doScan(scanPackage + "." + file.getName());
}
else {
if (!file.getName().endsWith(".class")) {
continue;
}
else {
String className = (scanPackage + "." + file.getName()).replace(".class", "");
classNameList.add(className);
}
}
}
}
/**
* 从 web.xml 读取contextConfigLocation
把 {@code classpath*:application.properties} 载入配置
*/
private void doLoadConfig() {
InputStream resourceAsStream = null;
try {
// 获取servlet的配置
ServletConfig servletConfig = this.getServletConfig();
String configInitParameter = servletConfig.getInitParameter(CONTEXT_CONFIG_LOCATION)
.replace("classpath*:", "");
// 读取配置文件
resourceAsStream = this.getClass().getClassLoader()
.getResourceAsStream(configInitParameter);
SPRING_CONTEXT_CONFIG.load(resourceAsStream);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (resourceAsStream != null) {
try {
resourceAsStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@Override
protected void doPost(HttpServletRequest req,
HttpServletResponse resp) throws ServletException, IOException {
try {
dispath(req, resp);
} catch (Exception e) {
e.printStackTrace();
resp.getWriter().write(Arrays.toString(e.getStackTrace()));
}
}
private void dispath(HttpServletRequest req, HttpServletResponse resp) throws Exception {
String requestURI = req.getRequestURI();
String contextPath = req.getContextPath();
requestURI = requestURI.replaceAll(contextPath, "").replaceAll("/+", "/");
if (!HandlerMapping.containsKey(requestURI)) {
throw new RuntimeException("不存在的url");
}
Method method = HandlerMapping.get(requestURI);
if (method != null) {
// 获取 method 所在的class
String simpleName = method.getDeclaringClass().getSimpleName();
Object o = IOC_NAME.get(simpleName);
// 请求参数
Map parameterMap = req.getParameterMap();
// 参数动态赋值
Class>[] parameterTypes = method.getParameterTypes();
Object[] paramValues = new Object[parameterTypes.length];
for (int i = 0; i < paramValues.length; i++) {
// 获取 controller 中的参数类型
Class> parameterType = parameterTypes[i];
if (parameterType.equals(req.getClass())) {
paramValues[i] = req;
}
else if (parameterType.equals(resp.getClass())) {
paramValues[i] = resp;
}
// todo: 2020/7/25 参数内容的设置
else if (parameterType.equals(String.class)) {
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
for (Annotation a : parameterAnnotations[i]) {
if (a instanceof HFRequestParam) {
String paramName = ((HFRequestParam) a).name();
if (!"".equals(paramName.trim())) {
String value = Arrays.toString(parameterMap.get(paramName))
.replaceAll("\\[|\\]", "").replaceAll("\\s", ",");
paramValues[i] = value;
}
}
}
}
else if (parameterType.equals(Integer.class) || parameterType.equals(int.class)) {
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
for (Annotation a : parameterAnnotations[i]) {
if (a instanceof HFRequestParam) {
String paramName = ((HFRequestParam) a).name();
if (!"".equals(paramName.trim())) {
String value = Arrays.toString(parameterMap.get(paramName))
.replaceAll("\\[|\\]", "").replaceAll("\\s", ",");
paramValues[i] = Integer.valueOf(value);
}
}
}
}
}
Object invoke = method.invoke(o, paramValues);
resp.getWriter().write(invoke.toString());
}
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
try {
dispath(req, resp);
} catch (Exception e) {
e.printStackTrace();
resp.getWriter().write(Arrays.toString(e.getStackTrace()));
}
}
}