SpringMVC(一)
SpringMVC的基本概念
-
三层架构
- 表现层
- 业务层
- 持久层
-
MVC模型
- Model(模型):通常就是指我们的数据模型,一般情况下用于封装数据。
- View(视图):通常指的就是我们的jsp或者html,一般用于展示数据。通常视图依据数据模型创建。
- Controller(控制器):是应用程序中处理用户交互的部分,一般用于处理程序逻辑。
-
SpringMVC :是一种基于 java 的实现 MVC 设计模型的请求驱动类型的轻量级 web 框架,属于Spring Framework 的后续产品,已经融合在Spring Web Flow中。spring框架提供了构建Web应用程序的全功能MVC模块。
-
它通过一套注解,让一个简单的 Java 类成为处理请求的控制器,而无需实现任何接口,同时它还支持Restful 编程风格的请求。
-
优势:
- 清晰的角色划分
- 前端控制器(DispatcherServlet)
- 请求到处理器映射(HandlerMapping)
- 处理器适配器(HandlerAdapter)
- 视图解析器(ViewResolver)
- 处理器或页面控制器(Controller)
- 验证器(Validator)
- 命令对象(Command 请求参数绑定到的对象就叫命令对象)
- 表单对象(Form Object 提供给表单展示和提交到的对象就叫表单对象)
- 分工明确,扩展灵活
- 由于命令对象就是一个POJO,无需继承框架特定API,可以使用命令对象直接作为业务对象。
- 和 spring 其他框架无缝集成,是其他 web 框架所不具备的。
- 可适配。通过 HandlerAdapter 可以支持任意的类作为处理器。
- 可定制性,HandlerMapping、ViewResolver 等能够非常简单的定制。
- 功能强大的数据验证、格式化、绑定机制。
- 利用spring 提供的 Mock 对象能够非常简单的进行 Web 层单元测试。
- 本地化、主题的解析的支持,使我们更容易的进行国际化和主题的切换。
- 强大的 JSP 框架,使 JSP 编写更加容易。
- Restful 风格的支持,简单的文件上传,约定大于配置的契约式编程支持,基于注解的零配置支持等等。
- 清晰的角色划分
-
SpringMVC 和 Struts2 的优劣分析:
-
共同点:
- 他们都是表现层框架,都是基于 MVC 模型编写的。
- 他们的底层都离不开原始 ServletAPI。
- 他们处理请求的机制都是一个核心控制器。
-
区别:
- springMVC 的入口是 Servlet,而 Struts2 是 Filter。
- SpringMVC 是基于方法设计的,而 Struts2 是基于类,Struts2 每次执行都会创建一个动作类。所以SpringMVC(单例) 会稍微比 Struts2(多例) 快些。
- SpringMVC使用更加简洁,同时还支持JSR303(一套JavaBean 参数校验的标准,定义了很多常用的校验注解),处理 ajax 的请求更方便。
- struts2 的 OGNL 表达式使页面的开发效率相比 SpringMVC 更高些,但执行效率并没有比 JSTL 提升,尤其是 struts2 的表单标签,远没有 HTML 执行效率高。
-
SpringMVC的入门
-
需求:
- index.jsp (包含一个超链接标签)——> 发送请求 ——> 编写类、方法 ——> 转发到成功 jsp 页面
-
实现:
-
搭建开发环境
- 在pom.xml中引入依赖
- 配置web.xml
<servlet> <!--配置前端控制器--> <servlet-name>dispatcherServlet</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>dispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
- 配置springmvc.xml
<!--IOC开启注解扫描--> <context:component-scan base-package="com.ssm"></context:component-scan> <!--视图解析器对象--> <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!--文件所在目录--> <property name="prefix" value="/WEB-INF/pages/"/> <!--文件后缀名--> <property name="suffix" value=".jsp"/> </bean> <!--开启springMVC框架的注解支持--> <mvc:annotation-driven/>
-
编写入门程序
//控制器类 @Controller public class HelloController { @RequestMapping(path = "/hello") public String sayHello(){ System.out.println("Hello SpringMVC"); return "success"; } }
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <h3>入门程序</h3> <a href="hello">入门程序</a> </body> </html>
-
@RequestMapping
:- 如果出现在类上,表示请求URL的一级访问目录,不写则默认根目录,他的出现是为了使我们的url可以按照模块化管理。
- 如果出现在方法上,表示请求URL的二级访问目录。
- 属性:
value:用于指定请求的URL。他和path属性作用一样。
method:用于指定请求的方式。
params:用于指定限制请求参数的条件。支持简单表达式,要求参数的key和value必须和配置一模一样。
headers:用于指定限制请求消息头的条件。
-
请求参数的绑定
- 设置方法参数名与配置一样
@Controller
@RequestMapping("/param")
public class ParamController {
/**
* 请求参数的绑定
* @return
*/
@RequestMapping("/testParam")
public String testParam(String username,String password){
System.out.println("执行了……");
System.out.println("用户名:" + username);
System.out.println("密码:" + password);
return "success";
}
}
<%--此处username和password必须与controller中方法参数名一样--%>
<body>
<a href="param/testParam?username=jinlu&password=123">请求参数的绑定</a>
</body>
</html>
- 把数据封装到Account类(JavaBean)中,类中存在对象引用、list、map集合等
/**
* 请求参数绑定把数据封装到JavaBean的类中
* @param account
* @return
*/
@RequestMapping("saveAccount")
public String saveAccount(Account account){
System.out.println("执行……");
System.out.println(account);
return "success";
}
<%--把数据封装到Account类中,类中存在对象引用--%>
<form action="param/saveAccount" method="post">
姓名:<input type="text" name="username"/><br/>
密码:<input type="text" name="password"/><br/>
金额:<input type="text" name="money"/><br/>
用户:<input type="text" name="user.uname"/><br/>
年龄:<input type="text" name="user.age"/><br/>
<input type="submit" value="提交"/>
</form>
<%--把数据封装到Account类中,类中存在list和map集合--%>
<form action="param/saveAccount" method="post">
姓名:<input type="text" name="username"/><br/>
密码:<input type="text" name="password"/><br/>
金额:<input type="text" name="money"/><br/>
用户:<input type="text" name="list[0].uname"/><br/>
年龄:<input type="text" name="list[0].age"/><br/>
用户:<input type="text" name="map['one'].uname"/><br/>
年龄:<input type="text" name="map['one'].age"/><br/>
<input type="submit" value="提交"/>
</form>
-
自定义类型转换器
- 当实体类中含有如Date类型数据时,在jsp页面必须填写
yyyy/mm/dd
格式,如果写成yyyy-mm-dd
则会出错,无法转换日期类型,因为在jsp页面上的数据其实都是以 String 类型返回的。 - 自定义类型转换类及其方法:(必须实现接口Converter)
public class StringToDateConverter implements Converter<String,Date> { /** * 自定义类型转换 * @param s 传入的字符串 * @return */ @Nullable @Override public Date convert(String s) { //判断 if (s == null){ throw new RuntimeException("请您传入数据"); } DateFormat df = new SimpleDateFormat("yyyy-mm-dd"); try { //把字符串转换为日期 return df.parse(s); } catch (Exception e) { throw new RuntimeException("数据类型转换出错"); } } }
配置自定义类型转换器:springmvc.xml
<!--自定义类型转换器--> <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"> <property name="converters"> <set> <bean class="com.ssm.utils.StringToDateConverter"/> </set> </property> </bean> <!--开启springMVC框架的注解支持--> <mvc:annotation-driven conversion-service="conversionService"/>
- 当实体类中含有如Date类型数据时,在jsp页面必须填写
-
获取Servlet 原生API
/**
* 获取Servlet原生API
* @return
*/
@RequestMapping("testServlet")
public String testServlet(HttpServletRequest request, HttpServletResponse response){
System.out.println("执行……");
System.out.println(request);
HttpSession session = request.getSession();
System.out.println(session);
ServletContext context = session.getServletContext();
System.out.println(context);
System.out.println(response);
return "success";
}
常用注解
-
@RequestParam
- 作用:把请求中指定名称的参数给控制器中的形参赋值
- 属性:
- value:请求参数中的名称
- required:请求参数中是否必须提供此参数,。默认为true,表示必须提供,如果不提供将报错。
@RequestMapping("/testRequestParam") public String testRequestParam(@RequestParam(name = "uname") String username){ System.out.println("执行了……"); System.out.println(username); return "success"; }
<a href="anno/testRequestParam?uname=hah">requestParam注解</a>
-
@RequestBody
- 作用:用于获取请求体内容,直接使用得到是key=value&key=value……结构的数据。get请求方式不适用。
- 属性:
- required:是否必须有请求体,默认值为true,当取值为true时,get请求方式会报错。如果取值为false,get请求得到的是null。
@RequestMapping("/testRequestBody") public String testRequestBody(@RequestBody String body){ System.out.println("执行了……"); System.out.println(body); return "success"; }
<form action="anno/testRequestBody" method="post"> 用户姓名:<input type="text" name="username"><br/> 用户年龄:<input type="text" name="age"><br/> <input type="submit" value="提交"> </form>
-
PathVariable
- 作用:用于绑定url中的占位符,例如:请求url中的 /delete/{id},这个 {id} 就是url的占位符。url 支持占位符是spring3.0 之后加入的。是springmvc 支持restful 风格 url 的一个重要标志。
- 属性:
- value:用于指定 url 中的占位符名名称。
- required:是否必须提供占位符。
@RequestMapping("/testPathVariable/{sid}") public String testPathVariable(@PathVariable(name = "sid") String id){ System.out.println("执行了……"); System.out.println(id); return "success"; }
<a href="anno/testPathVariable/10">PathVariable注解</a>
-
@RequestHeader
- 作用:用于获取请求消息头。
- 属性:
- value:提供消息头名称
- required:是否必须有此消息头。
@RequestMapping("/testRequestHeader") public String testRequestHeader(@RequestHeader(value = "Accept") String header){ System.out.println("执行了……"); System.out.println(header); return "success"; }
-
@CookieValue
- 作用:用于把指定cookie 名称的值传入控制器方法参数。
- 属性:
- value:指定cookie的名称
- required:是否必须有此cookie
@RequestMapping("/testCookieValue") public String testCookieValue(@CookieValue(value = "JSESSIONID") String cookieValue){ System.out.println("执行了……"); System.out.println(cookieValue); return "success"; }
-
@ModelAttribute
- 作用:
- 出现在方法上:表示当前方法会在控制器的方法前先执行。他可以修饰没有返回值的方法,也可以修饰有具体返回值的方法。
- 出现在参数上:获取指定数据的给参数赋值。
- 属性:
- value:用于获取数据的key。key可以是POJO的属性名称,也可以是map结构的key。
- 应用场景:
- 当表单提交数据不是完整的实体类数据时,保证没有提交数据的字段使用数据库原来的数据。
- 例如:我们在编辑一个用户时,用户有一个创建信息字段,该字段的值是不允许被修改的。在提交表单数据时肯定没有此字段的内容,一旦更新会把该字段的内容置为null,此时就可以使用此注解解决问题。
@RequestMapping("/testModelAttribute") public String testModelAttribute(User user){ System.out.println("testModelAttribute执行了……"); System.out.println(user); return "success"; } //该方法会先执行 @ModelAttribute public User showUser(String uname){ System.out.println("showUser执行了……"); //通过uname查询数据库(模拟) User user = new User(); user.setUname(uname); user.setAge(20); user.setDate(new Date()); return user; }
@RequestMapping("/testModelAttribute") public String testModelAttribute(@ModelAttribute("abc") User user){ System.out.println("testModelAttribute执行了……"); System.out.println(user); return "success"; } //该方法会先执行 @ModelAttribute public void showUser(String uname, Map<String,User> map){ System.out.println("showUser执行了……"); //通过uname查询数据库(模拟) User user = new User(); user.setUname(uname); user.setAge(20); user.setDate(new Date()); map.put("abc",user); }
- 作用:
-
@SessionAttribute
- 作用:用于多次执行控制器方法间的参数共享。只能作用在类上。
- 属性:
- value:用于指定存入的属性名称。
- type:用于指定存入的数据类型。
//在类上添加 @SessionAttributes(value = "msg") //把 msg=靳璐 存入到 session 域对象中
//存入值 @RequestMapping("/testSessionAttributes") public String testSessionAttributes(Model model){ System.out.println("testSessionAttributes执行了……"); model.addAttribute("msg","靳璐"); return "success"; } //获取值 @RequestMapping("/getSessionAttributes") public String getSessionAttributes(ModelMap modelMap){ System.out.println("getSessionAttributes执行了……"); String str = (String) modelMap.get("msg"); System.out.println(str); return "success"; } //删除值 @RequestMapping("/delSessionAttributes") public String delSessionAttributes(SessionStatus status){ System.out.println("delSessionAttributes执行了……"); status.setComplete(); return "success"; }
success.xml
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %> <html> <head> <title>Title</title> </head> <body> <h3>成功!!!</h3> ${requestScope.msg} ${sessionScope} </body> </html>