《SpringBoot+Vue》Chapter04 Spring

作者: So_ProbuING | 来源:发表于2023-12-04 15:07 被阅读0次

    返回JSON数据

    默认实现

    依赖

     <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    

    在springboot web依赖中加入了jackson-databind作为JSON处理器

    创建一个实体类对象

    public class Book {
        private String name;
        private String author;
        @JsonIgnore
        private Float price;
        @JsonFormat(pattern = "yyyy-MM-dd")
        private Date publicationDate;
    }
    

    创建BookController

    @Controller
    public class BookController {
        @GetMapping("/book")
        @ResponseBody
        public Book book() {
            Book book = new Book("bookName", "bookAuthor", 100f, new Date());
            return book;
        }
    }
    

    自定义Json转换器

    Gson

    Gson是google的一个开源JSON解析框架。使用Gson,需要加入Gson依赖,并去除默认的jackson-databind

     <dependency>
                <groupId>com.google.code.gson</groupId>
                <artifactId>gson</artifactId>
    </dependency>
    

    SpringBoot中默认提供了Gson自动转换,因此Gson的依赖添加后,就可以直接使用Gson。但是在Gson进行转换时,如果想对日期数据进行格式化。需要开发者自定义HttpMessageConverter自定义HttpMessageConverter

     @Bean
        GsonHttpMessageConverter gsonHttpMessageConverter() {
            GsonHttpMessageConverter converter = new GsonHttpMessageConverter();
            GsonBuilder gsonBuilder = new GsonBuilder();
            //设置时间格式
            gsonBuilder.setDateFormat("yyyy-MM-dd");
            //设置gson解析时遇到protected修饰的字段被过滤掉
            gsonBuilder.excludeFieldsWithModifiers(Modifier.PROTECTED);
            Gson gson = gsonBuilder.create();
            converter.setGson(gson);
            return converter;
        }
    

    fastjson

    fastjson是阿里巴巴的一个开源JSON解析框架。是目前最快的JSON解析框架

    静态资源访问

    默认策略

    SpringBoot中对于SpringMVC的自动化配置都在WebMvcAutoConfiguration类
    SpringBoot默认会过滤所有的静态资源、而静态资源的位置一共有5个:

    • classpath:/META-INF/resources
    • classpath:/resources/
    • classpath:/static/
    • classpath:/public/
    • /


      资源文件优先级

    自定义策略

    在配置文件中定义

    spring.mvc.static-path-pattern=/static/**
    spring.web.resources.static-locations=classpath:/static/
    
    • 过滤规则为/static/** 静态资源位置为classpath:/static/

    Java编码定义

    public class MyWebMvcConfig implements WebMvcConfigurer{
      @Override
      public void addResourceHandlers(ResourceHandlerRegistry registry){
      registry.addResourceHandler("/static/**")
                  .addResourceLocations("classpath:/static/")
    }
    }
    

    文件上传

    单文件上传

    首先创建一个SpringBoot项目并添加spring-boot-starter-web依赖
    在resources目录下的static目录创建一个upload.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <form action="/upload" method="post" enctype="multipart/form-data">
        <input type="file" name="uploadFile" value="选择文件">
        <input type="submit" value="上传">
    </form>
    </body>
    </html>
    

    创建文件上传接口

     @PostMapping("/upload")
        public String upload(MultipartFile uploadFile, HttpServletRequest req) {
            String realPath = req.getSession().getServletContext().getRealPath("/uploadFile");
            String format = sdf.format(new Date());
            File folder = new File(realPath + format);
            if (!folder.isDirectory()) {
                folder.mkdirs();
            }
            String oldName = uploadFile.getOriginalFilename();
            String newName = UUID.randomUUID().toString() + oldName.substring(oldName.lastIndexOf("."), oldName.length());
            try {
                uploadFile.transferTo(new File(folder, newName));
                String filePath = req.getScheme() + "://" + req.getServerName() + ":" + req.getServerPort() + "/uploadFile/" + format + newName;
                return filePath;
            } catch (IOException e) {
                e.printStackTrace();
            }
            return "上传失败";
        }
    

    @ControllerAdvice

    全局异常处理

    @ControllerAdvice最常见的使用场景就是全局异常处理

    @ControllerAdvice
    public class CustomExceptionHandler {
        @ExceptionHandler(MaxUploadSizeExceededException.class)
        public void uploadException(MaxUploadSizeExceededException maxUploadSizeExceededException,
                                    HttpServletResponse resp){
            resp.setContentType("text/html;charset=utf-8");
            PrintWriter writer = null;
            try {
                writer = resp.getWriter();
                writer.write("超出文件大小限制");
                writer.flush();
                writer.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }
    

    当系统启动时,标记@ControllerAdvice就会被扫描到Spring容器中,然后在方法上添加@ExceptionHandler注解,其中定义的MaxUploadSizeExceededException.class表明该方法用来处理MaxUploadSizeExceededExceptioon类型的异常。

    添加全局数据

    @ControllerAdvice是一个全局数据处理组件。可以在@ControllerAdvice中使用@ModelAttribute注解配置全局数据

    @ControllerAdvice
    public class GlobalConfig {
        @ModelAttribute(value = "info")
        public Map<String,String> userInfo() {
            HashMap<String, String> map = new HashMap<>();
            map.put("username", "lgz");
            map.put("gender", "男");
            return map;
        }
    }
    
    • 在全局配置中添加userInfo方法,返回map,该方法有一个注解@ModelAttribute,其中的value属性表示这条返回数据的key,而方法的返回值是返回数据的value
    • 在任意请求的Controller中,通过Model参数可以获取map数据

    请求参数预处理

    @ControllerAdvice结合@InitBinder能实现请求参数预处理,即将表单中的数据绑定到实体类上时进行一些额外处理
    例如有两个实体类Book和Author

    public class Book {
        private String name;
        private String author;
    //.....getter/setter
    }
    public class Author {
        private String name;
        private int age;
    //......getter/setter
    }
    

    Controller

    @RestController
    public class InitBinderController {
        @GetMapping("/book")
        public String book(Book book, Author author) {
            return book.toString() + ">>>" + author.toString();
        }
    }
    

    这样在传递参数时,两个实体类中的name就会容易混淆。@ControllerAdvice结合@InitBinder可以顺利解决该问题

    @RestController
    public class InitBinderController {
        @GetMapping("/book")
        public String book(@ModelAttribute("a") Book book,
                           @ModelAttribute("b") Author author) {
            return book.toString() + ">>>" + author.toString();
        }
    }
    

    配置@ControllerAdvice

      @InitBinder("a")
        public void init(WebDataBinder binder) {
            binder.setFieldDefaultPrefix("b.");
        }
    
        @InitBinder("b")
        public void init2(WebDataBinder binder) {
            binder.setFieldDefaultPrefix("a.");
        }
    
    • @InitBinder("b")表示该方法是处理@ModelAttribute("b")对应的参数的,第二个@InitBinder("a")表示该方法是处理@ModelAttribute("a")对应的参数的

    然后在浏览器中访问:http://localhost:8080/book?b.name=bname&b.author=bauthor&a.name=aname&a.age=aAge即可成功地区分出name属性

    • WebDataBinder对象中,还可以设置允许的字段、禁止的字段、必填字段以及验证器等

    自定义错误页

    @ControllerAdvice可以处理应用级别的异常,有一些容器级别的错误就处理不了。
    在SpringBoot中,默认情况下,404、500等会有统一的处理页面。
    SpringBoot在返回错误信息时不一定返回HTML页面,而是根据实际情况返回HTML页面或者一段JSON
    SpringBoot中的错误默认是由BasicErrorContoller类来处理的

    @RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
        public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
            HttpStatus status = getStatus(request);
            Map<String, Object> model = Collections
                .unmodifiableMap(getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.TEXT_HTML)));
            response.setStatus(status.value());
            ModelAndView modelAndView = resolveErrorView(request, response, status, model);
            return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);
        }
    
        @RequestMapping
        public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
            HttpStatus status = getStatus(request);
            if (status == HttpStatus.NO_CONTENT) {
                return new ResponseEntity<>(status);
            }
            Map<String, Object> body = getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.ALL));
            return new ResponseEntity<>(body, status);
        }
    

    errorHtml方法用来返回错误HTML页面,error用来返回错误JSON
    DefaultErrorViewResolver是SpringBoot中默认的错误信息视图解析器,SpringBooot默认是在error目录下查找4xx、5xx的文件作为错误视图,找不到时会回到errorHtml中,然后使用error作为默认的错误页面视图名,如果error的视图也找不到,用户就会看到默认的错误提示页面

    简单配置

    如果想自定义错误页面,只需要提供4xx、5xx的页面即可。
    如果不需要向用户展示详细的错误信息,可以把错误信息定义成静态页面,直接在resources/static目录下创建error目录,然后在error目录中创建错误展示页面,错误展示页面的命名规则有:

    • 4xx.html、5xx.html
    • 404.html、405.html、500.html
      此时当用户访问一个不存在的网站时,就会展示404.html中的内容
    • 动态页面
      如果采用视图模板技术,先添加依赖,然后在templates目录下创建error目录,然后创建错误展示页


      error目录

    CORS支持

    CORS:是一种跨域资源共享技术标准,目的就是为了解决前端的跨域请求。CORS支持多种HTTP请求
    定义一个Controller

    @RestController
    @RequestMapping("/book")
    public class BookController {
        @PostMapping("/")
        public String addBook(String bookName) {
            return "receive" + bookName;
        }
    
        @DeleteMapping("{id}")
        public String delBook(@PathVariable("id") Long id) {
            return String.valueOf("id");
        }
    }
    

    配置跨域

    跨域有两个配置地方,

    • 一个是直接在相应的请求方法上加注解
    @RestController
    @RequestMapping("/book")
    public class BookController {
        @PostMapping("/")
        @CrossOrigin(value = "http://localhost:8081",maxAge = 1800,allowedHeaders = "*")
        public String addBook(String bookName) {
            return "receive" + bookName;
        }
    
        @DeleteMapping("{id}")
        @CrossOrigin(value = "http://localhost:8081",maxAge = 1800,allowedHeaders = "*")
        public String delBook(@PathVariable("id") Long id) {
            return String.valueOf("id");
        }
    }
    
    • @CrossOrigin中的value表示支持的域,这段代码中表示来自http://localhost:8081域的请求是支持跨域
    • maxAge表示探测请求的有效期 单位是秒
    • allowedHeaders表示允许的请求头,*表示所有请求头都被允许

    使用全局配置 创建配置类Config

    @Configuration
    public class MyWebMvcConfig implements WebMvcConfigurer {
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/book/**")
                    .allowedHeaders("*")
                    .allowedMethods("*")
                    .maxAge(1800)
                    .allowedOrigins("http://localhost:8081");
        }
    }
    

    配置类与XML配置

    SpringBoot的配置类需要添加@Configuration注解,@ComponentScan注解会扫描所有的Spring组件。也包括@Configuration

    注册拦截器

    创建拦截器实现HanlderInterceptor接口

    public class MyInterceptor implements HandlerInterceptor {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            System.out.println("preHandler>>>>");
            return true;
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            System.out.println("postHandler>>>>");
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            System.out.println("afterCompletion>>>>");
        }
    }
    

    拦截器中的方法将按照

    preHandle->Controller->postHandle->afterCompletion
    
    • 只有preHandle方法返回true时后面的方法才会执行,
    • 当拦截器内存在多个拦截器时postHandler在拦截器内的所有拦截器返回成功时才会调用
    • afterCompletion只有preHandler返回true才调用
    • 若拦截器内的第一个拦截器的preHandle返回false,则后面的方法都不执行
      配置拦截器
    @Configuration
    public class MyWebMvcConfig implements WebMvcConfigurer {
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/book/**")
                    .allowedHeaders("*")
                    .allowedMethods("*")
                    .maxAge(1800)
                    .allowedOrigins("http://localhost:8081");
        }
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(new MyInterceptor())
                    .addPathPatterns("/**")
                    .excludePathPatterns("/hello");
        }
    }
    

    启动系统任务

    有一些特殊的任务需要在系统启动时执行,配置文件加载,数据库初始化等操作。SpringBoot对此提供了两种解决方案 CommandLineRunner 和 ApplicationRunner

    CommandLineRunner

    SpringBoot项目在启动时会遍历所有CommandLineRunner的实现类并调用其中的run方法,如果有多个实现类,那么可以使用@Order注解对这些实现类的调用顺序进行排序

    @Component
    @Order(1)
    public class MyCommandLineRunner implements CommandLineRunner {
        @Override
        public void run(String... args) throws Exception {
            System.out.println("first runner"+ Arrays.toString(args));
        }
    }
    @Component
    @Order(2)
    public class MyCommandLineRunner2 implements CommandLineRunner {
        @Override
        public void run(String... args) throws Exception {
            System.out.println("second runner"+ Arrays.toString(args));
        }
    }
    
    
    • @Order(1)注解用来描述CommandLineRunner的执行顺序,数字越小越先执行
    • run方法中是调用的核心逻辑,参数是系统启动时传入的参数

    ApplicationRunner

    ApplicationRunner和CommandLineRunner基本一致,区别主要体现在run方法的参数上

    整合Servlet、Filter和Listener

    • @WebServlet标记HttpServlet
    • @WebListener标记Listener
    • @WebFilter标记Filter
    • 在项目入口类上添加@ServletComponentScan注解,实现对Servlet、Filter以及Listener的扫描

    路径映射

    在使用页面模板后,用户需要通过Controller才能访问页面,一些页面需要在控制器中加载数据,然后渲染,才能显示出来。还有一些页面在控制器中不需要加载数据,只是完成简单的跳转。对于这种页面,可以直接配置路径映射,提高访问速度

     @Override
        public void addViewControllers(ViewControllerRegistry registry) {
            registry.addViewController("/login").setViewName("login");
            registry.addViewController("/index").setViewName("index");
        }
    

    配置AOP

    AOP中的概念

    AOP中的概念

    SpringBoot支持

    SpringBoot在Spring的基础上对AOP的配置提供了自动化配置解决方案:spring-boot-starter-aop,使开发者能够更加便捷地在SpringBoot项目中使用AOP

    • 添加AOP依赖
     <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-aop</artifactId>
            </dependency>
    
    • 创建一个测试的业务Service类
    @Service
    public class UserService {
        public String getUserById(Integer id) {
    
            System.out.println("getUserById = " + id);
            return "user";
        }
        public void deleteUserById(Integer id) {
            System.out.println("deleteUserById"+ id);
        }
    }
    
    • 创建切面
    @Component
    @Aspect
    public class LogAspect {
    
        @Pointcut("execution(* com.probuing.springbootcros.service.*.* (..))")
        public void pc1() {
    
        }
    
        @Before(value = "pc1()")
        public void before(JoinPoint jp) {
            String name = jp.getSignature().getName();
            System.out.println(name + "方法开始执行");
        }
    
        @After(value = "pc1()")
        public void after(JoinPoint jp) {
            String name = jp.getSignature().getName();
            System.out.println(name + "方法执行结束");
        }
    
        @AfterReturning(value = "pc1()", returning = "result")
        public void afterRunning(JoinPoint jp, Object result) {
            String name = jp.getSignature().getName();
            System.out.println(name + "方法返回值为" + result);
        }
    
    
        @AfterThrowing(value = "pc1()", throwing = "e")
        public void afterThrowing(JoinPoint jp, Exception e) {
            String name = jp.getSignature().getName();
            System.out.println(name + "方法异常了" + e.getMessage());
        }
    
    
        @Around(value = "pc1()")
        public Object around(ProceedingJoinPoint pjp) throws Throwable {
            return pjp.proceed();
        }
    
    }
    
    • @Aspect表明这是一个切面类
    • @Pointcut 是切入点的定义,execution中的第一个表示方法返回任意值,第二个表示方法返回任意值,第二个* 表示service包下的任意类,第三个*表示类中的任意方法。括号中的两个点表示方法参数任意。这里的切点表达式表示切入点为service包下所有类中的所有方法
    • @Before表示这是一个前置哦通知,该方法在目标方法执行之前执行,通过JoinPoint参数可以获取目标方法的方法名,修饰符
    • @After表示这是一个后置通知,该方法在目标方法执行之后执行
    • @AfterReturning注解,表示这是一个返回通知,在该方法中可以获取目标方法的返回值。@AfterReturning注解的returning参数是指返回值的变量名,对应方法的参数。
    • @AfterThrowing,表示这是一个异常通知,即当目标方法发生异常时,该方法会被调用,异常类型为Exception表示所有的异常都会进入该方法中执行
    • @Around,表示这是一个环绕通知,环绕通知是所有通知里最强大的通知,可以实现前置通知、后置通知、异常通知以及返回通知的功能 目标方法进入环绕通知后,通过调用ProceedingJoinPoint对象的proceed方法使目标方法继续执行,开发者可以在此修改目标方法的执行参数、返回值等,并且可以在此处理目标方法的异常

    其他设置

    自定义欢迎页

    SpringBoot项目在启动后,首先会去静态资源路径下查找index.html作为首页文件,若查找不到,则会去查找动态的index文件作为首页
    如果使用静态的index.html作为项目首页,那么只需要在resources/static目录下创建index.html。如果使用动态页面作为项目首页则需要在resources/templates目录下创建index.html,然后在Controller中返回逻辑视图名。

    自定义favicon

    favicon.ico是浏览器选项卡左上角的图标,可以放在静态资源路径下或者类的路径下,静态资源路径下的favicon.ico优先级高于favicon.co
    将图标文件复制到/resources/static 目录下

    相关文章

      网友评论

        本文标题:《SpringBoot+Vue》Chapter04 Spring

        本文链接:https://www.haomeiwen.com/subject/vmilgdtx.html