一、异常显示页面
1. 设置具体的状态码页面
在templates/下新建error文件夹,在error中新建:状态码.html的页面。例如当出现500时显示的页面为500.html。
2. 使用x进行模糊匹配
当出现5开头状态码的错误时,显示页面可以命名为5xx.html
当出现50开头状态码的错误时,显示页面可以命名为50x.html
3. 统一错误显示页面
在templates下新建error.html。如果项目中不存在具体状态码的页面或没有使用x成功匹配的页面时,显示error.html作为错误显示页面。
二、异常处理机制
常见方式如下:
在控制器类中添加一个方法,结合@ExceptionHandler。只能对当前控制器中方法出现异常有效。
新建全局异常类,通过@ControllerAdvice结合@ExceptionHandler。当全局异常处理和局部处理同时存在时,局部生效(就近原则)。
1. 通过@ExceptionHandler注解处理异常
@Controller
public class UsersController {
@RequestMapping("showInfo")
public String showInfo(){
String str = null;
str.length();
return "ok";
}
@ExceptionHandler(value = {java.lang.NullPointerException.class} )
public ModelAndView nullpointExcepitonHandler(Exception e){
ModelAndView mv = new ModelAndView();
mv.addObject("err",e.toString());
mv.setViewName("error1");
return mv;
}
}
2. 通过@ControllerAdvice与@ExceptionHandler注解处理异常
/**
* 全局异常处理类
*/
@ControllerAdvice
public class GlobalException {
@ExceptionHandler(value = {java.lang.NullPointerException.class} )
public ModelAndView nullpointExcepitonHandler(Exception e){
ModelAndView mv = new ModelAndView();
mv.addObject("err",e.toString());
mv.setViewName("error1");
return mv;
}
@ExceptionHandler(value = {java.lang.ArithmeticException.class} )
public ModelAndView arithmeticExceptionHandler(Exception e){
ModelAndView mv = new ModelAndView();
mv.addObject("err",e.toString());
mv.setViewName("error2");
return mv;
}
}
3. 通过SimpleMappingExceptionResolver对象处理异常
/**
* 全局异常
* SimpleMappingExceptionResolver
*/
@Configuration
public class GlobalException2 {
/**
* 此方法返回值必须是SimpleMappingExceptionResolver对象
* @return
*/
@Bean
public SimpleMappingExceptionResolver getSimpleMappingExceptionResolver(){
SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver();
Properties properties = new Properties();
/*
* 参数一:异常类型,并且是全名
* 参数二:视图名称
*/
properties.put("java.lang.NullPointerException","error3");
properties.put("java.lang.ArithmeticException","error4");
resolver.setExceptionMappings(properties);
return resolver;
}
}
4. 通过自定义HandlerExceptionResolver对象处理异常
/**
* 自定义HandlerExceptionResolver对象处理异常
* 必须要实现HandlerExceptionResolver
*/
@Component
public class GlobalException3 implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler, Exception e) {
ModelAndView mv = new ModelAndView();
//判断不同异常类型,做不同视图的跳转
if(e instanceof NullPointerException){
mv.setViewName("error5");
}
if(e instanceof ArithmeticException){
mv.setViewName("error6");
}
mv.addObject("error",e.toString());
return mv;
}
}
三、文件上传和下载
1. 文件上传
上传文件解析器已经被自动配置,直接只用MultipartFile接收上传的文件。
所以在文件上传时都会生成一个全局唯一的文件名。常见有两种方式:
(1)时间戳+随机数
(2)UUID
/**
* 文件上传控制单元方法实现
* @param photo 名字必须和表单中文件域的name属性值相同
* @return
* @throws IOException transferTo抛出的异常,可以使用try...catch处理异常。示例中为了让代码看起来简洁直接抛出了。
*/
@RequestMapping("/upload")
@ResponseBody
public String upload(MultipartFile photo, HttpServletRequest request) throws IOException {
// 判断上传文件流是否为空。如果不为空继续执行
if(!photo.isEmpty()) {
// 使用UUID生成文件名称
// String fileName = UUID.randomUUID().toString();
// 使用时间戳+随机数生成文件名
long timeMillis = System.currentTimeMillis();
Random random = new Random();
String fileName = timeMillis + "" + random.nextInt(1000);
// 获取上传时文件名
String oldName = photo.getOriginalFilename();
// 获取上传时文件的扩展名
String suffix = oldName.substring(oldName.lastIndexOf("."));
// 获取到当前项目images目录,发布到Tomcat后的绝对路径。
String realPath = request.getServletContext().getRealPath("/images");
//重要部分
photo.transferTo(new File(realPath,fileName + suffix));
return "ok";
}
return "err";
}
2. 文件下载
如果希望所有的文件都是下载,而不是能打开则打开。可以在响应头中设置Content-Disposition参数为attachment。attachment结合filename可以设置下载文件的名称。
@RequestMapping("/download")
public void download(HttpServletRequest req, HttpServletResponse response, String filename) {
try {
// filename=的值就是客户端看到的下载文件名称
String newFilename = new String(filename.getBytes("utf-8"),"iso-8859-1");
response.setHeader("Content-Disposition", "attachment;filename=" + newFilename);
File file = new File(req.getServletContext().getRealPath("/images"), filename);
FileInputStream fis = new FileInputStream(file);
ServletOutputStream os = response.getOutputStream();
//重要部分
IOUtils.copy(fis, os);
} catch (IOException e) {
e.printStackTrace();
}
}
四、内容协商
根据客户端接收能力不同,返回不同媒体类型的数据。示例:浏览器发送两次请求,访问资源相同,第一次请求要求返回json数据,第二次请求要求返回xml数据。
1. 导入依赖
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
2. 编写页面发送请求
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>数据协商访问</title>
<script src="https://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
</head>
<body>
<button onclick="getJson()">json格式</button>
<button onclick="getXml()">Xml格式</button>
<script>
function getJson(){
$.ajax({
url:"show",
type:"get",
dataType:"json",
success:function(data){
console.log(data);
}
});
}
function getXml(){
$.ajax({
url:"show",
type:"get",
dataType:"xml",
success:function(data){
console.log(data);
}
});
}
</script>
</body>
</html>
3. 编写controller
@RequestMapping("/show")
public Map<String,Object> map(){
Map<String,Object> map = new HashMap<>();
map.put("id",1);
map.put("name","张三");
return map;
}
五、更换内置的服务器
1. 添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
2. 更换服务器
根据SpringBoot的⼯作机制,⽤什么技术,加⼊什么依赖就⾏了。SpringBoot提供了3款内置的服务器:
tomcat(默认):apache出品,粉丝多,应⽤⾯⼴,负载了若⼲较重的组件
jetty:更轻量级,负载性能远不及tomcat
undertow:负载性能勉强跑赢tomcat
想⽤哪个,加个坐标就OK。前提是把tomcat排除掉,因为tomcat是默认加载的。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
</dependencies>
六、Spring Boot多环境配置文件
Spring Boot应用支持多环境配置文件,在开发、测试、上线等不同的环境中,可以激活不同的环境配置,解决切换环境时修改配置文件的麻烦。
1. 环境配置文件定义要求
必须定义在application配置文件所在目录中,即只能是 classpath、classpath/config、 root、 root/config 等四个目录之一。
配置文件命名是application-xxx。其中xxx即环境名称,如: application-dev、application-test、application-prod等。
application配置文件中需要激活相应的环境配置,使用 spring.profiles.active=环境名称 来激活。或者在启动时,增加启动参数 -Dspring.profiles.active=环境名称。
配置文件类型: yaml、yml、properties。
可以一次性激活若干环境配置,按照激活顺序依次读取环境配置,后读取的覆盖先读取的同名配置。不同名的配置使用保留|追加方式处理。
2. 环境配置文件使用方式
在application配置文件激活
-
配置公共的配置
-
激活指定的配置文件
# 选择使用的环境
spring:
profiles:
active: dev
application-dev.yml配置如下:
server:
port: 81
七、SpringBoot整合拦截器
1. 新建拦截器类
//不要忘记类上注解@Component
@Component
public class DemoInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("执行拦截器");
return false;
}
}
2. 配置拦截器
//类上有注解@Configuration。此类相当于SpringMVC配置文件。
@Configuration
public class MyConfig implements WebMvcConfigurer {
@Autowired
private DemoInterceptor demoInterceptor;
//配置拦截器的映射
@Override
public void addInterceptors(InterceptorRegistry registry) {
//addPathPattern(): 拦截哪些URL。 /* 拦截全部
//excludePathPatterns(): 不拦截哪些URL。
//当和addPathPattern()冲突时,excludePathPatterns()生效。
registry.addInterceptor(demoInterceptor)
.addPathPatterns("/*")
.excludePathPatterns("/login");
}
}
八、原生组件注入
注入Servlet的过滤器,拦截器。
1. 启动器配置
@SpringBootApplication
//扫描Servlet原生组件
@ServletComponentScan({"com.xxx.filter", "com.xxx.listener"})
public class Springboot02Application {
public static void main(String[] args) {
SpringApplication.run(Springboot02Application.class, args);
}
}
2. 自定义过滤器,拦截器
九、SpringBoot项目打jar包
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<fork>true</fork>
</configuration>
</plugin>
</plugins>
</build>
十、Spring Boot中的常见注解
1. @Configuration
通常使用在配置类(组件)上。声明该类为配置类
2. @Bean
通常用在方法上,方法的返回值交由IOC管理,IOC中的默认id为方法名.
3. @Import
通常用在配置类上,导入其它的类,被导入的类交由IOC管理,类中的方法有@Bean注解。
4. @Conditional
提交注解,符合条件才会进行。
注解 | 例如 | 解释 |
---|---|---|
@ConditionalOnBean | @ConditionalOnBean(User.class) | Spring容器中存在对应的bean生效 |
@ConditionalOnMissingBean | @ConditionalOnMissingBean(name = "user") | Spring容器中不存在对应的bean生效 |
@ConditionalOnClass | @ConditionalOnClass(Menu.class) | 类加载器可以加载到对应的类生效 |
@ConditionalOnMissingClass | @ConditionalOnMissingClass(Menu.class) | 类加载器加载不到对应的类生效 |
@ConditionalOnProperty | @ConditionalOnProperty(prefix = “spring.aop”) | 应用环境中的属性满足条件生效 |
@ConditionalOnSingleCandidate | @ConditionalOnMissingClass(Menu.class) | 表示当指定Bean在容器中只有一个,或者虽然有多个但是指定首选Bean |
@ConditionalOnWebApplication | 当前应用是Web应用生效 | |
@ConditionalOnNotWebApplication | 当前应用不是Web应用生效 |
5. @ConfigurationProperties
读取Springboot配置文件中的数据,自动注入到属性中。
1.Springboot配置文件application.yml中添加
menu:
id: 1
name: Good
url: menu/show
2.创建类Menu
3.测试
@SpringBootTest
class Springboot02ApplicationTests {
@Autowired
Menu menu;
@Test
void contextLoads() {
//Menu(id=1, name=Good, url=menu/show)
System.out.println(menu);
}
}
6. @EnableConfigurationProperties
-
我们注入的是第三方提供的类(组件)上不能添加@Component注解,通过@EnableConfigurationProperties注解注册第三方组件。