再探springMVC——01
原创 無柳先生
1、SpringMVC的核心器件
1、DispatcherServlet:前端控制器
用户请求到达前端控制器,它就相当于mvc模式中的c,dispatcherServlet是整个流程控制的中心,由它调用其它组件处理用户的请求,dispatcherServlet的存在降低了组件之间的耦合性。
2、HandlerMapping:处理器映射器
HandlerMapping负责根据用户请求url找到Handler即处理器,springmvc提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。
3、Handler:处理器(书写,即Controller)
Handler 是继DispatcherServlet前端控制器的后端控制器,在DispatcherServlet的控制下Handler对具体的用户请求进行处理。
由于Handler涉及到具体的用户业务请求,所以一般情况需要程序员根据业务需求开发Handler。
4、HandlAdapter:处理器适配器
通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。
5、ViewResolver:视图解析器
View Resolver负责将处理结果生成View视图,View Resolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。
6、View:视图(书写)
springmvc框架提供了很多的View视图类型的支持,包括:jstlView、freemarkerView、pdfView等。我们最常用的视图就是jsp。
2、SpringMVC运行流程
1、启动服务器,加载配置文件
- DispatcherServlet 对象创建
- springmvc.xml 配置文件被加载
- HelloController 创建成对象
2、发送请求,后台处理请求,并展示结果
3、SpringMVC执行过程详解
SpringMVC框架基于组件方式执行流程
执行流程详解
- 发送请求(request)
- 前端控制器(dispatcherServlet)接受到请求,交给处理器映射器(handlerMapping),根据请求路径和方法上配置的path找到要执行的方法,并返回。
- 前端控制器(dispatcherServlet)接收到返回的结果后交给处理器适配器(HandlerAdapter)执行,处理器适配器使用适配器模式,可以对传过来的各种方法进行执行,返回ModelAndView对象。
- ModelAndView对象经过前端控制器的转发交给试图解析器,解析完毕后加上我们配置的前缀后后缀,返回view 对象,这时浏览器接收到后就可以进行解析和展示。
- 视图再经过前端控制器的转发响应给用户,便得到了我们的结果。
注意一点,在整个流程中都离不开前端控制器(dispatcherServlet),但是他的作用只是将任务进行分发。没有对接收的对象或者数据做任何的处理。
4、SpringMVC注解详解
1、@RequestMapping
@RequestMapping使用案例
@Controller
@RequestMapping("/user")
public class HelloController {
@RequestMapping(value = "/hello" , method = RequestMethod.GET , params = "username" , headers = "Accept") //此时指定该方法只能接收get请求,并且请求中必须有username这个参数,在请求中必须有 Accept这个请求头
public String hello(){
System.out.println("hello , springMVC");
return "success";
}
}
2、@RequestMapping 源码分析
@Target({ElementType.METHOD, ElementType.TYPE}) //表示可以加在方法或者类上
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
String name() default "";
@AliasFor("path") //取了个别名叫 path
String[] value() default {};
@AliasFor("value") //取了个别名叫 value
//两个互相对应,表示两个属性的功能是相同的,都是指定url访问路径,value是默认的属性,如果只写这一个属性,那么value= 可以省略
String[] path() default {};
RequestMethod[] method() default {}; //指定请求的方式 ,RequestMethod是一个枚举类型,参数有:GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE 8种
String[] params() default {};
/*
params属性用于指定限制请求参数的条件。它支持简单的表达式。要求参数的key和value必须和配置的一模一样。
例如:params = {"accountName"},表示请求参数必须有accountName
params = {"money!100"},表示请求参数中的money不能是100
*/
String[] headers() default {};//用于指定限制请求消息头的条件
String[] consumes() default {};
String[] produces() default {};
}
5、SpringMVC请求参数绑定
请求参数的绑定说明
1、绑定机制
- 表单提交的数据都是k=v格式的 username=haha&password=123
- SpringMVC的参数绑定过程是把表单提交的请求参数,作为控制器中方法的参数进行绑定的
- 要求:提交表单的name和参数的名称是相同的
2、支持的数据类型
- 基本数据类型和字符串类型
- 实体类型(JavaBean)
- 集合数据类型(List、map集合等)
3、基本数据类型和字符串类型
- 提交表单的name和参数的名称是相同的
- 区分大小写
4、实体类型(JavaBean)
- 提交表单的name和JavaBean中的属性名称需要一致
- 如果一个JavaBean类中包含其他的引用类型,那么表单的name属性需要编写成:对象.属性 例如: address.name
5、给集合属性数据封装
- JSP页面编写方式:list[0].属性
复合参数传递实例
表单
<form action="${pageContext.request.contextPath}/param/test01" method="get">
用户名:<input type="text" name="username"/><br>
密码:<input type="text" name="password"/><br>
金额:<input type="text" name="money"/><br>
姓名:<input type="text" name="user.name"/><br>
年龄:<input type="text" name="user.age"/><br>
<input type="submit" value="提交">
</form>
实体类
public class Account {
private String username;
private String password;
private double money;
private User user;
//省略get、set方法及toString方法
}
public class User {
private String name;
private Integer age;
//省略get、set方法及toString方法
}
Controller
@Controller
@RequestMapping("/param")
public class ParamController {
@RequestMapping(value = "/test01")
public String testParam01(Account account){
System.out.println("测试springMVC请求参数绑定");
System.out.println(account);
return "success";
}
}
打印结果
测试springMVC请求参数绑定
Account{username='wuliuxs', password='ldyy122415', money=1000.0, user=User{name='lamoda', age=23}}
结论
SpringMVC 根据表单的name属性和方法的参数列表实现了对参数的自动封装 , 我们在传递时只需要注意name属性的值和我们实体类的属性名称是否对应。SpringMVC就可以实现对参数的自动封装,大大提高了开发的效率。
备注
如果是list和map集合进行数据的封装 , 那么使用 list[0].name 或 map[‘one’].name 进行封装即可,以下给出jsp及java代码。
<form action="account/updateAccount" method="post">
用户名称:<input type="text" name="username" ><br/>
用户密码:<input type="password" name="password" ><br/>
用户年龄:<input type="text" name="age" ><br/>
账户 1 名称:<input type="text" name="accounts[0].name" ><br/>
账户 1 金额:<input type="text" name="accounts[0].money" ><br/>
账户 2 名称:<input type="text" name="accounts[1].name" ><br/>
账户 2 金额:<input type="text" name="accounts[1].money" ><br/>
账户 3 名称:<input type="text" name="accountMap['one'].name" ><br/>
账户 3 金额:<input type="text" name="accountMap['one'].money" ><br/>
账户 4 名称:<input type="text" name="accountMap['two'].name" ><br/>
账户 4 金额:<input type="text" name="accountMap['two'].money" ><br/>
<input type="submit" value=" 保存 ">
</form>
public class User implements Serializable {
private String username;
private String password;
private Integer age;
private List<Account> accounts;
private Map<String,Account> accountMap;
}
请求参数中文乱码的解决
-
在web.xml中配置Spring提供的过滤器类
<!--配置解决中文乱码的过滤器--> <filter> <filter-name>characterEncodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <!--初始值--> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>characterEncodingFilter</filter-name> <!--拦截所有--> <url-pattern>/*</url-pattern> </filter-mapping>
6、自定义类型转换器
- 表单提交的任何数据类型全部都是字符串类型,但是后台定义Integer类型,数据也可以封装上,说明 Spring框架内部会默认进行数据类型转换。
- 如果想自定义数据类型转换,可以实现Converter的接口
public class StringToDateConverter implements Converter<String , Date> { //如果要自定义类型转换器,必须实现Conveter< S , T >接口,S为传入的String类型,T为你需要转换的类型。
/**
* 类型转换方法
* @param source 传入的字符串
* @return 返回一个转换完成的值
*/
public Date convert(String source) {
if ("".equals(source)){
throw new NullPointerException();
}
DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
try {
return df.parse(source);
}catch (Exception e){
e.printStackTrace();
throw new ClassCastException();
}
}
}
- 注册自定义类型转换器,在springmvc.xml配置文件中编写配置
<!--配置自定义类型转换器-->
<bean id="conversionService1" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.ldyy.utils.StringToDateConverter"/>
</set>
</property>
</bean>
<!--开启springMVC注解支持-->
<mvc:annotation-driven conversion-service="conversionService1"/>
-
在控制器中使用原生的ServletAPI对象
-
只需要在控制器的方法参数定义HttpServletRequest和HttpServletResponse对象
@RequestMapping(value = "/servlet") public String testParam02(HttpServletRequest request , HttpServletResponse response){ System.out.println("测试获取requset和response对象"); System.out.println(request); System.out.println(response); System.out.println(request.getSession()); return "success"; }
-
6、常用的注解
1、@RequestParam
作用: 把请求中指定名称的参数给控制器中的形参赋值。
属性:
value:请求参数中的名称。
required:请求参数中是否必须提供此参数。默认值:true。表示必须提供,如果不提供将报错
代码示例
@Controller
@RequestMapping("/annotation")
public class AnnotationController {
@RequestMapping(value = "/test")
public String test01(@RequestParam(name = "username") String name){
System.out.println(name);
return "success";
}
}
@RequestParam注解源码
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestParam {
@AliasFor("name")
String value() default "";
@AliasFor("value")
String name() default "";
boolean required() default true;
String defaultValue() default ValueConstants.DEFAULT_NONE;
}
2、@ RequestBody
作用: 用于获取请求体内容。直接使用得到是 key=value&key=value…结构的数据。
get 请求方式不适用。( get请求没有请求体 )
属性:
required:是否必须有请求体。默认值是:true。当取值为 true 时,get 请求方式会报错。如果取值 为 false,get 请求得到是 null。
代码示例
@RequestMapping(value = "/test01")
public String test01(@RequestBody String body){
System.out.println(body);
return "success";
}
//输入为: 大熊 23
//打印结果为: name=%E5%A4%A7%E7%86%8A&age=23
@ RequestBody 源码
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestBody {
boolean required() default true;
}
3、@ PathVaribale
作用: 用于绑定 url 中的占位符。
例如:请求 url 中 /delete/{id},这个{id}就是 url 占位符。
url 支持占位符是 spring3.0 之后加入的。是 springmvc 支持 rest 风格 URL 的一个重要标志。
属性:
value:用于指定 url 中占位符名称。
required:是否必须提供占位符。
代码示例
@RequestMapping(value = "/test02/{id}")
public String test02(@PathVariable("id") String id){
System.out.println(id);
return "success";
}
//访问路径为: <a href="${pageContext.request.contextPath}/annotation/test02/12">PathVariable注解测试</a>
//打印结果为:12
@ PathVaribale 源码
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PathVariable {
@AliasFor("name")
String value() default "";
@AliasFor("value")
String name() default "";
boolean required() default true;
}
@RequestHeader(一般不会用到,不做过多的介绍)
作用:
用于获取请求消息头。
属性:
value:提供消息头名称
required:是否必须有此消息头
注: 在实际开发中一般不怎么用。
@CookieValue(一般不会用到,不做过多的介绍)
作用:
用于把指定 cookie 名称的值传入控制器方法参数。
属性:
value:指定 cookie 的名称。
required:是否必须有此 cookie。
@ModelAttribute
作用:
该注解是 SpringMVC4.3 版本以后新加入的。它可以用于修饰方法和参数。
出现在方法上,表示当前方法会在控制器的方法执行之前,先执行。
它可以修饰没有返回值的方法,也可 以修饰有具体返回值的方法。
出现在参数上,获取指定的数据给参数赋值。
属性:
value:用于获取数据的 key。key 可以是 POJO 的属性名称,也可以是 map 结构的 key。
应用场景:
当表单提交数据不是完整的实体类数据时,保证没有提交数据的字段使用数据库对象原来的数据。
例如:
我们在编辑一个用户时,用户有一个创建信息字段,该字段的值是不允许被修改的。在提交表单数 据是肯定没有此字段的内容,一旦更新会把该字段内容置为 null,此时就可以使用此注解解决问题。
代码示例 (@ModelAttribute修饰的方法有返回值)
@RequestMapping(value = "/test03")
public String test03(User user){
System.out.println(user);
return "success";
}
/**
* 说明:加上该注解的方法会在该执行的方法之前执行,可以帮助封装我们没有传递的参数,
* 他有两种情况 1、有返回值(如下) 2、没有返回值(可以使用一个map进行值的传递)
*/
@ModelAttribute
public User show(String name){
User user = new User();
user.setName(name);
user.setBirthday(new Date());
return user;
}
//访问路径为:
<form action="${pageContext.request.contextPath}/annotation/test03" method="post">
姓名:<input type="text" name="name"/><br>
年龄:<input type="text" name="age"/><br>
<input type="submit" value="提交">
</form>
//打印结果为:User{name='Java框架', age=23, birthday=Sat Oct 10 18:58:51 CST 2020}
代码示例 (@ModelAttribute修饰的方法无返回值)
@RequestMapping(value = "/test03")
public String test03(@ModelAttribute("abc") User user){
System.out.println(user);
return "success";
}
/**
* 说明:加上该注解的方法会在该执行的方法之前执行,可以帮助封装我们没有传递的参数,
* 对于没有返回值的方法,我们可以使用map进行封装,传递一个map即可
*/
@ModelAttribute
public void show(String name , Map<String , User> map){
User user = new User();
user.setName(name);
user.setBirthday(new Date());
map.put("abc" , user);
}
//访问路径为:
<form action="${pageContext.request.contextPath}/annotation/test03" method="post">
姓名:<input type="text" name="name"/><br>
年龄:<input type="text" name="age"/><br>
<input type="submit" value="提交">
</form>
//打印结果为:User{name='java高级', age=34, birthday=Sat Oct 10 19:07:56 CST 2020}
@SessionAttribute
作用:
用于多次执行控制器方法间的参数共享。
属性:
value:用于指定存入的属性名称
type:用于指定存入的数据类型。
代码示例
@Controller
@RequestMapping("/annotation")
@SessionAttributes("msg")
public class AnnotationController {
//设置session
@RequestMapping(value = "/setSession")
public String setSession(Model model){
model.addAttribute("msg" , "wuliuxs");
return "success";
}
//获取session
@RequestMapping(value = "/getSession")
public String getSession(ModelMap modelMap){
String msg = (String)modelMap.get("msg");
System.out.println(msg);
return "success";
}
//删除session
@RequestMapping(value = "/delSession")
public String delSession(SessionStatus status){
status.setComplete();//完成,清除session
return "success";
}
}
<a href="${pageContext.request.contextPath}/annotation/setSession">存入session</a>
<a href="${pageContext.request.contextPath}/annotation/getSession">获取session</a>
<a href="${pageContext.request.contextPath}/annotation/delSession">删除session</a>