目的
为了能够更好的理解SpringMVC,自己动手去实现一个SpringMVC的简化版,能够记忆更加深刻.
整体思路
- 配置web.xml文件,配置自己自定义的前端控制器FatDispatcherServlet
- 配置自定义配置文件.
- 配置Annotation文件,自定义自己注解,这里定义了几个常用的注解,
@FatController,@FatService,@FatAutowired,@FatRequestMapping,@FatRequestParam - 新建FatDispatcherServlet类,继承HttpServlet,并重写其中的init()方法和doGet()方法.
- 在初始化方法中完成配置文件的加载,类的扫描,对象的创建并放到ioc容器中,创建完对象之后进行依赖注入,最后做url路径和对应控制器中的方法映射.
- 在doGet()方法中根据url请求路径,做请求分发及参数处理.
具体实现代码
- web.xml
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<servlet>
<servlet-name>FatDispatcherServlet</servlet-name>
<servlet-class>mvcframework.servlet.FatDispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>application.properties</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>FatDispatcherServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
- 自定义配置文件application.properties (就一行代码,你没看错)
scanPackage=com.fat.demo
- 自定义注解(五个注解)
package mvcframework.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @ClassName FatController
* @Auther LangGuofeng
* @Date: 2019/8/28/028 10:43
* @Description: TODO
*/
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface FatController {
}
package mvcframework.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @ClassName FatService
* @Auther LangGuofeng
* @Date: 2019/8/28/028 10:46
* @Description: TODO
*/
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface FatService {
String value() default "";
}
package mvcframework.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @ClassName FatAutowired
* @Auther LangGuofeng
* @Date: 2019/8/28/028 11:25
* @Description: TODO
*/
@Target({ElementType.TYPE,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface FatAutowired {
String value() default "";
}
package mvcframework.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @ClassName FatRequestMapping
* @Auther LangGuofeng
* @Date: 2019/8/28/028 12:31
* @Description: TODO
*/
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface FatRequestMapping {
String value();
}
package mvcframework.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface FatRequestParam {
String value();
}
- FatDispatcherServlet(这个类就比较核心了)
package mvcframework.servlet;
import mvcframework.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.lang.reflect.Parameter;
import java.net.URL;
import java.util.*;
/**
* @ClassName FatDispatcherServlet
* @Auther LangGuofeng
* @Date: 2019/8/28/028 8:52
* @Description: TODO
*/
public class FatDispatcherServlet extends HttpServlet {
//保存application配置文件中的内容
private Properties contexConfig = new Properties();
//保存扫描到的类名
private List<String> classNames = new ArrayList<>();
//IOC容器
private Map<String, Object> ioc = new HashMap<>();
//保存url和Method的对应关系
private Map<String, Method> handlerMapping = new HashMap<>();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//7.调用, 运行阶段
try {
doDispatch(req, resp);
} catch (Exception e) {
e.printStackTrace();
resp.getWriter().write("500 Excetion,Detail : " + Arrays.toString(e.getStackTrace()));
}
}
@Override
public void init(ServletConfig config) throws ServletException {
// 1. 加载配置文件
doLoadConfig(config.getInitParameter("contextConfigLocation"));
// 2.扫描相关的类
doScanner(contexConfig.getProperty("scanPackage"));
// 3.初始化扫描到的类,并将它放入IOC容器
doInstance();
// 4.完成依赖注入
doAutowired();
// 5.初始化HandlerMapping
initHandlerMapping();
System.out.println("Fat Spring framework is init.");
}
/**
* @Author LangGuofeng
* @Description 请求分发及参数处理
* @Date 2019/8/30/030 15:22
* @Param [req, resp]
* @return void
**/
private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws IOException, InvocationTargetException, IllegalAccessException {
//绝对路径
String url = req.getRequestURI();
//处理成相对路径
String contexPath = req.getContextPath();
url = url.replaceAll(contexPath, "").replaceAll("/+", "/");
if (!this.handlerMapping.containsKey(url)) {
resp.getWriter().write("404 Not Found!!!");
return;
}
Method method = this.handlerMapping.get(url);
//通过反射通过method获取class method.getDeclaringClass()
String beanName = toLowerFirstCase(method.getDeclaringClass().getSimpleName());
//从request中获取参数列表
Map<String, String[]> parameterMap = req.getParameterMap();
//从method中获取形参列表
Parameter[] params = method.getParameters();
Object[] paramValues = new Object[params.length];
for (int i = 0; i < params.length; i++) {
Parameter param = params[i];
Class parameterType = param.getType();
if (parameterType == HttpServletRequest.class) {
paramValues[i] = req;
continue;
} else if (parameterType == HttpServletResponse.class) {
paramValues[i] = resp;
continue;
} else {
//获取param上的注解
Annotation[] annos = param.getAnnotations();
for (Annotation annotation : annos) {
if (annotation instanceof FatRequestParam) {
String paramName = ((FatRequestParam) annotation).value();
//判断req参数列表中是否包含paramName
if (parameterMap.containsKey(paramName.trim())) {
String value = Arrays.toString(parameterMap.get(paramName)).replaceAll("\\[|\\]|,", "");
System.out.println(value);
Object val = convert(parameterType, value);
paramValues[i] = val;
}
}
}
}
}
method.invoke(ioc.get(beanName), paramValues);
}
/**
* @return java.lang.Object
* @Author LangGuofeng
* @Description String 转换成对应类型
* @Date 2019/8/30/030 10:31
* @Param [parameterType, value]
**/
private Object convert(Class parameterType, String value) {
Object val = null;
if (parameterType == Integer.class || parameterType == int.class) {
val = new Integer(value);
}
if (parameterType == String.class) {
val = value;
}
return val;
}
/**
* @return void
* @Author LangGuofeng
* @Description 初始化url和method的一对一对应关系
* @Date 2019/8/28/028 12:01
* @Param []
**/
private void initHandlerMapping() {
if (ioc.isEmpty()) {
return;
}
for (Map.Entry<String, Object> entry : ioc.entrySet()) {
Class<?> clazz = entry.getValue().getClass();
if (!clazz.isAnnotationPresent(FatController.class)) {
continue;
}
//保存写在类上面的@FatRequestMapping("/demo")
String baseUrl = "";
if (clazz.isAnnotationPresent(FatRequestMapping.class)) {
FatRequestMapping fatRequestMapping = clazz.getAnnotation(FatRequestMapping.class);
baseUrl = fatRequestMapping.value();
}
//默认获取所有的public方法
for (Method method : clazz.getMethods()) {
if (!method.isAnnotationPresent(FatRequestMapping.class)) {
continue;
}
FatRequestMapping requestMapping = method.getAnnotation(FatRequestMapping.class);
String url = ("/" + baseUrl + "/" + requestMapping.value()).replaceAll("/+", "/");
handlerMapping.put(url, method);
System.out.println("Mapped :" + url + "," + method);
}
}
}
/**
* @return void
* @Author LangGuofeng
* @Description 依赖注入
* @Date 2019/8/28/028 11:17
* @Param []
**/
private void doAutowired() {
if (ioc.isEmpty()) {
return;
}
for (Map.Entry<String, Object> entry : ioc.entrySet()) {
//Declared 所有的, 特定的 字段, 包括private
Field[] fields = entry.getValue().getClass().getDeclaredFields();
for (Field field : fields) {
if (!field.isAnnotationPresent(FatAutowired.class)) {
continue;
}
FatAutowired autowired = field.getAnnotation(FatAutowired.class);
//如果用户没有自定义beanName,默认就根据类型注入
String beanName = autowired.value().trim();
if ("".equals(beanName)) {
//获得接口的类型名,做为key,用来在ioc中取值
beanName = field.getType().getName();
}
//如果是public以外的修饰符,只要加了@Autowired注解,都要强制赋值
//反射中叫做暴力赋值
field.setAccessible(true);
try {
field.set(entry.getValue(), ioc.get(beanName));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
/**
* @return void
* @Author LangGuofeng
* @Description 初始化为DI做准备
* @Date 2019/8/28/028 10:24
* @Param []
**/
private void doInstance() {
if (classNames.isEmpty()) {
return;
}
for (String className : classNames) {
try {
Class<?> clazz = Class.forName(className);
if (clazz.isAnnotationPresent(FatController.class)) {
Object instance = clazz.newInstance();
String beanName = toLowerFirstCase(clazz.getSimpleName());
ioc.put(beanName, instance);
} else if (clazz.isAnnotationPresent(FatService.class)) {
//1.自定义的beanName
FatService service = clazz.getAnnotation(FatService.class);
String beanName = service.value();
//2.默认类名首字母小写
if ("".equals(beanName)) {
beanName = toLowerFirstCase(clazz.getSimpleName());
}
Object instance = clazz.newInstance();
ioc.put(beanName, instance);
//3.根据类型自动赋值
for (Class<?> i : clazz.getInterfaces()) {
if (ioc.containsKey(i.getName())) {
throw new Exception("The " + i.getName() + " is exists!!");
}
ioc.put(i.getName(), instance);
}
} else {
continue;
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* @return java.lang.String
* @Author LangGuofeng
* @Description 首字母大写转小写
* @Date 2019/8/28/028 10:49
* @Param [simpleName]
**/
private String toLowerFirstCase(String simpleName) {
char[] chars = simpleName.toCharArray();
chars[0] += 32;
return String.valueOf(chars);
}
/**
* @return void
* @Author LangGuofeng
* @Description 扫描出相关的类
* @Date 2019/8/28/028 9:31
* @Param [scanPackage]
**/
private void doScanner(String scanPackage) {
//scanPackage=com.fat.demo , 存储的是包路径
//转换为文件路径,实际上就是把 . 替换为 /
//classpath
URL url = this.getClass().getClassLoader().getResource("/" + scanPackage.replaceAll("\\.", "/"));
File classPath = new File(url.getFile());
for (File file : classPath.listFiles()) {
if (file.isDirectory()) {
doScanner(scanPackage + "." + file.getName());
} else {
if (!file.getName().endsWith(".class")) {
continue;
}
String className = (scanPackage + "." + file.getName().replace(".class", ""));
classNames.add(className);
}
}
}
/**
* @return void
* @Author LangGuofeng
* @Description 加载配置文件
* @Date 2019/8/28/028 9:25
* @Param [contextConfigLocation]
**/
private void doLoadConfig(String contextConfigLocation) {
//直接从类路径中找到Spring主配置文件所在的路径
//并且将其读取出来放到properties对象中
//相当于将配置文件保存到内存中
InputStream fis = null;
fis = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
try {
contexConfig.load(fis);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fis != null) {
}
}
}
}
- Controller类(简易版,传参的时候一定要使用@FatRequestParam注解)
package com.fat.demo;
import mvcframework.annotation.FatAutowired;
import mvcframework.annotation.FatController;
import mvcframework.annotation.FatRequestMapping;
import mvcframework.annotation.FatRequestParam;
import javax.management.Query;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @ClassName DemoController
* @Auther LangGuofeng
* @Date: 2019/8/28/028 12:45
* @Description: TODO
*/
@FatController()
@FatRequestMapping("demo")
public class DemoController {
@FatAutowired()
private IDemoService demoService;
@FatRequestMapping("query")
public void query(HttpServletResponse resp, HttpServletRequest req, @FatRequestParam("name")String name) {
String res = demoService.query(name);
try {
resp.getWriter().write(res);
} catch (IOException e) {
e.printStackTrace();
}
}
@FatRequestMapping("add")
public void add(@FatRequestParam("a")int a, @FatRequestParam("b")int b, HttpServletResponse resp){
int c = a + b;
String str = a+" + "+b + " = " + c;
try {
resp.getWriter().write(str);
} catch (IOException e) {
e.printStackTrace();
}
}
}
- IDemoService
package com.fat.demo;
public interface IDemoService {
String query(String name);
}
- DemoServiceImpl
package com.fat.demo;
import mvcframework.annotation.FatService;
/**
* @ClassName DemoServiceImpl
* @Auther LangGuofeng
* @Date: 2019/8/28/028 12:50
* @Description: TODO
*/
@FatService()
public class DemoServiceImpl implements IDemoService {
@Override
public String query(String name) {
return "The Student name is " + name + ".";
}
}
注意: 需要注意的一点就是你的FatDispatcherServlet需要继承HttpServlet的话需要引用servlet的api,我这里用的maven构建的项目,如果不是maven构建的自行下个jar包.
- maven引入servlet-api
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
总结
每天进步一点点,只要你去做了,就一定会有收获.