web浏览器发送请求
第一站:前端控制器(DispatcherServlet),其作用是将从处理器映射(handler mapping)获取请求的下一站是哪里(哪个控制器)。
第二站:将请求数据交由控制器处理(controller)或者是控制器将业务逻辑委托给一个或多个服务对象处理,
处理完的数据称为模型(model),再进行友好的方式进行格式化,最后发送给视图(view)
第三站:视图渲染数据响应给web浏览器。
public class SpittrWebAppInitializer
extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected String[] getServletMappings() {
return new String[] {"/"}; // 将DispatcherServlet映射到"/"
}
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] {RootConfig.class};
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] {WebConfig.class};
}
}
@Configuration
@EnableWebMvc
public class WebConfig {
}
@Configuration
@ComponentScan(basePackages={"spittr"},
excludeFilters={
@Filter(type=FilterType.ANNOTATION, value=EnableWebMvc.class)
})
public class RootConfig {
}
@Controller // 声明为一个控制器
public class DemoController {
@RequestMapping(value="/", method=GET) // 处理对"/"的Get请求
public String demo() {
return "demo"; // 发送给视图“demo”
}
}
略。。。详细可从书中p144获知
@Controller
@RequestMapping("/")
public class DemoController {
...
}
略。。。详细可从书中p147获知
@Controller
@RequestMapping("/")
public class DemoController {
@Autowired
private DemoService demoService;
@RequestMapping(value="/getList", method=GET)
public List<String> getList(@RequestParam("conut") long count) {
return demoService.selectByCount(count);
}
@RequestMapping(value="/{id}", method=GET)
public List<String> getObj(@PathVariable("id") long id) {
return demoService.selectById(id);
}
}
略。。。详细可从书中p158获知
在Spring MVC中提供了对java校验API的支持,详细可从书中p164获知或百度
public class Demo {
@NotNull
@Size(min=5, max=16)
private Long id;
...
}
@Controller
@RequestMapping("/")
public class DemoController {
@RequestMapping(value="/valid", method=POST)
public Boolean valid(@Valid Demo demo, Errors errors) {
if (errors.hasErrors()) {
return false;
}
return true;
}
}
Spring自带了13个视图解析器
视图解析器 | 描述 |
---|---|
BeanNameViewResolver | 将视图解析为Spring应用上下文中的bean,其中bean的ID与视图的名字相同 |
ContentNegotiatingViewResolver | 通过考虑客户端的内容类型来解析视图,委托给另一个能够产生对应内容类型的视图解析器 |
FreeMarkerViewResolver | 将视图解析为FreeMarker模板 |
InternalResourceViewResolver | 将视图解析为Web应用的内部资源(一般为JSP) |
JasperReportsViewResolver | 将视图解析为JasperReports定义 |
ResourceBundleViewResolver | 将视图解析为资源bundle(一般为属性文件) |
TilesViewResolver | 将视图解析为Apache Tiles定义,其中tile ID与视图名称相同(注意有两个不同的TilesViewResolver实现,分别对应Tiles 2.0 和 Tiles 3.0) |
UrlBasedViewResolver | 直接更具视图的名称解析视图,视图的名称会匹配一个物理视图的定义 |
VelocityLayoutViewResolver | 将视图解析为Velocity布局,从不同的Velocity模板中组合页面 |
VelocityViewResolver | 将视图解析为Velocity模板 |
XmlViewResolver | 将视图解析为XML文件中bean的定义(类似于BeanNameViewResolver) |
XsltViewReslover | 将视图解析为XSLT转换后的结果 |
ThymeleafViewResolver | 将逻辑视图名称解析为Thymeleaf模板视图 |
@bean
public ViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INFO/views/");
resolver.setSuffix(".jsp");
// 解析JSTL标签
resolver.setViewClass("org.springframework.web.servlet.view.JstlView.class");
return resolver;
}
<bean id="viewResolver" class= "org.springframework.web.servlet.view.InternalResourceViewResolver"
p:prefix="/WEB-INFO/views/" p:suffix=".jsp"
p:viewClass="org.springframework.web.servlet.view.JstlView" />
<%@ tagblib uri="http://wwww.springframwork.org/tags/form" prefix="sf" %>
<%@ tagblib uri="http://wwww.springframwork.org/tags" prefix="s" %>
@Bean
public MessageSource messageSource() {
ResourceBunleMessageSource messageSource = new ResourceBunleMessageSource();
messageSource.setBeanname("message");
return messageSource;
}
@Bean
public MessageSource messageSource() {
ReloadableResourceBunleMessageSource messageSource = new ReloadableResourceBunleMessageSource();
messageSource.setBasename("file://etc/spittr/messages"); // 类路径以 classpath: 为前缀
messageSource.setCacheSeconds(10);
return messageSource;
}
// en.properties
spittr.welcome=Welcome to Spittr!
// zh.properties
spittr.welcome=欢迎来到Spittr!
。。。详细可从书中p182获知
。。。详细可从书中p184获知
@Bean
public TilesConfigurer tilesConfigurer() {
TilesConfigurer tiles = new TilesConfigurer();
// 指定Tile的定义的位置 "/WEB-INFO/**/tiles.xml"
tiles.setDefinitions(new String[] {
"/WEB-INFO/layout/tiles.xml"
});
// 启用刷新功能
tiles.setCheckRefresh(true);
return tiles;
}
@Bean
public ViewResolver viewResolver() {
return new TilesViewResolver();
}
<bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles3.TilesConfigurer">
<property name="definitions">
<list>
<value>/WEB-INFO/layout/tiles.xmlvalue>
<value>/WEB-INFO/**/tiles.xmlvalue>
list>
property>
bean>
<bean id="viewResolver" class="org.springframework.web.servlet.view.tiles3.ViewResolver" />
<xml version="1.0" encoding="UTF-8">
<DOCTYPE tiles-definitions PUBLIC
"...">
<tiles-definitions>
<definition name="base" template="/WEB-INF/layout/page.jsp">
<put-attribute name="body" value="/WEB-INF/layout/header.jsp" />
<put-attribute name="footer" value="/WEB-INF/layout/footer.jsp" />
definition>
<definition name="home" template="/WEB-INF/layout/home.jsp">
<put-attribute name="body" value="/WEB-INF/layout/home.jsp" />
definition>
tiles-definitions>
<%@ tagblib uri="http://wwww.springframwork.org/tags" prefix="s" %>
<%@ tagblib uri="tiles.apache.org/tags-tiles" prefix="t" %>
...
略。。。详细可从书中p190~196获知
略。。。详细可从书中p200~205获知
从Spring3.1开始,Spring内置了两个MultipartResolver的实现供我们选择
multipart解析器 | 描述 |
---|---|
CommonsMultipartResolver | 使用Jakarta Commons FileUpload解析multipart请求 |
StandardServletMultipartResolver | 依赖于servlet3.0对multipart请求的支持 |
@bean
public MultipartResolver multipartResolver() throws IOException {
return new StandardServletMultipartResolver();
}
// java配置
@Override
protected void custiomizeRegistration(Dynamic registration) {
// 临时路径,文件大小不超过2MB,整个请求不超过4MB,所有文件都写入磁盘
registration.setMultipartConfig(new MultipartConfigElement("/tmp/spittr/uploads", 2096000, 4192000, 0));
}
<servlet>
<servlet-name>appServletservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<load-on-startup>1load-on-startup>
<multipart-config>
<location>/tmp/spittr/uploadslocation>
<max-file-size>2096000max-file-size>
<max-request-size>4192000max-request-size>
multipart-config>
servlet>
@bean
public MultipartResolver multipartResolver() throws IOException {
CommonsMultipartResolver resolver = new CommonsMultipartResolver();
resolver.setUploadTempDir(new FileSystemResource("/tmp/spittr/uploads")); // 临时路径
resolver.setMaxUploadSize(2096000); // 最大的文件容量
resolver.setMaxInMemorySize(0); // 最大的内存大小
return resolver;
}
// 方式一:无法获取文件信息
@RequestMapping(value="/register", method=POST)
public String processRegistration(
@RequestPart("profilePicture") btye[] profilePicture,
@Valid Spitter spitter,
Errors errors) {
...
}
// 方式二:使用part,能获取文件信息
@RequestMapping(value="/register", method=POST)
public String processRegistration(
@RequestPart("profilePicture") Part profilePicture,
@Valid Spitter spitter,
Errors errors) {
...
}
@ResponseStatus(value=HttpStatus.NOT_FOUND, reson="Spittle Not Found")
public class SpitterNotFoundException extends RuntimeException {
...
}
// 方式一
@RequestMapping(method=POST)
public String saveSpitter(SpittleForm form, Model model) {
try {
spittleRepository.save(new Spittle(null, form.getName()));
return "redirect:/spittles";
} catch (DuplicateSpittleException e) {
return "error/duplicate";
}
}
// 方式二
// 该控制器里的方法抛出DuplicateSpittleException异常就会调用handleDuplicateSpittle()方法来处理异常
@RequestMapping(method=POST)
public String saveSpitter(SpittleForm form, Model model) {
spittleRepository.save(new Spittle(null, form.getName()));
return "redirect:/spittles";
}
@ExceptionHandler(DuplicateSpittleException.class)
public String handleDuplicateSpittle() {
return "error/duplicate";
}
// 任意控制器抛出DuplicateSpittleException异常都会调用handleDuplicateSpittle()方法来处理异常
@ControllerAdvice
public class AppWideExceptionHandller {
@ExceptionHandler(DuplicateSpittleException.class)
public String handleDuplicateSpittle() {
return "error/duplicate";
}
}
跨重定向请求传递数据 |
---|
使用URL模板以路径变量或查询参数的形式传递数据 |
通过flash属性发送数据 |
@RequestMapping(value="/register", method=POST)
public String processRegistration(Spitter spitter, Model model) {
spittleRepository.save(Spitter);
model.adddAttribute("name", Spitter.getName());
return "redirect:/spittles/{name}";
}
@RequestMapping(value="/register", method=POST)
public String processRegistration(Spitter spitter, Model model) {
spittleRepository.save(spitter);
model.adddAttribute("name", spitter.getName());
model.adddFlashAttribute("spitter", spitter);
return "redirect:/spittles/{name}";
}
@RequestMapping(value="/{name}", method=GET)
public String showSpitterProfile(@PathVariable String name, Model model) {
if (!model.contaionsAttribute("spitter")) {
model.adAttribute(spittleRepository.findByName(name));
}
return "profile";
}
<flow:flow-executor id="flowExecutor" />
<flow:flow-registry id="flowRegistry" base-path="/WEB-INF/flows">
<flow:flow-location-pattern value="*-flow.xml" />
flow:flow-registry>
<flow:flow-registry id="flowRegistry">
<flow:flow-location value="/WEB-INF/flows/springpizza.xml" />
flow:flow-registry>
<flow:flow-registry id="flowRegistry">
<flow:flow-location id="pizza" value="/WEB-INF/flows/springpizza.xml" />
flow:flow-registry>
<bean class="org.springframework.webflow.mvc.servlet.FlowHandlerMapping">
<propertry name="flowRegistry" ref="flowRegistry" />
bean>
<bean class="org.springframework.webflow.mvc.servlet.FlowHandlerAdapter">
<propertry name="flowExecutor" ref="flowExecutor" />
bean>
类型 | 作用 |
---|---|
行为(Action) | 流程逻辑发生的地方 |
决策(Decision) | 将流程基于流程数据确定分成两个方向 |
结束(End) | 流程最后一站,一旦进入流程终止 |
子流程(Subflow) | 会在当前正在运行大队流程上下文中启动一个新的流程 |
视图(View) | 暂停流程并邀请用户参与流程 |
<action-state id="saveOrder">
<evaluate expression="pizzaFlowActions.saveOrder(order)" />
<transition to="thankYou" />
action-state>
<decision-state id="checkDeiveryArea">
<if test="pizzaFlowAtions.checkDeliveryArea(customer.zipCode)"
then="addCustomer"
else="deliveryWarning" />
decision-state>
<end-state id="customerReady" />
<subflow-state id="order" subflow="pizza/order">
<input name="order" value="order" />
<transition on="orderCreated" to="payment" />
subflow-state>
<view-state id = "welcome" />
<view-state id = "welcome" view="greeting"/>
<view-state id = "welcome" modedl="flowScope.paymentDetails"/>
<transition to="customerReady" />
<transition on="phoneEntered" to="customerReady" />
<transition on-exception="com.springinaction.pizza.service.CustomerNotFoundException" to="customerReady" />
<global-transitions>
<transition on="cancel" to="endstate" />
global-transitions>
<var name="customer" class="com.springinaction.pizza.domain.Customer" />
<evaluate result="viewScope.toppingsList" expression="T(com.springinaction.pizza.ddomain.Topping).asList()" />
<set name="flowScope.pizza" value="new com.springinaction.pizza.domain.Pizza()" />
范围 | 作用域和可见性 |
---|---|
Conversation | 最高层级的的流程开始时创建,最高层级的流程结束时销毁。被最高层级的流程和其所有的子流程所共享 |
Flow | 当流程开始时创建,在流程结束时销毁。只有在创建它的流程中是可见的 |
Request | 当一个请求进入流程时创建,在流程返回时销毁 |
Flash | 当流程开始时创建,在流程结束时销毁。在视图状态渲染后,它会被清除 |
View | 当进入视图状态时创建,当这个状态退出时销毁。只在视图状态内是可见的 |
略。。。详细可从书中p234~248获知