波及知识点:
- slf4j
- java annotation 如何定义
- 通过 classloader 获取 java 文件的门路等信息 – https://www.cnblogs.com/seven…
- map 遍历 https://blog.csdn.net/tjcyjd/… 办法二
- class.forname 实例化 https://www.cnblogs.com/shosk…
- java 反射中的调用办法 – https://www.cnblogs.com/qingc…
- java 获取我的项目门路的形式
第一局部, 工程根底配置
- 创立 Dynamic web 工程
- 为工程增加 runtime librariy. 目标: 为了实现 servlet 性能 (次要须要其中的 servlet-api 等 jar 文件) 此为 servlet 基础知识.
- 创立 DispatcherServlet 并继承 HttpServlet
-
web.xml 中申明此 dispatcherServlet, 目标: ①示意这是个会被 tomcat 容器辨认的 servlet, ②拦挡所有申请
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <display-name>SpringSim2</display-name> <servlet> <servlet-name>springsim2</servlet-name> <servlet-class>com.spring.sim.DispatcherServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>springsim2</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> </web-app>
-
src 下创立 application.properties, 指定须要被扫描的包
path=com.spring.sim
-
web.xml 中将 properties 文件设置为启动时被 load
⚠ 此步骤为可选, 在 DispatcherServlet 中申明也能够
此步实现后, web.xml 不须要再改变
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <display-name>SpringSimv1</display-name> <servlet> <servlet-name>springsim</servlet-name> <servlet-class>pro.yizheng.DispatcherServlet</servlet-class> <!-- 指定配置文件 --> <init-param> <param-name>webXmlInitParam-properties</param-name> <param-value>application.properties</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springsim</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> </web-app>
-
申明要用到的 Annotation
- 波及到的 annotation 有: Autowired, Controller, RequestMapping, RequestParam, Service
- 波及 Annotation 知识点
- 这一步骤
-
Annotation source code:
@Documented @Retention(RUNTIME) @Target(FIELD) public @interface SSAutowired {String value() default ""; } @Documented @Retention(RUNTIME) @Target({TYPE}) public @interface SSController {String value() default ""; } @Documented @Retention(RUNTIME) @Target({TYPE,METHOD}) public @interface SSRequestMapping {String value() default ""; } @Documented @Retention(RUNTIME) @Target(PARAMETER) public @interface SSRequestParam {String value() default ""; } @Documented @Retention(RUNTIME) @Target(TYPE) public @interface SSService {String value() default ""; }
-
编写 service 类, 目标: 模仿 spring 组件的性能
@SSService public class Service {public void get(String name) {System.out.println(name); } }
-
编写 Action, 目标: 模仿 Springmvc 的 action, 实际上模仿的 Action 曾经和 SpringMVC 的雷同了
@SSController @SSRequestMapping("/demo") public class DemoAction { @SSAutowired private SSDemoService service; @SSRequestMapping("/query") public void query(HttpServletRequest requeust, HttpServletResponse response, @SSRequestParam("name") String name) {String reString = service.get(name); try {response.getWriter().write(reString); } catch (Exception e) {e.printStackTrace(); } } @SSRequestMapping("/add") public void add(HttpServletRequest requeust, HttpServletResponse response, @SSRequestParam("name") String name) { try {response.getWriter().write("this is add"); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace();} } }
第二局部, 编写 DispatcherServlet
DispatcherServlet 是整个工程的大脑, 它负责整个容器的的初始化, 资源读取治理和申请散发.
此工程中, dispatcherServlet 分为以下几个局部:
-
初始化容器
- 遍历工程所有子节点, 找到并记录所有 Java 类型文件, 为后续解析并获取 annotation 注解的办法和变量发明先决条件
- 从 java 文件中获取被 Annotation 正文的办法和 Field, 并将其记录
-
接管并解决申请
- 依据申请门路., 找到记录中对应 Action, 通过反射调用 Action 对应的办法
看起来仿佛很简略, 但实现起来还是有一些麻烦的.
上面咱们开始.
-
编写初始化办法 – 此办法名无奈更改, 只能是 init 办法, 这是 Servlet 标准
而在 init 办法中, 第一步须要提取出所有的 Java 类型文件, 每一个文件都要对应一个或多个门路, 所以咱们
- 新建一个全局 map<String, Object>
private Map<String, Object> javaFiles = new HashMap<>();
- 遍历工程, 获取所有 Java 类, 此时咱们新建一个办法, 将其从 init 办法中抽离进去
private void scanJavaFiles(String path) { // 通过 application.properties 中设定的包名, 找到其下所有的 java class 文件 File pathDir = new File(this.getClass().getResource(path).getPath()); String[] pathList = pathDir.list(); for (String temFile : pathList) {File tempFile = new File(pathDir+ "/" + temFile); // 如果是目录的话, 即递归操作 if (tempFile.isDirectory()) {scanJavaFiles(path + "/" + temFile); } // 非目录, 失去子节点 if (temFile.endsWith(".class")) {javaFiles.put(path+ "/" + temFile.replace(".class", ""), null); } } }
-