共计 15393 个字符,预计需要花费 39 分钟才能阅读完成。
手写 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
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<display-name>HuiFer web application</display-name>
<servlet>
<servlet-name>HuiFer mvc</servlet-name>
<servlet-class>org.huifer.spring.servlet.v1.HFDispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:application.properties</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>HuiFer mvc</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
- 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 <br> 把 {@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<String, Object> 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<String, Object> 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<String, String[]> 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<String> classNameList = new ArrayList<>();
private final Map<String, Method> HandlerMapping = new HashMap<>();
/**
*
*/
Map<String, Object> 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<String, Object> 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<String, Object> 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 <br> 把 {@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<String, String[]> 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()));
}
}
}
正文完