SpringBoot

SpringBoot

SpringBoot_第1张图片

1.概述

1.1 为什么需要springboot?

现有的SSM(spring+springMVC+mybatis)框架存在的问题

  • 大量的maven冗余配置,依赖
  • 每次构建项目都需要花费大量时间来编写大量的相同配置
  • 整合第三方技术都需要编写相关配置文件
  • 项目测试每次都需要部署到Tomcat

1.2 引言

Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化Spring应用的 初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不 再需要定义样板化的配置。通过这种方式,Spring Boot致力于在蓬勃发展的快速应 用开发领域(rapid application development)成为领导者。

Spring Boot 全新框架 作用: 简化spring应用初始搭建和开发过程

如何简化: 开发人员使用springboot只要基于特定方式进行配置 简化spring使用

SpringBoot 微框架: 5分钟 完成之前ssm中环境

springboot(微框架) = springmvc(控制器) + spring core(项目管理)

1.3 spring的优势

  1. 创建完整的独立的Spring应用程序 spring springmvc 只有一个容器
  2. 嵌入的Tomcat,无需部署WAR文件 springboot 内嵌tomcat 应用跑在内嵌服务器
  3. 简化Maven配置,自动配置Spring Springmvc,没有XML配置 几个依赖
    • 用来springboot spring应用再无xml

1.4 SpringBoot的约定

  • springboot 项目中必须在**src/main/resources中放入application.yml(.properties)核心配置文件 名字必须为:application

  • springboot 项目中必须在**src/main/java中所有子包之外构建全局入口类型,xxApplication,入口类一个springboot项目只能有一个

约定的项目结构:

SpringBoot_第2张图片


2.HelloWorld(第一个springboot)

2.1 搭建步骤

  1. pom.xml
<parent>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-parentartifactId>
    <version>2.6.4version>
    <relativePath/> 
parent>
<dependencies>
    
	<dependency>
     	<groupId>org.springframework.bootgroupId>
    	 <artifactId>spring-boot-starter-webartifactId>
	dependency>

	<dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-testartifactId>
        <scope>testscope>
    dependency>
dependencies>
 
  1. 引入配置文件application.yml

    src/main/resources/application.yml

    SpringBoot_第3张图片

    server:
      port: 8181  #自定义访问的端口号
      servlet:
        context-path: /boot   #项目的根目录
    
  2. 编写入口类

    @SpringBootApplication
    public class Boot01Application {
    	//main方法里面必须有SpringApplication.run()方法,且参数为该入口类的class对象和main的参数args
        public static void main(String[] args) {
            SpringApplication.run(Boot01Application.class, args);
        }
    
    }
    
  3. 启动项目测试运行

    SpringBoot_第4张图片

    没有报错的话表示项目环境搭建正常可以继续进行下一步操作

  4. 建立并创建控制器Controller

    /**
     * @author Liu
     */
    @RestController
    public class HelloController {
    
        @RequestMapping("hello")
        public String hello(){
            System.out.println("hello");
            return "hello";
        }
    }
    
  5. 访问项目

    项目的路径为http://localhost:8181/boot/hello

    http://localhost:(配置的端口号) / (配置的项目路径) / (访问路径)

  6. 测试访问

    SpringBoot_第5张图片

这里第一个简单的hello world就搭建完成了,比传统的ssm开发简洁了不少

2.2 相关注解说明

入口类 SpringApplication

  • SpringBootApplication: 全局入口类 有且只能有一个
  • main 函数参数可以在启动时指定jvm参数覆盖默认配置

@SpringBootApplication 注解等价于:

  • @SpringBootConfiguration 标识这是一个springboot的配置类,默认自定配置Spring环境
  • @EnableAutoConfiguration 自动与项目中集成的第三方技术进行集成
  • @ComponentScan 扫描入口类所在子包以及子包后代包中注解

2.3 配置文件

2.3.1 配置文件的拆分

说明: 在实际开发过程中生产环境和测试环境有可能是不一样的 因此将生产中的配置和测试中的配置拆分开,是非常必要的在springboot中也提供了配置文件拆分的方式. 这里以生产中项名名称不一致为例:

  • 生产中项目名为: boot_prod

  • 开发中项目名为: boot_dev

  • 端口同时为: 8080

image-20220321113118451

application.yml

#主配置文件 书写公共配置
server:
  port: 8181  #自定义访问的端口号

#在主配置文件中,指定哪一个环境生效
spring:
  profiles:
    active: dev #指定那个环境配置生效 dev为环境配置文件的简名

application-dev.yml

#development:开发环境
server:
  servlet:
    context-path: /boot_dev

application-prod.yml

#product 生产环境的配置文件
server:
  servlet:
    context-path: /boot_prod #项目访问路径

测试路径:

SpringBoot_第6张图片

2.3.2 启动指定的配置文件

说明: 往往在企业级开发过程中为SpringBoot应用启动时指定一个完整外部配置也是经常用到的,在SpringBoot中也提供这个方式来启动项目如

  1. 创建一个完整的配置文件

SpringBoot_第7张图片

  1. 启动时指定配置文件位置

–spring.config.location=C:\Users\ASUS\IdeaProjects\JavaWeb\review\review\boot-01\src\main\resources\application-local.yml

SpringBoot_第8张图片

SpringBoot_第9张图片


3.工厂创建对象

3.1 创建单个对象

在springboot中可以管理单个对象可以直接使用spring框架中注解形式创建。

  • @Component 通用的对象创建注解
    • @Controller 用来创建控制器对象
    • @Service 用来创建业务层对象
    • @Repository 用来创建DAO层对象
    • 以上注解都有value属性,value属性用来指定工厂中对象名称

3.2 创建多个对象

​ 如何在springboot中像spring框架一样通过xml创建多个对象,在SpringBoot中也提供了相同注解如**@Configuration + @Bean**注解进行创建

  • @Configuration 代表这是一个spring的配置类相当于Spring.xml配置文件
  • @Bean 用来在工厂中创建这个@Bean注解标识的对象
    • 默认使用@Bean创建对象在工厂中唯一标识为方法名称
    • 修改在工厂中对象标识可以在使用**@Bean(“工厂中名字”)指定一个名字**

管理复杂对象的创建

/**
 * @author Liu
 * SpringBoot配置类
 */
@Configuration
public class Beans {
//    复杂对象的创建
    @Bean
    public Calendar calendar(){
        return Calendar.getInstance();
    }
}

使用复杂对象还是通过@AutoWired注入属性

@SpringBootTest
class Boot01ApplicationTests {

    @Autowired
    private Calendar calendar;

    @Test
    void contextLoads() {
        System.out.println("hello world");
        System.out.println(calendar.getTime());
        //hello world
        //Mon Mar 21 13:41:30 CST 2022  
    }

}
  1. @Configuration 用来在工厂中一次性创建多个对象

  2. @Component 用来创建单个对象


4.属性注入

4.1 通过@Value 实现基本属性注入

实例:

注入基本类型、数组、日期类型、String

  • 配置文件:

    #基本数据类型 + string + date
    name: 张三
    age: 21
    sex: true
    price: 22.22
    birthday: 2000/11/29 05:20:00 #spring中的日期格式为
    
    #数组类型
    array: arr1,arr2,arr3,arr4
    list: list1,list2,list3,list4
    map: "{'aa':'1234','bb':'2344'}"
    
  • 测试:

@SpringBootTest
class Boot01ApplicationTests {
    //基本数据类型 + string + date
    @Value("${name}")
    private String name;
    @Value("${age}")
    private Integer age;
    @Value("${sex}")
    private Boolean sex;
    @Value("${price}")
    private Double price;
    @Value("${birthday}")
    private Date birthday;

    //数组类型
    @Value("${array}")
    private String[] array;
    @Value("${list}")
    private List<String> list;
    @Value("#{${map}}")
    private Map<String,String> map;


    @Test
    void di(){
        System.out.println("基本数据类型 + string + date");
        System.out.println("name = " + name);
        System.out.println("age = " + age);
        System.out.println("sex = " + sex);
        System.out.println("price = " + price);
        System.out.println("birthday = " + birthday);

        System.out.println("数组类型");
        System.out.println("array = ");
        for (String s : array) {
            System.out.println(s);
        }
        System.out.println("list = " + list);
        System.out.println("map = " + map);
//        基本数据类型 + string + date
//        name = 张三
//        age = 21
//        sex = true
//        price = 22.22
//        birthday = Wed Nov 29 05:20:00 CST 2000
//        数组类型
//        array =
//        arr1
//        arr2
//        arr3
//        arr4
//        list = [list1, list2, list3, list4]
//        map = {aa=1234, bb=2344}
    }
}

注意map集合的取值为 @Value("#{${map}}")

4.2 通过对象方式实现属性注入

注意:这种方式需要提供set方法

  • 配置文件:
#对象方式注入
user:
  id: 1
  username: 李四
  age : 23
  birthday : 2020/02/02
  #对象属性集合类型
  users:
    - { id: 1, name: zhangsan, age: 20 ,birthday: 2000/11/16 }
    - { id: 2, name: lisi, age: 21 ,birthday: 2000/12/11 }
    - { id: 3, name: wangwu, age: 22 ,birthday: 2000/11/29 }
  • 测试代码
    • 注意:
      • 必须提供@component
      • 必须提供属性的set方法
      • 修饰范围在类上,用来指定前缀的属性,注入到对象中的属性名一致的属性当中
      • 可以将对象集合注入
@SpringBootTest
@Component //必须提供component
@ConfigurationProperties(prefix = "user") //修饰范围在类上,用来指定前缀的属性,注入到对象中的属性名一致的属性当中
@Setter//必须提供属性的set方法
public class Test1 {
    private Integer id;
    private String username;
    private Integer age;
    private Date birthday;
    private List <User> users;

    @Test
    void test(){
        System.out.println("id = " + id);
        System.out.println("username = " + username);
        System.out.println("age = " + age);
        System.out.println("birthday = " + birthday);
        users.forEach(user -> System.out.println(user));
//        id = 1
//        username = 李四
//        age = 23
//        birthday = Sun Feb 02 00:00:00 CST 2020
//        User(id=1, name=zhangsan, age=20, birthday=Thu Nov 16 00:00:00 CST 2000)
//        User(id=2, name=lisi, age=21, birthday=Mon Dec 11 00:00:00 CST 2000)
//        User(id=3, name=wangwu, age=22, birthday=Wed Nov 29 00:00:00 CST 2000)

    }
}

引入依赖构建自定义的元数据,否则idea会提示异常

<dependency>
  <groupId>org.springframework.bootgroupId>
  <artifactId>spring-boot-configuration-processorartifactId>
  <optional>trueoptional>
dependency>

5.JSP模板集成

5.1 步骤

jsp相关


<dependency>
    <groupId>jstlgroupId>
    <artifactId>jstlartifactId>
    <version>1.2version>
dependency>


<dependency>
    <groupId>org.apache.tomcat.embedgroupId>
    <artifactId>tomcat-embed-jasperartifactId>
dependency>

引入jsp运行插件

<build>
    <plugins>
        
        <plugin>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-maven-pluginartifactId>
        plugin>
    plugins>
build>

配置springmvc视图解析器

spring:
  #配置视图解析器  视图前缀后缀
  mvc:
    view:
      prefix: / # /代表访问项目中webapp中页面
      suffix: .jsp
      

修改jsp无需重启应用

server:
  servlet:
    jsp:
      init-parameters:
        development: true

webapp的目录结构

SpringBoot_第10张图片

5.2 启动服务

两种方式

  1. 点击maven中的spring-boot:run插件启动
  2. idea中web方式启动

SpringBoot_第11张图片

  1. 访问地址:localhost:端口号:项目根路径/(index.jsp(可以省略))

SpringBoot_第12张图片


6.整合mybatis

6.1 Spring整合mybatis步骤回顾

  1. 引入依赖,Spring、MyBatis、mybatis-Spring、mysql、数据源…
  2. 数据库建立
  3. 开发实体类、dao接口
  4. spring.xml配置文件
    • 开启全局包扫描
    • 数据源 dataSource
      • driverClassName
      • url
      • username
      • password
    • mybatis核心对象SqlSessionFactoryBean的创建
      • 数据源注入
      • mapper文件注入
      • 起别名
    • 批量注册Dao MapperScannerConfiger
      • dao接口的包
    • 事务管理器 transactionManager class=DataSourceTransactionManager
      • 注入数据源
    • 开始注解事务生效
  5. 开发mapper

6.2 引入依赖

druid数据源、mybatis-springboot、mysql

<dependency>
  <groupId>com.alibabagroupId>
  <artifactId>druidartifactId>
  <version>1.2.4version>
dependency>

<dependency>
  <groupId>mysqlgroupId>
  <artifactId>mysql-connector-javaartifactId>
  <version>5.1.47version>
dependency>


<dependency>
  <groupId>org.mybatis.spring.bootgroupId>
  <artifactId>mybatis-spring-boot-starterartifactId>
  <version>2.1.4version>
dependency>


说明:由于springboot整合mybatis版本中默认依赖mybatis 因此不需要额外引入mybati版本,否则会出现冲突

6.3 配置配置文件

  1. 配置数据源
  2. 配置数据库
  3. 配置mybatis核心对象的相关配置
  4. @MapperScan或者@Mapper
spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource #指定数据源 为druid
    driver-class-name: com.mysql.jdbc.Driver   #指定驱动
    url: jdbc:mysql://localhost:3306/mybatis?useSSL=false&characterEncoding=UTF-8 #url
    username: root #username
    password: 200011 #password

#mybatis相关配置
mybatis:
  #注册mapper
  mapper-locations: classpath:com/boot/mapper/*.xml
  #起别名
  type-aliases-package: com.boot.entity

扫描注册Dao

@SpringBootApplication
@MapperScan("com.boot.dao")
public class Boot01Application {

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

}

实体类创建

/**
 * @author Liu
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Integer id;
    private String name;
    private Boolean gender;
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date birthday;
}

Dao接口 与 Mapper

//@Mapper
public interface UserDao {
    void save(User user);
    List<User> findAll();
}

DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.boot.dao.UserDao">
    
    <insert id="save" keyProperty="id" useGeneratedKeys="true" parameterType="user">
        insert into user values(#{id},#{name},#{birthday},#{gender})
    insert>
    
    <select id="findAll" resultType="user">
        select * from user
    select>
mapper>

Service ServiceImpl

/**
 * @author Liu
 */
public interface UserService {
    void save(User user);
    List <User> findAll();
}
@Service
@Transactional(rollbackFor = {Exception.class})
public class UserServiceImpl implements UserService{

    private UserDao userDao;

    @Autowired
    public UserServiceImpl(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void save(User user) {
        userDao.save(user);
    }

    @Override
    @Transactional(propagation = Propagation.SUPPORTS,rollbackFor = {Exception.class})
    public List <User> findAll() {
        return userDao.findAll();
    }
}

Controller层

@Controller
@RequestMapping("/users")
public class UserController {

    private UserService userService;

    @Autowired
    public UserController(UserService userService) {
        this.userService = userService;
    }

    @RequestMapping("save")
    public String save(User user){
        try {
            userService.save(user);
            return "redirect:/users/findAll";
        } catch (Exception e) {
            e.printStackTrace();
            return "redirect:/index.jsp";
        }

    }

    @RequestMapping("findAll")
    public String findAll(Model model){
        List <User> users = userService.findAll();

        users.forEach(System.out :: println);

        model.addAttribute("users",users);

        return "success";
    }

}

7.本地测试

往往在开发过程中业务代码课程非常复杂频繁启动服务器测试,非常麻烦!这个时候使用本地测试就是一个很好的解决方案,springboot也提供了本地测试解决方案!

7.1 引入依赖

<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-testartifactId>
     <scope>testscope>
dependency>

7.2 编写测试类

SpringBoot_第13张图片

  • @SpringBootTest
    • 修饰范围: 用在类上
    • 作用: 用来启动本地Spring环境
@SpringBootTest
class Boot01ApplicationTests {

    @Autowired
    private Calendar calendar;

    @Test
    void contextLoads() {
        System.out.println("hello world");
        System.out.println(calendar.getTime());
        //hello world
        //Mon Mar 21 13:41:30 CST 2022
    }
}

8.热部署工具

为了进一步提高开发效率,springboot为我们提供了全局项目热部署,日后在开发过程中修改了部分代码以及相关配置文件后,不需要每次重启使修改生效,在项目中开启了springboot全局热部署之后只需要在修改之后等待几秒即可使修改生效。

8.1 热部署工具依赖的引入

每次构建项目都需要引入该依赖

<dependency>
   <groupId>org.springframework.bootgroupId>
   <artifactId>spring-boot-devtoolsartifactId>
   <optional>trueoptional>
dependency>

8.2 设置idea支持自动编译

只需要设置一次

  1. 开启自动编译

Preferences | Build, Execution, Deployment | Compiler -> 勾选上 Build project automatically 这个选项

SpringBoot_第14张图片

  1. 开启允许在运行过程中修改文件
    ctrl + alt + shift + / ---->选择1.Registry —> 勾选 compiler.automake.allow.when.app.running 这个选项
SpringBoot_第15张图片 SpringBoot_第16张图片

8.3 启动项目检查热部署是否生效

Main变成了restartedMain表示热部署生效

SpringBoot_第17张图片


9.日志处理

9.1 引言

springboot框架 集成日志 logback 日志

  • ​ Logback是由log4j创始人设计的又一个开源日志组件。目前,logback分为三个模块:logback-core,logback-classic和logback-access。是对log4j日志展示进一步改进!
  • ​ 总结: logback 也是一个开源日志组件 和 log4j作用一致 都是用来生成日志 logback更加轻量

常用处理java的日志组件 slf4j,log4j,logback,common-logging 等

9.2 日志的隔离级别

SpringBoot_第18张图片

All < Trace < DEBUG < INFO < WARN < ERROR < Fatal < OFF

  • OFF | 关闭:最高级别,不打印日志。
  • FATAL | 致命:指明非常严重的可能会导致应用终止执行错误事件。
  • ERROR | 错误:指明错误事件,但应用可能还能继续运行。
  • WARN | 警告:指明可能潜在的危险状况。
  • INFO | 信息:指明描述信息,从粗粒度上描述了应用运行过程
  • DEBUG | 调试:指明细致的事件信息,对调试应用最有用。
  • TRACE | 跟踪:指明程序运行轨迹,比DEBUG级别的粒度更细。
  • ALL | 所有:所有日志级别,包括定制级别。

日志级别由低到高: 日志级别越高输出的日志信息越多

9.3 项目中日志的分类

  • 一种是rootLogger(根全局日志) : 用来监听项目中所有的运行日志 包括引入依赖jar中的日志
  • 一种是logger(指定包级别日志) : 用来监听项目中指定包中的日志信息

9.4 日志的配置在springboot中

注意:SpringBoot框架中默认根日志为INFO

logging:
  level:
    root: info #指定根日志级别(一般不推荐修改根日志,输出信息太多,推荐使用子日志)
    com.boot.dao: debug #指定某个包中日志
  file:
    name: 日志.log  #指定日志名称
    path: C:\Users\ASUS\IdeaProjects\JavaWeb\review\review\boot-01\日志  #指定日志文件目录

9.5 项目中使用日志

@RestController
public class HelloController {

    //声明日志成员
    private static final Logger logger = LoggerFactory.getLogger(HelloController.class);
    @RequestMapping("/hello")
    public String hello(){
        System.out.println("======hello world=======");
        logger.debug("DEBUG,{}","信息");
        logger.info("INFO,{}","信息");
        logger.warn("WARN,{}","信息");
        logger.error("ERROR,{}","信息");
        return "hello";
    }
}

image-20220322084136088

注意需要安装lombok插件


10.切面编程

springboot是对原有项目中spring框架和springmvc的进一步封装,因此在springboot中同样支持spring框架中AOP切面编程,不过在springboot中为了快速开发仅仅提供了注解方式的切面编程.

10.1 引入依赖

<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-aopartifactId>
dependency>

10.2 相关注解说明

  • @Aspect 用来类上,代表这个类是一个切面
  • @Before 用在方法上代表这个方法是一个前置通知方法
  • @After 用在方法上代表这个方法是一个后置通知方法
  • @Around 用在方法上代表这个方法是一个环绕的方法

10.3 代码实现

需要在配置类里面配置切面

  • 创建一个配置类
  • 在类上面使用@Aspect注解标注
  • 在类里面添加相应的方法
    • 在方法上面使用注解标注是什么通知方法
      • 注解参数为 切入点表达式

代码测试

/**
 * @author Liu
 */
@Configuration
@Aspect
public class MyAspect {

    private static final Logger log = LoggerFactory.getLogger(MyAspect.class);

    /**
     * execution (* com.boot.service.*.*(..)) 表示用在service包下的所有方法上  返回值为任意   参数为任意
     * @param joinPoint
     */
    @Before("execution (* com.boot.service.*.*(..))")
    public void before(JoinPoint joinPoint){
        System.out.println("前置通知");
        System.out.println(joinPoint.getTarget());
        System.out.println(joinPoint.getSignature());
        System.out.println(Arrays.toString(joinPoint.getArgs()));
    }

    @After("execution (* com.boot.service.*.*(..))")
    public void after(JoinPoint joinPoint){
        System.out.println("后置通知");
        System.out.println(joinPoint.getTarget());
        System.out.println(joinPoint.getSignature());
        System.out.println(Arrays.toString(joinPoint.getArgs()));
    }

    @Around("execution (* com.boot.service.*.*(..))")
    public Object around(ProceedingJoinPoint proceedingJoinPoint){
        System.out.println("进入环绕通知");
        Object proceed = null;
        try {
            proceedingJoinPoint.getTarget();//目标对象
            proceedingJoinPoint.getSignature();//方法签名
            proceedingJoinPoint.getArgs();//方法参数
            proceed = proceedingJoinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        System.out.println("目标方法执行之后回到环绕通知");
        return proceed;
    }

//    进入环绕通知
//    前置通知
//    com.boot.service.UserServiceImpl@7b0ad3bc
//    void com.boot.service.UserServiceImpl.save(User)
//    [User(id=null, name=刘凯旋, gender=true, birthday=Sat Mar 26 00:00:00 CST 2022)]
//    后置通知
//    com.boot.service.UserServiceImpl@7b0ad3bc
//    void com.boot.service.UserServiceImpl.save(User)
//    [User(id=56, name=刘凯旋, gender=true, birthday=Sat Mar 26 00:00:00 CST 2022)]
//    目标方法执行之后回到环绕通知
}

注意

  • 前置和后置都没有方法的返回值,参数为JoinPoint
  • 环绕通知存在返回值,参数为**ProceedingJoinPoint,**如果执行放行,不会执行目标方法,一旦放行必须将目标方法的返回值返回,否则调用者无法接受返回数据

11.文件上传与下载

11.1 文件上传

用户访问当前系统,将自己本地计算机中文件通过浏览器上传到当前系统所在的服务器过程中称之为文件的上传

文件上传: 用户将自己计算机中文件 上传到 项目所在服务器过程、文件服务器、OSS 称之为文件上传

准备上传界面
<body>
    <h1>测试文件上传页面h1>
   <%-- 
     1. 表单提交方式必须是post
     2. 表单的enctype属性必须为multipart/form-data
     3. 后台接受变量名字要与文件选择name属性一致
    --%>
    <form action="${pageContext.servletContext.contextPath}/file/upload" method="post" enctype="multipart/form-data">
        请选择上传的文件<input name="file" type="file"><br>
        <input type="submit" value="点击上传">
    form>
body>
配置文件上传下载的路径
#指定路径
file:
  upload:
    dir: C:\Users\ASUS\Desktop\文件
  download:
    dir: C:\Users\ASUS\Desktop\文件
编写控制器controller
 private static final Logger log = LoggerFactory.getLogger(FileController.class);
//指定路径注入上传
@Value("${file.upload.dir}")
private String realPath;
@RequestMapping("newupload")
public String fileUploadNew(MultipartFile multipartFile , HttpServletRequest request){
    log.debug("hello");
    System.out.println("hello");
    try {
        //将上传的文件保存在目录中
        multipartFile.transferTo(new File(realPath,multipartFile.getOriginalFilename()));//文件上传
    } catch (IOException e) {
        e.printStackTrace();
    }
    //重定向到上传页面
    return "redirect:../index.jsp";
}
文件过大 产生异常 修改上传文件大小限制
spring:
  #修改文件上传大小限制
  servlet:
    multipart:
      max-file-size: 1024MB #用来指定服务端最大文件大小   
      max-request-size: 1024MB #用来控制文件上传大小的限制

11.2 文件下载

文件下载: 将服务器某个资源文件下载到用户本地计算机过程称之为文件下载

提供下载文件链接
<h1>测试文件下载h1>
<a href="${pageContext.servletContext.contextPath}/file/download?fileName=你好.txt">你好.txta>
开发下载控制器
@Controller
public class downFileController {
    @Value("${file.download.dir}")
    private String realPath;


    @RequestMapping("download")
    public void downLoad(String name, HttpServletResponse response) throws IOException {
        File file = new File(realPath, name);
        FileInputStream inputStream = new FileInputStream(file);
        response.setHeader("content-disposition","attachment;fileName="+ URLEncoder.encode(name,"UTF-8"));
        ServletOutputStream outputStream = response.getOutputStream();
//        int len = 0;
//        byte[] b = new byte[1024];
//
//        while (true){
//            len = inputStream.read(b);
//            if(len == -1) {
//                break;
//            }
//            outputStream.write(b,0,len);
//        }
//        inputStream.close();
        FileCopyUtils.copy(inputStream,outputStream);
    }

}


12.拦截器(Interceptor)

Spring Boot拦截器(Interceptor)详解_hresh的博客-CSDN博客_spring拦截器

12.1 引言

**拦截器(Interceptor)**同 Filter 过滤器一样,它俩都是面向切面编程——AOP 的具体实现(AOP切面编程只是一种编程思想而已)。

你可以使用 Interceptor 来执行某些任务,例如在 Controller 处理请求之前编写日志,添加或更新配置…

在 Spring中,当请求发送到 Controller 时,在被Controller处理之前,它必须经过 Interceptors(0或多个)。

Spring Interceptor是一个非常类似于Servlet Filter 的概念 。

Interceptor 作用

  • 日志记录:记录请求信息的日志,以便进行信息监控、信息统计、计算 PV(Page View)等;
  • 权限检查:如登录检测,进入处理器检测是否登录;
  • 性能监控:通过拦截器在进入处理器之前记录开始时间,在处理完后记录结束时间,从而得到该请求的处理时间。(反向代理,如 Apache 也可以自动记录)
  • 通用行为:读取 Cookie 得到用户信息并将用户对象放入请求,从而方便后续流程使用,还有如提取 Locale、Theme 信息等,只要是多个处理器都需要的即可使用拦截器实现。

12.2 自定义拦截器

如果你需要自定义 Interceptor 的话必须实现org.springframework.web.servlet.HandlerInterceptor接口或继承 org.springframework.web.servlet.handler.HandlerInterceptorAdapter类,并且需要重写下面下面 3 个方法:

  1. preHandler(HttpServletRequest request, HttpServletResponse response, Object handler) 方法在请求处理之前被调用。该方法在 Interceptor 类中最先执行,用来进行一些前置初始化操作或是对当前请求做预处理,也可以进行一些判断来决定请求是否要继续进行下去。该方法的返回值是 Boolean 类型,当它返回 false 时,表示请求结束,后续的 Interceptor 和 Controller 都不会再执行;当它返回为 true 时会继续调用下一个 Interceptor 的 preHandle 方法,如果已经是最后一个 Interceptor 的时候就会调用当前请求的 Controller 方法。
  2. postHandler(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) 方法在当前请求处理完成之后,也就是 Controller 方法调用之后执行,但是它会在 DispatcherServlet 进行视图返回渲染之前被调用,所以我们可以在这个方法中对 Controller 处理之后的 ModelAndView 对象进行操作。
  3. afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex) 方法需要在当前对应的 Interceptor 类的 preHandle 方法返回值为 true 时才会执行。顾名思义,该方法将在整个请求结束之后,也就是在 DispatcherServlet 渲染了对应的视图之后执行。此方法主要用来进行资源清理。
拦截器
public class MyInterceptor implements HandlerInterceptor {

    private static final Logger log = LoggerFactory.getLogger(MyInterceptor.class);

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

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.debug("========4=======");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.debug("========6=======");
    }
}


public class MyInterceptor2 implements HandlerInterceptor {

    private static final Logger log = LoggerFactory.getLogger(MyInterceptor2.class);

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.debug("=======2======");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.debug("=======3======");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.debug("=======5======");
    }
}

配置拦截器

实现WebMvcConfigurer接口并重写addInterceptors方法

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //        registry.addInterceptor("拦截器")
//                .addPathPatterns("拦截路径")
//                .excludePathPatterns("排除路径")
//                .order("指定执行顺序")
        
        registry.addInterceptor(new MyInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns("/file/down")
                .order(1);

        registry.addInterceptor(new MyInterceptor2())
                .addPathPatterns("/**")
                .excludePathPatterns("/file/down")
                .order(2);
    }

}

开发控制器测试
@RestController
public class HelloController {

    //声明日志成员
    private static final Logger logger = LoggerFactory.getLogger(HelloController.class);
    @RequestMapping("/hello")
    public String hello(){
        System.out.println("======2=====");
        return "hello";
    }
    /*
    ======1=====
    ======2=====
    =====3=====
    =====4=====
    */
}

注意:order用来执行多个拦截器的执行顺序,order书写是自然数,按照自然数顺序执行


13.jar包和war包部署

13.1 war包部署

  1. 在pom文件中设置打包方式为war包

    war

  2. 配置入口类

    • 继承SpringBootServletInitializer
    • 覆盖configure方法
    @SpringBootApplication
    @MapperScan("com.boot.dao")
    //1.继承SpringBootServletInitializer
    //2.覆盖configure方法
    public class Boot01Application extends SpringBootServletInitializer {
    
        public static void main(String[] args) {
            SpringApplication.run(Boot01Application.class, args);
        }
        @Override
        protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
            return builder.sources(Boot01Application.class);
        }
    
    }
    
  3. 在插件中指定入口类

    <build>
        <plugins>
            
            <plugin>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-maven-pluginartifactId>
                
                <configuration>
                    <fork>truefork>
                    
                    <jvmArguments>-Dfile.encoding=UTF-8jvmArguments>
                    
                    <mainClass>com.baizhi.Boot01ApplicationmainClass>
                configuration>
            plugin>
    
        plugins>
    build>
    
  4. 排除内嵌的tomcat

    <dependency>
      <groupId>org.springframework.bootgroupId>
      <artifactId>spring-boot-starter-tomcatartifactId>
      <scope>providedscope>   
    dependency>
    
    <dependency>
      <groupId>org.apache.tomcat.embedgroupId>
      <artifactId>tomcat-embed-jasperartifactId>
      <scope>providedscope>  
    dependency>
    
  5. 打包测试

    • 将idea中生成的war包复制粘贴到tomcat的webapps目录下,并解压缩

      SpringBoot_第19张图片

    • 开启tomcat服务器

    • 访问 war包名称为项目名

      image-20220322104603289

注意:

  • application.yml 中配置port context-path 失效
  • 访问时使用打成war包的名字和外部tomcat端口号进行访问项目

13.2 jar包部署

  1. 设置打包方式为jar

    jar

    注意:默认方式也是jar

  2. 执行打包

    SpringBoot_第20张图片
  3. 测试访问

    C:\Users\ASUS\IdeaProjects\JavaWeb\review\review\boot-01\target>java -jar boot-01-0.0.1-SNAPSHOT.jar
    

    SpringBoot_第21张图片

    注意:springboot部署jsp时,插件版本必须指定为1.4.2版本,并进行jsp打包配置才可以,其他版本均不支持!!!

修改插件版本

在项目的pom.xml配置文件中build标签中修改

<plugins>
  
  <plugin>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-maven-pluginartifactId>
    <version>1.4.2.RELEASEversion>
  plugin>
plugins>

指定jsp打包配置

在项目的pom.xml配置文件中build标签中加入配置

<resources>
  
  <resource>
    
    <directory>src/main/webappdirectory>
    
    <targetPath>META-INF/resourcestargetPath>
    <includes>
      <include>**/**include>
    includes>
  resource>
  <resource>
    <directory>src/main/resourcesdirectory>
    <includes>
      <include>**/**include>
    includes>
    <filtering>falsefiltering>
  resource>
resources>

重新打包测试


14.Thymeleaf

14.1 引言

Thymeleaf是一个用于web和独立环境的现代服务器端Java模板引擎。

​ ——摘自官网https://www.thymeleaf.org/

Thymeleaf是跟Velocity、FreeMarker类似的模板引擎,它可以完全替代JSP,相较与其他的模板引擎相比, Thymeleaf在有网络和无网络的环境下皆可运行,即它可以让美工在浏览器查看页面的静态效果,也可以让程序员在服务器查看带数据的动态页面效果。

14.2 SpringBoot集成Thymeleaf

引入依赖

<dependency>
  <groupId>org.springframework.bootgroupId>
  <artifactId>spring-boot-starter-thymeleafartifactId>
dependency>
编写配置
spring:
  thymeleaf:
    cache: false #关闭缓存
    prefix: classpath:templates/
    suffix: .html
编写控制器测试
@Controller
public class HelloController {
    @GetMapping("/hello")
    public String hello(){
        System.out.println("hello");
        return "index";
    }
}
在templates目录中定义模板

SpringBoot_第22张图片

测试访问…


14.3 Thymeleaf模板的基本语法

前提:使用时必须在头部加入命名空间

使用时必须在页面中加入thymeleaf如下命名空间:
<html lang="en" xmlns:th="http://www.thymeleaf.org">
展示单个数据
设置数据
model.addAttribute("name","张三"); 或 request.setAttribute("name","小黑");
获取数据
<span th:text="${name}"/>  --->获取数据

image-20200323100747881

解析含有html标签数据
model.addAttribute("name","张三");
model.addAttribute("username","小陈");
  • 直接获取原样输出
<span th:text="${name}"/>

image-20200323100956740

  • 获取并解析
 <span th:utext="${name}"/>

image-20200323101457134

  • 将数据赋值给表单元素

    <input type="text" th:value="${username}"/>
    

    image-20200323103452620

# 总结
-  1.使用 th:text="${属性名}"  获取对应数据,获取数据时会将对应标签中数据清空,因此最好是空标签
-  2.使用 th:utext="${属性名}" 获取对应的数据,可以将数据中html先解析在渲染到页面
-  3.使用 th:value="${属性名}" 获取数据直接作为表单元素value属性
展示对象数据
model.addAttribute("user",new User("21","xiaochen",23,new Date()));
id:<span th:text="${user.id}">span>
name:<span th:text="${user.name}">span>
age:<span th:text="${user.age}">span>
bir: <span th:text="${user.bir}">span>  ====  <span th:text="${#dates.format(user.bir, 'yyyy-MM-dd HH:mm')}">span> 日期格式化
条件展示数据
model.addAttribute("user",new User("21","xiaochen",23,new Date()));
<span th:if="${user.age} eq 23">
  青年
span>
<span th:if="${user.age == 23}">
  青年
span>
# 运算符
    gt:great than(大于)>
    ge:great equal(大于等于)>=
    eq:equal(等于)==
    lt:less than(小于)<
    le:less equal(小于等于)<=
    ne:not equal(不等于)!=
展示多条数据
  • 直接遍历集合
 <ul th:each="user:${users}">
   <li th:text="${user.id}">li>
   <li th:text="${user.name}">li>
   <li th:text="${user.age}">li>
   <li th:text="${#dates.format(user.bir,'yyyy-MM-dd')}">li>
ul>
  • 遍历时获取遍历状态
 <ul th:each="user,userStat:${users}">
   <li><span th:text="${userStat.count}"/>-<span th:text="${user.id}"/>li>   获取遍历次数  count 从1开始 index 从0开始
   <li><span th:text="${userStat.odd}"/>-<span th:text="${user.name}"/>li>   获取当前遍历是否是奇数行
   <li><span th:text="${userStat.even}"/>-<span th:text="${user.age}"/>li>   获取当前遍历是否是偶数行
   <li><span th:text="${userStat.size}"/>-<span th:text="${user.bir}"/>li>   获取当前集合的总条数
ul>
引入静态资源
# 使用thymeleaf模板项目中静态资源默认放在resources路径小static目录中
  • 项目中放入对应静态资源

    image-20200323142307411

  • 页面中引入

     <link rel="stylesheet" th:href="@{/css/index.css}">
     <script th:src="@{/js/jquery-min.js}">script>
    

    注意: @{/}代表通过thymeleaf语法动态获取应用名

在js代码中获取项目名
<script>
  const ctx = '[[@{/}]]';
</script>

注意:[[书写thymeleaf语法]],这里[[]]是thymeleaf内嵌表达式


15.RestFul

SpringBoot_第23张图片

15.1 引言

REST全称是Representational State Transfer,中文意思是表述(编者注:通常译为表征)性状态转移。 它首次出现在2000年Roy Fielding的博士论文中,Roy Fielding是HTTP规范的主要编写者之一。 他在论文中提到:“我这篇文章的写作目的,就是想在符合架构原理的前提下,理解和评估以网络为基础的应用软件的架构设计,得到一个功能强、性能好、适宜通信的架构。REST指的是一组架构约束条件和原则。” 如果一个架构符合REST的约束条件和原则,我们就称它为RESTful架构。

RestFul: 是一种以网络为基础构架一种架构风格 一个架构符合Rest设计原则和约束成这个架构为RestFul。

Rest 词: 更新技术 组件 服务 让我们web请求能够利用web中标准 和 能力 更好描述架构

REST本身并没有创造新的技术、组件或服务,而隐藏在RESTful背后的理念就是使用Web的现有特征和能力, 更好地使用现有Web标准中的一些准则和约束。虽然REST本身受Web技术的影响很深, 但是理论上REST架构风格并不是绑定在HTTP上,只不过目前HTTP是唯一与REST相关的实例。 所以我们这里描述的REST也是通过HTTP实现的REST。

15.2 总结

Restful 一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。

web开发: 实现所有功能

restful: 软件设计风格 标准 简洁 层次 优雅

rest设计原则 和 约束架构称之为restFul

15.3 URL定义

  • 资源:互联网所有的事物都可以被抽象为资源
    • 一首歌 一张图片 数据库一条记录
  • 资源操作:使用POST(添加)、DELETE(删除)、PUT(修改)、GET(查询),使用不同请求方法对资源进行操作。
    • 删除 delete
    • 查询 get
    • 添加 post
    • 修改 put (修改全部字段)| patch(更新部分字段)
传统方式操作资源
  • http://127.0.0.1/item/queryUser.action?id=1 查询,GET

  • http://127.0.0.1/item/saveUser.action 新增,POST

  • http://127.0.0.1/item/updateUser.action 更新,PUT

  • http://127.0.0.1/item/deleteUser.action?id=1 删除,DELETE

    **注意:**传统的操作是没有问题的,大神认为是有问题的,有什么问题呢?你每次请求的接口或者地址,都在做描述,例如查询的时候用了queryUser,新增的时候用了saveUser ,修改的时候用了updateUser,其实完全没有这个必要,我使用了get请求,就是查询.使用post请求,就是新增的请求,PUT就是修改,delete就是删除,我的意图很明显,完全没有必要做描述,这就是为什么有了restful.

使用RESTful操作资源
  • 【GET】 /users # 查询用户信息列表

  • 【GET】 /users/1001 # 查看某个用户信息

  • 【POST】 /users # 新建用户信息

  • 【PUT】 /users/1001 # 更新用户信息(全部字段)

  • 【PATCH】 /users/1001 # 更新用户信息(部分字段)

  • 【DELETE】 /users/1001 # 删除用户信息

15.4 Rest API设计风格原则

  1. 使用名词而不是动词
  2. Get方法和查询参数不应该涉及状态改变
  3. 使用复数名词
  4. 使用子资源表达关系
  5. 使用Http头声明序列化格式
  6. 为集合提供过滤 排序 选择和分页等功能
  7. 版本化你的API 例如: v1 v2 v3
  8. 使用Http状态码处理错误
# 1.使用名词而不是动词
- 不要使用:
	如:
    /getAllUsers    get   /users  get /users/002
    /createNewUser  post  /users
    /deleteAllUser  delete /users/001

# 2.Get方法和查询参数不应该涉及状态改变
- 使用PUT, POST 和DELETE 方法 而不是 GET 方法来改变状态,不要使用GET 进行状态改变


# 3.使用复数名词
- 不要混淆名词单数和复数,为了保持简单,只对所有资源使用复数。
	如:
    /cars 而不是 /car
    /users 而不是 /user
    /products 而不是 /product
    /settings 而不是 /setting
    /orders   而不是 /order
		
# 4. 使用子资源表达关系
- 如果一个资源与另外一个资源有关系,使用子资源:
	如:
    GET /cars/711/drivers/ 返回 car 711的所有司机
    GET /cars/711/drivers/4 返回 car 711的4号司机
    GET /users/11/pets      返回 user 11的所有宠物
    GET /users/11/pets/2    返回 user 11的2号宠物

# 5.使用Http头声明序列化格式
- 在客户端和服务端,双方都要知道通讯的格式,格式在HTTP-Header中指定
	如:
	Content-Type 定义请求格式
	Accept 定义系列可接受的响应格式

# 6.为集合提供过滤 排序 选择和分页等功能
- Filtering过滤:使用唯一的查询参数进行
	GET /cars?color=red 返回红色的cars
	GET /cars?seats<=2 返回小于两座位的cars集合

- Sorting排序:允许针对多个字段排序
	GET /cars?sort=-manufactorer,+model
	这是返回根据生产者降序和模型升序排列的car集合

- Field selection
	移动端能够显示其中一些字段,它们其实不需要一个资源的所有字段,给API消费者一个选择字段的能力,这会降低网络流量,提高API可用性。
	GET /cars?fields=manufacturer,model,id,color
	
- Paging分页
	使用 limit 和offset.实现分页,缺省limit=20 和offset=0;
	GET /cars?offset=10&limit=5
	为了将总数发给客户端,使用订制的HTTP头: X-Total-Count.
	链接到下一页或上一页可以在HTTP头的link规定,遵循Link规定:
	Link: ; rel="next",; rel="last",; rel="first",; rel="prev",
	

# 7.版本化你的API  支付宝  v1  v2   v3
- 使得API版本变得强制性,不要发布无版本的API,使用简单数字,避免小数点如2.5.
	一般在Url后面使用?v
	/blog/api/v1

# 8. 使用Http状态码处理错误
- 如果你的API没有错误处理是很难的,只是返回500和出错堆栈不一定有用
- Http状态码提供70个出错,我们只要使用10个左右:
 	`200 – OK – 一切正常
  `201 – OK – 新的资源已经成功创建
  `204 – OK – 资源已经成功删除
  `304 – Not Modified – 客户端使用缓存数据
  `400 – Bad Request – 请求无效,需要附加细节解释如 "JSON无效"
  `401 – Unauthorized – 请求需要用户验证
  `403 – Forbidden – 服务器已经理解了请求,但是拒绝服务或这种请求的访问是不允许的。
  `404 – Not found – 没有发现该资源
  `422 – Unprocessable Entity – 只有服务器不能处理实体时使用,比如图像不能被格式化,或者重要字段丢失。
  `500 – Internal Server Error – API开发者应该避免这种错误。
使用详细的错误包装错误:  状态码    数据  header头信息
{
  "errors": [
   {
    "userMessage": "Sorry, the requested resource does not exist",
    "internalMessage": "No car found in the database",
    "code": 34,
    "more info": "http://dev.mwaysolutions.com/blog/api/v1/errors/12345"
   }
  ]
}

15.5 RestAPI案例

Controller代码
/**
 * @author Liu
 */
@RestController
@RequestMapping("users")
public class UserController {
    private UserService userService;
    @Autowired
    public UserController(UserService userService) {
        this.userService = userService;
    }

    /**
     * 保存用户
     *
     * @param user
     * @return
     */
    @PostMapping
    public ResponseEntity <Void> saveUser(@RequestBody User user) {
        System.out.println(user);
        try {
            userService.save(user);
        } catch (Exception e) {
            e.printStackTrace();
            return new ResponseEntity<Void>(HttpStatus.INTERNAL_SERVER_ERROR);
        }
        return new ResponseEntity<Void>(HttpStatus.CREATED);
    }

    /**
     * 删除用户
     *
     * @param id
     * @return
     */
    @DeleteMapping("{id}")
    public ResponseEntity<Void> deleteUser(@PathVariable("id") Integer id) {
        try {
            userService.delete(id);
        } catch (Exception e) {
            e.printStackTrace();
            return new ResponseEntity<Void>(HttpStatus.INTERNAL_SERVER_ERROR);
        }
        return new ResponseEntity<Void>(HttpStatus.NO_CONTENT);
    }

    /**
     * 更新用户
     *
     * @param user
     * @return
     */
    @PutMapping
    public ResponseEntity<Void> updateUser(@RequestBody User user) {
        userService.update(user);
        return new ResponseEntity<Void>(HttpStatus.NO_CONTENT);
    }

    /**
     * 用户详情
     *
     * @param id
     * @return
     */
    @GetMapping("{id}")
    public ResponseEntity<User> findUserById(@PathVariable("id") Integer id) {
        User user = userService.findById(id);
        if (user == null) {
            return new ResponseEntity<User>(HttpStatus.INTERNAL_SERVER_ERROR);
        }
        return new ResponseEntity<User>(user, HttpStatus.OK);
    }

    /**
     * 用户列表
     *
     * @return
     */
    @GetMapping
    public ResponseEntity<List <User>> users() {
        List<User> users = null;
        try {
            users = userService.findAll();
        } catch (Exception e) {
            e.printStackTrace();
            return new ResponseEntity<List<User>>(HttpStatus.INTERNAL_SERVER_ERROR);
        }
        return new ResponseEntity<List<User>>(users, HttpStatus.OK);
    }
}
使用Postman测试案例
  1. POST saveUser

    SpringBoot_第24张图片
  2. GET 获取全部

    SpringBoot_第25张图片
  3. 更新PUT update

    SpringBoot_第26张图片
  4. GET请求 根据id获取对象

    SpringBoot_第27张图片
  5. DELETE 根据id删除对象

    SpringBoot_第28张图片

16.异常处理

16.1 传统开发的异常处理

当控制器中的任意一个方法出现异常时,如果该控制器的方法没有处理异常的机制,就会自动进入resolveException方法,处理异常

@Component
public class GlobalExceptionHandler implements HandlerExceptionResolver {
    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        return new ModelAndView("500");
    }
}

SpringBoot_第29张图片

16.2 RestFul的异常处理

/**
 * 全局异常处理之RestFul
 */
@ControllerAdvice
public class GlobalExceptionHandlerRest {
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public ResponseEntity<String> exceptionHandler(Exception e) {
        return new ResponseEntity<>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

17.CORS跨域

17.1 概述

CORS跨域

  • CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)

  • ​ 它允许浏览器向跨源服务器,发出XMLHttpRequest(ajax)请求,从而克服了AJAX只能同源使用的限制

同源策略

  • 同源策略[same origin policy]是浏览器的一个安全功能,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源。 同源策略是浏览器安全的基石。

  • [origin]就是协议、域名和端口号。例如:http://www.baidu.com:80这个URL。

同源

  • ​ 若地址里面的协议、域名和端口号均相同则属于同源。

    *同源举例*
    - 例如判断下面的URL是否与 http://www.a.com/test/index.html 同源
    	http://www.a.com/dir/page.html --------->同源
    	http://www.child.a.com/test/index.html ->不同源,域名不相同
    	https://www.a.com/test/index.html ------>不同源,协议不相同
    	http://www.a.com:8080/test/index.html -->不同源,端口号不相同
    

哪些操作不受同源限制

  • 页面中的链接,重定向以及表单提交是不会受到同源策略限制的;
  • 跨域资源的引入是可以的。如嵌入到页面中的