目录
1、工作流程
图一:
执行流程:
- 客户端请求被DispatcherServlet(前端控制器)接收
- DispatcherServlet请求HandlerMapping查询Handler
- HandlerMapping根据请求URL查找Handler,将Handler和HandlerInterceptor以HandlerExecutionChain的形式一并返回给DispatcherServlet
- DispatcherServlet请求HandlerAdapter执行Handler
- HandlerAdapter调用Handler的方法做业务逻辑处理
- HandlerAdapter处理完Handler会生成一个ModelAndView对象
- 将ModelAndView对象返回给DispatcherServlet
- DispatcherServlet将获取的ModelAndView对象传给ViewResolver视图解析器,请求进行视图解析
- ViewResolver将逻辑视图解析成物理视图View,返回给DispatcherServlet
- DispatcherServlet根据View进行视图渲染(将模型数据填充到视图中)
- DispatcherServlet将渲染后的视图响应给客户端
图二:
2、配置流程
step1:配置servlet.DispatcherServlet文件
init-param的作用是在启动servlet启动时规定其地地址及名称去搜寻其springmvc配置文件
<!-- springmvc的前端控制器 -->
<servlet>
<servlet-name>comqing-manager</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- contextConfigLocation不是必须的, 如果不配置contextConfigLocation, springmvc的配置文件默认在:WEB-INF/servlet的name+"-servlet.xml" -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>comqing-manager</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
step2:配置handlermapping
在springmvc.xml中配置文件
附:handlermapping的三种方式(作用就是找controller)
1、默认方式BeanNameUrlHandlerMapping,根据benan的属性name值寻找对应的controller(class)
2、ControllerClassNameHandlerMapping,根据controller名称进行对应,注意命名规则,地址栏上写最后的类名要全部小写,如果最后是controller,可以省略,也可以首字母不区分大小写
3、simpleurlHandlermappering,根据property的props的prop进行id的定位id
一般handlermapping一般采用默认方式,下面“comqing-manager”对应step1中的servlet-name:
<mvc:default-servlet-handler default-servlet-name="comqing-manager"/>
同事要配置扫描包的位置
<!-- 对包中的所有类进行扫描,以完成Bean创建和自动依赖注入的功能 -->
<context:component-scan base-package="com.comqing.controller" />
step3:配置视图解析器
<!-- 用于Spring MVC视图解析 -->
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/pages/" />
<property name="suffix" value=".jsp" />
</bean>
step4:根据需求配置其他
包括拦截器、资源映射、定义文件上传解析器、properties配置文件、SwaggerConfig配置类等
<!-- 自定义拦截器 -->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/api/**" />
<bean class="com.comqing.interceptor.CrmInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
<!-- 资源映射 -->
<mvc:resources location="/WEB-INF/css/" mapping="/css/**" />
<mvc:resources location="/WEB-INF/js/" mapping="/js/**" />
<!-- 定义文件上传解析器 -->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 设定默认编码 -->
<property name="defaultEncoding" value="UTF-8"></property>
<!-- 设定文件上传的最大值5MB,5*1024*1024 -->
<property name="maxUploadSize" value="5242880"></property>
</bean>
<!-- properties配置文件 -->
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations" value="classpath:resource/resource.properties" />
</bean>
<!--SwaggerConfig配置类注入 -->
<bean class="com.comqing.conf.SwaggerConfig" />
<!--配置swagger资源 拦截 -->
<mvc:resources mapping="swagger-ui.html" location="classpath:/META-INF/resources/" />
<mvc:resources mapping="/webjars/**"
location="classpath:/META-INF/resources/webjars/" />
</beans>
3、过滤器、拦截器
3.1、过滤器
Filter是Spring框架中的一个过滤器,然而过滤器就是对其中请求的信息进行处理,然后再传送。Filter不像Servlet,它不能产生一个请求或者响应,它只能修改对某一资源的请求,或修改从某一的响应。它实现了javax.servlet.Filter接口的服务端程序,主要作用是过滤字符编码、做一些业务逻辑判断,主要用于对用户请求进行预处理。
Filter是基于函数回调(doFilter()方法)的。
web.xml配置
<!-- 设置过滤器 !-->
<filter>
<filter-name>LoginFilter</filter-name>
<filter-class>com.sxkj.filter.LoginFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>LoginFilter</filter-name>
<url-pattern>/user/*</url-pattern>
</filter-mapping>
定义过滤器LoginFilter
过滤器Filter也具有生命周期:init()->doFilter()->destroy(),由部署文件中的filter元素驱动
public class LoginFilter implements Filter {
//init: 用于完成Filter 的初始化
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("过滤器初始化...");
chain.doFilter(request, response);
}
//doFilter:实现过滤功能,该方法就是对每个请求及响应增加的额外处理。
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("过滤器doFilter...");
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
}
//destroy:用于Filter 销毁前,完成某些资源的回收
@Override
public void destroy() {
System.out.println("过滤器销毁...");
}
}
在方法doFilter中的chain.doFilter(request, response)最终是调用Servlet的doService()方法
3.2、拦截器
对controller起作用
它主要用于拦截用户请求并作相应的处理。例如通过拦截器可以进行权限验证、记录请求信息的日志、判断用户是否登录等。
Interceptor则是基于Java反射的(AOP思想)
springmvc.xml中配置
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/user/**"/>
<bean class="com.sxkj.interceptor.LoginIntercepter"/>
</mvc:interceptor>
</mvc:interceptors>
下面代码执行流程:
- 1.程序先执行preHandle()方法,如果该方法的返回值为true,则程序会继续向下执行处理器中的方法,否则将不再向下执行。
- 2.在业务处理器(即控制器Controller类)处理完请求后,会执行postHandle()方法,然后会通过DispatcherServlet向客户端返回响应。
- 3.在DispatcherServlet处理完请求后,才会执行afterCompletion()方法。
/**
* 用户登录状态拦截器
*/
public class LoginIntercepter111 implements HandlerInterceptor {
/**
* 该方法将在Controller处理之前进行调用
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception {
return true;
}
/**
* @Description 该方法将在Controller处理之后进行调用
**/
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
}
/**
* @Description 处理完请求后带调用
**/
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
}
}
3.3、总结
过滤器的运行是依赖于servlet容器的,跟springmvc等框架并没有关系。并且多个过滤器的执行顺序跟web.xml文件中定义的先后关系有关。
拦截器的执行顺序跟在SpringMVC的配置文件中定义的先后顺序有关。
如下是过滤器与拦截器合并执行的流程
Filter对几乎所有的请求起作用,而Interceptor只能对action请求起作用。
拦截器(Interceptor)是基于Java的反射机制,而过滤器(Filter)是基于函数回调。从灵活性上说拦截器功能更强大些,Filter能做的事情,都能做,而且可以在请求前,请求后执行,比较灵活。Filter主要是针对URL地址做一个编码的事情、过滤掉没用的参数、安全校验(比较泛的,比如登录不登录之类),再细的话,建议用interceptor。
4、监听器
Servlet的监听器Listener,它是实现了javax.servlet.ServletContextListener接口的服务器端程序,它也是随web应用的启动而启动,只初始化一次。主要作用是:做一些初始化的内容添加工作、设置一些基本的内容、比如一些参数或者是一些固定的对象等等。
实现:
首先在web.xml中配置:
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>example.ConfigListener</listener-class>
</listener>
java代码:
public class ConfigListener implements ServletContextListener {
@Autowired
private ConfigService configService;
@Override
public void contextInitialized(ServletContextEvent sce) {
configService.initConfig();
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
}
}
注意上面代码configService是取不到的:
项目启动时抛出空指针异常!ConfigService实例并没有成功注入。这是为什么呢?要理解这个问题,首先要区分Listener的生命周期和spring管理的bean的生命周期。
(1)Listener的生命周期是由servlet容器(例如tomcat)管理的,项目启动时上例中的ConfigListener是由servlet容器实例化并调用其contextInitialized方法,而servlet容器并不认得@Autowired注解,因此导致ConfigService实例注入失败。
(2)而spring容器中的bean的生命周期是由spring容器管理的。
那么该如何在spring容器外面获取到spring容器bean实例的引用呢:
@Override
public void contextInitialized(ServletContextEvent sce) {
ConfigService configService = WebApplicationContextUtils.getWebApplicationContext(sce.getServletContext()).getBean(ConfigService.class);
configService.initConfig();
}