- SpringMVC
- SpringMVC 三个核心
- 一、
- 二、返回数据
- 三、获取数据
- 3.3 POJO类型参数
- 3.4 数组类型参数
- 3.5 集合类型参数
- 四、静态资源
- 五、解决中文乱码问题
- 六、请求参数名和字段不一样
- 八、自定义类型转换器
- 5.3.4 文件上传
- 七、拦截器
- 7.5 SpringMVC异常处理
- 7.6 使用SpringMVC异常处理两种方式
- 2. 注解通知类型
- 3. 抽取切点表达式
- 九、Spring事务
SpringMVC
什么是SpringMVC?
含义:SpringMVC是一种基于Java实现的MVC设计模型的请求驱动类型的轻量级的web层框架,属于SpringFramework后续产品,已经融合在SpringWeb flow中。SpringMVC已经成为目前主流的MVC框架之一,并随着任凭3.0的发布,已经全面超越身体乳提升struts2,成为最优秀的MVC框架,他通过一套注解,让一个简单的Java类成为处理请求的控制器,而无需时间任何借口,同时还支持Restful编程风格的请求。
5.2 使用SpringMVC
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.18.RELEASE</version>
</dependency>
package com.xxgc.spring.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class LoginController {
@RequestMapping("/login")
public String userLogin(){
return "";
}
}
在web.xml中配置前端控制器
<!-- 配置SpringMVC的前端控制器-->
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
SpringMVC的数据响应
在SpringMVC的各个组件中,处理器、映射器、处理适配器称为SpringMVC的三大组件。
使用了以上代码可以自动加载RequestMappingHandlerMapping(处理映射器),和RequestMappingHandlerAdapler(处理适配器),来替代注解处理器和适配器的配置。
使用了它,默认底层会集成Jackson进行对象或集合的son字符串转换。
SpringMVC 三个核心
页面@RequestMapping(“/userLogin”)注解
返回字符串,字符串内容为页面名称
一、
前缀后缀加载Spring-mvc.xml配置文件中
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 视图解析器的前缀-->
<property name="prefix" value="/WEB-INF/jsp/"></property>
<!-- 视图解析器的后缀-->
<property name="suffix" value=".jsp"></property>
</bean>
二、返回数据
@ResponseBody
就不会走视图解析器,会直接返回数据。
如果想返回json格式需要导包
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.3</version>
</dependency>
需要在Spring-mvc.xml配置一句话
<!-- 添加MVC的注解驱动-->
<mvc:annotation-driven/>
返回数据的时候直接返回对象,SpringMVC会帮你解析成json
@RequestMapping("/userLogin9")
@ResponseBody
public Users userLogin9() {
System.out.println("userLogin9 run...");
Users users = new Users();
users.setUId(1);
users.setName("张三");
users.setPass("123456");
return users;
}
三、获取数据
3.1 获取请求参数
客户端请求的格式/login?name-value&pass-value....
服务器端要获得请求的参数,有时还需要进行数据的封装,SpringMVC可以接收如下类型的 参数:
- 基本类型参数
- POJO类型参数
- 数组类型参数
- 集合类型参数
3.2 基本类型参数
Controller中的业务方法的参数名称要与请求参数name一致,参数值会自动映射匹配。
@ResponseBody
@RequestMapping("/live1")
public void userLive(int liveId,String liveName){
System.out.println("直播间ID = " + liveId);
System.out.println("liveName = " + liveName);
}
http://localhost/live/live1?liveId=666&liveName=LOL
卸了参数不一定需要传递,可以传某个值,也可以完全不传,但是要注意,不传值会给一个null,如果是int类型会报错,应改为Integer。
3.3 POJO类型参数
Controller中的业务方法的POJO参数的属性名与请求参数的name一致,参数值会自动映射匹配。
@ResponseBody
@RequestMapping("/live2")
public Users userLive2(Users users){
System.out.println(users);
return users;
}
http://localhost:8080/live/live2?uId=123&name=lisi&pass=123456
在对象中,不需要传递所有参数,也可以不传,不传递会显示null或默认值。
3.4 数组类型参数
Controller中的业务方法数组名称与请求参数name一致,参数会自动映射匹配。
@ResponseBody
@RequestMapping("/live3")
public String[] userLive3(String[] names){
System.out.println(Arrays.asList(names));
return names;
}
传递参数时,必须保证key的名称和方法参数名一致,否则拿不到数据,属性类型必须一致。
3.5 集合类型参数
获取集合数据时,要将集合参数包装到一个POJO(bean包)中才可以
@Data
public class UserExtend {
private List<Users> userList;
}
@ResponseBody
@RequestMapping("/live4")
public void userLive4(UserExtend ue){
for (Users users : ue.getUserList()) {
System.out.println("users = " + users);
}
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
登录${msg}
<form action="${pageContext.request.contextPath}/live/live4" method="post">
<input type="text" name="userList[0].uId"><br>
<input type="text" name="userList[0].pass"><br>
<input type="text" name="userList[0].name"><br>
<input type="text" name="userList[1].uId"><br>
<input type="text" name="userList[1].pass"><br>
<input type="text" name="userList[1].name"><br>
<input type="submit" value="提交">
</form>
</body>
</html>
集合类型参数二
当使用Ajax提交时,可以指定contentType为json格式,那么在方法参数位置使用@RequestBody可以直接接收集合数据而无需使用POJO进行包装。
@ResponseBody
@RequestMapping("/live5")
public void userLive5(@RequestBody List<Users> users){
for (Users user : users) {
System.out.println("user = " + user);
}
}
[
{
"uId": "1",
"name": "zhangsan",
"pass": "123456"
},
{
"uId": "2",
"name": "lisi",
"pass": "654321"
}
调试工具改
把body调成Application/json
ajax改
Context-Type = json
四、静态资源
方式一
<!-- 开放静态资源都访问 -->
<mvc:resources mapping="/js/**" location="/js/"/>
如果你想拿到静态资源,必须添加以上代码,给SpringMVC一个映射关系。
方式二
如果加了这句话SpringMVC没有找到静态资源,会让容器(Tomcat)来找。
注意:加上过后,所有的静态资源都会被用户看见,不想让用户看见的文件放在WEB-INF下面
注意:
- 通常情况下我们把不容易发生改变的静态资源放在web文件夹下的CDN文件夹下。
- 需要保密的静态资源放在WEB-INF下,通过@RequestMapping获取
五、解决中文乱码问题
解决方案一
在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>
解决方案二
在spring-mvc.xml中添加
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="defaultCharset" value="UTF-8"/>
<property name="writeAcceptCharset" value="false"/>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
六、请求参数名和字段不一样
使用场景:当某些字段需要隐藏其真实含义需要使用@requestParam
解决:@requestParam来更改默认参数
有三个参数
- value修改参数名称 必填 可省略value
- required 是否设置为必须携带的参数 默认为true 非必填
- defaultValue 没有传递参数时的默认值,写上过后required失效 非必填
八、自定义类型转换器
什么是自定义转换器?
Spring MVC默认已经提供了一些常用的类型转换器,列入客户端提交的字符串转换为int 或 long进行参数设置。但是不是所有的数据类型都提供了转换器,没有提供转换器的就需要自定义转换器,例如:时间类型的数据就需要自定义转换器。
实现步骤:
- 定义一个转换器实现Converter借口
- 在配置文件中声明转换器
- 在中引用
8、方法中传递原生的对象
使用了SpringMVC后,框架帮我们将原生的Request,Response,Session对象封装起来了,当然我们仍然可以使用这些原生的对象,只需要在方法的参数列表中声明对应的对象类型即可:
@RequestMapping("/testObject")
public String testObject(HttpServletRequest request,
HttpServletResponse response,
HttpSession session) {
System.out.println("request = " + request);
System.out.println("response = " + response);
System.out.println("session = " + session);
return "welcome";
}
9. 获取请求头的信息
使用@RequestHeader注解放在方法的参数前,可以获取请求头的信息。
@RequestMapping("/header")
public String getRequestHeader(@RequestHeader("Accept") String accept){
System.out.println("accept = " + accept);
return "welcome";
}
@RequestHeader注解中的参数如下:
参数 | 作用 |
---|---|
value | 请求头Key值 |
name | 请求头Key值,与value同义,语义化的体现 |
required | 是否必须携带这个请求头信息,默认是true |
defaultValue | 默认值,默认为空 |
5.3.4 文件上传
文件上传有三个必要条件:
- 表单提交方式必须为post提交
- 必须使用表单提交
- 需要在form表单中添加一个参数enctype=”multipart/form-data”
<form action="/file/upload" method="post" enctype="multipart/form-data">
<input type="file" name="upload" placeholder="请选择文件"><br>
<button type="submit">立即上传</button>
</form>
首先我们需要引入commons-fileupload依赖和commons-io
<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
在spring-mvc.xml中配置文件上传解析器
七、拦截器
7.1 什么是拦截器?
SpringMVC的拦截器类似于Servlet的过滤器Filter,用于对请求处理器进行预处理和后处理。
将拦截器按一定的顺序结成一条链,这条链成为拦截器链(Interceptor Chain),在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。拦截器也是AOP思想的具体实现。
7.2 拦截器和过滤器的区别
区别 | 过滤器 | 拦截器 |
---|---|---|
使用范围 | 是servlet规范中的一部分,任何JavaWeb工程都可以使用 | 是SpringMVC框架自己的,只有使用了SpringMVC框架的工程才可以使用 |
拦截范围 | 在url-pattern中配置/*之后,可以对所有要访问的资源拦截 | 在 |
7.3 拦截器的实现
package com.xxgc.spring.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyInterceptor implements HandlerInterceptor {
//在目标方法之前执行
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return false;
}
//在目标方法之后执行,在试图对象返回之前执行
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
//在流程都执行完毕过后执行
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
在真实项目中,一般我们会对每个请求进行采样,当样本到达一定数量过后,进行系统性的分析。
比如:接口A-B端耗时,B-C端耗时,或某个接口的错误率
A-B端耗时大的解决方法:
- 在A基数大的地区部署服务器
- 增加服务器带宽
- A端上传进行压缩
B-C端耗时大的解决方法
- 升级服务器
- 优化代码
C-D耗时大的解决方法
- 压缩静态资源
- 增加服务器带宽
- 提示用户升级电脑配置
7.5 SpringMVC异常处理
什么是异常?
在系统中异常包括两类:预期异常和运行时异常RuntimeException,前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发、测试等手段减少运行时异常的发生。
系统的Dao、Service、Controller出现都通过throws Exception向上抛出,最后由SpringMVC前端控制器交出由异常处理器进行异常处理。
7.6 使用SpringMVC异常处理两种方式
- 使用SpringMVC提供的简单异常处理器SimpleMappingExceptionResolver
- 实现Spring的异常处理接口HandlerExceptionResolver自定义自己的异常处理器
2.切点表达式
execution([修饰符] 返回类型 包名.类名.方法名(参数))
execution(public boolean com.xxgc.aop.spring.UserLoginSpring.userLogin())
execution(boolean com.xxgc.aop.spring.UserLoginSpring.userLogin(String,String))
execution(boolean com.xxgc.aop.spring.UserLoginSpring.*(..))
execution(com.xxgc.aop.spring.UserLoginSpring.get*(..))
execution(* com.xxgc.aop.spring.*.*(..))
execution(* com.xxgc.aop.spring..*.*(..))
execution(* *..*.*(..))
- 完整路径无参数的写法
- 有参完整路径
- 布尔返回类型 类下面的所有方法 参数不限
- 所有返回类型 且方法名为get开头 参数不限
- Spring包下面的所有类的所有方法
- Spring包下面的包下面的…任意方法
- 所有方法
3.通知类型
<aop:config>
<aop:aspect ref="切面类">
<aop:before method="通知方法名称" pointcut="切点表达式"></aop:before>
</aop:aspect>
</aop:config>
通知类型的配置语法:
<aop:通知类型 method="切面类的方法" pointcut="切点表达式"></aop:通知类型>
<!--前置 -->
<aop:before method="before" pointcut="execution(* com.xxgc.aop.spring.UserLoginSpring.*(..))"></aop:before>
<!--后置 -->
<aop:after-returning method="afterReturning" pointcut="execution(* com.xxgc.aop.spring.UserLoginSpring.*(..))"></aop:after-returning>
<!--最终 -->
<aop:after method="before" pointcut="execution(* com.xxgc.aop.spring.UserLoginSpring.*(..))"></aop:after>
<!--环绕执行 -->
<aop:around method="around" pointcut="execution(* com.xxgc.aop.spring.UserLoginSpring.*(..))"></aop:around>
<!--抛出异常 -->
<aop:after-throwing method="throwing" pointcut="execution(* com.xxgc.aop.spring.UserLoginSpring.*(..))"></aop:after-throwing>
切点表达式的抽取
定义
<!--共用的切点表达式-->
<aop:pointcut id="userLogin" expression="execution(* com.xxgc.aop.spring.UserLoginSpring.*(..))"></aop:pointcut>
引用
<aop:before method="before" pointcut-ref="userLogin"></aop:before>
8.7 基于注解的AOP开发
1.开发步骤
- 创建目标接口和目标类(内部有切点)
- 创建切面类(内部有通知、增强方法)
- 将目标类和切面类的对象创建权给Spring
- 在切面类中使用注解配置织入关系
- 在配置文件中开启组件扫描和AOP的自动代理
- 测试
目标接口
package com.xxgc.spring.service.impl;
import com.xxgc.spring.POJO.User;
import com.xxgc.spring.service.IUserLogin;
import org.springframework.stereotype.Service;
import java.util.List;
//把业务逻辑托管给Spring进行管理
@Service
public class UserLoginService implements IUserLogin {
public int updateById(User user) {
System.out.println("修改用户");
return 0;
}
public int insertUser(User user) {
System.out.println("添加用户");
return 0;
}
public List<User> selectUserByName(String name) {
System.out.println("根据用户名查询用户");
return null;
}
public List<User> selectUserById(long uId) {
System.out.println("根据用户ID查询");
return null;
}
}
切面类
package com.xxgc.spring.aspect;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Component //把这个类交给Spring
@Aspect //告诉Spring这是一个切面类
public class UserLoginAspect {
@Before("execution(* com.xxgc.spring.service.impl.UserLoginService.*(..))")
public void before(){
System.out.println("前置增强");
}
}
开启组件扫描和AOP自动代理
package com.xxgc.spring.service;
import com.xxgc.spring.POJO.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class UserLoginTest {
@Autowired
private IUserLogin iul;
@Test
public void test(){
iul.insertUser(new User());
}
}
2. 注解通知类型
通知的配置语法:@通知注解(“切点表达式”)
名称 | 注解 | 说明 |
---|---|---|
前置通知 | @Before | 前置通知,指定增强方法在切入点前执行 |
后置通知 | @AfterReturning | 后置通知,指定增强方法在切入点后执行 |
环绕通知 | @Around | 环绕通知,指定增强方法在切入点前后都执行 |
异常通知 | @AfterThrowing | 异常通知,指定方法抛出异常时执行 |
最终通知 | @After | 最终通知,无论增强方法执行是否有异常都会执行 |
3. 抽取切点表达式
//抽出切点表达式
@Pointcut("execution(* com.xxgc.spring.service.impl.UserLoginService.*(..))")
public void myPoint(){
}
//引用切点表达式
@Before("UserLoginAspect.myPoint()")
public void before(){
System.out.println("前置增强");
}
九、Spring事务
9.1 什么是事务?
事务必须服从ACID原则。ACID指的是原子性(atomicity)、一致性(consistency)、隔离性(isolation)和持久性(durability)。通俗理解,事务其实就是一系列指令的集合。
原子性:操作这些指令时,要么全部执行成功,要么全部不自信。只要其中一个指令执行失败,所有的指令都执行失败,数据进行回滚,回到执行指令前的数据状态。
一致性:事务的执行使数据从一个状态转换为另一个状态,但是对于整个数据的完整性保持稳定。
隔离性:在该事务执行的过程中,无论发生的任何数据的改变都应该只存在于该事务之中,对外界不存在任何影响。只有在事务确定正确提交之后,才会显示该事务对数据的改变,其他事务才能获取到这些改变后的数据。
持久性:但事务正确完成后,它对于数据的改变是永久性的。
9.2 了解Spring事务的三个对象
1.PlatformTransactionManager
PlatformTransactionManager接口是Spring的事务管理器,它里面提供了我们常用的事务的方法。
方法 | 说明 |
---|---|
TranscationStatus getTransaction(TransactionDefination defination) | 获取事务的状态信息 |
void commit(Transaction status) | 提高事务 |
void rollback(Transaction status | 回滚事务 |
注意:
PlatformTransactionManager是接口类型,不同的Dao层技术则有不同的实现类,例如:
Dao层技术是jdbc或mybatis时:org.springframework.jdbc.datasource.DataSourceTransactionManager
Dao层技术是hibernate时:org.springframework.orm.hibernate5.HibernateTransactionManager
2.TransactionDefinition
TransactionDefinition是事务的定义信息对象,里面有如下方法:
方法 | 说明 |
---|---|
int getIsolationLevel() | 获得事务的隔离级别 |
int getPropogationBahavior() | 获得事务的传播行为 |
int getTimeout() | 获得超时时间 |
boolean isReadOnly() | 是否仅读 |
事务隔离级别
设置事务隔离级别,可以解决事务并发产生的问题,如脏读、不可重复读和虚读。
ISOLATION_DEFAULT 默认
ISOLATION_READ_UNCOMMITTED 读未提交
ISOLATION_READ_COMMITTED 读已提交
ISOLATION_PEPEATABLE_READ 可重复读
ISOLATION_SERIALIZABLE 序列化
事务的传播行为
REQUIRED:如果没有事务,就新建一个事务,如果已经存在一个事务,加入到这个事务中,一般的选择(默认值)
SUPPORTS:支持当前事务,如果当前没有事务,就以非事务的方式执行(没有事务)
MANDATORY:使用当前事务,如果当前没有事务,就当前事务挂起
REQUERS_NEW:新建事务,如果当前在事务中,把当前事务挂起
NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
NEVER:以非事务方式运行,如果当前存在事务,跑出异常
NESTED:如果当前存在事务,则在嵌套事务内部执行。如果当前没有事务,则执行REQUIRED类似的操作
超时时间:默认值是-1,没有限制时间。如果有,以秒为单位进行设置
是否仅读:建议查询时设置为仅读
3.TransactionStatus
TransactionStatus接口提供的是事务具体的运行状态,方法介绍如下:
方法 | 说明 |
---|---|
boolean hasSavepoint() | 是否存储回滚点 |
boolean isCompleted() | 事务是否完成 |
boolean isNewTransaction() | 是否是新事务 |
boolean isRollbackOnly | 事务是否回滚 |
9.3 声明式事务
Spring的声明式事务顾名思义就是采用声明的方式来处理事务。这里所说声明,就是在指配置文件中声明,用在Spring配置文件中声明式的处理事务来代替代码式的处理事务。
声明式事务处理的作用
事务管理事务不入侵开发的组件。具体来说,业务逻辑对象就不会意识到正在事务管理之中。事实上也应该如此,因为事务管理是属于系统层面的服务,而不是业务逻辑的一部分,如果想要改变事务管理策略的话,也只需要在定义文件中重新配置即可。
在不需要事务管理的时候,只有在设定文件上修改一下,即可移去事务管理服务,无需改变代码重新编译,这样维护起来极其方便