上面的四篇文章介绍并写完了SpringBean的生命周期,现在开始来写Spring MVC
Spring MVC我们只写一层 C层 即 Controller 控制层(没有父子容器)
众所周知 Spring MVC有一个中央处理器 叫 DispatcherServlet,实现了HttpServlet 是一个Servlet
既然是一个Servlet 肯定是要遵循Servlet的规范。
我们通常知道的服务器一般有tomcat 和 jetty等等 ,这两个容器也实现了Servlet的规范。
在容器运行完成会调用META-INF/services 下面的 javax.servlet.ServletContainerInitializer这个文件里面对象方法,这个对象必须实现 ServletContainerInitializer这个接口,而这个接口里面做的事情可以实现零配置启动一个SpringWeb项目
先来看下我们手写SpringMVC的流程,需要将我们写的spring项目打包并引入
启动tomcat需要做什么事情,我们仿照Spring boot的方式启动tomcat
@XxSpringBootApplication
public class SimpleMVCApplication {
public static void main(String[] args) throws Exception{
XxSpringApplication.run(SimpleMVCApplication.class);
}
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@XxConfiguration
public @interface XxSpringBootApplication {
}
run方法执行的时候会将当前类传进去,传进去之后会初始化我们spring容器。再放到缓存中
public static void run(Class clazz){
try {
XxAnnotationConfigApplicationContext springContext = new XxAnnotationConfigApplicationContext(clazz);
XxLocalCache.CONTEXT_CACHE.put("springContext",springContext);
Tomcat tomcat = new Tomcat();
tomcat.setPort(80);
Context context = tomcat.addContext("/", System.getProperty("java.io.tmpdir"));
context.addLifecycleListener((LifecycleListener) Class.forName(tomcat.getHost().getConfigClass()).newInstance());
tomcat.start();
tomcat.getServer().await();
}catch (Exception e){
}
}
启动tomcat之后会执行Servlet规范的接口,得到了XxDispatcherServlet 并将它放到了容器里面
public class XxWebApplicationInitializer implements ServletContainerInitializer {
@Override
public void onStartup(Set<Class<?>> set, ServletContext servletContext) {
XxAnnotationConfigApplicationContext springContext = (XxAnnotationConfigApplicationContext) XxLocalCache.CONTEXT_CACHE.get("springContext");
XxDispatcherServlet dispatcherServlet = springContext.getBean(XxDispatcherServlet.class);
ServletRegistration.Dynamic registration = servletContext.addServlet("app", dispatcherServlet);
registration.setLoadOnStartup(1);
registration.addMapping("/*");
}
}
而初始化 Spring容器的时候 XxDispatcherServlet会进行初始化,及完成扫描并填充映射的map集合。XxDispatcherServlet 实现了Spring 的一个Aware接口 来获取上下文实现扫描并注册beanDefinition的功能,又实现一个XxInitializingBean 接口来实现XxDispatcherServlet初始化后填充map映射的结果集
@XxComponent
public class XxDispatcherServlet extends HttpServlet implements XxInitializingBean, XxApplicationContextAware {
public XxAnnotationConfigApplicationContext context;
public Map<String, Method> handlerMapping = new ConcurrentHashMap<>(256);
public Map<String,String[]> pathVariableMapping = new ConcurrentHashMap<>(256);
public List<XxHandlerInterceptor> handlerInterceptors = new ArrayList<>();
public static List<AnnotationTypeParameterParser> parameterParsers = new ArrayList<>();
static {
parameterParsers.add(new XxRequestParamParser());
parameterParsers.add(new XxPathVariableParser());
parameterParsers.add(new XxRequestBodyParser());
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doDispatcher(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doDispatcher(req, resp);
}
@Override
public void afterPropertiesSet() {
List<Class<?>> loadClass = context.getXxDefaultListableBeanFactory().loadRootResources();
for (Class<?> aClass : loadClass) {
if(aClass.isAnnotationPresent(XxController.class)){
XxRequestMapping aClassAnnotation = aClass.getAnnotation(XxRequestMapping.class);
String aClassValue = handleUrl(aClassAnnotation.value());
Method[] methods = aClass.getDeclaredMethods();
for (Method method : methods) {
if(method.isAnnotationPresent(XxRequestMapping.class)){
XxRequestMapping aMethodAnnotation = method.getAnnotation(XxRequestMapping.class);
String requestUrl = handleUrl(aClassValue + aMethodAnnotation.value());
handlerMapping.put(requestUrl,method);
}
}
}
}
}
@Override
public void setXxApplicationContext(XxAnnotationConfigApplicationContext context) {
this.context = context;
}
private String handleUrl(String value){
if(!value.startsWith("/")){
value += "/";
}
if(value.endsWith("/")){
value = value.substring(0,value.length()-1);
}
if(value.contains("/{") && value.contains("}")){
String key = value.substring(0,value.indexOf("/{"));
String values = value.substring(value.indexOf("/{")+1);
pathVariableMapping.put(key,values.split("/"));
value = key;
}
return value;
}
//doDispatcher......
}
handlerMapping 映射的结果集类级别XxRequestMapping值加上方法级XxRequestMapping值
/user + /get01
pathVariableMapping 存放处理路径包含值的映射结果集
/user + /get01/001/waf 001 代表id waf = 名称 理解为通过id和名称去查询结果
handlerInterceptors 拦截器集合,在Spring扫描的时候会填充一个集合,集合的作用是通过一个接口获取到该接口的所有实现类
parameterParsers 参数解析器 本项目中实现了 XxRequestParam XxPathVariable XxRequestBody 三种注解的请求参数解析
具体就在执行方法的逻辑里面,响应参数由于进行了统一的封装,直接使用了json包进行返回的,如果不需要进行统一的返回封装,可直接串行化进行返回,会自动的解析成json字符串。
代码详情可参照gitee地址:https://gitee.com/wanganfen/xx-spring/tree/master/simple-mvc