手写简易SpringMVC
思路分析:
1.启动tomcat
2.spi机制加载spring容器,扫描包,生成bean,并且将注解解析到Map<String,RequestMappingInfo> map中
3.get/post请求时,.
- 根据url获取对应的RequestMappingInfo对象
- 去匹配对应的适配器HandlerAdapter
- 根据适配器类型选择合适的适配器去执行方法,返回数据
- 将数据解析成json并写回前端
实现流程
项目结构如下
添加依赖pom.xml
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<!--使用maven tomcat插件时,当前依赖需要注释掉,不然会产生冲突。-->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>8.5.31</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<version>8.5.31</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.74</version>
</dependency>
</dependencies>
<build>
<finalName>customize-springmvc</finalName>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
</plugins>
</pluginManagement>
</build>
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>
</web-app>
启动Tomcat
package com.yoocar.springmvc;
import org.apache.catalina.startup.Tomcat;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("com.yoocar.springmvc")
public class Start {
public static void main(String[] args) throws Exception {
// 创建Tomcat应用对象
Tomcat tomcat = new Tomcat();
// 设置Tomcat的端口号
tomcat.setPort(8888);
tomcat.addWebapp("/", "D:\\workspace\\springMVC-study\\customize-springmvc\\src\\main\\webapp");
//启动tomcat容器
tomcat.start();
//等待
tomcat.getServer().await();
}
}
三个常用注解
RequestMapping
package com.yoocar.springmvc.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMapping {
String value() default "";
}
RequestMappingInfo
package com.yoocar.springmvc.servlet;
import java.lang.reflect.Method;
/**
* 存放RequestMapping注解信息
*/
public class RequestMappingInfo {
//表示RequestMapping注解标识的方法
private Method method;
//表示RequestMapping注解的value属性,即url
private String uri;
//表示执行RequestMapping注解标识的方法的bean
private Object obj;
public Object getObj() {
return obj;
}
public void setObj(Object obj) {
this.obj = obj;
}
public Method getMethod() {
return method;
}
public void setMethod(Method method) {
this.method = method;
}
public String getUri() {
return uri;
}
public void setUri(String uri) {
this.uri = uri;
}
}
RequestParam
package com.yoocar.springmvc.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 RequestParam {
String value() default "";
}
ResponseBody
package com.yoocar.springmvc.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ResponseBody {
}
处理注解的
spi机制加载spring容器
package com.yoocar.springmvc.init;
import com.yoocar.springmvc.Start;
import com.yoocar.springmvc.servlet.DispatcherServlet;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletRegistration;
import java.util.Set;
//spi机制
public class Myinit implements ServletContainerInitializer{
@Override
public void onStartup(Set<Class<?>> c,ServletContext servletContext) {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Start.class);
DispatcherServlet servlet = new DispatcherServlet(ac);
ServletRegistration.Dynamic dy = servletContext.addServlet("app",servlet);
dy.setLoadOnStartup(1);
dy.addMapping("*.do");
}
}
DispatcherServlet
import com.yoocar.springmvc.init.handlerAdapter.HandlerAdapter;
import com.yoocar.springmvc.init.handlerMapping.HandlerMapping;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collection;
import java.util.Map;
public class DispatcherServlet extends HttpServlet {
//适配器
static Collection<HandlerAdapter> handlerAdapters ;
//映射器
static Collection<HandlerMapping> handlerMappings ;
public DispatcherServlet() {
}
public DispatcherServlet(AnnotationConfigApplicationContext ac) {
//组件初始化
Map<String, HandlerMapping> handlerMappingMaps = ac.getBeansOfType(HandlerMapping.class);
handlerMappings = handlerMappingMaps.values();
Map<String, HandlerAdapter> handlerAdapterMaps = ac.getBeansOfType(HandlerAdapter.class);
handlerAdapters = handlerAdapterMaps.values();
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
//获取映射器
Object handlerMapping = getHandlerMapping(req);
if(handlerMapping == null){
System.out.println("未匹配到handlerMapping");
return;
}
//获取适配器
HandlerAdapter handlerAdapter = getHandlerAdapter(handlerMapping);
if(handlerAdapter == null){
System.out.println("未匹配到handlerAdapter");
return;
}
//使用适配器(处理器)去处理
Object result = handlerAdapter.handle(req,resp,handlerMapping);
PrintWriter writer = resp.getWriter();
writer.println(result);
writer.flush();
writer.close();
}
protected Object getHandlerMapping(HttpServletRequest request) {
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
Object handler = mapping.getHandlerMapping(request.getRequestURI());
if (handler != null) {
return handler;
}
}
}
return null;
}
protected HandlerAdapter getHandlerAdapter(Object handlerMapping) {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
boolean flag = adapter.supports(handlerMapping);
if (flag) {
return adapter;
}
}
}
return null;
}
}