1 关于三层架构和 MVC
1.1 三层架构
在JavaEE 开发中,几乎全都是基于 B/S 架构的开发。那么在 B/S 架构中,系统标准的三层架构,包括:表现层、业务层、持久层
- 表现层:
也就是我们常说的web层。它负责接收客户端请求,向客户端响应结果,通常客户端使用http协议请求web 层,web 需要接收 http 请求,完成 http 响应。
表现层包括展示层和控制层:控制层负责接收请求,展示层负责结果的展示。
表现层依赖业务层,接收到客户端请求一般会调用业务层进行业务处理,并将处理结果响应给客户端。
表现层的设计一般都使用 MVC 模型。(MVC 是表现层的设计模型,和其他层没有关系) - 业务层:
也就是我们常说的 service 层。它负责业务逻辑处理,和我们开发项目的需求息息相关。web 层依赖业务层,但是业务层不依赖 web 层。
业务层在业务处理时可能会依赖持久层,如果要对数据持久化需要保证事务一致性。(也就是我们说的,事务应该放到业务层来控制) - 持久层:
也就是我们是常说的 dao 层。负责数据持久化,包括数据层即数据库和数据访问层,数据库是对数据进行持久化的载体,数据访问层是业务层和持久层交互的接口,业务层需要通过数据访问层将数据持久化到数据库中。通俗的讲,持久层就是和数据库交互,对数据库表进行曾删改查的。
1.2 MVC 模型
MVC 全名是 Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,是一种用于设计创建 Web 应用程序表现层的模式。
MVC 中每个部分各司其职:
- Model(模型):
通常指的就是我们的数据模型。作用一般情况下用于封装数据。 - View(视图):
通常指的就是我们的 jsp 或者 html。作用一般就是展示数据的。
通常视图是依据模型数据创建的。 - Controller(控制器):
是应用程序中处理用户交互的部分。作用一般就是处理程序逻辑的。
2 SpringMVC 的入门
2.1 入门案例
- 导入项目所需坐标,将maven项目下的文件夹补全,创建配置文件springmvc.xml,加入一些约束信息。
- 配置spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--开启注解扫描 spring创建容器时要扫描的包-->
<context:component-scan base-package="cn.itcast"></context:component-scan>
<!--配置视图解析器对象-->
<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--控制器执行成功后跳转到指定页面-->
<!--找目录下文件-->
<property name="prefix" value="/WEB-INF/pages/"></property>
<!--文件后缀-->
<property name="suffix" value=".jsp"></property>
</bean>
<!--开启springMVC框架注解的支持-->
<mvc:annotation-driven></mvc:annotation-driven>
</beans>
- 配置web.xml
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<!--配置前端控制器 一个servlet-->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--加载配置文件springmvc.xml -> 注解扫描生效 -> 类生效-->
<!--全局的初始化-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!--原本应该是第一次发请求,servlet才创建,配置标签后,在启动服务器时,
容器就加载并初始化这个servlet,然后执行init-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
- 控制器
//前端:
<body>
<h3>入门程序</h3>
<%--相对路径--%>
<a href="hello">入门程序</a>
</body>
//后台处理
/**
* 控制器类
*/
//把类交给ioc容器管理
@Controller
public class HelloController {
//请求的映射:提供请求路径 (用于建立请求 URL 和处理请求方法之间的对应关系。)
@RequestMapping(path = "/hello")
public String sayHello(){
System.out.println("hello springMvc");
return "success"; //默认表示jsp名字
}
}
- 流程总结:
2.2 组件和注解
组件
- DispatcherServlet:前端控制器
用户请求到达前端控制器,它就相当于 mvc 模式中的 c,dispatcherServlet 是整个流程控制的中心,由它调用其它组件处理用户的请求,dispatcherServlet 的存在降低了组件之间的耦合性。 - HandlerMapping:处理器映射器
HandlerMapping 负责根据用户请求找到 Handler 即处理器,SpringMVC 提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。 - Handler:处理器
它就是我们开发中要编写的具体业务控制器。由 DispatcherServlet 把用户请求转发到 Handler。由Handler 对具体的用户请求进行处理。 - HandlAdapter:处理器适配器
通过 HandlerAdapter 对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。 - View Resolver:视图解析器
View Resolver 负责将处理结果生成 View 视图,View Resolver 首先根据逻辑视图名解析成物理视图名,即具体的页面地址,再生成 View 视图对象,最后对 View 进行渲染将处理结果通过页面展示给用户。
之前的案例中,我们并没有把组件全部配置,但是仍然可以使用,原因是我们开启了springMvc的注解支持:
- <mvc:annotation-driven>说明
在 SpringMVC 的各个组件中,处理器映射器、处理器适配器、视图解析器称为 SpringMVC 的三大组件。
使 用 <mvc:annotation-driven> 自动加载 RequestMappingHandlerMapping (处理映射器) 和RequestMappingHandlerAdapter ( 处 理 适 配 器 ) , 可 用 在 SpringMVC.xml 配 置 文 件 中 使 用<mvc:annotation-driven>替代注解处理器和适配器的配置。
注解
- @RequestMapping
- 作用:用于建立请求 URL 和处理请求方法之间的对应关系
- 定义:可以定义在类上或者方法上,如果定义在类上则表示一级目录,此时类上的注解表示二级目录,这样可以使Url模块化管理,也可以直接定义在方法上,表示要访问的路径。
- 属性:
value:用于指定请求的 URL。它和 path 属性的作用是一样的。
method:用于指定请求的方式。
params:用于指定限制请求参数的条件。它支持简单的表达式。要求请求参数的 key 和 value 必须和配置的一模一样。
//method = {RequestMethod.POST}超链接失败405,Request method 'GET' not supported
//params必须传入对应参数,要求请求参数的 key 和 value 必须和配置的一模一样
//headers:用于指定限制请求消息头的条件,需要包含指定请求头
@RequestMapping(value = "/testRequestMapping",params = {"username=heihei"},headers = {"Accept"})
3 请求参数的绑定
- 简单的数据
对于较少的数据,获取请求参数,可以直接在controller方法的形参中接收。
jsp
<%--请求参数绑定--%>
<a href="param/testParam?username=hehe&password=123">请求参数绑定</a>
@Controller
@RequestMapping("/param")
public class ParamCtroller {
@RequestMapping("testParam")
public String testParam(String username,String password){
System.out.println("执行...");
System.out.println(username+"--->"+password);
return "success";
}
}
- 数据封装到 JavaBean
<form action="param/saveAccount" method="post">
<%--name与javaBean的属性一致,会去找对应的set方法--%>
姓名:<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>
实体类Bean
public class Account implements Serializable {
private String username;
private String password;
private Double money;
private User user; //引用类型
...
}
Controller
//请求参数绑定,把数据封装到javaBean的类中
@RequestMapping("saveAccount")
public String saveAccount(Account account){ //默认表单传过来的数据会被封装成Account
System.out.println("执行...");
System.out.println(account);
return "success";
}
- 请求参数绑定集合类型
<%--把数据封装到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>
<%--list 把参数封装成user对象,存到list--%>
用户姓名:<input type="text" name="list[0].uname"><br>
用户年龄:<input type="text" name="list[0].age"><br>
<%--map key为'one' --%>
用户姓名:<input type="text" name="map['one'].uname"><br>
用户年龄:<input type="text" name="map['one'].age"><br>
<input type="submit" value="提交">
</form>
实体类存在集合类型
public class Account implements Serializable {
private String username;
private String password;
private Double money;
private List<User> list;
private Map<String,User> map;
}
- 配置解决中文乱码的过滤器
web.xml
<!--配置解决中文乱码的过滤器-->
<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>
- 自定义类型转换器
/**
* 把字符串转换为日期
*/
public class StringToDateConverter implements Converter<String, Date> {
@Override
public Date convert(String source) { //String source:传进来的字符串
//判断
if(source==null){
throw new RuntimeException("请传入参数");
}
DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
//把字符串转换为日期
try {
return df.parse(source);
} catch (ParseException e) {
throw new RuntimeException("数据类型转换错误");
}
}
}
springmvc.xml
<!--配置自定义类型转换器-->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="cn.itcast.utils.StringToDateConverter"></bean>
</set>
</property>
</bean>
<!--开启springMVC框架注解的支持
conversion-service="conversionService":使自定义类型转换器生效
-->
<mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>
- 使用 ServletAPI 对象作为方法参数
SpringMVC 还支持使用原始 ServletAPI 对象作为控制器方法的参数。
//原生servletApi --- 直接在方法上加参数(想获得的对象)
@RequestMapping("/textServlet")
public String textServlet(HttpServletRequest request, HttpServletResponse response){
System.out.println("执行...");
System.out.println(request);
System.out.println(response);
HttpSession session = request.getSession();
System.out.println(session);
ServletContext servletContext = session.getServletContext();
System.out.println(servletContext);
return "success";
}
4 常用注解
- RequestParam
- 作用:把请求中指定名称的参数给控制器中的形参赋值。
- 属性:
value:请求参数中的名称。
required:请求参数中是否必须提供此参数。默认值:true。表示必须提供,如果不提供将报错。
jsp
<a href="anno/textRequestParam?name=哈哈">RequestParam</a>
控制器
@Controller
@RequestMapping("/anno")
public class AnnoController {
@RequestMapping("/textRequestParam")
//必须传入指定名称参数,required 默认true,修改false则参数不必要,可不提供
public String textRequestParam(@RequestParam(name = "name",required = true) String username){
System.out.println("执行了");
System.out.println(username);
return "success";
}
}
- RequestBody
- 作用:
用于获取请求体内容。直接使用得到是 key=value&key=value…结构的数据。
get 请求方式不适用(get没有请求体)。 - 属性:
required:是否必须有请求体。默认值是:true。当取值为 true 时,get 请求方式会报错。如果取值为 false,get 请求得到是 null。
<form action="anno/textRequestBody" method="post">
用户姓名:<input type="text" name="username"><br>
用户年龄:<input type="text" name="age"><br>
<input type="submit" value="提交">
</form>
--------------------------------------------
//获取请求体的内容
@RequestMapping("/textRequestBody")
public String textRequestBody(@RequestBody String body){
System.out.println("执行了");
System.out.println(body); //username=zhangsan+&age=29
return "success";
}
- PathVaribale
- 作用:
用于绑定 url 中的占位符。例如:请求 url 中 /delete/{id},这个{id}就是 url 占位符。
url 支持占位符是 spring3.0 之后加入的。是 springmvc 支持 rest 风格 URL 的一个重要标志。 - 属性:
value:用于指定 url 中占位符名称。
required:是否必须提供占位符。
直接 / 后加参数
<a href="anno/textPathVaribale/10">textPathVaribale</a>
--------------------------------------------
//PathVaribale注解
@RequestMapping("/textPathVaribale/{sid}")
//绑定uri中占位符
public String textPathVaribale(@PathVariable(name = "sid") String id){
System.out.println("执行了");
System.out.println(id);
return "success";
}
- RequestHeader(不常用)
- 作用:用于获取请求消息头。
- 属性:
value:提供消息头名称
required:是否必须有此消息头
<a href="anno/textRequestHeader">RequestHeader</a>
//获取请求头的值
@RequestMapping("/textRequestHeader")
//获取特定请求头的值
public String textRequestHeader(@RequestHeader(value = "Accept") String header){
System.out.println("执行了");
System.out.println(header);
return "success";
}
- CookieValue
- 作用:用于把指定 cookie 名称的值传入控制器方法参数。
- 属性:
value:指定 cookie 的名称。
required:是否必须有此 cookie。
<a href="anno/textCookieValue">CookieValue</a>
//获取Cookie的值
@RequestMapping("/textCookieValue")
public String textCookieValue(@CookieValue(value = "JSESSIONID") String cookieValue){
System.out.println("执行了");
System.out.println(cookieValue);
return "success";
}
- ModelAttribute
- 作用:
该注解是 SpringMVC4.3 版本以后新加入的。它可以用于修饰方法和参数。
出现在方法上,表示当前方法会在控制器的方法执行之前,先执行。它可以修饰没有返回值的方法,也可以修饰有具体返回值的方法。
出现在参数上,获取指定的数据给参数赋值。 - 属性:
value:用于获取数据的 key。key 可以是 POJO 的属性名称,也可以是 map 结构的 key。 - 应用场景:
当表单提交数据不是完整的实体类数据时,保证没有提交数据的字段使用数据库对象原来的数据。
例如:
我们在编辑一个用户时,用户有一个创建信息字段,该字段的值是不允许被修改的。在提交表单数据是肯定没有此字段的内容,一旦更新会把该字段内容置为 null,此时就可以使用此注解解决问题。
//ModelAttribute注解
@RequestMapping("/textModelAttribute")
//1 带返回值user
public String textModelAttribute(User user){
//2 不带返回值,从map中取数据
//public String textModelAttribute(@ModelAttribute(value = "abc") User user){
System.out.println("textModelAttribute执行了...");
System.out.println(user);
return "success";
}
//1 ModelAttribute 修饰方法带返回值
@ModelAttribute //修饰方法,优先执行
public User showUser(String uname){
System.out.println("showUser执行...");
//通过用户名查询数据库
User user = new User();
user.setUname(uname);
user.setAge(20);
user.setDate(new Date());
return user;
}
//2 修饰方法,无返回值
@ModelAttribute
public void showUser(String uname, Map<String,User> map){
System.out.println("showUser执行...");
//通过用户名查询数据库
User user = new User();
user.setUname(uname);
user.setAge(20);
user.setDate(new Date());
map.put("abc",user);
}
- SessionAttribute
- 作用:用于多次执行控制器方法间的参数共享。
- 属性:
value:用于指定存入的属性名称
type:用于指定存入的数据类型。
<a href="anno/textSessionAttribute">SessionAttribute</a>
<a href="anno/getSessionAttribute">getSessionAttribute</a>
<a href="anno/delSessionAttribute">delSessionAttribute</a>
//修饰在类上
@SessionAttributes(value = {"msg"}) //把msg=美美存入到session域中
public class AnnoController {
...
//SessionAttribute注解
@RequestMapping("/textSessionAttribute")
public String textSessionAttribute(Model model){
System.out.println("执行了SessionAttribute");
//底层会存储到request域中
model.addAttribute("msg","美美");
return "success";
}
//获取session值
@RequestMapping("/getSessionAttribute")
public String getSessionAttribute(ModelMap modelMap){
System.out.println("getSessionAttribute...");
String msg = (String) modelMap.get("msg"); //从session取值
System.out.println(msg); //美美
return "success";
}
//清除session
@RequestMapping("/delSessionAttribute")
public String delSessionAttribute(SessionStatus status){
System.out.println("delSessionAttribute...");
status.setComplete(); //设置完成,清除session
return "success";
}
}