Spring是Java企业版(Java Enterprise Edition,JEE,也称J2EE)的轻量级代替品。无需开发重量级的Enterprise JavaBean(EJB),Spring为企业级Java开发提供了一种相对简单的方法,通过依赖注入和面向切面编程,用简单的Java对象(Plain Old Java Object,POJO)实现了EJB的功能。
虽然Spring的组件代码是轻量级的,但它的配置却是重量级的。一开始,Spring用XML配置,而且是很多XML配置。Spring 2.5引入了基于注解的组件扫描,这消除了大量针对应用程序自身组件的显式XML配置。Spring 3.0引入了基于Java的配置,这是一种类型安全的可重构配置方式,可以代替XML。
所有这些配置都代表了开发时的损耗。因为在思考Spring特性配置和解决业务问题之间需要进行思维切换,所以编写配置挤占了编写应用程序逻辑的时间。和所有框架一样,Spring实用,但与此同时它要求的回报也不少。
除此之外,项目的依赖管理也是一件耗时耗力的事情。在环境搭建时,需要分析要导入哪些库的坐标,而且还需要分析导入与之有依赖关系的其他库的坐标,一旦选错了依赖的版本,随之而来的不兼容问题就会严重阻碍项目的开发进度。
1. Spring 的优点
1.1 Spring 是什么? 一个框架、解决Java企业开发复杂度
1.2 Spring 优点? IOC/AOP/容器/非侵入式
IOC/DI区别(编程思想/具体实现)
2. Spring 的缺点
1.配置复杂
2.依赖复杂
其实人们把Spring Boot 称为搭建程序的脚手架
。其最主要作用就是帮我们快速的构建庞大的spring项目,并且尽可能的减少一切xml配置,做到开箱即用,迅速上手,让我们关注与业务而非配置。
java一直被人诟病的一点就是臃肿、麻烦。当我们还在辛苦的搭建项目时,可能Python程序员已经把功能写好了,究其原因注意是两点:
复杂的配置
项目各种配置其实是开发时的损耗, 因为在思考 Spring 特性配置和解决业务问题之间需要进行思维切换,所以写配置挤占了写应用程序逻辑的时间。
一个是混乱的依赖管理
项目的依赖管理也是件吃力不讨好的事情。决定项目里要用哪些库就已经够让人头痛的了,你还要知道这些库的哪个版本和其他库不会有冲突,这难题实在太棘手。并且,依赖管理也是一种损耗,添加依赖不是写应用程序代码。一旦选错了依赖的版本,随之而来的不兼容问题毫无疑问会是生产力杀手。
Spring Boot 简化了基于Spring的应用开发,只需要“run”就能创建一个独立的、生产级别的Spring应用。Spring Boot为Spring平台及第三方库提供开箱即用的设置(提供默认设置,存放默认配置的包就是启动器starter),这样我们就可以简单的开始。多数Spring Boot应用只需要很少的Spring配置。
Spring Boot 主要目标是:
起步依赖本质上是一个Maven项目对象模型(Project Object Model,POM),定义了对其他库的传递依赖,这些东西加在一起即支持某项功能。
简单的说,起步依赖就是将具备某种功能的坐标打包到一起,并提供一些默认的功能。
Spring Boot的自动配置是一个运行时(更准确地说,是应用程序启动时)的过程,考虑了众多因素,才决定Spring配置应该用哪个,不该用哪个。该过程是Spring自动完成的。
采用大量的默认配置,用户零配置或者少量配置
1. SpringBoot 的项目需要继承哪个parent ?
spring-boot-starter-parent
spring-boot-starter-parent 的作用是
依赖版本锁定
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.1.4.RELEASEversion>
parent>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
dependencies>
注解:@SpringBootApplication 包含了一下三个注解
@SpringBootConfiguration
在这个注解上面,又有一个@Configuration
注解。通过’上面的注释阅读我们知道:这个注解的作用就是声明当前类是一个配置类,然后Spring会自动扫描到添加了@Configuration
的类,并且读取其中的配置信息。而@SpringBootConfiguration
是来声明当前类是SpringBoot应用的配置类,项目中只能有一个。所以一般我们无需自己添加。
@EnableAutoConfiguration
SpringBoot内部对大量的第三方库或Spring内部库进行了默认配置,这些配置是否生效,取决于我们是否引入了对应库所需的依赖,如果有那么默认配置就会生效。
所以,我们使用SpringBoot构建一个项目,只需要引入所需框架的依赖,配置就可以交给SpringBoot处理了。除非你不希望使用SpringBoot的默认配置,它也提供了自定义配置的入口。
@ComponentScan
run方法:SpringApplication.run()
@SpringBootApplication
public class MySpringBootApplication {
public static void main(String[] args) {
SpringApplication.run(MySpringBootApplication.class, args);
}
}
@RestController @GetMapping
引导类的同一级或者下一级,SpringBootApplication已经扫包
SpringBoot 的 SpringMVC 起步依赖是什么?
spring-boot-starter-web
//@RestController 等于下面的和
//@Controller
//@ResponseBody //返回JSON格式数据
@RestController
@RequestMapping("/quick")
public class QuickController {
@GetMapping
public String quick() {
return "SpringBoot 从入门到精通!!";
}
}
起步依赖:
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
//替换运行器
@RunWith(SpringRunner.class)
//指定启动类
//@SpringBootTest(classes = XiaofeifeiApplication.class)
@SpringBootTest //如果测试类在引导类的同级目录或者子目录下可以省略指定
public class Test01_junit {
@Autowired
private BookService bookService;
@Test
public void test(){
Book book = bookService.queryById(1);
System.out.println(book);
}
}
SpringBoot的properties的配置文件的名称是? application.properties
目标 |
---|
掌握yml文件的语法,创建普通数据、对象数据、对象数组数据 |
yaml文件介绍:
YML文件格式是YAML (YAML Aint Markup Language)编写的文件格式,YAML是一种直观的能够被电脑识别的的数据序列化格式,并且容易被人类阅读,容易和脚本语言交互的,可以被支持YAML库的不同的编程语言程序导入,比如: C/C++, Ruby, Python, Java, Perl, C#, PHP等。YML文件是以数据为核心的,比传统的xml方式更加简洁。
YML文件的扩展名可以使用.yml或者.yaml。
核心代码
#普通数据: 冒号后面必须有空格
name: 小鸭鸭
age: 15
#对象数据
person:
name: 小飞飞
age: 16
gender: 男
#数组
addr:
- 天河九巷
- 天河八巷
- 天河十巷
#对象数组
persons:
- name: 小野鸡
age: 18
- name: 小龟龟
age: 6
小结:
1)多个yml配置文件;在spring boot中是被允许的。这些配置文件的名称必须为application-***.yml,并且这些配置文件必须要在application.yml【这个必须有】配置文件中激活之后才可以使用。
#激活配置文件;需要指定其它的配置文件名称
spring:
profiles:
active: prod,dev
2)如果properties和yml配置文件同时存在在spring boot项目中;那么这两类配置文件都有效。在两个配置文件中如果存在同名的配置项的话会以properties文件的为主。
掌握读取配置文件的两种方法
读取配置文件的两种方法涉及到的两个注解分别是?
@Value
@ConfigurationProperties
核心代码
第一种方法:@Value : 基本数据类型和String
package com.itheima.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/*@Controller
@ResponseBody*/
/**
* Controller
* @author Johnny.Chen
*
*/
@RestController
public class HelloController {
@Value("${person.name}")
private String n;
@Value("${person.age}")
private int a;
//@RequestMapping(value="/sayHello",method=RequestMethod.GET)
@GetMapping("/sayhello")
public String sayHello() {
return n+" 真的好骚啊!===>"+a;
}
}
第二种方法:@ConfigurationProperties
: 除了基本数据类型和String之外还可以注入数组
package com.itheima.controller;
import java.util.Arrays;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/*@Controller
@ResponseBody*/
/**
* Controller
* @author Johnny.Chen
*
*/
@RestController
@ConfigurationProperties(prefix="person")
public class HelloController2 {
private String name;
private int age;
private String[] addr;
public String[] getAddr() {
return addr;
}
public void setAddr(String[] addr) {
this.addr = addr;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
//@RequestMapping(value="/sayHello",method=RequestMethod.GET)
@GetMapping("/sayhello2")
public String sayHello() {
return name+" 真的好骚啊!===>"+age+"====>"+Arrays.toString(addr);
}
}
SpringBoot为我们提供了默认配置,而默认配置生效的步骤:
META-INF/spring.factories
文件,读取其中以EnableAutoConfiguration
为key的所有类的名称,这些类就是提前写好的自动配置类@Configuration
注解,并且通过@Bean
注解提前配置了我们所需要的一切实例。完成自动配置@ConditionalOn
注解,满足一定条件才会生效。比如条件之一:是一些相关的类要存在1)启动器
所以,我们如果不想配置,只需要引入依赖即可,而依赖版本我们也不用操心,因为只要引入了SpringBoot提供的stater(启动器),就会自动管理依赖及版本了。
因此,玩SpringBoot的第一件事情,就是找启动器,SpringBoot提供了大量的默认启动器
2)全局配置
另外,SpringBoot的默认配置,都会读取默认属性,而这些属性可以通过自定义application.properties
文件来进行覆盖。这样虽然使用的还是默认配置,但是配置中的值改成了我们自定义的。
因此,玩SpringBoot的第二件事情,就是通过application.properties
来覆盖默认属性值,形成自定义配置。我们需要知道SpringBoot的默认属性key,非常多,可以再idea中自动提示
5 快速构建SpringBoot项目
掌握快速构建SpringBoot项目的方法
1、要在一个网络环境较好的地方去创建
2、创建项目选择Spring Initializr 项目
3、勾选需要的起步依赖即可
如果有两个以上的SpringBoot的项目,右下角会弹出一个框:
点击向下箭头
RunDashboard界面:
扩展:spring的banner
只需要在resource目录下,创建banner.txt
${AnsiColor.BRIGHT_YELLOW}
// _ooOoo_ //
// o8888888o //
// 88" . "88 //
// (| ^_^ |) //
// O\ = /O //
// ____/`---'\____ //
// .' \\| |// `. //
// / \\||| : |||// \ //
// / _||||| -:- |||||- \ //
// | | \\\ - /// | | //
// | \_| ''\---/'' | | //
// \ .-\__ `-` ___/-. / //
// ___`. .' /--.--\ `. . ___ //
// ."" '< `.___\_<|>_/___.' >'"". //
// | | : `- \`.;`\ _ /`;.`/ - ` : | | //
// \ \ `-. \_ __\ /__ _/ .-` / / //
// ========`-.____`-.___\_____/___.-`____.-'======== //
// `=---=' //
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //
// 佛祖保佑 永不宕机 永无BUG //
${AnsiColor.BRIGHT_GREEN}
Application Version: ${application.version}${application.formatted-version}
Spring Boot Version: ${spring-boot.version}${spring-boot.formatted-version}
创建的 Spring Initializr 项目一定要联网吗?
我们编写pojo时,经常需要编写构造函数和gettersetter方法,属性多的时候,就非常浪费时间,使用lombok插件可以解决这个问题:
在idea中安装lombok插件:
需要在maven中引入依赖:
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.6version>
<scope>providedscope>
dependency>
然后可以在Bean上使用:
@Data
:自动提供getter和setter、hashCode、equals、toString等方法
@Getter
:自动提供getter方法
@Setter
:自动提供setter方法
@Slf4j
:自动在bean中提供log变量,其实用的是slf4j的日志功能。
@ToString
:生成toString方法
@NonNull
:这个注解可以用在成员方法或者构造方法的参数前面,会自动产生一个关于此参数的非空检查,如果参数为空,则抛出一个空指针异常。
例如,我们在javabean上加@Data
,那么就可以getter和setter等方法的编写,lombok插件会帮我们自动生成:
package com.itheima.pojo;
import lombok.*;
@ToString
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Book {
private Integer id;
private String bookname;
private Long price;
private String pic;
private String bookdesc;
}
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>2.0.1version>
dependency>
配置,一些文件位置相关的,mybatis知道,需要我们来指定:
#mybatis配置
mybatis:
type-aliases-package: com.leyou.pojo #配置实体类的别名
mapper-locations:
- classpath:/mapper/*.xml #扫描配置映射文件
configuration:
map-underscore-to-camel-case: true #驼峰命名
Mapper接口的扫描有两种实现方式:
Mapper接口的位置在application.yml中并不能配置,Mapper接口的扫描有两种实现方式:
方式一
我们需要给每一个Mapper接口添加@Mapper
注解,由Spring来扫描这些注解,完成Mapper的动态代理。
@Mapper
public interface UserDao {
}
方式二
在启动类上添加扫描包注解@MapperScan
(推荐):
这种方式的好处是,不用给每一个Mapper都添加注解。
@SpringBootApplication
@MapperScan("cn.itcast.mapper")
public class Application {
public static void main(String[] args) {
// 启动代码
SpringApplication.run(Application.class, args);
}
}
2.通用mapper(重点)
通用Mapper的作者也为自己的插件编写了启动器,我们直接引入即可:
<dependency>
<groupId>tk.mybatisgroupId>
<artifactId>mapper-spring-boot-starterartifactId>
<version>2.1.5version>
dependency>
注意:一旦引入了通用Mapper的启动器,会覆盖Mybatis官方启动器的功能,因此需要移除对官方Mybatis启动器的依赖。
无需任何配置就可以使用了。如果有特殊需要,可以到通用mapper官网查看:https://github.com/abel533/Mapper/wiki/3.config
另外,我们需要把启动类上的@MapperScan注解修改为通用mapper中自带的:
注意 @MapperScan是tk.mybatis.spring.annotation
包下的,用于扫描Mapper接口
通用mapper的使用步骤
继承了Mapper接口,就自动实现了增删改查的常用方法。
public interface UserDao extends Mapper<User>{
}
@Table
和 **@Id
都是JPA注解,@Table
**用于配置表与实体类的映射关系,@Id
用于标识主键属性。
@Table(name = "tb_user")// 表名
public class User{
@Id
@KeySql(useGeneratedKeys = true) // 开启自增主键回显功能
private Long id;
@Column(name = "user-Name") // 若实体类与表字段不对应 需要指定
private String userName;
private String password;
private String name;
private Integer age;
private Integer sex;
private Date birthday;
private Date created;
private Date updated;
private String note;
}
对UserService的代码进行简单改造:
@Service
public class UserService {
@Autowired
private UserDao userDao;
public User queryById(Long id){
// 查询
return userDao.selectByPrimaryKey(id);
}
@Transactional
public Integer saveUser(User user){
// 新增
return userDao.insertSelective(user);
}
}
虽然默认配置已经可以使用SpringMVC了,不过我们有时候需要进行自定义配置。
@Slf4j //加入这个注解就可以使用日志
日志级别控制:
logging:
level:
root: info #所有包的日志基本
cn.itcast: debug
org.springframework: debug
其中:
我们编写的控制器加入日志:
@RestController
@Slf4j //加入这个注解就可以使用日志
public class ConfigController {
//映射
@GetMapping("/hello1")
public String sayHello(){
//记录日志
log.debug("debug 小鸭鸭大战冲击钻");
log.info("info 小飞飞大战乔碧萝!");
log.warn("warn 告警日志:弗利萨来了。。。");
log.error("error 错误日志。。。。。。");
return ".....";
}
}
现在,我们的项目是一个jar工程,那么就没有webapp,我们的静态资源该放哪里呢?
回顾我们上面看的源码,有一个叫做ResourceProperties的类,里面就定义了静态资源的默认查找路径:
默认的静态资源路径为:
只要静态资源放在这些目录中任何一个,SpringMVC都会帮我们处理。
我们习惯会把静态资源放在classpath:/static/
目录下。我们创建目录,并且添加一些静态资源:
重启项目后测试,访问图片,如果配置了上下文路径,记得加上
拦截器也是我们经常需要使用的,在SpringBoot中该如何配置呢?
拦截器不是一个普通属性,而是一个类,所以就要用到java配置方式了。在SpringBoot官方文档中有这么一段
如果你想要保持Spring Boot 的一些默认MVC特征,同时又想自定义一些MVC配置(包括:拦截器,格式化器, 视图控制器、消息转换器 等等),你应该让一个类实现
WebMvcConfigurer
,并且添加@Configuration
注解,但是千万不要加@EnableWebMvc
注解。如果你想要自定义HandlerMapping
、HandlerAdapter
、ExceptionResolver
等组件,你可以创建一个WebMvcRegistrationsAdapter
实例 来提供以上组件。如果你想要完全自定义SpringMVC,不保留SpringBoot提供的一切特征,你可以自己定义类并且添加
@Configuration
注解和@EnableWebMvc
注解
通过实现WebMvcConfigurer
并添加@Configuration
注解来实现自定义部分SpringMvc配置。
首先我们定义一个拦截器:
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
log.debug("preHandle method is now running!");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
log.debug("postHandle method is now running!");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
log.debug("afterCompletion method is now running!");
}
}
然后,我们定义配置类,注册拦截器:
@Configuration
public class MvcConfig implements WebMvcConfigurer{
/**
* 重写接口中的addInterceptors方法,添加自定义拦截器
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 通过registry来注册拦截器,通过addPathPatterns来添加拦截路径
registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**");
}
}
结构如下:
接下来运行并查看日志:
你会发现日志中什么都没有,因为我们记录的log级别是debug,默认是显示info以上,我们需要进行配置。
SpringBoot通过logging.level.*=debug
来配置日志级别,*填写包名
# 设置com.leyou包的日志级别为debug
logging:
level:
com.leyou=debug
再次运行查看:
2018-05-05 17:50:01.811 DEBUG 4548 --- [p-nio-80-exec-1] com.leyou.interceptor.LoginInterceptor : preHandle method is now running!
2018-05-05 17:50:01.854 DEBUG 4548 --- [p-nio-80-exec-1] com.leyou.interceptor.LoginInterceptor : postHandle method is now running!
2018-05-05 17:50:01.854 DEBUG 4548 --- [p-nio-80-exec-1] com.leyou.interceptor.LoginInterceptor : afterCompletion method is now running!
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
#Redis 配置信息
#远程
#spring.redis.host=192.168.207.50
#spring.redis.port=6379
#如果设置了需要密码,则需要添加密码
#spring.redis.password=12345678
#本地
spring.redis.host=127.0.0.1
spring.redis.port=6379
package com.leyou.test;
import com.leyou.pojo.Book;
import com.leyou.service.BookService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.test.context.junit4.SpringRunner;
//替换运行器
@RunWith(SpringRunner.class)
//指定启动类
//@SpringBootTest(classes = XiaofeifeiApplication.class)
@SpringBootTest //如果测试类在引导类的同级目录或者子目录下可以省略指定
public class Test02_redis {
/**
* serivce
*/
@Autowired
private BookService bookService;
//redis模板
@Autowired
private RedisTemplate redisTemplate;
/*
缓存使用:
1、先去缓存中找
1.1 如果没有,去数据库中找,找到返回之前先存入缓存中
1.2 如果有直接返回
*/
@Test
public void test(){
//书本id
int id = 1;
//去缓存中找
Book book = (Book) redisTemplate.opsForValue().get("book_"+id);
if(book==null){
//1.1 如果没有,去数据库中找,找到返回之前先存入缓存中
book = bookService.queryById(1);
//保存到缓存中
redisTemplate.opsForValue().set("book_"+id, book);
//返回
System.out.println("从数据库中找:"+book);
}else{
System.out.println("从缓存中找:"+book);
}
}
}
1 spring中的jdbc连接和事务是配置中的重要一环,在SpringBoot中该如何处理呢?
答案是不需要处理,我们只要找到SpringBoot提供的启动器即可:
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-jdbcartifactId>
dependency>
2数据库驱动,SpringBoot并不知道我们用的什么数据库,这里我们选择MySQL:
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.47version>
dependency>
3 SpringBoot中通过注解来控制。就是我们熟知的@Transactional
@Service
public class UserService {
public User queryById(Long id){
// 开始查询
return new User();
}
@Transactional
public void deleteById(Long id){
// 开始删除
System.out.println("删除了: " + id);
}
}
其实,在刚才引入jdbc启动器的时候,SpringBoot已经自动帮我们引入了一个连接池:
HikariCP应该是目前速度最快的连接池了,我们看看它与c3p0的对比:
因此,我们只需要指定连接池参数即可:
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/mydb01
username: root
password: root
统一异常的处理器
为了使我们的代码更容易维护,我们创建一个类集中处理异常
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
@ControllerAdvice //在controller中抛出的异常都能被该类统一解析
public class BaseExceptionHandler {
//注意:@ControllerAdvice注解,全局捕获异常类,所有的异常都会被捕获。
@ExceptionHandler(value = Exception.class)
@ResponseBody//返回json数据
public Result error(Exception e) {
e.printStackTrace();
return new Result(false, StatusCode.ERROR, e.getMessage());
}
}
import com.baomidou.mybatisplus.extension.api.R;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.web.ErrorProperties;
import org.springframework.boot.autoconfigure.web.ResourceProperties;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController;
import org.springframework.boot.autoconfigure.web.servlet.error.ErrorViewResolver;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.Servlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/* 自定义异常处理 下面有两种方式自定义异常处理
对于应用级别的业务异常处理,我们可以通过注解 @ControllerAdvice 或 @RestControllerAdvice
来实现异常处理。但是上面的注解只能捕获处理应用级别的异常,
例如 Controller 中抛出自定义的异常。却不能处理容器级别的异常,例如 Filter 中抛出的异常等。
要想处理容器级别的异常,需要继承 BasicErrorController 类,重写 errorHtml 和 error 方法。或者实现 ErrorController 接口,
起到和类 BasicErrorController 相似的作用。
*/
/* (一) 处理应用级别异常
@RestControllerAdvice 与 @ControllerAdvice +@ResponseBody 起到的作用是一样的
@ExceptionHandler 里面的 value 的类是我们捕获的处理异常, 此类异常会被defaultErrorHandler 方法处理.
@ResponseStatus(HttpStatus.BAD_REQUEST) 注解, 指定了此异常返回的状态码, 因为是缺少参数, 所以返回 400 的状态码
R 类为一个 ResultJSON 类, 把内容封装起来, 一般有 code, meg , data 三个属性. 返回一个 JSON 字符串
下面是返回 JSON 格式的处理类*/
@RestControllerAdvice
public class ExpectedException {
private static final long serialVersionUID = 1L;
//@ExceptionHandler 里面的 value 的类是我们捕获的处理异常, 此类异常会被defaultErrorHandler 方法处理.
@ExceptionHandler(value = MissingServletRequestParameterException.class)
//@ResponseStatus(HttpStatus.BAD_REQUEST) 注解, 指定了此异常返回的状态码, 因为是缺少参数, 所以返回 400 的状态码
@ResponseStatus(HttpStatus.BAD_REQUEST)
public R defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
// R 类为一个 ResultJSON 类, 把内容封装起来, 一般有 code, meg , data 三个属性. 返回一个 JSON 字符串
return new R<>(null, "缺少参数");
}
}
/* (二) 处理容器级别的异常
@ControllerAdvice 注解的异常处理类并不能处理容器级别的异常, 我们可以通过继承 BasicErrorController 类重写 errorHtml 或 error 方法,
以达到我们想要的返回结果。
还可以通过实现 ErrorController 接口的方法来实现自定义异常处理,BasicErrorController 类也是实现了 ErrorController 接口,
因此这种方式和 BasicErrorController 类有相似作用。实现起来相对麻烦,本文只讲解继承 BasicErrorController 类的方式。 */
@Controller
public class CustomizeErrorController extends BasicErrorController {
public CustomizeErrorController(ErrorAttributes errorAttributes, ErrorProperties errorProperties, List<ErrorViewResolver> errorViewResolvers){
super(errorAttributes, errorProperties, errorViewResolvers);
}
@RequestMapping(produces = {"text/html"})
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
HttpStatus status = this.getStatus(request);
Map<String, Object> model = Collections.unmodifiableMap(this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.TEXT_HTML)));
response.setStatus(status.value());
ModelAndView modelAndView = this.resolveErrorView(request, response, status, model);
return modelAndView == null ? new ModelAndView("error", model) : modelAndView;
}
@RequestMapping
@ResponseBody
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
Map<String, Object> body = this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.ALL));
HttpStatus status = this.getStatus(request);
//自定义一个类封装数据
ApiResponseBody responseBody = new ApiResponseBody((int)body.get("status"), (String) body.get("error") + ": " + (String) body.get("message"));
return new ResponseEntity(responseBody, status);
}
}
/* 该类将会覆盖 BasicErrorController 类起到处理异常的作用。但这里要注意,如果想要保留对SpringBoot默认的对浏览器请求的异常处理
(也就是根据异常错误状态码返回 error 文件夹下对应的错误页面),
还需新建一个配置文件 CustomErrorConfiguration*/
@Configuration
@ConditionalOnWebApplication
@ConditionalOnClass({Servlet.class, DispatcherServlet.class})
@AutoConfigureBefore({WebMvcAutoConfiguration.class})
@EnableConfigurationProperties({ResourceProperties.class})
public class CustomErrorConfiguration {
private final ServerProperties serverProperties;
private final List<ErrorViewResolver> errorViewResolvers;
public CustomErrorConfiguration(ServerProperties serverProperties,ObjectProvider <List<ErrorViewResolver>> errorViewResolversProvider) {
this.serverProperties = serverProperties;
this.errorViewResolvers = (List)errorViewResolversProvider.getIfAvailable();
}
@Bean
public CustomizeErrorController customizeErrorController(ErrorAttributes errorAttributes){
return new CustomizeErrorController(errorAttributes, this.serverProperties.getError(),errorViewResolvers);
}
}
将Spring Boot项目使用maven指令打成jar包并运行测试
分析:
package
;jdk环境即可 + springboot自带的tomcat即可
步骤
添加打包组件
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
打包 执行命令
mvn clean package
SpringBoot 项目部署运行
java -jar xxxx.jar
然后浏览器访问 http://ip:port/index 即可
将springboot项目以war的方式发布和部署
步骤
war
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-tomcatartifactId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-tomcatartifactId>
<scope>providedscope>
dependency>
注意:把tomcat排除以后还要依赖,请注意这个时候的作用域是 provided
,也就是告诉springboot在编译和打包的时候会依赖一下容器中提供的servlet和jsp环境,但是在最终的war中会自动排除在外。否则会出现打包失败的情况,因为没有servlet容器环境。
@SpringBootApplication
@MapperScan("com.itbooking.mapper")
public class ItbookingAppApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(ItbookingAppApplication.class, args);
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(ItbookingAppApplication.class);
}
}
<build>
<finalName>ROOTfinalName>
build>
ROOT是tomcat的默认工程名,也是唯一一个不需要加工程访问的目录,所以我们打包的时候用finalName指定的名字打包就直接生成的WAR包就是ROOT.war
发布工程ROOT.war
1:安装JDK1.8环境
2:安装Tomcat 把 ROOT.war部署到webapps下即可
4:启动Tomcat,bin/startup.bat
即可,会自动解压ROOT.war
5:访问http://ip:8080/index 查看效果