文章目录
- 1.创建Controller和自定义注解
- 1.目录
- 2.MonsterController.java 怪物控制器
- 3.Controller.java 自定义Controller注解
- 4.RequestMapping.java 自定义RequestMapping注解
- 2.dom4j解析sunspringmvc.xml
- 1.XMLParser.java
- 2.sunspringmvc.xml
- 3.XMLParserTest.java
- 4.结果
- 3.开发自己的Spring容器(简易版,所有实例均为单例)
- 1.目录
- 2.创建几个空的Controler和Service用于测试扫描
- 3.XMLParser.java 升级,解析spring配置文件,扫描所有component-scan元素,获取所有base-package属性值
- 4.sunspringmvc.xml 指定要扫描的基本包以及子包的类
- 5.SunWebApplicationContext.java 扫描所有包以及子包,并将有Controller注解的实例化到容器中
- 6.SunDispatcherServlet.java 中央控制器重写init方法,初始化容器
- 7.测试
- 4.初始化映射器
- 1.目录
- 2.修改一下controller和service的包路径
- 3.在TestConroller.java中加@Controller注解,测试没有方法的控制器
- 4.HandlerMapping.java 映射器实体类
- 5.SunHandler.java 扫描IOC容器,初始化映射器
- 6.SunDispatcherServlet.java 调用SunHandler初始化映射器
- 7.测试
- 5.完成请求分发到目标方法
- 1.目录
- 2.SunDispatcherServlet.java
- 1.获取请求路径,匹配映射器实体
- 2.请求分发
- 3.重写doGet和doPost方法,调用executeDispatch方法,统一处理请求
- 4.完整代码
- 3.测试
1.创建Controller和自定义注解
1.目录

2.MonsterController.java 怪物控制器
package com.sunxiansheng.springmvc.controller;import com.sunxiansheng.springmvc.annotation.Controller;
import com.sunxiansheng.springmvc.annotation.RequestMapping;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@Controller
public class MonsterController {private static final Logger log = LoggerFactory.getLogger(MonsterController.class);@RequestMapping(url = "/list/monster")public void listMonster(HttpServletRequest request, HttpServletResponse response) {response.setContentType("text/html;charset=utf-8");try {PrintWriter writer = response.getWriter();writer.write("<h1>monster1, monster2, monster3</h1>");log.info("MonsterController listMonster: monster1, monster2, monster3");} catch (IOException e) {throw new RuntimeException(e);}}}
3.Controller.java 自定义Controller注解
package com.sunxiansheng.springmvc.annotation;import java.lang.annotation.*;
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = ElementType.TYPE)
@Documented
public @interface Controller {String name() default "";}
4.RequestMapping.java 自定义RequestMapping注解
package com.sunxiansheng.springmvc.annotation;import java.lang.annotation.*;
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = {ElementType.TYPE, ElementType.METHOD})
@Documented
public @interface RequestMapping {String url() default "";}
2.dom4j解析sunspringmvc.xml
1.XMLParser.java
package com.sunxiansheng.springmvc.xml;import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;import java.io.InputStream;
public class XMLParser {public static String getBasePackage(String xmlPath) {SAXReader saxReader = new SAXReader();InputStream resourceAsStream = XMLParser.class.getClassLoader().getResourceAsStream(xmlPath);try {Document document = saxReader.read(resourceAsStream);Element rootElement = document.getRootElement();Element componentScanElement = rootElement.element("component-scan");return componentScanElement.attributeValue("base-package");} catch (Exception e) {throw new RuntimeException(e);}}}
2.sunspringmvc.xml
<?xml version="1.0" encoding="utf-8" ?>
<beans><component-scan base-package="com.sunxiansheng.springmvc.controller" />
</beans>
3.XMLParserTest.java
package com.sunxiansheng.springmvc.xml;import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class XMLParserTest {private static final Logger log = LoggerFactory.getLogger(XMLParserTest.class);@Testpublic void readXML() {String basePackage = XMLParser.getBasePackage("sunspringmvc.xml");log.info("basePackage: {}", basePackage);}
}
4.结果

3.开发自己的Spring容器(简易版,所有实例均为单例)
1.目录

2.创建几个空的Controler和Service用于测试扫描

3.XMLParser.java 升级,解析spring配置文件,扫描所有component-scan元素,获取所有base-package属性值
package com.sunxiansheng.springmvc.xml;import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
public class XMLParser {public static List<String> getBasePackages(String xmlPath) {List<String> basePackages = new ArrayList<>();SAXReader saxReader = new SAXReader();InputStream resourceAsStream = XMLParser.class.getClassLoader().getResourceAsStream(xmlPath);if (resourceAsStream == null) {throw new RuntimeException("Cannot find the specified XML file: " + xmlPath);}try {Document document = saxReader.read(resourceAsStream);Element rootElement = document.getRootElement();List<Element> componentScanElements = rootElement.elements("component-scan");for (Element element : componentScanElements) {String basePackage = element.attributeValue("base-package");if (basePackage != null && !basePackage.trim().isEmpty()) {basePackages.add(basePackage);}}} catch (Exception e) {throw new RuntimeException("Failed to parse XML file: " + xmlPath, e);}return basePackages;}
}
4.sunspringmvc.xml 指定要扫描的基本包以及子包的类
<?xml version="1.0" encoding="utf-8" ?>
<beans><component-scan base-package="com.sunxiansheng.springmvc.controller" /><component-scan base-package="com.sunxiansheng.springmvc.service" />
</beans>
5.SunWebApplicationContext.java 扫描所有包以及子包,并将有Controller注解的实例化到容器中
package com.sunxiansheng.springmvc.context;import com.sunxiansheng.springmvc.annotation.Controller;
import com.sunxiansheng.springmvc.xml.XMLParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
public class SunWebApplicationContext {private static final Logger log = LoggerFactory.getLogger(SunWebApplicationContext.class);private List<String> classFullPathList = new ArrayList<>();public ConcurrentHashMap<String, Object> ioc = new ConcurrentHashMap<>();public void init() {List<String> basePackages = XMLParser.getBasePackages("sunspringmvc.xml");log.info("Tomcat启动</load-on-startup>,init方法被调用,解析spring配置文件,获取要扫描的basePackages: {}" ,basePackages);if (!basePackages.isEmpty()) {for (String basePackage : basePackages) {scanPackage(basePackage);}}log.info("扫描的包以及子包的类的全路径列表:{}", classFullPathList);executeInstance();log.info("IOC容器:{}", ioc);}public void scanPackage(String path) {String scanPath = path.replace(".", "/");ClassLoader classLoader = SunWebApplicationContext.class.getClassLoader();URL resource = classLoader.getResource(scanPath);File dir = new File(resource.getFile());for (File file : dir.listFiles()) {if (file.isDirectory()) {scanPackage(path + "." + file.getName());} else {if (file.getName().endsWith(".class")) {String classFullPath = path + "." + file.getName().replace(".class", "");classFullPathList.add(classFullPath);}}}}public void executeInstance() {if (classFullPathList.isEmpty()) {return;}classFullPathList.forEach(classFullPath -> {try {Class<?> clazz = Class.forName(classFullPath);if (clazz.isAnnotationPresent(Controller.class)) {Object instance = clazz.getDeclaredConstructor().newInstance();Controller controller = clazz.getAnnotation(Controller.class);if ("".equals(controller.name())) {ioc.put(toLowerFirstWord(clazz.getSimpleName()), instance);} else {ioc.put(controller.name(), instance);}} else {}} catch (Exception e) {e.printStackTrace();}});}private String toLowerFirstWord(String simpleName) {char[] chars = simpleName.toCharArray();chars[0] += 32;return String.valueOf(chars);}}
6.SunDispatcherServlet.java 中央控制器重写init方法,初始化容器
package com.sunxiansheng.springmvc.servlet;import com.sunxiansheng.springmvc.context.SunWebApplicationContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class SunDispatcherServlet extends HttpServlet {private static final Logger log = LoggerFactory.getLogger(SunDispatcherServlet.class);@Overridepublic void init() throws ServletException {SunWebApplicationContext sunWebApplicationContext = new SunWebApplicationContext();sunWebApplicationContext.init();}@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {log.info("SunDispatcherServlet doGet");}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {log.info("SunDispatcherServlet doPost");}}
7.测试

4.初始化映射器
1.目录

2.修改一下controller和service的包路径

3.在TestConroller.java中加@Controller注解,测试没有方法的控制器

4.HandlerMapping.java 映射器实体类
package com.sunxiansheng.springmvc.handler;import java.lang.reflect.Method;
public class HandlerMapping {private String url;private Object controller;private Method method;public String getUrl() {return url;}public void setUrl(String url) {this.url = url;}public Object getController() {return controller;}public void setController(Object controller) {this.controller = controller;}public Method getMethod() {return method;}public void setMethod(Method method) {this.method = method;}@Overridepublic String toString() {return "HandlerMapping{" +"url='" + url + '\'' +", controller=" + controller +", method=" + method +'}';}
}
5.SunHandler.java 扫描IOC容器,初始化映射器
package com.sunxiansheng.springmvc.handler;import com.sunxiansheng.springmvc.annotation.Controller;
import com.sunxiansheng.springmvc.annotation.RequestMapping;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
public class SunHandler {private static final Logger log = LoggerFactory.getLogger(SunHandler.class);private static final List<HandlerMapping> HANDLER_MAPPING_LIST = new ArrayList<>();public void initHandlerMapping(ConcurrentHashMap<String, Object> ioc) {if (ioc.isEmpty()) {log.error("IOC容器为空,映射器初始化失败");return;}ioc.forEach((beanName, beanObject) -> {Class<?> clazz = beanObject.getClass();if (clazz.isAnnotationPresent(Controller.class)) {Method[] declaredMethods = clazz.getDeclaredMethods();if (declaredMethods.length == 0) {log.error("类{}没有方法", clazz.getName());} else {for (Method method : declaredMethods) {if (method.isAnnotationPresent(RequestMapping.class)) {RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);String url = requestMapping.url();HandlerMapping handlerMapping = new HandlerMapping();handlerMapping.setUrl(url);handlerMapping.setController(beanObject);handlerMapping.setMethod(method);HANDLER_MAPPING_LIST.add(handlerMapping);}}}}});log.info("映射器初始化完成:{}", HANDLER_MAPPING_LIST);}
}
6.SunDispatcherServlet.java 调用SunHandler初始化映射器

7.测试

5.完成请求分发到目标方法
1.目录

2.SunDispatcherServlet.java
1.获取请求路径,匹配映射器实体
private HandlerMapping getHandlerMapping(HttpServletRequest request) {String requestURI = request.getRequestURI();for (HandlerMapping handlerMapping : SunHandler.HANDLER_MAPPING_LIST) {if (requestURI.equals(handlerMapping.getUrl())) {log.info("匹配到映射器:{}", handlerMapping);return handlerMapping;}}return null;}
2.请求分发
private void executeDispatch(HttpServletRequest request, HttpServletResponse response) {HandlerMapping handlerMapping = getHandlerMapping(request);if (handlerMapping == null) {try {response.getWriter().write("404 NOT FOUND");} catch (Exception e) {throw new RuntimeException(e);}return;}Object controller = handlerMapping.getController();Method method = handlerMapping.getMethod();try {method.invoke(controller, request, response);} catch (Exception e) {throw new RuntimeException(e);}}
3.重写doGet和doPost方法,调用executeDispatch方法,统一处理请求

4.完整代码
package com.sunxiansheng.springmvc.servlet;import com.sunxiansheng.springmvc.context.SunWebApplicationContext;
import com.sunxiansheng.springmvc.handler.HandlerMapping;
import com.sunxiansheng.springmvc.handler.SunHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
public class SunDispatcherServlet extends HttpServlet {private static final Logger log = LoggerFactory.getLogger(SunDispatcherServlet.class);private SunWebApplicationContext sunWebApplicationContext = null;@Overridepublic void init() throws ServletException {sunWebApplicationContext = new SunWebApplicationContext();sunWebApplicationContext.init();SunHandler sunHandler = new SunHandler();sunHandler.initHandlerMapping(sunWebApplicationContext.ioc);}private void executeDispatch(HttpServletRequest request, HttpServletResponse response) {HandlerMapping handlerMapping = getHandlerMapping(request);if (handlerMapping == null) {try {response.getWriter().write("404 NOT FOUND");} catch (Exception e) {throw new RuntimeException(e);}return;}Object controller = handlerMapping.getController();Method method = handlerMapping.getMethod();try {method.invoke(controller, request, response);} catch (Exception e) {throw new RuntimeException(e);}}private HandlerMapping getHandlerMapping(HttpServletRequest request) {String requestURI = request.getRequestURI();for (HandlerMapping handlerMapping : SunHandler.HANDLER_MAPPING_LIST) {if (requestURI.equals(handlerMapping.getUrl())) {log.info("匹配到映射器:{}", handlerMapping);return handlerMapping;}}return null;}@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {executeDispatch(req, resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {executeDispatch(req, resp);}}
3.测试

