SpringBoot学习笔记

简化spring应用初始化搭建和开发过程

Springboot=Springmvc(控制器Controller) + Spring(工厂)

约定:

  1. springboot项目中有且只有一个入口类,类名:推荐:xxxApplication.java
  2. 入口类必须在所有子包之上
  3. 入口类中必须存在一个启动项目的main函数
  4. springboot项目约定必须在项目根目录中存在一个application.yml或者application.properties配置文件

Hello World探究

1.pom文件

1.父项目

1
2
3
4
5
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
</parent>

springboot的版本仲裁中心,真正管理springboot应用里面的所有依赖版本,

1
2
3
4
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>1.5.9.RELEASE</version>

2.导入的依赖

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

spring-boot-starter:spring-boot场景启动器,帮我们导入了web模块正常运行所依赖的组件

springboot将所有的场景都抽取出来,做出一个个的starters(启动器),只需要在项目里面导入依赖就行,根据场景需要而导入。

2.主程序类,主入口类

@SpringBootApplication:springboot应用标注在某个类上说明这个类是springboot的主配置类,springboot就应该运行这个类的main方法来启动springboot应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)

@SpringBootConfiguration:springboot配置类

@EnableAutoConfiguration:开启自动配置功能

1
2
@AutoConfigurationPackage
@Import({EnableAutoConfigurationImportSelector.class})

@AutoConfigurationPackage:自动配置包

@Import:spring底层注解,给容器导入一个组件

将主配置类所在包下面的所有组件扫描到spring容器

EnableAutoConfigurationImportSelector:导入哪些组件的选择器,将所有需要导入的组件以全类名的方式返回,这些组件就会被添加到容器中,会给容器中导入非常多的自动配置类

springboot在启动的时候从类路径下的META-INF/spring.factories获取EnableAutoConfiguration指定值,将这些值作为自动配置类导入到容器中,自动配置类就生效,帮我们进行自动配置工作

配置文件

1.springboot使用一个全局的配置文件,配置文件名是固定的

  • application.properties
  • application.yml

配置文件的作用:修改spring自动配置的默认值,springboot在底层给我们配置好

YAML(YAML Ain’t Markup Language),以数据为中心,比xml、json更适合做配置文件

例子:

YAML:

1
2
server:
port:80

XML:

1
2
3
<server>
<port>80</port>
</server>

2.基本语法

基本语法

k(空格)v:表示一对键值对,空格必须要有

空格的缩进来控制层级关系,只要是左对齐的一列数据,都是同一个层级的

属性和值都是大小写敏感

值的写法:

字面量:普通的值(数字、字符串、布尔)

k:v:字面直接来写

​ 字符串默认不用加上单引号或者双引号

​ 双引号:不会转义字符串里面的特殊字符

​ name: “we \n rng” 输出:we

​ rng

​ 单引号:会转义特殊字符

​ name: ‘we \n rng’ 输出:we \n rng

对象(属性和值,键值对):

对象还是k:v的方式

friends:

​ name:zhangsan

​ age:20

行内写法:

friends:{name:zhangsan,age:20}

数组(List,Set)

用 -值表示数组中的一个元素

animals:

​ -dog

​ -cat

​ -bird

行内写法:

animals:[dog,cat,bird]

@ConfigurationProperties @Value
功能 批量注入配置文件中的属性 需要一个一个的写
松散语法绑定 支持 不支持
SpEL 不支持 person.age=#{11*2} 支持 @Value(“#{11*2}”)
JSR303数据校验 支持 不支持
复杂类型封装 支持 不支持
  • 不管配置文件是yml还是properties都可以获取到值
  • 如果说,我们只是在某个业务逻辑中需要获取一下配置文件中的值,就用@Value()
  • 如果说,我们专门有一个javabean来和配置文件进行映射,那就用@ConfigurationProperties()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* 将配置文件中配置的每一个属性,映射到这个组件中
* @ConfigurationProperties:告诉springboot将本类中的所有属性和配置文件的配置的属性绑定
* 只有这个组件是容器中的组件,才能容器提供@ConfigurationProperties功能
*
*/
@Component()
//@ConfigurationProperties(prefix = "person")
@Validated()
public class Person {
@Value("${person.name}")
@Email
private String name;
@Value("#{11*2}")
private int age;
@Value("true")
private boolean boss;
private Date birth;
private Map<String,Object>maps;
private List<Object>lists;
private Dog dog;

拆分配置文件

1
2
3
4
-resources
application.yml
application-dev.yml
application-product.yml

可能有本地生产环境配置文件、实际上线配置文件:

在application.yml中指定 spring-profiles-active

1
2
3
4
#配置文件拆分
spring:
profiles:
active: dev

外部配置文件:

在tomcat配置:environment下的program arguments中配置如下

–spring.config.location=D:/IDEA/测试springboot外部配置文件/application-out.yml

spirngboot 微框架 = spring(工厂) 用来管理项目对象 + springmvc(控制器)

微:快速开发 通过遵守默认约定 简化项目中样板化配置

spring工厂创建对象

  1. 基于配置文件形式创建对象

    1
    <bean id="" class="">
  2. 基于注解形式方式创建对象

    1
    2
    3
    4
    @Compoent          作用:在工厂中创建对象
    @Controller 创建控制器注解
    @Service 创建业务层注解
    @Responsity 创建Dao层注解

springboot创建对象

  1. 使用原始spring框架中注解创建对象 只能创建单个对象

    1
    2
    3
    4
    @Compoent          作用:在工厂中创建对象
    @Controller 创建控制器注解
    @Service 创建业务层注解
    @Responsity 创建Dao层注解
  2. 使用配置方式创建对象 可以创建多个对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    @Configuration 修饰范围用在类上,相当于spring中的spring.xml,代表这个类是一个springboot中的配置类

    @Bean 创建对象,相当于spring.xml中的bean标签

    @Configuration
    public class BeanConfig(){
    @Bean
    public User user(){
    return new User();
    }

    @Bean
    public Order order(){
    return new Order();
    }
    }

spring框架中属性注入

引用类型注入

1
2
@Autowried    spring框架提供的,默认是根据类型注入
@Resource java EE规范,默认是根据名称注入,找不到根据类型注入

八种基本类型+String类型

1
2
3
4
5
6
@Value("")
private String username;

<bean id="" calss="">
<prpperty name="username" value="">
</bean>

Springboot属性注入

配置文件@Value

配置文件加上@ConfigurationProperties,必须要有set方法

Springboot框架如何整合mybatis框架?

  1. 引入依赖

    springboot-starter-web

    mysql相关 驱动 数据源

    mybatis相关 mybatis-spring-boot-starter

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    <!--druid-->
    <dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.2.4</version>
    </dependency>
    <!-- mysql-->
    <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.22</version>
    </dependency>
    <!-- mybatis-spring-boot-starter-->
    <dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.4</version>
    </dependency>
  2. 书写配置

    a.开启注解扫描

    b.创建数据源

    ​ 指定数据源类型、驱动、协议、用户名、密码

    c.创建SqlSessionFactory

    ​ 指定mapper文件配置

    ​ 指定实体类所在包位置 、起别名

    d.创建Dao

    ​ 指定Dao接口所在包

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #整合mybatis相关配置
    spring:
    datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/springboot?characterEncoding=UTF-8
    username: root
    password: 123456
    #指定mapper配置文件位置
    mybatis:
    mapper-locations: classpath:/mapper/*.xml
    #起别名
    type-aliases-package: yxnu.edu.entity
    1
    2
    3
    4
    5
    6
    7
    8
    9
    @SpringBootApplication
    @MapperScan("yxnu.edu.dao") //用在类上,扫描dao接口所在包,同时将所有dao接口在工厂中创建对象
    public class Springboot02Application {

    public static void main(String[] args) {
    SpringApplication.run(Springboot02Application.class, args);
    }

    }
  3. 测试

springboot本地测试

  1. 引入依赖

    1
    2
    3
    4
    5
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
    </dependency>
  2. 编写测试类

    1
    @SpringBootTest    用在类上,启动本地spring环境测试

    可以写一个类让其他类来继承它,这样就不用每次都写@SpringBootTest

    1
    2
    3
    @SpringBootTest
    public class BasicTest {
    }

    继续BasicTest

    1
    2
    3
    4
    5
    6
    7
    8
    public class UserServiceTests extends BasicTest{
    @Autowired
    private UserService userService;
    @Test
    public void test1(){
    userService.findAll().forEach(list-> System.out.println(list));
    }
    }

日志处理

springboot集成的是logback日志

分为8个级别:级别越高,输出的信息越多,ALL级别最高

  • OFF、FATAL 、ERROR、WARN、INFO、DEBUBG、TRACE、ALL

分类:一种是rootLogger(全局日志),用来监听项目中所有的运行日志,包括引入依赖jar中的内容

​ 一种是logger(子日志),用来监听项目中指定包中的日志信息

​ 默认是INFO级别

1
2
3
4
5
6
7
8
9
10
#配置日志
logging:
level:
root: info
yxnu.edu.dao: debug #指定包为debug,可以看到sql语句
yxnu.edu.service: debug
yxnu.edu.controller: debug
file:
name: run.log #指定生成日志文件名称
path: ./ #将日志生成到当前目录,
1
2
3
4
5
//声明一个日志对象
private static final Logger log= LoggerFactory.getLogger(UserService.class);
public void test(){
log.debug("测试logback日志处理");
}

或者使用lombok

1
2
3
4
5
<!-- lombok依赖-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
1
2
3
4
5
6
@Slf4j  //只能用在类上
public class UserServiceImpl(){
publi void test(){
log.debug("测试使用lombok及@Slf4j注解");
}
}

切面编程

Aspect(切面) = Advice(通知) + 切入点(Pointcut)

  1. 引入aop依赖
  2. 在springboot项目中新建config配置包
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package yxnu.edu.config;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.context.annotation.Configuration;

@Configuration
@Aspect //切面配置类
public class AspectConfig {
//前置通知
@Before("execution(* yxnu.edu.service.*.*(..))")
public void before(JoinPoint joinPoint){
System.out.println("前置附加操作"+joinPoint);
//获取类名
System.out.println(joinPoint.getTarget());
//获取方法名
System.out.println(joinPoint.getSignature().getName());
}

//后置通知
//@After

//环绕通知
@Around("execution(* yxnu.edu.service.*.*(..))")
public Object around(ProceedingJoinPoint point) throws Throwable {
System.out.println("进入环绕的前置操作");
System.out.println(point);
Object proceed = point.proceed();
System.out.println("进入环绕后置操作");
return proceed;
}

}

切入点函数:

execution:最宽泛

within:某个类

annotion:只有在自定义的注解方法用上才有效

文件上传

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>文件上传</title>
</head>
<body>
<h1>文件上传</h1>
<form method="post" enctype="multipart/form-data" action="${pageContext.request.contextPath}/file/upload">
<input type="file" name="file">
<input type="submit" value="点击上传">
</form>
<h1>文件上传外部自定义位置</h1>
<form method="post" enctype="multipart/form-data" action="${pageContext.request.contextPath}/file/uploadByJar">
<input type="file" name="file">
<input type="submit" value="点击上传">
</form>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
@Slf4j
@Configuration
@RequestMapping("/file")
public class UplodController {
//方法一
@RequestMapping("/upload")
//参数名 file 必须与前端input便签中name属性一致
public String upload(MultipartFile file, HttpServletRequest req) throws IOException {
log.debug("文件名{}", file.getOriginalFilename());
log.debug("文件大小{}", file.getSize());
log.debug("文件类型{}", file.getContentType());
//1.根据相对上传获取绝对路径
String realPath = req.getSession().getServletContext().getRealPath("/upload-file");
log.debug("绝对路径:{}", realPath);
//2、上传文件,参数:将文件写入到那个目录
// file.transferTo(new File(realPath,file.getOriginalFilename()));
//修改文件名
//拿到文件后缀
String t = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
String newFileName = new SimpleDateFormat("yyyyMMddhhmmssSSS").format(new Date()) + t;
file.transferTo(new File(realPath, newFileName));
return "redirect:/upload.jsp";
}

//方法二 适合于任何方式部署
//war包部署的话,放到tomcat会解压运行
//而jar包不行,不能把文件上传到jar内部
//找一个外部目录通过配置文件注入到jar中
@Value("${uploadPath}")
private String uploadPath;

@RequestMapping("/uploadByJar")
public String uploadByJar(MultipartFile file) throws IOException {
String t = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
String newFileName = new SimpleDateFormat("yyyyMMddhhmmssSSS").format(new Date()) + t;
//
file.transferTo(new File(uploadPath, newFileName));
return "redirect:/upload.jsp";

}
}

文件下载

  1. 确定项目中哪些资源可以被下载
  2. 将可以被下载的资源放入服务器指定位置
  3. 开发一个下载页面,链接
1
2
3
4
5
6
7
8
9
10
11
12
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>文件下载</title>
</head>
<body>
<h1>文件下载</h1>
<a href="${pageContext.request.contextPath}/file/download?fileName=歌词.txt">
点击下载
</a>
</body>
</html>
1
2
3
4
5
#文件上传路径
uploadPath: D:/IDEA/Project/uploadFile

#文件下载路径
downloadPath: D:/IDEA/Project/springboot_02/src/main/webapp/download-file/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
@Slf4j
@Controller
@RequestMapping("/file")
public class DownloadController {

@Value("${downloadPath}")
private String downloadPath;

@RequestMapping("/download")
public void download(String fileName, HttpServletResponse resp) throws IOException {
log.debug("文件:{}", fileName);
log.debug("下载路径:{}", downloadPath);
//1.获取要下载的文件名,去指定目录中读取文件
File file = new File(downloadPath, fileName);
//2.将文件读取为输入流
FileInputStream in = new FileInputStream(file);
//在获取响应流之前,一定要设置已附件形式下载
resp.setHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode(fileName, "utf-8"));
//3.获取响应输出流
ServletOutputStream out = resp.getOutputStream();
// //4.将获取的输入流拷贝到输出流
// int len=0;
// byte[] b=new byte[1024];
// while (true){
// len = in.read(b);
// if (len==-1) break;
// out.write(b,0,len);
// }
// //5.释放资源
// in.close();

//工具类替代拷贝流
StreamUtils.copy(in, out);

}
}

拦截器 interceptor

注意:只能拦截controller相关的请求,底层是aop,java web中的filter可以拦截所有

作用:将controller中共有的代码放入到拦截器中执行,减少controller中代码冗余

特性:如果请求前配置了拦截器,先执行拦截器,放行后执行controller,controller执行完后回到拦截器

拦截器开发:

1
2
3
4
5
6
7
8
9
10
11
12
implement HandlerInterceptor
preHandler 预处理方法:最先执行,返回值布尔类型,true表示放行请求
postHandler 过程中处理:controller返回后会执行这个方法,执行完成这个方法开始响应到浏览器
afterCompletion 最后完成:当响应结束后悔执行拦截器中的这个方


配置拦截器
implent WebMvcConfigurer{
1.使用哪个拦截器
2.拦截器拦截哪些请求
3.排除哪些请求
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//自定义拦截器
@Slf4j
public class MyInterceptor1 implements HandlerInterceptor {

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.debug("controller之前执行1");
return true;
}

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.debug("controller之后执行2");
// log.debug("view:{}", modelAndView.getViewName());
}

//相当于finally,总是执行
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
log.debug("执行完controller回到拦截器3");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Configuration
public class MvcConfig implements WebMvcConfigurer {

@Override
public void addInterceptors(InterceptorRegistry registry) {
//拦截器一
registry.addInterceptor(new MyInterceptor1()) //指定拦截器
.addPathPatterns("/**") //拦截所有
.excludePathPatterns("/verifyCode").order(2); //排除哪些路径
//拦截器二
registry.addInterceptor(new Myinterceptor2()).addPathPatterns("/**").order(1);
//执行顺序 1 4 5 2 6 3 栈 先进后出 4 1 2 5 3 6
//数字相同的话按照顺序执行
}
}

springboot项目两种部署方式

  1. war部署

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    1.springboot默认为jar,修改为war,在pom.xml中加入<packing>war<packing>

    2.排除内嵌tomcat
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-tomcat</artifactId>
    <scope>provied</scope>
    </dependency>
    如果还有其他依赖,修改范围为provied

    3. 修改入口类
    4.springboot内嵌的tomcat端口号失效,项目名等也会失效
    5.放到tomcat中,启动tomcat会自动解压运行,按照端口访问即可
  2. jar部署

    1
    2
    1.打包成jar
    2.java -jar 项目名

RESTFUL API

设计原则

  1. 使用名词而不是动词

  2. Get方法和查询参数不应该涉及状态改变

  3. 使用复数名词

  4. 使用子资源表达关系

    1
    2
    例子:
    GET /users/001/cars/007 代表user 001的007号车
  5. 使用HTTP头声明序列化格式

    1
    2
    3
    如:
    Accept 定义可接受的响应格式,如json
    Content-type 定义请求格式,如json
  6. 为集合提供过滤、排序、选择、分页等功能

    1
    2
    3
    4
    5
    例子:
    GET /cars?color=red 返回红色的车
    GET /cars?sort=price 返回根据价格排序的car集合
    GET /cars?fields=price,color,model 移动端显示其中一些字段,降低API网络流量
    GET /cars?offset=10&limit=5 分页
  7. 版本化你的API

    1
    2
    /users/api/v1
    /users/api/v2
  8. 使用HTTP状态码处理错误

    1
    状态码+错误信息,缺一不可,正确的话数据+状态码
  9. 使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    @Slf4j
    @RestController
    @RequestMapping("/users")
    public class UserController {

    /**
    * @ResponseEntity springmvc封装的一个专用于restful响应类 ,这个类中可以自定义响应体以及状态码
    * HttpStatus springmvc封装的一个枚举类型类,为网络中的状态码
    */
    //查询某个用户
    //@PathVariable代表从请求路径中获取参数
    @GetMapping("/{id}")
    public ResponseEntity<User> queryBYId(@PathVariable("id") Integer id) {
    log.debug("本次id;{}", id);
    User user = new User("112", 18, new Date(), 22.22);
    return new ResponseEntity<>(user, HttpStatus.OK);
    }


    //查询所有用户
    @GetMapping()
    public ResponseEntity<List<User>> queryAll() {
    List<User> list = new ArrayList<>();
    list.add(new User("777", 22, new Date(), 77.77));
    list.add(new User("888", 22, new Date(), 788.77));
    return new ResponseEntity<>(list, HttpStatus.OK);
    }

    //添加用户
    //@RequestBody 表示把前端接收的json格式的数据转换为对象
    @PostMapping()
    public ResponseEntity<Void> saveUser(@RequestBody User user) {
    log.debug("user对象:{}", user);
    return new ResponseEntity<>(HttpStatus.NO_CONTENT);//没有内容
    }


    //更新用户
    @PutMapping("{id}")
    public ResponseEntity<Void> updateUser(@PathVariable("id") Integer id, @RequestBody User user) {
    log.debug("id:{}", id);
    log.debug("user对象:{}", user);
    return new ResponseEntity<>(HttpStatus.NO_CONTENT);
    }


    //删除用户
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteUser(@PathVariable("id") Integer id) {
    log.debug("id值:{}", id);
    return new ResponseEntity<>(HttpStatus.NO_CONTENT);
    }
  10. 测试接口

    使用postman来测试

异常处理

当controller中方法在执行过程中出现异常,我们应该如何处理这种异常

  • 传统方式开发异常处理

    1
    HandlerExceptionReslover  处理异常解析类
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    @Component
    public class GlobalExceptionResolver implements HandlerExceptionResolver {

    //resolveException 当控制器中任意一个方法出现异常,控制器没有自己的异常处理,则会进入当前方法
    @Override
    public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
    ModelAndView mv = new ModelAndView();
    //针对不同类型的异常跳转到不同的页面
    if (e instanceof UserNameNotFoundException) {
    ModelAndView mv1 = new ModelAndView();
    mv1.setViewName("login_error"); //login_error为登录出错页面
    return mv1;
    }
    mv.setViewName("500"); //500为错误页面
    return mv;
    }
    }
    1
    2
    3
    4
    5
    6
    7
    package yxnu.edu.exception;
    //自定义的用户名找不到异常
    public class UserNameNotFoundException extends RuntimeException {
    public UserNameNotFoundException(String message) {
    super(message);
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    @RequestMapping("/login")
    public String login(String username,String pwd){
    if ("admin".equals(username) && "123465".equals(pwd)){
    return "index";
    }else {
    throw new UserNameNotFoundException("用户名不正确!!");
    }
    }
  • 前后端分离开发异常处理

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    @ControllerAdvice //控制器中所有方法都会被通知
    public class GlobalExceptionResolverRESTFUL {

    //处理指定(指定的自定义)异常
    @ExceptionHandler(value = UserNameNotFoundException.class)
    @ResponseBody
    public ResponseEntity<String> exceptionHandler1(Exception ex) {
    return new ResponseEntity<>(ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
    }

    //处理全局异常
    @ExceptionHandler(value = Exception.class) //用在方法上,处理指定异常,value属性用来指定异常处理类型
    @ResponseBody
    public ResponseEntity<String> exceptionHandler2(Exception ex) {
    return new ResponseEntity<>(ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
    }

    }
    1
    2
    3
    4
    5
    6
    7
    8
    @GetMapping("/{username}/{pwd}")
    public String login(@PathVariable("username")String username,@PathVariable("pwd")String pwd){
    if ("admin".equals(username) && "123465".equals(pwd)){
    return "index";
    }else {
    throw new UserNameNotFoundException("用户名不正确!!");
    }
    }

CORS跨域

CORS:全称”跨域资源共享”(Cross-origin resource sharing)

CORS需要浏览器和服务器同时支持,才可以实现跨域请求,目前几乎所有浏览器都支持CORS,IE则不能低于IE10。CORS的整个过程都由浏览器自动完成,前端无需做任何设置,跟平时发送ajax请求并无差异。所以,实现CORS的关键在于服务器,只要服务器实现CORS接口,就可以实现跨域通信。

什么是源:

1
源就是协议,域名,端口号,例如:http://www.baidu.com:80 ,一个要三者都相同才是同源

哪些操作不会受到同源限制?

1
<script>  <img>  <link>  <iframe> , Ajax会受到同源限制

springboot中如何解决跨域问题?

  1. 局部解决

    1
    @CrossOrigin  用在类上,代表这个类中所有方法运行允许其他域中资源访问
  2. 全局解决

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    @Configuration
    public class CrosConfig {

    @Bean
    public CorsFilter crosFilter() {
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    CorsConfiguration configuration = new CorsConfiguration();
    configuration.addAllowedHeader("*"); //允许任何头
    configuration.addAllowedOrigin("*"); //允许任何域名
    configuration.addAllowedMethod("*"); //允许任何方法
    source.registerCorsConfiguration("/**",configuration); //处理所有请求的跨域
    return new CorsFilter(source);
    }

    }

Jasypt加密

Jasypt 是一个 Java 库,它允许开发者以最小的努力为他/她的项目添加基本的加密功能,而且不需要对密码学的工作原理有深刻的了解。高安全性、基于标准的加密技术,既可用于单向加密也可用于双向加密。加密密码、文本、数字、二进制文件。

  1. 引入依赖

    1
    2
    3
    4
    5
    6
    <!--jasypt依赖-->   
    <dependency>
    <groupId>com.github.ulisesbocchio</groupId>
    <artifactId>jasypt-spring-boot-starter</artifactId>
    <version>2.1.1</version>
    </dependency>
  2. 使用

    1
    2
    加密:类——root+秘钥(自定义的秘钥) 每一次运行都会生成一个新的加密结果,且每个结果都可用
    解密:加密后的结果——类.方法 秘钥———得到结果
    1
    2
    3
    # 生产环境建议采用启动参数的形式传入
    1、项目打成jar包部署时,启动命令也要加上 -Djasypt.encryptor.password=加密密钥
    2、Junit测试方法的上启动参数也要加上 -Djasypt.encryptor.password=加密密钥
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    #jasypt加密
    # 测试环境可以采用在配置文件中配置
    # 生产环境建议采用启动参数的形式传入
    jasypt:
    encryptor:
    password: 121q5weqsaa #自定义的秘钥


    #整合mybatis相关配置
    spring:
    datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/springboot?characterEncoding=UTF-8&serverTimezone=UTC
    username: ENC(zpnOBQjHWSOlSBHiHfCohw==)
    password: ENC(lXipkCbkAIhdVnRUt2dECw==)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @Autowired
    StringEncryptor stringEncryptor;

    @Test
    public void test1() {
    String secret = stringEncryptor.encrypt("root");
    String decrypt = stringEncryptor.decrypt("zpnOBQjHWSOlSBHiHfCohw==");
    System.out.println(secret);
    System.out.println(decrypt);
    }