手写Spring+demo+思路
我在学习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
对你有帮助,麻烦点下星,谢谢