SpringMVC框架
SpringMVC是什么
SpringMVC 是一种基于 Java 的实现 MVC 设计模型的请求驱动类型的轻量级 Web 框架 ,属于 SpringFrameWork 的后续产品,已经融合在 Spring Web Flow 里面。
三层架构
在 B/S 架构中,系统标准的三层架构包括:表现层、业务层、持久层。
表现层
也就是我们常说的 web 层。它负责接收客户端请求,向客户端响应结果,通常客户端使用 http 协议请求web 层,web 需要接收 http 请求,完成 http 响应。
表现层包括展示层和控制层:控制层负责接收请求,展示层负责结果的展示。
表现层依赖业务层,接收到客户端请求一般会调用业务层进行业务处理,并将处理结果响应给客户端。
表现层的设计一般都使用 MVC 模型。(MVC 是表现层的设计模型,和其他层没有关系)
业务层
也就是我们常说的 service 层。它负责业务逻辑处理,和我们开发项目的需求息息相关。web 层依赖业务层,但是业务层不依赖 web 层。
业务层在业务处理时可能会依赖持久层,如果要对数据持久化需要保证事务一致性。(也就是我们说的,事务应该放到业务层来控制)
持久层:
也就是我们是常说的 dao 层。负责数据持久化,包括数据层即数据库和数据访问层,数据库是对数据进行持久化的载体, 数据访问层是业务层和持久层交互的接口,业务层需要通过数据访问层将数据持久化到数据库中。通俗的讲,持久层就是和数据库交互,对数据库表进行曾删改查的。
MVC模型
MVC 全名是 Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,是一种用于设计创建 Web 应用程序表现层的模式
Model(模型)
通常指的就是我们的数据模型。作用一般情况下用于封装数据。
View(视图)
通常指的就是我们的 jsp 或者 html。作用一般就是展示数据的。
通常视图是依据模型数据创建的。
Controller(控制器)
是应用程序中处理用户交互的部分。作用一般就是处理程序逻辑的。
SpringMVC在三层架构中位置
位于三层架构中的表现层,使用MVC模型设计
简单入门案例对springMVC的实现的流程图
请求参数绑定
SpringMVC绑定请求参数的过程是通过把表单提交请求参数,作为控制器中方法参数进行绑定;SpringMVC绑定请求参数是自动实现的,但是要想使用,必须遵循使用要求。
支持的请求类型
-
基本类型参数:基本类型和String类型
-
POJO类型参数:包括实体类以及关联的实体类
-
数据和集合类型参数:包括List结构和Map结构的集合
使用要求
基本类型或String类型
要求我们的参数名称必须和控制器中方法的形参名称保持一致(严格区分大小写)
POJO类或者它的关联对象
要求表单中参数名称和POJO类的属性名称保持一致。并且控制器的方法的参数类型是POJO类型
实体类代码
jsp请求代码,属性名称和实体类属性一致
数组和集合类型参数
第一种:
要求集合类型的请求参数必须在POJO中,在表单中请求参数名称要和POJO中集合属性名称相同。
给List集合中的元素赋值,使用下标。
给Map集合中的元素赋值,使用键值对。
实体类代码
JSP代码
控制器代码
第二种:
接受的请求参数是json格式数据,需要借助一个主机实现
注意:实现一些数据类型自动转换,内置转换器全都在:org.springframework.core.convert.support包下
使用ServletAPI对象作为方法参数
SpringMVC还支持使用原始ServletAPI对象作为控制器方法的参数。支持原始ServletAPI对象有:
常用注解
@RequestParam
作用:把请求中指定名称的参数给控制器中的形参赋值
属性:
value:请求参数中的名称
required:请求参数中是否必须提供此参数。默认值:true,表示必须提供,不提供将报错
jsp 中的代码:
<!-- requestParams 注解的使用 -->
<a href="springmvc/useRequestParam?name=test">requestParam 注解</a>
//控制器代码
@RequestMapping("/useRequestParam")
public String useRequestParam(@RequestParam("name") String username,@RequestParam(value="age",required=false)Integer age){
System.out.println(username+","+age);
return "success";
}
@RequestBody
作用:用于获取请求体内容。直接使用得到的是key=value&key=value...结构的数据;get请求方式不适用
属性:
required:是否必须有请求体。默认值是:true。当取值为true时,get请求方式会报错。
<!-- request body 注解 -->
<form action="springmvc/useRequestBody" method="post">
用户名称:<input type="text" name="username" ><br/>
用户密码:<input type="password" name="password" ><br/>
用户年龄:<input type="text" name="age" ><br/>
<input type="submit" value="保存">
</form>
//控制器代码
@RequestMapping("/useRequestBody")
public String useRequestBody(@RequestBody(required=false) String body){
System.out.println(body);
return "success";
}
@PathVaribale
作用:用于绑定url中的占位符。例如:请求url中/delete/{id},这个{id}就是url占位符。
属性:
value:用于指定url中占位符名称
required:是否必须提供占位符
使用示例
<!--jsp 代码: -->
<!-- PathVariable 注解 -->
<a href="springmvc/usePathVariable/100">pathVariable 注解</a>
//控制器代码
@RequestMapping("/usePathVariable/{id}")
public String usePathVariable(@PathVariable("id") Integer id){
System.out.println(id);
return "success";
}
@ModelAttribute
作用:该注解是 SpringMVC4.3 版本以后新加入的。它可以用于修饰方法和参数。
出现在方法上,表示当前方法会在控制器的方法执行之前,先执行。它可以修饰没有返回值的方法,也可以修饰有具体返回值的方法。
出现在参数上,获取指定的数据给参数赋值。
属性:
value:用于获取数据的 key。key 可以是 POJO 的属性名称,也可以是 map 结构的 key。
应用场景:我们在编辑一个用户时, 用户有一个创建信息字段,该字段的值是不允许被修改的。在提交表单数据是肯定没有此字段的内容,一旦更新会把该字段内容置为 null,此时就可以使用此注解解决问题。
@ModelAttribute
public void showModel(User user) {
System.out.println("执行了 showModel 方法"+user.getUsername());
}
@RequestMapping("/testModelAttribute")
public String testModelAttribute(User user) {
System.out.println("执行了控制器的方法"+user.getUsername());
return "success";
}
//执行了 showModel 方法
//执行了控制器的方法
@SessionAttribute
作用:
用于多次执行控制器方法间的参数共享。
属性:
value:用于指定存入的属性名称
type:用于指定存入的数据类型。
示例:
@RequestMapping("/springmvc")
@SessionAttributes(value ={"username","password"},types={Integer.class})
public class SessionAttributeController {
@RequestMapping("/testPut")
public String testPut(Model model){
model.addAttribute("username", "泰斯特");
model.addAttribute("password","123456");
model.addAttribute("age", 31);
//跳转之前将数据保存到 username、password 和 age 中,因为注解@SessionAttribute 中有这几个参数
return "success";
}
//可直接获取SessionAttribute域中的对象值
@RequestMapping("/testGet")
public String testGet(ModelMap model){
System.out.println(model.get("username")+";"+model.get("password")+";"+model.get("age"));
@ResponseBody
作用:使用@ResponseBody 注解实现将 controller 方法返回对象转换为 json 响应给客户端。
将返回的Account对象转变为json数据响应给客户端
@RequestMapping("/testResponseJson")
public @ResponseBody Account testResponseJson(@RequestBody Account account) {
System.out.println("异步请求:"+account);
return account;
}
响应数据和结果视图
返回值类型
字符串
controller 方法返回字符串可以指定逻辑视图名,通过视图解析器解析为物理视图地址。
视图解析器配置,跳转到success页面
@RequestMapping("/testReturnString")
public String testReturnString() {
System.out.println("AccountController 的 testReturnString 方法执行了。。。。");
return "success";
}
<!-- 配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" >
<property name="prefix" value="/WEB-INF/pages/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
void
在 controller 方法形参上可以定义 request 和 response,使用 request 或 response 指定响应结果
@RequestMapping("/testReturnVoid")
public void testReturnVoid(HttpServletRequest request,HttpServletResponse response)throws Exception {
//1、使用 request 转向页面,如下:
request.getRequestDispatcher("/WEB-INF/pages/success.jsp").forward(request,response);
//2、也可以通过 response 页面重定向:
response.sendRedirect("testRetrunString")
}
ModelAndView
ModelAndView 是 SpringMVC 为我们提供的一个对象,该对象也可以用作控制器方法的返回值。
该对象中有两个方法:
addObject(String attributeName,Object attributeValue)
添加模型到该对象中
页面上可用el表达式取值 ${attributeName}
public ModelAndView addObject(String attributeName,Object attributeValue){
getModelMap().addAttribute(attributeName,attributeValue);
return this;
}
setViewName(@Nullable String viewName)
用于设置逻辑视图名称,视图解析器会根据名称前往指定的视图
public void setViewName(@Nullable String viewName){
this.view = viewName;
}
示例代码:
@RequestMapping("/testReturnModelAndView")
public ModelAndView testReturnModelAndView(){
ModelAndView mv = new ModelAndView();
mv.addObject("username","small_fish"); //键值对绑定
mv.setViewName("success"); //设置逻辑视图,根据名称跳转到指定视图
return mv;
}
SpringMVC文件上传
传统方式上传原理
编写jsp文件
<form action="/fileUpload" method="post" enctype="multipart/form-data">
名称:<input type="text" name="picname"/>
图片:<input type="file" name="uploadFile"/><br/>
<input type="submit" value="上传"/>
</form>
编写文件上传控制器
@Controller("fileUploadController")
public class FileUploadController {
@RequestMapping("/fileUpload")
public String testResponseJson(String uploadFile,HttpServletRequest request) throws Exception{
//定义文件名
String fileName = "";
//1.获取原始文件名
String uploadFileName = uploadFile.getOriginalFilename();
//2.截取文件扩展名
String extendName = uploadFileName.substring(uploadFileName.lastIndexOf(".")+1,uploadFileName.length());
//3.把文件加上随机数,防止文件重复
String uuid = UUID.randomUUID().toString().replace("-", "").toUpperCase();
//4.判断是否输入了文件名
if(!StringUtils.isEmpty(picname)) {
fileName = uuid+"_"+picname+"."+extendName;
}else {
fileName = uuid+"_"+uploadFileName;
}
System.out.println(fileName);
//5.获取文件路径
ServletContext context = request.getServletContext();
String basePath = context.getRealPath("/uploads");
//6.解决同一文件夹中文件过多问题
String datePath = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
//7.判断路径是否存在
File file = new File(basePath+"/"+datePath);
if(!file.exists()) {
file.mkdirs();
}
//8.使用 MulitpartFile 接口中方法,把上传的文件写到指定位置
uploadFile.transferTo(new File(file,fileName));
return "success";
}
}
配置文件解析器
<!--配置文件上传解析器 -->
<bean id="multipartResolver" <!-- id 的值是固定的-->
class="org.springframework.web.multipart.commons.CommonsMultipartResolver" >
<!-- 设置上传文件的最大尺寸为 5MB -->
<property name="maxUploadSize">
<value>5242880</value>
</property>
</bean>
文件上传的解析器 id 是固定的,不能起别的名称,否则无法实现请求参数的绑定。(不光是文件,其他字段也将无法绑定)
SpringMVC异步文件上传
//5.创建 sun 公司提供的 jersey 包中的 Client 对象
Client client = Client.create();
//6.指定上传文件的地址,该地址是 web 路径
WebResource resource = client.resource(FILESERVERURL+fileName);
//7.实现上传
String result = resource.put(String.class,uploadFile.getBytes());
System.out.println(result);
return "success";
SpringMVC中的异常处理
SpringMVC拦截器
拦截器的作用
Spring MVC 的处理器拦截器类似于 Servlet 开发中的过滤器 Filter,用于对处理器进行预处理和后处理。拦截器 它是只会拦截访问的控制器方法,如果访问的是 jsp,html,css,image 或者 js 是不会进行拦
我们要想自定义拦截器, 要求必须实现:HandlerInterceptor
接口。
拦截器流程图
请求先经过拦截器1,运行代码1,若返回值为true,继续转到拦截器2
拦截器2,运行代码1,若返回值为true,跳转运行controller层
运行controller层完成后,返回拦截器2,运行代码2,继续返回拦截器1,运行代码1
拦截器配置
Springmvc.xml
<!--配置拦截器-->
<mvc:interceptors>
<!--配置拦截器-->
<mvc:interceptor>
<!--要拦截的具体的方法-->
<mvc:mapping path="/user/*"/>
<!--不要拦截的方法
<mvc:exclude-mapping path=""/>
-->
<!--配置拦截器对象-->
<bean class="com.tyut.interceptor.MyInterceptor1" />
</mvc:interceptor>
<!--配置第二个拦截器-->
<mvc:interceptor>
<!--要拦截的具体的方法-->
<mvc:mapping path="/**"/>
<!--不要拦截的方法
<mvc:exclude-mapping path=""/>
-->
<!--配置拦截器对象-->
<bean class="com.tyut.interceptor.MyInterceptor2" />
</mvc:interceptor>
</mvc:interceptors>
自定义拦截器 MyInterceptor1 implements HandlerInterceptor
/**
* 自定义拦截器
*/
public class MyInterceptor1 implements HandlerInterceptor{
/**
* 预处理,controller方法执行前
* return true 放行,执行下一个拦截器,如果没有,执行controller中的方法
* return false不放行
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("MyInterceptor1执行了...前1111");
// request.getRequestDispatcher("/WEB-INF/pages/error.jsp").forward(request,response);
return true;
}
/**
* 后处理方法,controller方法执行后,success.jsp执行之前
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("MyInterceptor1执行了...后1111");
// request.getRequestDispatcher("/WEB-INF/pages/error.jsp").forward(request,response);
}
/**
* success.jsp页面执行后,该方法会执行
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("MyInterceptor1执行了...最后1111");
}
}