MVC的工作原理
- 客户端的所有请求都交给前端控制器DispatcherServlet来处理,它会负责调用系统的其他模块来真正处理用户的请求。
- DispatcherServlet收到请求后,将根据请求的信息(包括URL、HTTP协议方法、请求头、请求参数、Cookie等)以及HandlerMapping的配置找到处理该请求的Handler(任何一个对象都可以作为请求的Handler)。
- 在这个地方Spring会通过HandlerAdapter对该处理器进行封装。
- HandlerAdapter是一个适配器,它用统一的接口对各种Handler中的方法进行调用。
- Handler完成对用户请求的处理后,会返回一个ModelAndView对象给DispatcherServlet,ModelAndView顾名思义,包含了数据模型以及相应的视图的信息。
- ModelAndView的视图是逻辑视图,DispatcherServlet还要借助ViewResolver完成从逻辑视图到真实视图对象的解析工作。
- 当得到真正的视图对象后,DispatcherServlet会利用视图对象对模型数据进行渲染。
- 客户端得到响应,可能是一个普通的HTML页面,也可以是XML或JSON字符串,还可以是一张图片或者一个PDF文件。
Servlet3.0注解
ServletContainerInitializer容器初始化
这是一个接口,用来规范容器的初始化操作,在Spring MVC中,实现类是SpringServletContainerInitializer:
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
@Override
public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
throws ServletException {
List<WebApplicationInitializer> initializers = new LinkedList<>();
// webAppInitializerClasses 就是servlet3.0规范中为我们收集的 WebApplicationInitializer 接口的实现类的class
// 从webAppInitializerClasses中筛选并实例化出合格的相应的类
if (webAppInitializerClasses != null) {
for (Class<?> waiClass : webAppInitializerClasses) {
// Be defensive: Some servlet containers provide us with invalid classes,
// no matter what @HandlesTypes says...
if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
try {
initializers.add((WebApplicationInitializer)
ReflectionUtils.accessibleConstructor(waiClass).newInstance());
}
catch (Throwable ex) {
throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
}
}
}
}
if (initializers.isEmpty()) {
...
}
servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
AnnotationAwareOrderComparator.sort(initializers);
for (WebApplicationInitializer initializer : initializers) {
initializer.onStartup(servletContext);
}
}
}
- SpringServletContainerInitializer 由支持Servlet3.0+的Servlet容器实例化并调用。
- Servlet容器还会查询classpath下SpringServletContainerInitializer类上修饰的@HandlesTypes注解所标注的WebApplicationInitializer接口的实现类. 这一步也是容器帮我们完成的。
- SpringServletContainerInitializer通过实现ServletContainerInitializer将自身并入到Servlet容器的生命周期中, 并通过自身定义的WebApplicationInitializer将依赖于Spring框架的系统初始化需求与Servlet容器解耦. 即依赖于spring的系统可以通过实现WebApplicationInitializer来实现自定义的初始化逻辑. 而不需要去实现ServletContainerInitializer。
ServletRegistration注册
实现了ServletContextInitializer,约定了在Servlet启动时,调用#onStartup。
- 当前注册bean的优先级,通过属性order指定,缺省值为最低优先级Ordered.LOWEST_PRECEDENCE。
- 当前注册bean是否被禁用,通过属性enabled指定。如果被禁用,Servlet容器启动时并不执行该注册bean的注册动作。缺省值为true。
- 通过抽象方法的方式约定了实现子类必须实现某些功能,比如具体注册什么以及具体的注册逻辑都必须由子类实现提供。
FilterRegistration过滤器
注册过滤器组件,具体可以配合@Bean将组件注册到容器中,同样地,ServletRegistration也可以注册,主要代码如下:
@Configuration
public class Config {
/**
* 注册一个Filter
*
* @return FilterRegistrationBean
*/
@Bean
public FilterRegistrationBean registerFilter() {
// 创建Filter注册Bean
FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>();
// 创建自定义的Filter对象
SecondFilter filter = new SecondFilter();
// 注册Filter
registration.setFilter(filter);
// 设置Filter名称
registration.setName("second_filter");
// 设置Fliter匹配规则
registration.addUrlPatterns("/*");
// 设置排序,在存在多个Filter实例的情况下确定Filter的执行顺序
registration.setOrder(1);
return registration;
}
/**
* 注册一个Servlet
*
* @return ServletRegistrationBean
*/
@Bean
public ServletRegistrationBean registerServlet() {
// 创建Servlet注册Bean
ServletRegistrationBean<Servlet> registration = new ServletRegistrationBean<>();
// 创建自定义的Servlet对象
SecondServlet servlet = new SecondServlet();
// 注册Servlet
registration.setServlet(servlet);
// 设置Servlet名称
registration.setName("second_servlet");
// 设置Servlet配置规则
registration.addUrlMappings("/second_servlet");
// 设置加载参数
registration.setLoadOnStartup(1);
return registration;
}
/**
* 注册一个Listener
*
* @return ServletListenerRegistrationBean
*/
@Bean
public ServletListenerRegistrationBean registerListener() {
// 创建Listener注册Bean
ServletListenerRegistrationBean<EventListener> registration = new ServletListenerRegistrationBean<>();
// 创建自定义的Listener对象
SecondListener listener = new SecondListener();
// 注册Listener
registration.setListener(listener);
// 设置排序,在存在多个Listener实例的情况下确定Listener的执行顺序
registration.setOrder(1);
return registration;
}
}
各个拦截组件调用顺序
@Autowired和@Resource的区别
都可以用来自动注入bean,写在字段或setter方法上。
@Autowired默认按照类型装配,@Resource默认按照名称装配。
存在多个Bean
比如这个接口有许多个实现类,那么我们需要在注入上面注解@Qualifier(实现类Bean名称)。
不存在Bean
要在括号内写入required=false,比如:@Autowired(required = false)
。
@RestController 和 @Controller 有什么区别?
@RestController 注解,在 @Controller 基础上,增加了 @ResponseBody 注解,更加适合目前前后端分离的架构下,提供 Restful API ,返回例如 JSON 数据格式。当然,返回什么样的数据格式,根据客户端的 ACCEPT 请求头来决定。