三层架构
我们的开发架构一般都是基于两种形式,一种是 C/S 架构,也就是客户端/服务器,另一种是 B/S 架构,也就 是浏览器服务器。在 JavaEE 开发中,几乎全都是基于 B/S 架构的开发。那么在 B/S 架构中,系统标准的三层架构 包括:表现层、业务层、持久层。三层架构在我们的实际开发中使用的非常多 。三层架构中,每一层各司其职 。
表现层
? 也就是我们常说的web层。它负责接收客户端请求,向客户端响应结果,通常客户端使用http协议请求 web 层,web 需要接收 http 请求,完成 http 响应。
? 表现层包括展示层和控制层:控制层负责接收请求,展示层负责结果的展示 。
? 表现层依赖业务层,接收到客户端请求一般会调用业务层进行业务处理,并将处理结果响应给客户端。
? 表现层的设计一般都使用 MVC 模型。(MVC 是表现层的设计模型,和其他层没有关系)
业务层
? 也就是我们常说的 service 层。它负责业务逻辑处理,和我们开发项目的需求息息相关。web 层依赖业 务层,但是业务层不依赖 web 层。
? 业务层在业务处理时可能会依赖持久层,如果要对数据持久化需要保证事务一致性。(也就是我们说的, 事务应该放到业务层来控制)
持久层
? 也就是我们是常说的 dao 层。负责数据持久化,包括数据层即数据库和数据访问层,数据库是对数据进 行持久化的载体,数据访问层是业务层和持久层交互的接口,业务层需要通过数据访问层将数据持久化到数据库 中。通俗的讲,持久层就是和数据库交互,对数据库表进行曾删改查的。
MVC 模型
? MVC 全名是 Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写, 是一种用于设计创建 Web 应用程序表现层的模式。MVC 中每个部分各司其职:
Model(模型):
? 通常指的就是我们的数据模型。作用一般情况下用于封装数据 。
View(视图):
? 通常指的就是我们的 jsp 或者 html。作用一般就是展示数据的。 通常视图是依据模型数据创建的。
Controller(控制器):
? 是应用程序中处理用户交互的部分。作用一般就是处理程序逻辑的。
? 它相对于前两个不是很好理解,这里举个例子:
? 例如:
? 我们要保存一个用户的信息,该用户信息中包含了姓名,性别,年龄等等。
? 这时候表单输入要求年龄必须是 1~100 之间的整数。姓名和性别不能为空。并且把数据填充 到模型之中。
? 此时除了 js 的校验之外,服务器端也应该有数据准确性的校验,那么校验就是控制器的该做 的 。
? 当校验失败后,由控制器负责把错误页面展示给使用者。
? 如果校验成功,也是控制器负责把数据填充到模型,并且调用业务层实现完整的业务需求 。
Spring MVC是什么
? SpringMVC 是一种基于 Java 的实现 MVC 设计模型的请求驱动类型的轻量级 Web 框架,属于 Spring FrameWork 的后续产品,已经融合在 Spring Web Flow 里面。Spring 框架提供了构建 Web 应用程序的全功 能 MVC 模块。使用 Spring 可插入的 MVC 架构,从而在使用 Spring 进行 WEB 开发时,可以选择使用 Spring 的 Spring MVC 框架或集成其他 MVC 开发框架,如 Struts1(现在一般不用),Struts2 等。
? SpringMVC 已经成为目前最主流的 MVC 框架之一,并且随着 Spring3.0 的发布,全面超越 Struts2,成 为最优秀的 MVC 框架。
? 它通过一套注解,让一个简单的 Java 类成为处理请求的控制器,而无须实现任何接口。同时它还支持 RESTful 编程风格的请求。
SpringMVC 在三层架构的位置
SpringMVC 的优势
1、清晰的角色划分:
2、分工明确,而且扩展点相当灵活,可以很容易扩展,虽然几乎不需要。
3、由于命令对象就是一个 POJO,无需继承框架特定 API,可以使用命令对象直接作为业务对象。
4、和 Spring 其他框架无缝集成,是其它 Web 框架所不具备的。
5、可适配,通过 HandlerAdapter 可以支持任意的类作为处理器。
6、可定制性,HandlerMapping、ViewResolver 等能够非常简单的定制。
7、功能强大的数据验证、格式化、绑定机制。 8、利用 Spring 提供的 Mock 对象能够非常简单的进行 Web 层单元测试。
9、本地化、主题的解析的支持,使我们更容易进行国际化和主题的切换。
10、强大的 JSP 标签库,使 JSP 编写更容易。
………………还有比如RESTful风格的支持、简单的文件上传、约定大于配置的契约式编程支持、基于注解的零配 置支持等等。
SpringMVC 和 Struts2 的优略分析
共同点:
区别:
Spring MVC 的入口是 Servlet, 而 Struts2 是 Filter
Spring MVC 是基于方法设计的,而 Struts2 是基于类,Struts2 每次执行都会创建一个动作类。所
以 Spring MVC 会稍微比 Struts2 快些。
Spring MVC 使用更加简洁,同时还支持 JSR303, 处理 ajax 的请求更方便。(JSR303 是一套 JavaBean 参数校验的标准,它定义了很多常用的校验注解,我们可以直接将这些注解加在我们 JavaBean 的属性上面,就可以在需要校验的时候进行校验了。)
Struts2 的 OGNL 表达式使页面的开发效率相比 Spring MVC 更高些,但执行效率并没有比 JSTL 提
升,尤其是 struts2 的表单标签,远没有 html 执行效率高。
需求
通过SpringMVC调度web
Maven导入相关依赖
<dependencies>
<!--spring-context-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<!--spring-web-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<!--spring-webmvc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<!--javax.servlet-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<!--jsp-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>SpringMvc入门程序</title>
</head>
<body>
<h1>入门程序</h1>
<a href="hello">进入入门程序</a><br>
</body>
</html>
HelloController
package cn.lpl666.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* 控制器类
*/
@Controller
public class HelloController {
@RequestMapping(path = "/hello")
public String hello(){
System.out.println("Hello SpringMvc");
return "success";
}
}
Spring配置文件
<?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.lpl666"></context:component-scan>
<!--配置视图解析器-->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--路径-->
<property name="prefix" value="/WEB-INF/pages/"></property>
<!--文件-->
<property name="suffix" value=".jsp"></property>
</bean>
<!--配置SpringMvc开启Mvc注解的支持-->
<mvc:annotation-driven enable-matrix-variables="true" />
</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-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--配置servlet初始化参数,调用springMvc配置文件,创建spring容器-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<!--请求项目中任何路径都会经过DispatcherServlet核心控制器-->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
运行
启动服务器,进入index.jsp页面,点击链接
大概执行过程
1、服务器启动,应用被加载。读取到 web.xml 中的配置创建 spring 容器并且初始化容器中的对象。 从入门案例中可以看到的是:HelloController 和 InternalResourceViewResolver,但是远不 止这些。
2、浏览器发送请求,被 DispatherServlet 捕获,该 Servlet 并不处理请求,而是把请求转发出去。转发 的路径是根据请求 URL,匹配@RequestMapping 中的内容。
3、匹配到了后,执行对应方法。该方法有一个返回值。
4、根据方法的返回值,借助 InternalResourceViewResolver 找到对应的结果视图 。
5、渲染结果视图,响应浏览器。
SpringMVC 的请求响应流程
详细的执行过程
DispatcherServlet:前端控制器
用户请求到达前端控制器,它就相当于 mvc 模式中的 c,dispatcherServlet 是整个流程控制的中心,由 它调用其它组件处理用户的请求,dispatcherServlet 的存在降低了组件之间的耦合性。
HandlerMapping:处理器映射器
HandlerMapping 负责根据用户请求找到 Handler 即处理器,SpringMVC 提供了不同的映射器实现不同的 映射方式,例如:配置文件方式,实现接口方式,注解方式等。
Handler处理器
它就是我们开发中要编写的具体业务控制器。由 DispatcherServlet 把用户请求转发到 Handler。由 Handler 对具体的用户请求进行处理。
HandlAdapter:处理器适配器
通过 HandlerAdapter 对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理 器进行执行。
View Resolver:视图解析器
View Resolver 负责将处理结果生成 View 视图,View Resolver 首先根据逻辑视图名解析成物理视图名 即具体的页面地址,再生成 View 视图对象,最后对 View 进行渲染将处理结果通过页面展示给用户。
View 视图
SpringMVC 框架提供了很多的 View 视图类型的支持,包括:jstlView、freemarkerView、pdfView 等。我们最常用的视图就是 jsp。
一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开 发具体的页面。
<mvc:annotation-driven>
说明
在 SpringMVC 的各个组件中,处理器映射器、处理器适配器、视图解析器称为 SpringMVC 的三大组件。
使 用<mvc:annotation-driven>
自动加载 RequestMappingHandlerMapping (处理映射器) 和 RequestMappingHandlerAdapter ( 处 理 适 配 器 ) , 可 用 在 SpringMVC.xml 配 置 文 件 中 使 用<mvc:annotation-driven>
替代注解处理器和适配器的配置。
它就相当于在 xml 中配置了:
<!-- 上面的标签相当于 如下配置-->
<!-- Begin -->
<!-- HandlerMapping -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerM
apping"></bean>
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"></bean>
<!-- HandlerAdapter -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerA
dapter"></bean>
<bean class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter"></bean>
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"></bean>
<!-- HadnlerExceptionResolvers -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExcept
ionResolver"></bean>
<bean class="org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolv
er"></bean>
<bean class="org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver"
></bean>
<!-- End -->
源码
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
}
作用
用于建立请求 URL 和处理请求方法之间的对应关系。
出现位置
类上: 请求 URL 的第一级访问目录。此处不写的话,就相当于应用的根目录。写的话需要以/开头。 它出现的目的是为了使我们的 URL 可以按照模块化管理:
例如:
账户模块:
加粗的部分就是把 RequsetMappding 写在类上,使我们的 URL 更加精细。
方法上:请求 URL 的第二级访问目录 。
属性
value:用于指定请求的 URL。它和 path 属性的作用是一样的。
method:用于指定请求的方式。
params:用于指定限制请求参数的条件。它支持简单的表达式。要求请求参数的 key 和 value 必须和 配置的一模一样。 例如:params = {"accountName"},表示请求参数必须有 accountName, params = {"moeny!100"},表示请求参数中 money 不能是 100。
headers:用于指定限制请求消息头的条件。
代码演示
@Controller
@RequestMapping("user")
public class UserController {
@RequestMapping("find")
public String find(){
System.out.println("查询用户操作");
return "success";
}
@RequestMapping(value = "save",method = {RequestMethod.POST})
public String save(){
System.out.println("保存用户操作");
return "success";
}
}
绑定机制
我们都知道,表单中请求参数都是基于 key=value 的。
SpringMVC 绑定请求参数的过程是通过把表单提交请求参数,作为控制器中方法参数进行绑定的。
例如:<a href="account/findAccount?accountId=10">查询账户</a>
中请求参数是: accountId=10
/**
* 查询账户
* @return
*/
@RequestMapping("/findAccount")
public String findAccount(Integer accountId) {
System.out.println("查询了账户。。。。"+accountId);
return "success";
}
支持的数据类型
SpringMVC 绑定请求参数是自动实现的,但是要想使用,必须遵循使用要求。
如果是基本类型或者 String 类型:要求我们的参数名称必须和控制器中方法的形参名称保持一致。(严格区分大小写)
如果是 POJO 类型,或者它的关联对象:要求表单中参数名称和 POJO 类的属性名称保持一致。并且控制器方法的参数类型是 POJO 类型。
如果是集合类型,有两种方式:
注意:它还可以实现一些数据类型自动转换。内置转换器全都在:
org.springframework.core.convert.support 包下。有:
java.lang.Boolean -> java.lang.String : ObjectToStringConverter
java.lang.Character -> java.lang.Number : CharacterToNumberFactory
java.lang.Character -> java.lang.String : ObjectToStringConverter
java.lang.Enum -> java.lang.String : EnumToStringConverter
java.lang.Number -> java.lang.Character : NumberToCharacterConverter
java.lang.Number -> java.lang.Number : NumberToNumberConverterFactory
java.lang.Number -> java.lang.String : ObjectToStringConverter
java.lang.String -> java.lang.Boolean : StringToBooleanConverter
java.lang.String -> java.lang.Character : StringToCharacterConverter
java.lang.String -> java.lang.Enum : StringToEnumConverterFactory
java.lang.String -> java.lang.Number : StringToNumberConverterFactory
java.lang.String -> java.util.Locale : StringToLocaleConverter
java.lang.String -> java.util.Properties : StringToPropertiesConverter
java.lang.String -> java.util.UUID : StringToUUIDConverter
java.util.Locale -> java.lang.String : ObjectToStringConverter
java.util.Properties -> java.lang.String : PropertiesToStringConverter
java.util.UUID -> java.lang.String : ObjectToStringConverter
......
如遇特殊类型转换要求,需要我们自己编写自定义类型转换器。
实体类
User
public class User {
private String uname;
private int age;
private Date date;
public String getUname() {
return uname;
}
public void setUname(String uname) {
this.uname = uname;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
@Override
public String toString() {
return "User{" +
"uname='" + uname + '\'' +
", age=" + age +
", date=" + date +
'}';
}
}
Account
public class Account {
private String name;
private double money;
private User user;
private List<User> list;
private Map<String,User> map;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public List<User> getList() {
return list;
}
public void setList(List<User> list) {
this.list = list;
}
public Map<String, User> getMap() {
return map;
}
public void setMap(Map<String, User> map) {
this.map = map;
}
@Override
public String toString() {
return "Account{" +
"name='" + name + '\'' +
", money=" + money +
", user=" + user +
", list=" + list +
", map=" + map +
'}';
}
}
AccountController
@Controller
@RequestMapping("account")
public class AccountController {
@RequestMapping(value = "test",method = {RequestMethod.POST})
public String test(String name,int money){
System.out.println(name);
System.out.println(money);
return "success";
}
@RequestMapping(value = "save",method = {RequestMethod.POST})
public String save(Account account){
System.out.println(account);
return "success";
}
jsp页面
<h2>表单1</h2>
<form method="post" action="account/test">
账户名:<input type="text" name="name"><br>
金额:<input type="text" name="money"><br>
<input type="submit">
</form>
<h2>表单2</h2>
<form method="post" action="account/save">
账户名:<input type="text" name="name"><br>
金额:<input type="text" name="money"><br>
用户名:<input type="text" name="user.uname"><br>
年龄:<input type="text" name="user.age"><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">
</form>
post 请求方式: 在 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>
使用场景:
jsp代码
<h2>表单3</h2>
<form method="post" action="user/save2">
用户名:<input type="text" name="uname"><br>
年龄:<input type="text" name="age"><br>
日期:<input type="text" name="date" value="2018-01-01">
<!--日期格式传入 yyyy-MM-dd -->
<input type="submit">
</form>
控制器代码
@RequestMapping(value = "save2",method = {RequestMethod.POST})
public String save2(User user){
System.out.println("保存用户操作");
System.out.println(user);
return "success";
}
提交订单运行结果
使用步骤
第一步:定义一个类,实现 Converter 接口,该接口有两个泛型
public interface Converter<S, T> {//S:表示接受的类型,T:表示目标类型
/**
* 实现类型转换的方法
*/
@Nullable
T convert(S source);
}
第二步: 自定义类型转换器
public class StringToDateConverter implements Converter<String,Date> {
@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 (ParseException e) {
throw new RuntimeException("数据类型转换错误");
}
}
}
第三步:在 spring 配置文件中配置类型转换器
<!--配置自定义类型转换器-->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="cn.lpl666.util.StringToDateConverter"/>
</set>
</property>
</bean>
<!--配置SpringMvc开启Mvc注解的支持-->
<mvc:annotation-driven enable-matrix-variables="true" conversion-service="conversionService"/>
ServletAPI对象
SpringMVC 还支持使用原始 ServletAPI 对象作为控制器方法的参数。支持原始 ServletAPI 对象有:
我们可以把上述对象,直接写在控制的方法参数中使用。
代码演示
部分示例代码:
jsp 代码:
<!-- 原始 ServletAPI 作为控制器参数 -->
<a href="account/testServletAPI">测试访问 ServletAPI</a>
控制器中的代码:
@RequestMapping("/testServletAPI")
public String testServletAPI(HttpServletRequest request,
HttpServletResponse response,
HttpSession session) {
System.out.println(request);
System.out.println(response);
System.out.println(session);
return "success";
}
执行结果:
作用
把请求中的指定名称的参数传递给控制器中的形参赋值
属性
代码
@RequestMapping("requestParamTest")
public String requestParamTest(@RequestParam(name = "name") String userName) {
System.out.println(userName);
return "success";
}
作用
作用:用于获取请求体的内容(注意:get方法不可以)
属性
required:是否必须有请求体,默认值是true
代码
@RequestMapping("requestBodyTest")
public String requestBodyTest(@RequestBody String body) {
System.out.println(body);
return "success";
}
作用
作用:拥有绑定url中的占位符的。例如:url中有/delete/{id},{id}就是占位符
属性
value:指定url中的占位符名称
Restful风格的URL
代码
// <a href="user/hello/1">入门案例</a>
/**
* 接收请求
* @return
*/
@RequestMapping(path="/hello/{id}")
public String sayHello(@PathVariable(value="id") String id) {
System.out.println(id);
return "success";
}
作用
作用:获取指定请求头的值
属性
value:请求头的名称
代码
@RequestMapping("requestHeaderTest")
public String requestHeaderTest(@RequestHeader("accept") String header) {
System.out.println(header);
return "success";
}
作用
作用:用于获取指定cookie的名称的值
属性
value:cookie的名称
代码
@RequestMapping("cookieValueTest")
public String cookieValueTest(@CookieValue("JSESSIONID") String id) {
System.out.println(id);
return "success";
}
作用
应用场景
代码-方式1
@RequestMapping("saveUser")
public String save(User user){
System.out.println(user);
return "success";
}
@ModelAttribute
public User modelAttributeTest(String uname){
User user = new User();
user.setUname(uname);
user.setAge(20);
user.setDate(new Date());
return user;
}
代码-方式2
@RequestMapping("saveUser")
public String save(@ModelAttribute("one") User user) {
System.out.println(user);
return "success";
}
@ModelAttribute
public void modelAttributeTest(Map<String, User> map, String uname) {
User user = new User();
user.setUname(uname);
user.setAge(20);
user.setDate(new Date());
map.put("one", user);
}
作用
作用:用于多次执行控制器方法间的参数共享
属性
value:指定存入属性的名称
代码
@Controller
@RequestMapping(path="/user")
@SessionAttributes(value= {"username","password","age"},types=
{String.class,Integer.class}) // 把数据存入到session域对象中
public class HelloController {
/**
* 向session中存入值
* @return
*/
@RequestMapping(path="/save")
public String save(Model model) {
System.out.println("向session域中保存数据");
model.addAttribute("username", "root");
model.addAttribute("password", "123");
model.addAttribute("age", 20);
return "success";
}
/**
* 从session中获取值
* @return
*/
@RequestMapping(path="/find")
public String find(ModelMap modelMap) {
String username = (String) modelMap.get("username");
String password = (String) modelMap.get("password");
Integer age = (Integer) modelMap.get("age");
System.out.println(username + " : "+password +" : "+age);
return "success";
}
/**
* 清除值
* @return
*/
@RequestMapping(path="/delete")
public String delete(SessionStatus status) {
status.setComplete();
return "success";
}
}
原文:https://www.cnblogs.com/lpl666/p/12358910.html