六:Day02_Spring Boot02

一、异常显示页面

1. 设置具体的状态码页面

在templates/下新建error文件夹,在error中新建:状态码.html的页面。例如当出现500时显示的页面为500.html。

2. 使用x进行模糊匹配

当出现5开头状态码的错误时,显示页面可以命名为5xx.html

当出现50开头状态码的错误时,显示页面可以命名为50x.html

3. 统一错误显示页面

在templates下新建error.html。如果项目中不存在具体状态码的页面或没有使用x成功匹配的页面时,显示error.html作为错误显示页面。

二、异常处理机制

常见方式如下:

  1. 在控制器类中添加一个方法,结合@ExceptionHandler。只能对当前控制器中方法出现异常有效。

  2. 新建全局异常类,通过@ControllerAdvice结合@ExceptionHandler。当全局异常处理和局部处理同时存在时,局部生效(就近原则)。

1. 通过@ExceptionHandler注解处理异常

java 复制代码
@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注解处理异常

java 复制代码
/**
 * 全局异常处理类
 */
@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对象处理异常

java 复制代码
/**
 * 全局异常 
 * 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对象处理异常

java 复制代码
/**
 * 自定义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

java 复制代码
/**
 * 文件上传控制单元方法实现
 * @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可以设置下载文件的名称。

java 复制代码
@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. 导入依赖

XML 复制代码
<dependency>
   <groupId>com.fasterxml.jackson.dataformat</groupId>
   <artifactId>jackson-dataformat-xml</artifactId>
</dependency>

2. 编写页面发送请求

html 复制代码
<!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

java 复制代码
@RequestMapping("/show")
public Map<String,Object> map(){
    Map<String,Object> map = new HashMap<>();
    map.put("id",1);
    map.put("name","张三");
    return map;
}

五、更换内置的服务器

1. 添加依赖

XML 复制代码
<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是默认加载的。

XML 复制代码
<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. 环境配置文件定义要求

  1. 必须定义在application配置文件所在目录中,即只能是 classpath、classpath/config、 root、 root/config 等四个目录之一。

  2. 配置文件命名是application-xxx。其中xxx即环境名称,如: application-dev、application-test、application-prod等。

  3. application配置文件中需要激活相应的环境配置,使用 spring.profiles.active=环境名称 来激活。或者在启动时,增加启动参数 -Dspring.profiles.active=环境名称

  4. 配置文件类型: yaml、yml、properties。

  5. 可以一次性激活若干环境配置,按照激活顺序依次读取环境配置,后读取的覆盖先读取的同名配置。不同名的配置使用保留|追加方式处理。

2. 环境配置文件使用方式

在application配置文件激活
  • 配置公共的配置

  • 激活指定的配置文件

XML 复制代码
# 选择使用的环境
spring:
  profiles:
    active: dev

application-dev.yml配置如下:

XML 复制代码
server:
  port: 81

七、SpringBoot整合拦截器

1. 新建拦截器类

java 复制代码
//不要忘记类上注解@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. 配置拦截器

java 复制代码
//类上有注解@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. 启动器配置

java 复制代码
@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包

XML 复制代码
<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中添加

XML 复制代码
menu:
    id: 1
    name: Good
    url: menu/show

2.创建类Menu

3.测试

java 复制代码
@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注解注册第三方组件。

十一、Spring Boot 启动流程和自动配置原理

相关推荐
J总裁的小芒果15 分钟前
Vue3 el-table 默认选中 传入的数组
前端·javascript·elementui·typescript
Lei_zhen9618 分钟前
记录一次electron-builder报错ENOENT: no such file or directory, rename xxxx的问题
前端·javascript·electron
咖喱鱼蛋20 分钟前
Electron一些概念理解
前端·javascript·electron
yqcoder21 分钟前
Vue3 + Vite + Electron + TS 项目构建
前端·javascript·vue.js
鑫宝Code38 分钟前
【React】React Router:深入理解前端路由的工作原理
前端·react.js·前端框架
Mr_Xuhhh2 小时前
重生之我在学环境变量
linux·运维·服务器·前端·chrome·算法
永乐春秋3 小时前
WEB攻防-通用漏洞&文件上传&js验证&mime&user.ini&语言特性
前端
鸽鸽程序猿3 小时前
【前端】CSS
前端·css
ggdpzhk3 小时前
VUE:基于MVVN的前端js框架
前端·javascript·vue.js
学不会•5 小时前
css数据不固定情况下,循环加不同背景颜色
前端·javascript·html