目录的大致情况:所有的类都会加进来.

1.首先先写基本的Controller Service ServiceImpl

Controller Service ServiceImpl是用来验证下面写的框架信息是否正常运行

package demo.controller;

import demo.service.HelloService;
import framework.annotation.FAutowried;
import framework.annotation.FController;
import framework.annotation.FRequestMapping;
import framework.annotation.FRequestParam; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter; /**
* DemoController.
*
* @author Feng Yongkang, 2019/9/9
* @version v1.0
*/
@FController
@FRequestMapping("/demo")
public class HelloController {
@FAutowried
private HelloService service; @FRequestMapping("/getName")
public void getName(HttpServletRequest req, HttpServletResponse resp, @FRequestParam("name") String name) {
service.firstServlet(name);
try {
System.out.println(name);
PrintWriter writer = resp.getWriter();
writer.write(name);
writer.flush();
writer.close();
} catch (IOException e) {
e.printStackTrace();
} finally { } }
}

  

package demo.service;

/**
* HelloService.
*
* @author Feng Yongkang, 2019/9/10
* @version v1.0
*/
public interface HelloService {
void firstServlet(String str);
}

  

package demo.service.impl;

import demo.service.HelloService;
import framework.annotation.FService; /**
* HelloService.
*
* @author Feng Yongkang, 2019/9/10
* @version v1.0
*/
@FService
public class HelloServiceImpl implements HelloService {
public void firstServlet(String str) {
System.out.println("你好,我接收到一个参数:" + str);
}
}

2.需要用的注解

这里是考虑最简单常用的几个注解.可以支持简单的请求处理

package framework.annotation;

import java.lang.annotation.*;

@Target(ElementType.FIELD)//这个注解的使用范围
@Retention(RetentionPolicy.RUNTIME)//生命周期
@Documented
public @interface
FAutowried {
String value() default "";
}

  

package framework.annotation;

import java.lang.annotation.*;

@Target(ElementType.TYPE)//这个注解的使用范围
@Retention(RetentionPolicy.RUNTIME)//生命周期
@Documented
public @interface FController {
String value() default "";
}

  

package framework.annotation;

import java.lang.annotation.*;

@Target({ElementType.METHOD, ElementType.TYPE})//这个注解的使用范围
@Retention(RetentionPolicy.RUNTIME)//生命周期
@Documented
public @interface FRequestMapping {
String value() default "";
}

  

package framework.annotation;

import java.lang.annotation.*;

@Target(ElementType.PARAMETER)//这个注解的使用范围
@Retention(RetentionPolicy.RUNTIME)//生命周期
@Documented
public @interface FRequestParam {
String value() default "";
}

  

package framework.annotation;

import java.lang.annotation.*;

@Target(ElementType.TYPE)//这个注解的使用范围
@Retention(RetentionPolicy.RUNTIME)//生命周期
@Documented
public @interface FService {
String value() default "";
}

3.核心DispatcherServlet

这个是关键.所有的功能全包含在这里.

dispatcherServlet查看顺序,先看init方法(里面方法步骤一个一个看).init方法执行完毕,则Spring容器也就启动完毕.

再看service方法,如何处理请求.一个请求到达的时候,根据uri去HandlerMapping用适合的方法处理请求.

package framework;

import framework.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.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern; /**
* FDispatcherServlet.
*
* @author Feng Yongkang, 2019/9/9
* @version v1.0
*/
public class FDispatcherServlet1 extends HttpServlet {
//resources下的配置文件中的内容
private Properties contextConfig = new Properties(); //指定要扫描路径的下的类名的集合
private List<String> classNames = new ArrayList<String>(); //classNames中被注解标识为bean的类实例
private Map<String, Object> ioc = new HashMap<String, Object>(); //保存所有url与映射的关系
private List<Handler> handlerMappping = new ArrayList<Handler>(); @Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp); } @Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
doDispatch(req, resp);
} catch (Exception e) {
e.printStackTrace();
resp.getWriter().write("500 EXCEPTION");
}
} /**
* 根据uri得到匹配的Handler.根据Handler中的paramIndexMapping填充方法中的参数.
* 参数是实参,根据请求中的数据获取的.
* 通过代理模式,执行ioc容器下保存的Controller的bean对应的URI的方法.完成请求处理.
*
* @param req
* @param resp
* @throws IOException
*/
private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
try {
//查找与请求匹配的handler
Handler handler = getHandler(req);
if (handler == null) {
resp.getWriter().write("404 NOT FOUND");
return;
}
//获得方法的形参类型
Class<?>[] parameterTypes = handler.method.getParameterTypes();
//在调用invoke代理时,根据参数的位置,传递参数用.
Object[] paramValues = new Object[parameterTypes.length]; //获得请求中的所有参数,并遍历,看请求中参数的key是否出现
Map<String, String[]> params = req.getParameterMap(); for (Map.Entry<String, String[]> param : params.entrySet()) {
String value = Arrays.toString(param.getValue()).replaceAll("\\[|\\]", "").replaceAll("/+", "/");
//如果找到对象开始填充参数值
if (!handler.paramIndexMapping.containsKey(param.getKey())) {
continue;
}
Integer index = handler.paramIndexMapping.get(param.getKey());
if (index != null) {
paramValues[index] = convert(parameterTypes[index], value);
}
}
//设置方法中的request与response对象,如果方法中有这个key就返回对应的value如果没有就返回null
Integer reqIndex = handler.paramIndexMapping.get(HttpServletRequest.class.getName());
if (reqIndex != null) {
paramValues[reqIndex] = req;
}
Integer respIndex = handler.paramIndexMapping.get(HttpServletResponse.class.getName());
if (respIndex != null) {
paramValues[respIndex] = resp;
} handler.method.invoke(handler.controller, paramValues);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
} @Override
public void init(ServletConfig config) throws ServletException {
//1 加载配置文件
doLoadConfig(config.getInitParameter("contextConfigLocation"));
//2 扫描所有相关的类
doScanner(contextConfig.getProperty("scanPackage"));
//3 初始化所有相关的类
doInstance();
//4 自动注入.到这里Spring的功能已经完成
doAutowired();
//5 初始HandlerMapping,这里开始就是SpringMVC的功能
doHandlerMapping();
System.out.println("Spring初始化完成");
} /**
* 遍历ioc容器的中bean的方法.private的方法一般不作为请求.
* public类型的被FAutowried修饰.根据类上面的注解和方法上面的注解,生成uri转为正则方便后续的匹配.
* uri的正则,Controller,还有对应的方法组成一个Handler.放入List集合中.方便请求时生成代理,处理请求.
*/
private void doHandlerMapping() {
if (ioc.isEmpty()) {
return;
}
for (Map.Entry<String, Object> entry : ioc.entrySet()) {
Class<?> claszz = entry.getValue().getClass();
if (!claszz.isAnnotationPresent(FController.class)) {
continue;
}
String baseUrl = "";
//获取公用uri
if (claszz.isAnnotationPresent(FRequestMapping.class)) {
FRequestMapping annotation = claszz.getAnnotation(FRequestMapping.class);
baseUrl = annotation.value();
}
//扫描所有公共方法
for (Method method : claszz.getMethods()) {
if (!method.isAnnotationPresent(FRequestMapping.class)) {
continue;
}
FRequestMapping annotation = method.getAnnotation(FRequestMapping.class);
//如果用户的FRequestMapping注解中没有/,在类注解和方法注解中,拼接的时候给他加上,防止用户加了,在拼接后的URI中,把多于一个/换成一个/
String methodUrl = ("/" + baseUrl + "/" + annotation.value()).replaceAll("/+", "/");
//handlerMapping.put(methodUrl, method);
Pattern pattern = Pattern.compile(methodUrl);
//pattern:uri的正则匹配格式,method:处理对应请求的方法,entry.getValue():方法所在的实例化后放在ioc中的bean(Controller实例)
handlerMappping.add(new Handler(entry.getValue(), method, pattern));
}
}
} /**
* 迭代ioc容器中所有bean的字段.如果这个字段有FAutowried修饰.
* 1.优先根据注解的别名去ioc容器中查找对应的实例进行注入.
* 2.字段所属类型的类型全名(一般写的都是接口类型)
* 3.没有考虑Controller通过接口的实现进行声明而不是接口类型进行声明.
*/
private void doAutowired() {
if (ioc.isEmpty()) {
return;
}
//循环IOC中的所有类,然后对需要赋值的属性进行赋值
for (Map.Entry<String, Object> entry : ioc.entrySet()) {
//获得bean实例中所有字段,判断字段是否有@Autowried,
// 如果有,优先注解自定义的属性名,默认字段对应的类型名(在放入IOC时,对于接口类型,直接接口的全名作为key,对于接口实现类,类名首字母小写作为类型名)
Field[] declaredFields = entry.getValue().getClass().getDeclaredFields();
for (Field declaredField : declaredFields) {
if (!declaredField.isAnnotationPresent(FAutowried.class)) {
continue;
}
FAutowried annotation = declaredField.getAnnotation(FAutowried.class);
String beanName = annotation.value().trim();
if ("".equals(beanName)) {
beanName = declaredField.getType().getName();
}
//根据key去容器中查找,注入到字段中.
boolean accessible = declaredField.isAccessible();
declaredField.setAccessible(true);
try {
declaredField.set(entry.getValue(), ioc.get(beanName));//注入
} catch (IllegalAccessException e) {
e.printStackTrace();
}
declaredField.setAccessible(accessible);
}
}
} /**
* 把classNames中保存的符合的作为bean的类进行实例化并放入到容器中
* 生成的bean的命名规则1.默认类名首字母小写 2.优先使用自定义名字 3.如果是一个接口,接口全名作为key,
* 如果一个接口有多个实例就会报错,体现在.查看这个实例的接口名是否已经存在,已经存在则说明,这个接口有别的实现类.
*/
private void doInstance() {
if (classNames.isEmpty()) {
return;
}
try {
for (String className : classNames) {
Class<?> clazz = Class.forName(className);
//实例化加了注解的类
//Controller注解的类实例化
if (clazz.isAnnotationPresent(FController.class)) {
String beanName = lowerFirstCase(clazz.getSimpleName());
ioc.put(beanName, clazz.newInstance());
//Service注解的类的实例化
} else if (clazz.isAnnotationPresent(FService.class)) {
//获得这个注解,看注解中是否有自定义的注解名,自定义的优先,然后是默认的类名首字符小写
//1.自定义的beanName
FService annotation = clazz.getAnnotation(FService.class);
String beanName = annotation.value();
//2.默认首字母小写的beanName
if ("".equals(beanName)) {
beanName = lowerFirstCase(clazz.getSimpleName());
}
Object instance = clazz.newInstance();
ioc.put(beanName, instance);
//3.这个类如果有接口.
//getInterfaces获得这个类所实现的所有接口,如果这个类对应的接口的getName已经存在,证明,这个接口不止自己一个实现类.
//一个接口如果有多个实现类,在注入的时候不知道注入哪一个,如果存在这种情况抛出异常.不存在就以接口名为key,存入实现类的实例.
for (Class<?> anInterface : clazz.getInterfaces()) {
if (ioc.containsKey(anInterface.getName())) {
throw new Exception("The BeanName is exists");
}
ioc.put(anInterface.getName(), instance);
}
} else {
continue;
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
} /**
* 把指定的路径下所有的class类的类名保存到classNames集合中.
*
* @param scanPackage 要扫描的包路径
*/
//得到所有的class.
private void doScanner(String scanPackage) {
//要把所有的文件路径处理成包路径
URL url = this.getClass().getClassLoader().getResource("/" + scanPackage.replaceAll("\\.", "/"));
File classDir = new File(url.getFile());
for (File file : classDir.listFiles()) {
if (file.isDirectory()) {
doScanner(scanPackage + "." + file.getName());
} else {
if (!file.getName().endsWith(".class")) {
//如果不是class文件进行下一次循环.
continue;
}
String className = scanPackage + "." + file.getName().replace(".class", "");
classNames.add(className);
}
}
} /**
* 加载外部的配置文件.这里用比较简单的properties;key-value类型的对象.
*
* @param contextConfigLocation 对应web.xml中的配置的资源文件的路径
*/
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();
}
}
}
} /**
* 把一个首字符大写的字符串转为首字母小写的字符串.
*
* @param str 首字母大写的字符串
* @return 首字母小写的字符串
*/
private String lowerFirstCase(String str) {
char[] chars = str.toCharArray();
chars[0] += 32;
return String.valueOf(chars);
} /**
* 根据请求路径获得与路径匹配的Handler
*
* @param req
* @return
* @throws Exception
*/
private Handler getHandler(HttpServletRequest req) throws Exception {
if (handlerMappping.isEmpty()) {
return null;
}
//绝对路径
String requestURI = req.getRequestURI();
//绝对路径中URI之外的路径
String contextPath = req.getContextPath();
requestURI.replace(contextPath, "").replaceAll("/+", "/");
for (Handler handler : handlerMappping) {
try {
//比较请求中的URL与存在handler中的是否匹配
Matcher matcher = handler.pattern.matcher(requestURI);
//如果没有继续下一个匹配
if (!matcher.matches()) {
continue;
}
return handler;
} catch (Exception e) {
throw e;
}
}
return null;
} private Object convert(Class<?> type, String value) {
if (Integer.class == type) {
return Integer.valueOf(value);
}
return value;
} /**
* Handler 记录Conroller中的RequestMapping和Method的对应关系
*/
private class Handler {
protected Object controller;
protected Method method;
protected Pattern pattern;
protected Map<String, Integer> paramIndexMapping;//参数的别名,或者类型名作为key,位置作为value. public Handler(Object controller, Method method, Pattern pattern) {
this.controller = controller;//保存方法对应的实例
this.method = method;//保存映射的方法
this.pattern = pattern;
paramIndexMapping = new HashMap<String, Integer>();//参数顺序
putParamIndexMapping(method);
} /**
* 把Controller中方法中的参数有注解的根据注解名,或者request response类型的参数放入到map中
* 只考虑有FRequestParam注解的别名与request,response的类型名.作为key,在参数中的位置作为value.
* 在调用invoke方法的时候,可以按照参数顺序进行传参.
*
* @param method
*/
private void putParamIndexMapping(Method method) {
//1.一个参数可能有很多注解,一个方法有可能有很多参数,所以返回一个注解的二维数组.如果注解里面指定了参数的参数的别名,就把参数别名作为key,参数位置作为value放入map.
//这里仅仅考虑FRequestParam注解,像验证,RequestBody,没有注解没有考虑.
Annotation[][] pa = method.getParameterAnnotations();
for (int i = 0; i < pa.length; i++) {
for (Annotation a : pa[i]) {
if (a instanceof FRequestParam) {
String paranName = ((FRequestParam) a).value();
if (!"".equals(paranName.trim())) {
paramIndexMapping.put(paranName, i);
}
}
}
}
//2.对于 request与response,默认放入根据类型名与参数位置放入map
Class<?>[] parameterTypes = method.getParameterTypes();
for (int i = 0; i < parameterTypes.length; i++) {
Class<?> type = parameterTypes[i];
if (type == HttpServletRequest.class || type == HttpServletResponse.class) {
paramIndexMapping.put(type.getName(), i);
}
}
}
}
}

Spring IOC MVC DI简单实现的更多相关文章

  1. Spring IoC 和 DI 简介(二)

    Spring IoC 和 DI 简介 IoC:Inverse of Control(控制反转) 读作“反转控制”,更好理解,不是什么技术,而是一种设计思想,就是将原本在程序中手动创建对象的控制权,交由 ...

  2. 利用递归,反射,注解等,手写Spring Ioc和Di 底层(分分钟喷倒面试官)了解一下

    再我们现在项目中Spring框架是目前各大公司必不可少的技术,而大家都知道去怎么使用Spring ,但是有很多人都不知道SpringIoc底层是如何工作的,而一个开发人员知道他的源码,底层工作原理,对 ...

  3. Spring 学习教程(一):浅谈对Spring IOC以及DI的理解

    一.个人对IoC(控制反转)和DI(依赖注入)的理解我们平时在开发java web程序的时候,每个对象在需要使用它的合作对象时,自己都要将它要合作对象创建出来(比如 new 对象),这个合作对象是由自 ...

  4. Spring IOC(DI)

    软件152 余建强 1 什么是IOC IOC—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想.在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不 ...

  5. (转)spring IOC、DI理解

    转自: http://www.cnblogs.com/xdp-gacl/p/4249939.html 个人理解: IOC控制反转,反转的是获取依赖对象的方式.传统的应用在存在依赖关系时,比如A依赖于B ...

  6. 对Spring Ioc 以及DI的精彩理解

    转:http://blog.csdn.net/cyjs1988/article/details/50352916 学习过spring框架的人一定都会听过Spring的IoC(控制反转) .DI(依赖注 ...

  7. Spring|IOC与DI

    一.IOC IOC(Inversion of Control),控制反转,是Spring的核心内容之一. 什么是“控制反转”? [示例] package com.my; /** * @Author j ...

  8. Spring IOC 源码简单分析 03 - 循环引用

    ### 准备 ## 目标 了解 Spring 如何处理循环引用 ##测试代码 gordon.study.spring.ioc.IOC03_CircularReference.java   ioc03. ...

  9. Spring IOC 源码简单分析 02 - Bean Reference

    ### 准备 ## 目标 了解 bean reference 装配的流程 ##测试代码 gordon.study.spring.ioc.IOC02_BeanReference.java   ioc02 ...

随机推荐

  1. PostgreSQL创建扩展出错

    问题: loraserver_as=# create extension pg_trgm; ERROR: could not open extension control file "/us ...

  2. python代码规范整理

    规范参考源: 1.pep8(python代码样式规范):中文文档      https://blog.csdn.net/ratsniper/article/details/78954852 2.pep ...

  3. Python爬虫(二)正则表达式

    一.介绍 1.概念 正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符.及这些特定字符的组合,组成一个"规则字符串",这个"规则字符串"用来 ...

  4. 目标检测YOLO进化史之yolov1

    yolov3在目标检测领域可以算得上是state-of-art级别的了,在实时性和准确性上都有很好的保证.yolo也不是一开始就达到了这么好的效果,本身也是经历了不断地演进的. yolov1 测试图片 ...

  5. 第一章 .NET基础-C#基础

    一.1.1. 基础语法 一.1.1.1. 注释符 一.1.1.1.1. 注释符的作用 l 注释 l 解释 一.1.1.1.2. C#中的3中注释符 l 单行注释 // l 多上注释 /* 要注释的内容 ...

  6. SpringBoot:高并发下浏览量入库设计

    一.背景 文章浏览量统计,low的做法是:用户每次浏览,前端会发送一个GET请求获取一篇文章详情时,会把这篇文章的浏览量+1,存进数据库里. 1.1 这么做,有几个问题: 在GET请求的业务逻辑里进行 ...

  7. 监听input实时输入

    // 用Jquery实现: $('#input-element').on('input',()=>{ console.log("你按了一下111");}) // 用Jquer ...

  8. Hive的安装及配置

    title: Hive的安装及配置 summary: 关键词:Hive ubuntu 安装和配置 Derby MySQL PostgreSQL 数据库连接 date: 2019-5-19 13:25 ...

  9. ionic $state.go() 传参

    例子: $state.go("tab.home" , {"hello": "world"}) 重点: 接受参数的页面--tab.home,需 ...

  10. 六.html基础

    web前端前几个月学过一段时间,现在在学习一遍,当作复习,最重要的看看web渗透常用的标签! <html></html>  不带任何属性 <body></bo ...