zoukankan      html  css  js  c++  java
  • springboot学习(三)整合web开发

    整合web开发

    静态资源

    静态资源默认在 resources/static 目录,Spring Boot 中默认情况下,一共有5个位置可以放静态资源,五个路径分别是如下5个:

    1. classpath:/META-INF/resources/

    2. classpath:/resources/

    3. classpath:/static/

    4. classpath:/public/

    5. /

    前四个目录分别对应了resources目录下不同的目录,第5个 / 表示 webapp 目录中的静态资源也不被拦截。

    @ControllerAdvice

    @ControllerAdvice 可以实现三个功能:

    1. 全局异常处理

    2. 全局数据绑定

    3. 全局数据预处理

    • 全局异常处理

    使用 @ControllerAdvice 实现全局异常处理,只需要定义类,添加该注解即可定义方式如下:

    @ControllerAdvice
    public class MyGlobalExceptionHandler {
       @ExceptionHandler(Exception.class)
       public ModelAndView customException(Exception e) {
           ModelAndView mv = new ModelAndView();
           mv.addObject("message", e.getMessage());
           mv.setViewName("myerror");
           return mv;
      }
    }

    @ExceptionHandler 注解用来指明异常的处理类型,即如果这里指定为 NullpointerException,则数组越界异常就不会进到这个方法中来。

    如果程序有异常抛出,且抛出的异常和@ExceptionHandler指明的异常类型相同,则@ExceptionHandler注解的方法捕获并处理这个异常。

    • 全局数据绑定

    全局数据绑定功能可以用来做一些数据初始化操作,我们可以将一些公共的数据定义在添加了 @ControllerAdvice 注解的类中,这样,在每一个 Controller 的接口中,就都能够访问导致这些数据

    //使用@ModelAttribute定义全局数据,定义完成后,在任何一个Controller 的接口中,都可以获取到这里定义的数据
    @ControllerAdvice
    public class MyGlobalExceptionHandler {
       @ModelAttribute(name = "model")
       public Map<String,Object> map(){
           HashMap<String,Object> map=new HashMap<>();
           map.put("age",48);
           map.put("gender","male");
           return map;
      }
    }
    //controller
    @RestController
    public class UserController {
       @GetMapping("/map")
       public String map(Model model){
           Map<String,Object> map=model.asMap();
           return map.toString();
      }
    }
    //接口访问结果 {model={gender=male, age=48}}
    • 全局数据预处理

    当有两个实体类的属性相同时,在接口传递这两个接口的属性,因为有相同属性会使前端无法区分, 解决步骤:

    • 在接口中给实体参数取别名

        @PostMapping("/book")
       public String addBook(@ModelAttribute("a")Author author, @ModelAttribute("b")Book book){
           return author.toString()+":"+book.toString();
      }
    • @ControllerAdvice 标记的类中进行请求数据预处理

    @ControllerAdvice
    public class MyGlobalExceptionHandler {
    //@InitBinder("b") 注解表示该方法用来处理和Book和相关的参数,在方法中,给参数添加一个 b 前缀,即请求参数要有b前缀    
       @InitBinder("a")
       public void a(WebDataBinder binder){
           binder.setFieldDefaultPrefix("a.");
      }
       @InitBinder("b")
       public void b(WebDataBinder binder){
           binder.setFieldDefaultPrefix("b.");
      }
    }

    请求接口时,使用a.xx 和b.xx来区分

    异常处理方案

    • 静态异常页面

    自定义静态异常页面,又分为两种,第一种 是使用 HTTP 响应码来命名页面,例如 404.html、405.html、500.html ....,另一种就是直接定义一个 4xx.html,表示400-499 的状态都显示这个异常页面,5xx.html 表示 500-599 的状态显示这个异常页面,默认存放在 classpath:/static/error/ 路径下

    • 动态的异常页

    动态的异常页面定义方式和静态的基本 一致,可以采用的页面模板有 jsp、freemarker、thymeleaf。动态异常页面,也支持 404.html 或者 4xx.html,默认存放在 classpath:/templates/error/路径下

    注意,动态页面模板,不需要开发者自己去定义控制器,直接定义异常页面即可 ,Spring Boot 中自带的异常处理器会自动查找到异常页面

    • 自定义异常数据

    继承 org.springframework.boot.web.servlet.error.DefaultErrorAttributes ,重写 getErrorAttributes方法,并将这个类注册为bean

    定义系统启动任务

    项目启动阶段要做一些数据初始化操作,这些操作有一个共同的特点,只在项目启动时进行,以后都不再执行,这里,容易想到web基础中的三大组件( Servlet、Filter、Listener )之一 Listener ,这种情况下,一般定义一个 ServletContextListener,然后就可以监听到项目启动和销毁,进而做出相应的数据初始化和销毁操作。Spring Boot 中针对系统启动任务提供了两种解决方案,分别是 CommandLineRunner 和 ApplicationRunner

    • CommandLineRunner

    @Component
    @Order(100)
    public class MyCommandLineRunner implements CommandLineRunner {
       @Override
       public void run(String... args) throws Exception {
           System.out.println("系统正在启动中....................");
           System.out.println(Arrays.toString(args));
      }
    }
    1. 实现CommandLineRunner接口和run方法,并注册为一个bean

    2. 添加 @Order注解,表示这个启动任务的执行优先级,因为在一个项目中,启动任务可能有多个,所以需要有一个排序。

    3. run 方法中,写启动任务的核心逻辑,当项目启动时,run方法会被自动执行。run方法的参数来自于项目的启动参数,即项目入口类中,main方法的参数会被传到这里

    4. 传入项目启动参数有两种方式:

      1. 在IDEA的启动项目处 edit configurations,设置 program arguments

      2. 使用命令 Java -jar启动项目时传入参数

    • ApplicationRunner

    ApplicationRunner 和 CommandLineRunner 功能一致,用法也基本一致,唯一的区别主要体现在对参数的处理上,ApplicationRunner 可以接收更多类型的参数(ApplicationRunner 除了可以接收 CommandLineRunner 的参数之外,还可以接收 key/value形式的参数)。

    ApplicationRunner 的run方法参数ApplicationArguments 说明:

    1. args.getNonOptionArgs();可以用来获取命令行中的无key参数(和CommandLineRunner一样)。

    2. args.getOptionNames();可以用来获取所有key/value形式的参数的key。

    3. args.getOptionValues(key));可以根据key获取key/value 形式的参数的value。

    4. args.getSourceArgs(); 则表示获取命令行中的所有参数。

    传入参数方式也和 CommandLineRunner 相同,传递键值对时使用 --key=value

    定时任务

    spring boot(spring+springMVC)实现定时任务有两种方案,一种是使用 Spring 自带的定时任务处理器 @Scheduled 注解,另一种就是使用第三方框架 Quartz

    • @Scheduled:只适合处理简单的计划任务,不能处理分布式计划任务。优势:是spring框架提供的计划任务,开发简单,执行效率比较高。且在计划任务数量太多的时候,可能出现阻塞,崩溃,延迟启动等问题。

      • 在spring boot的启动类上添加 @EnableScheduling 注解,开启定时任务

      • 使用 @Scheduled 添加一个方法上,表示一个定时任务,这个类注册为bean

      @Component
      public class MyScheduled {
         @Scheduled(fixedRate = 3000)
         public void print(){
             System.out.println("定时任务,没3s执行一次");
        }
      }

    @Scheduled的参数解析:

    1. fixedRate 表示任务执行之间的时间间隔,具体是指两次任务的开始时间间隔,即第二次任务开始时,第一次任务可能还没结束。单位ms

    2. fixedDelay 表示任务执行之间的时间间隔,具体是指本次任务结束到下次任务开始之间的时间间隔。单位ms

    3. initialDelay 表示首次任务启动的延迟时间。

    4. cron,支持cron表达式

    • Quartz:Quartz可以用来创建简单或为运行十个,百个,甚至是好几万个Jobs这样复杂的程序。

    一般在项目中,除非定时任务涉及到的业务实在是太简单,使用 @Scheduled 注解来解决定时任务,否则大部分情况可能都是使用 Quartz 来做定时任务。

    在开发Quartz相关应用时,只要定义了Job(任务),Trigger(触发器)和Scheduler(调度器),即可实现一个定时调度能力。其中Scheduler是Quartz中的核心,Scheduler负责管理Quartz应用运行时环境,Scheduler不是靠自己完成所有的工作,是根据Trigger的触发标准,调用Job中的任务执行逻辑,来完成完整的定时任务调度首先添加jar包依赖

    <dependency>
       <groupId>org.quartz-scheduler</groupId>
       <artifactId>quartz</artifactId>
       <version>2.3.1</version>
    </dependency>
    <!-- scheduled所属资源为spring-context-support,在Spring中对Quartz的支持,是集成在spring-context-support包中。org.springframework.scheduling.quartz -->
    <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-context-support</artifactId>
    </dependency>
    <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-tx</artifactId>
    </dependency>
    1. 在spring boot的启动类上添加 @EnableScheduling 注解,开启定时任务

    2. 定义JOB任务以及JOB任务调用的模拟业务对象

    @Component
    public class Hello {
       public void say(){
           System.out.println("定时任务执行......");
      }
    }
    //job任务
    public class MyJob extends QuartzJobBean {
       @Autowired
       private Hello hello;
       @Override
       protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
           System.out.println("定时任务"+new Date());
           hello.say();
      }
    }
    1. 创建Trigger以及JobDetail对象,并用Schedule配置定时任务

    @Configuration
    public class QuartzConfig {
       //创建JobDetail
       @Bean
       public JobDetailFactoryBean initJobDetailFactoryBean(){
           JobDetailFactoryBean factoryBean=new JobDetailFactoryBean();
           //设置job提供类
           factoryBean.setJobClass(MyJob.class);
           return factoryBean;
      }
       //创建Trigger,可用SimpleTrigger 或 CronTrigger
       @Bean
       public SimpleTriggerFactoryBean initSimpleTriggerFactoryBean(){
           SimpleTriggerFactoryBean factoryBean=new SimpleTriggerFactoryBean();
           factoryBean.setRepeatInterval(3000);
           factoryBean.setJobDetail(initJobDetailFactoryBean().getObject());
           return factoryBean;
      }
       //创建Schedule
       @Bean
       public SchedulerFactoryBean initSchedulerFactoryBean(MyJobFactory myJobFactory){
           SchedulerFactoryBean factoryBean=new SchedulerFactoryBean();
           factoryBean.setTriggers(initSimpleTriggerFactoryBean().getObject());
    //为Scheduler设置JobDetail的工厂。可以覆盖掉SpringBoot提供的默认工厂,保证JobDetail中的自动装配有效,否则job类的自动装配失效,会报空指针异常
           factoryBean.setJobFactory(myJobFactory);
           return factoryBean;
      }
    }
    1. 重写JobFactory

    @Component
    public class MyJobFactory extends AdaptableJobFactory {
       /**
        * AutowireCapableBeanFactory : 简单理解为Spring容器,是Spring容器Context的一个Bean对象管理工程。
        * 可以实现自动装配逻辑,和对象创建逻辑。
        * 是SpringIoC容器的一个重要组成部件。
        */
       @Autowired
       private AutowireCapableBeanFactory autowireCapableBeanFactory;
       @Override
       protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
           // 通过父类型中的方法,创建JobDetail对象。
           Object obj = super.createJobInstance(bundle);
           // 将JobDetail对象加入到Spring容器中,让Spring容器管理,并实现自动装配逻辑。
           this.autowireCapableBeanFactory.autowireBean(obj);
           return obj;
      }
    }

    Swagger

    自动生成接口文档,地址为 http://localhost:8080/swagger-ui.html,首先添加swagger2的依赖

    <dependency>
       <groupId>io.springfox</groupId>
       <artifactId>springfox-swagger-ui</artifactId>
       <version>2.9.2</version>
    </dependency>
    <dependency>
       <groupId>io.springfox</groupId>
       <artifactId>springfox-swagger2</artifactId>
       <version>2.9.2</version>
    </dependency>
    • Swagger2配置

    Swagger2的配置也是比较容易的,只需要开发者自己提供一个Docket的Bean即可,@EnableSwagger2注解启用Swagger2

    @Configuration
    @EnableSwagger2
    public class SwaggerConfig {
       @Bean
       public Docket createRestApi(){
           return new Docket(DocumentationType.SWAGGER_2)
                  .pathMapping("/")
                  .select()
                  .apis(RequestHandlerSelectors.basePackage("com.hjy.controller"))
                  .build().apiInfo(new ApiInfoBuilder()
                          .title("SpringBoot整合Swagger")
                          .description("SpringBoot整合Swagger,详细信息......")
                          .version("9.0")
                          .contact(new Contact("名字123","blog.csdn.net","aaa@gmail.com"))
                          .license("The Apache License")
                          .licenseUrl("http://www.baidu.com")
                          .build());
      }
    }
    • 创建controller和实体类

    @RestController
    @Api(tags = "book相关接口")
    @RequestMapping("/book")
    public class BookController {

       @PostMapping("/")
       @ApiOperation("添加书")
       public Book addBook(@RequestBody Book book){
           return book;
      }

       @GetMapping("/{id}")
       @ApiOperation("根据id查询书")
       @ApiImplicitParam(name = "id",value = "book.id",defaultValue = "12",required = true)
       public Book getBook(@PathVariable("id") String id){
           Book book=new Book();
           book.setId(id);
           book.setName("unsigned");
           return book;
      }
    }

    注解解析:

    1. @Api注解用来标记当前Controller的功能。

    2. @ApiOperation注解用来标记一个方法的作用。

    3. @ApiImplicitParam注解用来描述一个参数,可以配置参数的中文含义,也可以给参数设置默认值,这样在接口测试的时候可以避免手动输入。

    4. 如果有多个参数,则需要使用多个@ApiImplicitParam注解来描述,多个@ApiImplicitParam注解需要放在一个@ApiImplicitParams注解中。

    5. 需要注意的是,@ApiImplicitParam注解中虽然可以指定参数是必填的,但是却不能代替@RequestParam(required = true),前者的必填只是在Swagger2框架内必填,抛弃了Swagger2,这个限制就没用了,所以假如开发者需要指定一个参数必填,@RequestParam(required = true)注解还是不能省略。

    6. 如果参数是一个对象,对于参数的描述也可以放在实体类中。例如下面一段代码:

    @Data
    @Component
    @ApiModel
    public class Book {
       @ApiModelProperty(value = "book.id")
       private String id;
       @ApiModelProperty(value = "book.name")
       private String name;
    }
    • 在Security中的配置

    如果我们的Spring Boot项目中集成了Spring Security,那么如果不做额外配置,Swagger2文档可能会被拦截,此时只需要在Spring Security的配置类中重写configure方法,添加如下过滤即可:

    @Override
    public void configure(WebSecurity web) throws Exception{
    web.ignoring().
       antMatchers("/swagger-ui.html").
       antMatchers("/v2/**").
       antMatchers("/swagger-resources/**");
    }

     

  • 相关阅读:
    有道
    excel 数据入库
    iso-8859-1 Unicode 编码
    爬虫编码问题
    WIKi 百科爬虫
    降低耦合性获取微博数据
    Python基础总结3-字符串
    Python基础总结2
    Linux常用命令04(其他命令)
    Linux常用命令03(系统信息)
  • 原文地址:https://www.cnblogs.com/yjh1995/p/14164425.html
Copyright © 2011-2022 走看看