【源码】SpringMVC-启动
Demo
//@Controller; 如果有父子容器 @Component,默认是在父容器中,还找不到
//@Component+@RequestMapping
@RestController
public class HelloController {
public HelloController(){
System.out.println("HelloController.....");
}
@Autowired
HelloService helloService;
@GetMapping("/hello") // 所有的xxxMapping都是RequestMapping
public String sayHello(){
String mvc = helloService.say("MVC");
return mvc;
}
}@Service
public class HelloService {
public HelloService(){
System.out.println("HelloService.....");
}
public String say(String name){
return "Hello,"+name;
}
}/**
* Spring不扫描controller组件、AOP咋实现的....
*/
@ComponentScan(value = "com.demo.web",excludeFilters = {
@ComponentScan.Filter(type= FilterType.ANNOTATION,value = Controller.class)
})
@Configuration
public class SpringConfig {
//Spring的父容器
}/**
* SpringMVC只扫描controller组件,可以不指定父容器类,让MVC扫所有。@Component+@RequestMapping就生效了
*/
@ComponentScan(value = "com.demo.web",includeFilters = {
@ComponentScan.Filter(type= FilterType.ANNOTATION,value = Controller.class)
},useDefaultFilters = false)
public class SpringMVCConfig {
//SpringMVC的子容器,能扫描的Spring容器中的组件
}注解版的两种配置方式
/**
* 最快速的整合注解版SpringMVC和Spring的
*/
public class QuickAppStarter extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override //根容器的配置(Spring的配置文件===Spring的配置类)
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[]{SpringConfig.class};
}
@Override //web容器的配置(SpringMVC的配置文件===SpringMVC的配置类)
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[]{SpringMVCConfig.class};
}
@Override //Servlet的映射,DispatcherServlet的映射路径
protected String[] getServletMappings() {
return new String[]{"/"};
}
@Override
protected void customizeRegistration(ServletRegistration.Dynamic registration) {
// super.customizeRegistration(registration);
// registration.addMapping("");//
}
}/**
* 只要写了这个,相当于配置了SpringMVC的DispatcherServlet
* 1、Tomcat一启动就加载他
* 1)、创建了容器、制定了配置类(所有ioc、aop等spring的功能就ok)
* 2)、注册一个Servlet; DispatcherServlet;
* 3)、以后所有的请求都交给了 DispatcherServlet;
* 效果,访问Tomcat部署的这个Web应用下的所有请求都会被 DispatcherServlet 处理
* DispatcherServlet就会进入强大的基于注解的mvc处理流程(@GetMapping)
* 必须Servlet3.0以上才可以;Tomcat6.0以上才支持Servlet3.0规范
*
* Servlet3.0是javaEE的Web的规范标准,Tomcat是Servlet3.0规范的一个实现;
*/
public class AppStarter implements WebApplicationInitializer {
// @Override
public void onStartup(ServletContext servletContext) throws ServletException {
//1、创建ioc容器
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(SpringConfig.class); //2、传入一个配置类
//以上截止,ioc容器都没有启动
//3、配置了 DispatcherServlet,利用Servlet的初始化机制
DispatcherServlet servlet = new DispatcherServlet(context);
ServletRegistration.Dynamic registration = servletContext.addServlet("app", servlet);
registration.setLoadOnStartup(1);
registration.addMapping("/"); //映射路径
//启动了容器?上面的Servlet添加到 servletContext 里面以后,Tomcat就会对 DispatcherServlet进行初始化
//<servlet></servlet>
// servletContext.addServlet("abc",XXXX.class)
}
}SpringServletContainerInitializer
在Servlet规范里,会通过Spi的机制加载ServletContainerInitializer的实现类并调用onStartup方法。而Tomcat实现了Servlet规范,所以启动Tomcat时就会触发到org.springframework.web.SpringServletContainerInitializer的onStartup方法,在onStartup方法里,SpringMvc会遍历所有的WebApplicationInitializer实现类,并调用onStartup方法
此时就会触发到我们在Demo里通过注解配置SpringMvc的加载,后文以QuickAppStarter作为分析,我们的QuickAppStarter实现了AbstractAnnotationConfigDispatcherServletInitializer
org.springframework.web.servlet.support.AbstractDispatcherServletInitializer
先调用父类的onStartup方法
org.springframework.web.context.AbstractContextLoaderInitializer#onStartup
创建一个根容器(spring的容器),同时创建一个监听器,并把根容器放到监听器里,最后把监听器放到Servlet的上下文
创建根容器,getRootConfigClasses会回调到QuickAppStarter的getRootConfigClasses方法
org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer#createRootApplicationContext
创建监听器,当前web应用启动以后(Tomcat把web应用加载了以后),调用contextInitialized方法
registerDispatcherServlet
org.springframework.web.servlet.support.AbstractDispatcherServletInitializer#registerDispatcherServlet
创建一个mvc容器(springmvc的容器),同时创建一个DispatcherServlet,并把mvc容器放到DispatcherServlet里,最后把DispatcherServlet放到Servlet的上下文
创建springmvc容器,getServletConfigClasses会回调到QuickAppStarter的getServletConfigClasses方法,org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer#createServletApplicationContext
前面我们提到ContextLoaderListener会在web应用启动以后(Tomcat把web应用加载了以后),被tomcat调用contextInitialized方法
org.springframework.web.context.ContextLoader#initWebApplicationContext
刷新容器
接着Tomcat创建Servlet时,会进入Servlet的init方法,回调到我们的HttpServletBean的init方法
org.springframework.web.servlet.HttpServletBean#init
再回调到子类的方法
org.springframework.web.servlet.FrameworkServlet#initServletBean
设置mvc容器的父容器,并且刷新mvc容器 
