一、工作原理
核心控制器为DispatcherServlet
1. 客户端的所有请求都交给前端控制器DispatcherServlet来处理,它会负责调用系统的其他模块来真正处理用户的请求。
2.DispatcherServlet收到请求后,将根据请求的信息(包括URL、HTTP协议方法、请求头、请求参数、Cookie等)以及HandlerMapping的配置找到处理该请求的Handler(任何一个对象都可以作为请求的Handler)。当然,在这个地方Spring会通过HandlerAdapter对该处理器进行封装,HandlerAdapter是一个适配器,它用统一的接口对各种Handler中的方法进行调用。
3. Handler完成对用户请求的处理后,会返回一个ModelAndView对象给DispatcherServlet,
ModelAndView顾名思义,包含了数据模型以及相应的视图的信息。当然,这里的视图是逻辑视图,DispatcherServlet还要借助ViewResolver完成从逻辑视图到真实视图对象的解析工作。
4. 当得到真正的视图对象后,Dispatcher会利用视图对象对模型数据进行渲染。
5. 客户端得到响应,可能是一个普通的HTML页面,也可以是XML或JSON字符串,还可以是一张图片或者一个PDF文件。
二、核心类与接口
1、DispatcherServlet -- 前置控制器
2、HandlerMapping接口 -- 处理请求的映射
HandlerMapping接口的实现类:
SimpleUrlHandlerMapping 通过配置文件,把一个URL映射到Controller
DefaultAnnotationHandlerMapping 通过注解,把一个URL映射到Controller类上
3、HandlerAdapter接口 -- 处理请求的映射
AnnotationMethodHandlerAdapter类,通过注解,把一个URL映射到Controller类的方法上
4、Controller接口 -- 控制器
由于我们使用了@Controller注解,添加了@Controller注解注解的类就可以担任控制器(Action)的职责,
所以我们并没有用到这个接口。
5、HandlerInterceptor 接口--拦截器
我们自己实现这个接口,来完成拦截的器的工作。
6、ViewResolver接口的实现类
UrlBasedViewResolver类 通过配置文件,把一个视图名交给到一个View来处理
InternalResourceViewResolver类,比上面的类,加入了JSTL的支持
7、View接口
JstlView类
8、HandlerExceptionResolver接口 --异常处理
SimpleMappingExceptionResolver实现类
9、ModelAndView类
三、DispatcherServlet配置说明
DispatcherServlet是一个Servlet,所以可以配置多个DispatcherServlet
<servlet>
<servlet-name>springMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:/springMVC.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
1、<init-param> 指明了配置文件的文件名,不使用默认配置文件名,而使用springMVC.xml配置文件。
2、<load-on-startup>1</load-on-startup>是启动顺序,让这个Servlet随Servletp容器一起启动。
3、 <url-pattern>/</url-pattern> 会拦截请求。
①拦截*.do、*.htm, 例如:/user/add.do
这是最传统的方式,最简单也最实用。不会导致静态文件(jpg,js,css)被拦截。
②拦截/,例如:/user/add
可以实现现在很流行的REST风格。很多互联网类型的应用很喜欢这种风格的URL。
弊端:会导致静态文件(jpg,js,css)被拦截后不能正常显示。想实现REST风格,事情就是麻烦一些。后面有解决办法还算简单。
四、SpringMVC配置说明
1、自动扫描
2、加载资源文件
3、默认的注释驱动
4、视图解析器
5、拦截器
6、配置全局异常处理器
7、配置静态资源映射
1、<context:component-scanbase-package="com.yuhui" />
2、<context:property-placeholderlocation="classpath:db.properties"/>
3、<mvc:annotation-driven />
4、<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/><!--可为空,方便实现自已的依据扩展名来选择视图解释类的逻辑 -->
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
</bean>
5、<mvc:interceptors>
<beanclass="com.yuhui.interceptor.MyInterceptor"></bean> <!-- 拦截所有springmvc的url! -->
<mvc:interceptor>
<mvc:mappingpath="/user.do" />
<!-- <mvc:mappingpath="/test/*" /> -->
<beanclass="com.yuhui.interceptor.MyInterceptor2"></bean>
</mvc:interceptor>
</mvc:interceptors>
6、<bean class="com.yuhui.GlobalExceptionResolver"></bean>
7、<mvc:resources mapping="/images/**" location="/images/" cache-period="31556926"/>
<mvc:resources mapping="/js/**" location="/js/" cache-period="31556926"/>
<mvc:resources mapping="/css/**" location="/css/" cache-period="31556926"/>
<context:component-scan/> 扫描指定的包中的类上的注解,常用的注解有:
@Controller 声明Action组件
@Service 声明Service组件 @Service("myMovieLister")
@Repository 声明Dao组件
@Component 泛指组件, 当不好归类时.
@RequestMapping("/menu") 请求映射
@Resource 用于注入,( j2ee提供的) 默认按名称装配,@Resource(name="beanName")
@Autowired 用于注入,(srping提供的) 默认按类型装配
@Transactional( rollbackFor={Exception.class}) 事务管理
@ResponseBody
@Scope("prototype") 设定bean的作用域
@RequestParam 请求参数
<mvc:annotation-driven/> 是一种简写形式,完全可以手动配置替代这种简写形式,简写形式可以让初学都快速应用默认配置方案。<mvc:annotation-driven /> 会自动注册DefaultAnnotationHandlerMapping与AnnotationMethodHandlerAdapter 两个bean,是spring MVC为@Controllers分发请求所必须的。
并提供了:数据绑定支持,@NumberFormatannotation支持,@DateTimeFormat支持,@Valid支持,读写XML的支持(JAXB),读写JSON的支持(Jackson)。
<mvc:interceptors/>是一种简写形式。通过看前面的大图,知道,我们可以配置多个HandlerMapping。<mvc:interceptors/>会为每一个HandlerMapping,注入一个拦截器。其实我们也可以手动配置为每个HandlerMapping注入一个拦截器。
<mvc:resourcesmapping="/images/**" location="/images/"cache-period="31556926"/> 匹配URL /images/** 的URL被当做静态资源,由Spring读出到内存中再响应http。
五、拦截器和全局异常处理
拦截器:
1、实现HandlerInterceptor接口
2、三个方法
perHandle:Action之前执行,可进行编码、安全控制等处理
postHandler:生成视图之前执行,有机会修改ModelAndView
afterCompletion:最后执行,可用于释放资源,可以根据ex是否为null判断是否发生了异常,进行日志记录
参数中的Object handler是下一个拦截器
3、配置拦截器
全局异常处理:
1、实现HandlerExceptionResolver接口
2、在方法resolveException中定义异常处理(打印异常、日志文件、发邮件、发短信、展示错误页面)
3、配置全局异常处理器
六、父子上下文
使用了listener监听器来加载配置,Spring会创建一个WebApplicationContext上下文,称为父上下文(父容器),保存在 ServletContext中,key是WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE的值。
可以使用Spring提供的工具类取出上下文对象:WebApplicationContextUtils.getWebApplicationContext(ServletContext);
DispatcherServlet是一个Servlet,可以同时配置多个,每个 DispatcherServlet有一个自己的上下文对象(WebApplicationContext),称为子上下文(子容器),子上下文可以访问父上下文中的内容,但父上下文不能访问子上下文中的内容。它也保存在 ServletContext中,key是"org.springframework.web.servlet.FrameworkServlet.CONTEXT"+Servlet名称。当一个Request对象产生时,会把这个子上下文对象(WebApplicationContext)保存在Request对象中,key是DispatcherServlet.class.getName()+ ".CONTEXT"。
可以使用工具类取出上下文对象:RequestContextUtils.getWebApplicationContext(request);
说明 :Spring 并没有限制我们,必须使用父子上下文。我们可以自己决定如何使用。
方案一,传统型:
父上下文容器中保存数据源、服务层、DAO层、事务的Bean。
子上下文容器中保存Mvc相关的Action的Bean.
事务控制在服务层。
由于父上下文容器不能访问子上下文容器中内容,事务的Bean在父上下文容器中,无法访问子上下文容器中内容,就无法对子上下文容器中Action进行AOP(事务)。
当然,做为“传统型”方案,也没有必要这要做。
方案二,激进型:
不使用listener监听器来加载spring的配置文件,只使用DispatcherServlet来加载spring的配置,不要父子上下文,只使用一个DispatcherServlet,事情就简单了。
Java--大项目能做好--按传统方式做,规规矩矩的做,好扩展,好维护。
Java--小项目能做快--按激进方式做,一周时间就可以出一个版本,先上线接受市场(用户)的反馈,再改进,再反馈,时间就是生命(成本)。
七、ajax、转发与重定向
Ajax:
1、使用Jackson包
2、<mvc:annotation-driven />支持JSon
3、使用@ResponseBody注解绑定
转发与重定向:
可以通过redirect/forward:url方式转到另一个Action进行连续的处理。
可以通过redirect:url 防止表单重复提交 。
写法如下:
return "forward:/order/add";
return "redirect:/index.jsp";
带参数重定向—RedirectAttributes
redirectAttributes.addFlashAttribute("message", "操作成功");
return "redirect:/account/group/";