我在学习Spring的时候,感觉Spring是很难的,通过学习后,发现Spring没有那么难,只有你去学习了,你才会发现,你才会进步

1、手写Spring思路:

分为配置、初始化、运行三个阶段如下图

第一个阶段

配置阶段  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>HandWriting Web Application</display-name> <servlet>
<servlet-name>HandWriting MVC</servlet-name>
<servlet-class>com.wbg.framework.webmvc.servlet.HandWritingServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>application.properties</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>HandWriting MVC</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>

 在resources写properties文件

内容是需要扫描的包的路径

scanPackage=com.wbg.demo

第二、三个阶段

1、在初始化前,将注解自定义,我现在写了5个常用的注解,这些注解都是自定仪,如下图:

HandWritingAutowired
package com.wbg.framework.webmvc.annotation;

import java.lang.annotation.*;

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface HandWritingAutowired {
String value() default "";
}
HandWritingController
package com.wbg.framework.webmvc.annotation;

import java.lang.annotation.*;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface HandWritingController {
String value() default "";
}
HandWritingRequestMapping
package com.wbg.framework.webmvc.annotation;

import java.lang.annotation.*;

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface HandWritingRequestMapping {
String value() default "";
}
HandWritingRequestParam
package com.wbg.framework.webmvc.annotation;

import java.lang.annotation.*;

@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface HandWritingRequestParam {
String value() default "";
}
HandWritingService
package com.wbg.framework.webmvc.annotation;

import java.lang.annotation.*;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface HandWritingService {
String value() default "";
}

2、初始化阶段  init(ServletConfig config)

继承HttpServlet,重写init

3、运行阶段 doGet、doPost

继承HttpServlet,重写doGet、doPost

我这里创建一个 HandWritingServlet类继承HttpServlet

package com.wbg.framework.webmvc.servlet;

import javax.servlet.ServletConfig;
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 HandWritingServlet extends HttpServlet { @Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { } @Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { } @Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
}
}
  • 1、加载配置文件 获取web.xml文件的的param-name
  • 2、扫描所有相关联的类 //配置文件的scanPackage = com...
  • 3、初始化所有相关的类,并且将其保存到IOC容器之中
  • 4、执行依赖注入(把加了@Autoidwired注解的字段赋值)
  • 5、构造HandlerMapping,将URL和Method进行关联

运行阶段(doGet/doPost)

  • 1、通过request获得url,然后再去HandlerMapping
  • 2、用反射调用Method
  • 3、response.writer()

代码如下

package com.wbg.framework.webmvc.servlet;

import com.wbg.framework.webmvc.annotation.*;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
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.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern; public class HandWritingServlet extends HttpServlet {
//配置文件
private Properties contextConfig = new Properties();
//存配置文件 scanPackage=com.wbg.demo 下所有的类名
private List<String> classNames = new ArrayList<String>();
//IOC容器
private Map<String, Object> ioc = new HashMap<String, Object>();
//保存所有的URL和方法的映射关系
private List<Handler> handlerMapping = new ArrayList<Handler>(); @Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
this.doPost(req, resp);
} @Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
try {
doDispatch(req, resp);
} catch (Exception e) {
try {
resp.getWriter().write("500 =========" + e);
} catch (IOException e1) {
e1.printStackTrace();
}
}
} /**
* 初始化,加载配置
* @param config 配置文件
*/
@Override
public void init(ServletConfig config) { System.out.println("======初始化配置文件======");
//1、加载配置文件 获取web.xml文件的的param-name
doLoadConfig(config.getInitParameter("contextConfigLocation"));
//2、扫描所有相关联的类 //配置文件的scanPackage = com...
doScanner(contextConfig.getProperty("scanPackage"));
//3、初始化所有相关的类,并且将其保存到IOC容器之中
doInstance();
//4、执行依赖注入(把加了@Autoidwired注解的字段赋值)
doAutoWired(); //-------------------------Spring的核心功能已经完成 IOC DI注入 //5、构造HandlerMapping,将URL和Method进行关联
initHandlerMapping(); System.out.println("=========启动完毕============"); } /**
* 构造HandlerMapping,将URL和Method进行关联
*/
private void initHandlerMapping() {
if (ioc.isEmpty()) {
return;
}
for (Map.Entry<String, Object> entry : ioc.entrySet()) { Class<?> clazz = entry.getValue().getClass();
//如果该类上没有Controller 下一个
if (!clazz.isAnnotationPresent(HandWritingController.class)) {
continue;
} String baseUrl = "";
//如果该类上有RequestMapping注解
if (clazz.isAnnotationPresent(HandWritingRequestMapping.class)) {
HandWritingRequestMapping requestMapping = clazz.getAnnotation(HandWritingRequestMapping.class);
//获取RequestMapping注解的值
baseUrl = requestMapping.value();
}
//获取该类的所有方法
Method[] methods = clazz.getMethods(); for (Method method : methods) {
//如果该方法上面没有RequestMapping 下一个
if (!method.isAnnotationPresent(HandWritingRequestMapping.class)) {
continue;
} HandWritingRequestMapping requestMapping = method.getAnnotation(HandWritingRequestMapping.class);
//获取RequestMapping注解的值
String regex = requestMapping.value();
//将 类上、方法上 的RequestMapping 注解相加 得到一个url
regex = (baseUrl + regex).replaceAll("/+", "/");
Pattern pattern = Pattern.compile(regex);
handlerMapping.add(new Handler(pattern, entry.getValue(), method));
System.out.println("handlerMapping:" + regex);
} } } /**
* 执行依赖注入 将加了AutoWired注解的字段进行赋值
* 注入的意思就是把所有IOC容器中加了@Autowired注解的字段全部赋值
*/
private void doAutoWired() {
if (ioc.isEmpty()) {
return;
}
for (Map.Entry<String, Object> entry : ioc.entrySet()) {
//获取该类的声明字段 包过私有的字段
Field[] fields = entry.getValue().getClass().getDeclaredFields();
//循环加入IOC容器
for (Field field : fields) {
//如果字段不加Autowired注解 下一个
if (!field.isAnnotationPresent(HandWritingAutowired.class)) {
continue;
}
//获取这个字段上的Autowired注解
HandWritingAutowired autowired = field.getAnnotation(HandWritingAutowired.class);
//获取注解上的value
String beanName = autowired.value().trim();
//如果为空
if ("".equals(beanName)) {
//获取这个字段的名字
beanName = field.getType().getName();
}
//如果这个字段是私有的字段,强制访问
field.setAccessible(true);
try {
//赋值
field.set(entry.getValue(), ioc.get(beanName));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
} /**
* 初始化所有相关的类
* 加了注解的才初始化
*/
private void doInstance() {
if (classNames.isEmpty()) {
return;
}
for (String className : classNames) {
try {
Class<?> clazz = Class.forName(className);
//只有加了注解的 才初始化
if (clazz.isAnnotationPresent(HandWritingController.class)) {
Object instance = clazz.newInstance();
String beanName = lowerFirstClass(clazz.getSimpleName());
ioc.put(beanName, instance);
} else if (clazz.isAnnotationPresent(HandWritingService.class)) {
HandWritingService service = clazz.getAnnotation(HandWritingService.class);
//2、自定义命名,优先使用自定义命名
String beanName = service.value();
//1、默认类名首字母小写
if ("".equals(beanName.trim())) {
beanName = lowerFirstClass(clazz.getSimpleName());
}
Object instance = clazz.newInstance();
ioc.put(beanName, instance); // 3、自动类型匹配(例如将实现类赋值给接口)
Class<?>[] instances = clazz.getInterfaces();
for (Class<?> i : instances) {
ioc.put(i.getName(), instance);
}
} else {
continue;
} } catch (Exception e) {
e.printStackTrace();
}
}
} /**
* 将字符串的首字母变成小写 利用ASCII转换
* @param simpleName
* @return
*/
private String lowerFirstClass(String simpleName) {
char[] c = simpleName.toCharArray();
c[0] += 32;
return String.valueOf(c);
} protected void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
Handler handler = getHandler(req);
//如果为空 没有这个请求路径
if (handler == null) {
resp.getWriter().write("=========handWritingSpring=============404==========");
return;
}
try {
//获取方法的参数列表
Class<?>[] paramTypes = handler.method.getParameterTypes();
//保存所有需要自动赋值的参数值
Object[] paramValues = new Object[paramTypes.length];
//获取方法上的参数
Map<String, String[]> params = req.getParameterMap();
for (Map.Entry<String, String[]> param : params.entrySet()) {
//转换为String 去掉数组[]两边 去掉字符的空白
String value = Arrays.toString(param.getValue()).replaceAll("\\[|\\]", "").replaceAll("\\s", "");
//如果找到匹配的对象,则开始填充参数值
if (!handler.paramIndexMapping.containsKey(param.getKey())) {
continue;
}
int index = handler.paramIndexMapping.get(param.getKey());
paramValues[index] = convert(paramTypes[index], value);
}
int reqIndex = handler.paramIndexMapping.get(HttpServletRequest.class.getName());
paramValues[reqIndex] = req;
int respIndex = handler.paramIndexMapping.get(HttpServletResponse.class.getName());
paramValues[respIndex] = resp;
handler.method.invoke(handler.controller, paramValues);
} catch (Exception e) {
throw e;
}
} private Handler getHandler(HttpServletRequest req) throws Exception {
if (handlerMapping.isEmpty()) {
return null;
}
String url = req.getRequestURI();
String contextPath = req.getContextPath();
url = url.replaceAll(contextPath, "").replaceAll("/+", "/");
for (Handler handler : handlerMapping) {
try {
Matcher matcher = handler.pattern.matcher(url);
//如果没有匹配上继续下一个匹配
if (!matcher.matches()) {
continue;
}
return handler;
} catch (Exception e) {
throw e;
} }
return null;
} /**
* 转换类型Integer
* @param type
* @param value
* @return
*/
private Object convert(Class<?> type, String value) {
if (Integer.class == type) {
return Integer.valueOf(value);
}
return value;
} /**
* 扫描basePackage路径下所以的类
* @param basePackage
*/
private void doScanner(String basePackage) {
//获取路径
URL url = this.getClass().getClassLoader().getResource("/" + basePackage.replaceAll("\\.", "/"));
File dir = new File(url.getFile());
for (File file : dir.listFiles()) {
//如果是一个文件夹
if (file.isDirectory()) {
//递归
doScanner(basePackage + "." + file.getName());
} else {
//获取类名
String className = basePackage + "." + file.getName().replace(".class", "");
//将所有项目所有类存起来
classNames.add(className);
}
}
} /**
* 加载配置文件
* @param contextConfigLocation
*/
private void doLoadConfig(String contextConfigLocation) { InputStream is = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
try {
contextConfig.load(is);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (null != is) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
} /**
* Handler记录Controller中的RequestMapping和Method对应的关系
*/
private class Handler {
protected Object controller; //保存方法对应的实例
protected Method method; //保存映射的方法
protected Pattern pattern; //RequestMapping存的URL
protected Map<String, Integer> paramIndexMapping; //参数顺序 /**
* 基本参数
* @param pattern
* @param controller
* @param method
*/
protected Handler(Pattern pattern, Object controller, Method method) {
this.pattern = pattern;
this.controller = controller;
this.method = method;
paramIndexMapping = new HashMap<String, Integer>();
putParamIndexMapping(method); } /**
* 保存RequestParam注解的参数值
* @param method 类的方法
*/
private void putParamIndexMapping(Method method) {
//获取方法中加了注解的参数
Annotation[][] pa = method.getParameterAnnotations();
for (int i = 0; i < pa.length; i++) {
for (Annotation annotation : pa[i]) {
//如果使用到了HandWritingRequestParam注解
if (annotation instanceof HandWritingRequestParam) {
//获取该注解的值
String paramName = ((HandWritingRequestParam) annotation).value();
//如果这个值不为空
if (!"".equals(paramName.trim())) {
//保存参数
paramIndexMapping.put(paramName, i);
}
}
}
}
//提取方法中的request和response参数
Class<?>[] paramsTypes = method.getParameterTypes();
for (int i = 0; i < paramsTypes.length; i++) {
Class<?> type = paramsTypes[i];
if (type == HttpServletRequest.class || type == HttpServletResponse.class) {
paramIndexMapping.put(type.getName(), i);
}
} } }
}

以上代码完成后Spring就ok了,现在开始使用,我写的demo如下:

1、首先创建接口  DemoService

public interface DemoService {
String get(String name);
String sum(int a,int b);
String remove(String id);
}

2、在类上使用刚刚自定义的注解  @HandWritingService  调用DemoService

@HandWritingService
public class DemoServiceImp implements DemoService {
public String get(String name) {
return "My name is " + name;
} public String sum(int a,int b) {
return "a + b = " + (a+b);
} public String remove(String id) {
return "remove id = " + id;
}
}

3、开始写Controll控制器

1、在类上使用 @HandWritingController 相当于 Controlle

    @HandWritingRequestMapping 相当于RequestMapping

2、字段 注入   @HandWritingAutowired  相当于 Autowired

3、方法上使用 @HandWritingRequestMapping("/sum.json") 和类上一样,当然可以写一个注解 GetRequestMapping  get请求

4、参数上使用 @HandWritingRequestParam("name")  相当于 RequestParam

代码如下:

package com.wbg.demo.mvc.action;

import com.wbg.demo.service.DemoService;
import com.wbg.framework.webmvc.annotation.HandWritingAutowired;
import com.wbg.framework.webmvc.annotation.HandWritingController;
import com.wbg.framework.webmvc.annotation.HandWritingRequestMapping;
import com.wbg.framework.webmvc.annotation.HandWritingRequestParam; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException; @HandWritingController
@HandWritingRequestMapping("/demo")
public class DemoAction {
@HandWritingAutowired
private DemoService demoService; @HandWritingRequestMapping("/query.json")
public void query(HttpServletRequest req, HttpServletResponse resp, @HandWritingRequestParam("name")String name){
String result = demoService.get(name);
try{
resp.getWriter().write(result);
}catch (IOException e){
e.printStackTrace();
}
}
@HandWritingRequestMapping("/sum.json")
public void sum(HttpServletRequest req, HttpServletResponse resp, @HandWritingRequestParam("a") Integer a, @HandWritingRequestParam("b") Integer b){
String result = demoService.sum(a,b);
try{
resp.getWriter().write(result);
}catch (IOException e){
e.printStackTrace();
}
}
@HandWritingRequestMapping("/remove.json")
public void remove(HttpServletRequest req, HttpServletResponse resp, @HandWritingRequestParam("id") String id){
String result = demoService.remove(id);
try{
resp.getWriter().write(result);
}catch (IOException e){
e.printStackTrace();
}
}
}

现在启动服务,验证一下,如下图

项目demo:https://github.com/weibanggang/handwritingspring

对你有帮助,麻烦点下星,谢谢

手写Spring+demo+思路的更多相关文章

  1. 一个老程序员是如何手写Spring MVC的

    人见人爱的Spring已然不仅仅只是一个框架了.如今,Spring已然成为了一个生态.但深入了解Spring的却寥寥无几.这里,我带大家一起来看看,我是如何手写Spring的.我将结合对Spring十 ...

  2. 我是这样手写 Spring 的(麻雀虽小五脏俱全)

    人见人爱的 Spring 已然不仅仅只是一个框架了.如今,Spring 已然成为了一个生态.但深入了解 Spring 的却寥寥无几.这里,我带大家一起来看看,我是如何手写 Spring 的.我将结合对 ...

  3. 手写 Spring

    手写 Spring 不多说,简历装 X 必备.不过练好还是需要求一定的思维能力. 一.整体思路 思路要熟练背下来 1)配置阶段 配置 web.xml: XDispatchServlet 设定 init ...

  4. 我是这样手写Spring的,麻雀虽小五脏俱全

    人见人爱的Spring已然不仅仅只是一个框架了.如今,Spring已然成为了一个生态.但深入了解Spring的却寥寥无几.这里,我带大家一起来看看,我是如何手写Spring的.我将结合对Spring十 ...

  5. 30个类手写Spring核心原理之动态数据源切换(8)

    本文节选自<Spring 5核心原理> 阅读本文之前,请先阅读以下内容: 30个类手写Spring核心原理之自定义ORM(上)(6) 30个类手写Spring核心原理之自定义ORM(下)( ...

  6. 手写Spring框架,加深对Spring工作机制的理解!

    在我们的日常工作中,经常会用到Spring.Spring Boot.Spring Cloud.Struts.Mybatis.Hibernate等开源框架,有了这些框架的诞生,平时的开发工作量也是变得越 ...

  7. 手写Spring MVC

    闲及无聊 又打开了CSDN开始看一看有什么先进的可以学习的相关帖子,这时看到了一位大神写的简历装X必备,手写Spring MVC. 我想这个东西还是有一点意思的 就拜读了一下大佬的博客 通读了一遍相关 ...

  8. 手写Spring事务框架

    Spring事务基于AOP环绕通知和异常通知 编程事务 声明事务 Spring事务底层使用编程事务+AOP进行包装的   = 声明事务 AOP应用场景:  事务 权限 参数验证 什么是AOP技术 AO ...

  9. Spring学习之——手写Spring源码V2.0(实现IOC、D、MVC、AOP)

    前言 在上一篇<Spring学习之——手写Spring源码(V1.0)>中,我实现了一个Mini版本的Spring框架,在这几天,博主又看了不少关于Spring源码解析的视频,受益匪浅,也 ...

随机推荐

  1. 使用 Docker Alpine 镜像安装 nginx

    微镜像Alpine,Alpine Linux 是一款独立的⾮商业性的通⽤ Linux 发行版,Alpine Linux 围绕 musl libc 和 busybox 构建,尽管体积很小,Apline ...

  2. nrm : 无法加载文件 C:\Users\TANG\AppData\Roaming\npm\nrm.ps1,因为在此系统上禁止运行脚本。

    1.win+s 搜索powershell 以管理身份运行 2.使用set-ExecutionPolicy RemoteSigned命令将计算机上的执行策略更改为 RemoteSigned,输入Y确定 ...

  3. 使用python实现后台系统的JWT认证

    介绍 JWT协议似乎已经应用十分广泛,JSON Web Token--一种基于token的json格式web认证方法.基本的原理是,第一次认证通过用户名密码,服务端签发一个json格式的token.后 ...

  4. 三层架构介绍和MVC设计模型介绍

    springmvc是什么? Spring Web MVC是一种基于Java的实现了Web MVC设计模式的请求驱动类型的轻量级Web框架,即使用了MVC架构模式的思想,将web层 进行职责解耦,基于请 ...

  5. Linux-CentOS-Nginx安装

    原文转自 jerryhe326:https://www.cnblogs.com/jerrypro/p/7062101.html 一.安装准备 首先由于nginx的一些模块依赖一些lib库,所以在安装n ...

  6. 16、基于状态的iptable+高级路由(重点)

    --   基于状态的iptables   如果按照tcp/ip来划分连接状态,有12种之多 但iptables里只有4种状态:ESTABLISHED.NEW.RELATED及INVALID   这两个 ...

  7. pytest中怎么引用前置中的变量

    本文主要总结的是pytest中的前置后置怎么返回参数 我们在做自动化的过程中,如果运用的是pytest的话,会遇到怎么使用前置条件中的变量 比如: @pytest.fixture() def init ...

  8. css 盒子下

    1.padding 有小属性 padding-top: 30px; padding-right: 30px; padding-bottom: 30px; padding-left: 30px; 小属性 ...

  9. pdfium 之二

    https://www.foxitsoftware.cn/products/premium-pdfium/feature.php 基于谷歌PDFium开源代码 谷歌采用福昕的PDF技术为其PDF开源项 ...

  10. 在 Oracle 中使用正则表达式

    Oracle使用正则表达式离不开这4个函数: 1.regexp_like 2.regexp_substr 3.regexp_instr 4.regexp_replace 看函数名称大概就能猜到有什么用 ...