玩转springboot

973 阅读7分钟

前言

  • 【音乐博客】上线啦!
  • 因为音乐博客中的音乐为主题的后端是使用springboot使用minio作为文件服务器做的
  • 所以会持续更新关于springboot的一些坑及知识


六个方面

1. SpringBoot @Autowired用在static上

  • 场景
  • @Autowired
    public Minio minioTmp;

  • Minio被我定义为全局类,所以这里直接引入
  • 这样子写是可以获取到minioTmp的值的,但是在前面加上static就拿不到minioTmp里面的值了为null,如下:
  • @Autowired
    public static Minio minioTmp;

  • 但是我因为某种原因,一定要将Minio类定义为static,直接使用中,Spring是无法注入这个类的,奇怪的是Spring不把这种使用方法视为错误,没有任何的提示
  • 分析问题为什么加在class上为static则为null?
  • 在java中要使用到一个类的时候,是由类加载器来加载这个类的
  • 在类加载器加载这个类的时候,类加载器就会分析这个类中的静态成员然后创建并初始化
  • 但是在类加载器加载类中静态值的时候,仍未加载Spring上下文,所以会导致类加载器无法正确地将bean注入到静态变量中,所以会导致这个静态值被自动初始化为null,所以如果直接调用这个类可能会发生空指针异常
  • 下面看看springboot是如何解决的
  • 利用Spring中的@PostConstruct注解
  • @PostConstruct注解可以在依赖关系注入完毕之后立即执行,可以用这个来对类进行初始化
  • 用这个注解来注解一个初始化函数就可以达到为静态类加载填充内容的效果
  • 例子:
  • @Component
    public class MinioUtils{   private static Minio minio;
    
       @Autowired
       public Minio minioTmp;
       @PostConstruct
       private void init() {
          minio = this.minioTmp;   }
    
    }

  • 这样初始化后,minio类便可以获取到值了。
  • 注意这个init函数是不能带有参数的,这个方法的内容会在填充完成后自动由Spring调用

2. 整合Mybatis-Plus

  • pom文件引入
  • <!--        Mybatis-plus-->
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>3.3.1.tmp</version>
            </dependency>
    <!--        mybatis-plus  generator-->
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-generator</artifactId>
                <version>3.3.1.tmp</version>
            </dependency>
            <dependency>
                <groupId>org.apache.velocity</groupId>
                <artifactId>velocity-engine-core</artifactId>
                <version>2.2</version>
            </dependency>

  • application.yml配置
  • mybatis-plus:
      configuration:
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

  • 网上说yml配置文件要配很多东西,其实也可以不配置,这里只让他输出语句
  • Mybatis-Plus配置类
  • 该类主要配置mapper的扫描路径,配置mybatis的分页插件pageHelper及方言等其他。
  • 添加一个class类,随便在哪里都可以
  • package com.example.bkapi.common.entity;
    
    import com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer;
    import com.baomidou.mybatisplus.extension.MybatisMapWrapperFactory;
    import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.context.annotation.*;
    import org.springframework.transaction.annotation.EnableTransactionManagement;
    import org.springframework.transaction.annotation.Transactional;
    
    import java.util.Properties;
    
    /**
     * @program: bkapi
     * @ClassName MybatisPlusConfig
     * @description:
     * @author: zzm
     * @create: 2020-05-18 23:54
     * @Version 1.0
     **/
    @Configuration
    @MapperScan("com.example.bkapi.modules.music.dao.*")
    public class MybatisPlusConfig {
        /**
         * 分页插件
         */
        @Bean
        public PaginationInterceptor paginationInterceptor() {
            return new PaginationInterceptor();
        }
    
        /**
         * mybatis-plus返回类型map,转驼峰
         * @return
         */
        @Bean
        public ConfigurationCustomizer mybatisConfigurationCustomizer(){
            return new ConfigurationCustomizer(){
                @Override
                public void customize(org.apache.ibatis.session.Configuration configuration) {
                    configuration.setObjectWrapperFactory(new MybatisMapWrapperFactory());
                }
            };
        }
    
    }
  • 到这里就配置好mybatis-plus

3. 使用Mybatis-Plus分页

  • controller.java
  • @RequestMapping(value = "{sys.getAdminPath()}/music/singerSonger")
    @MapperScan("com.example.bkapi.modules.music.dao")
    @Controller
    public class SingerSongController {
    
    @Resource
        private ISingerSongService singerSongService;
    
        @ResponseBody
        @CrossOrigin
        @RequestMapping(value = "list")
        public Object list(@ModelAttribute("SingerSong") SingerSong singerSong, Page page){
            Map<String, List> data = new HashMap<>();
            IPage iPage = singerSongService.pageList(page, singerSong);
    
            List records = iPage.getRecords();
    		
    		}
    }

  • service层,要写两个文件,一个接口,一个实现类
  • 实现类ISingerSongServiceImpl.java
  • @Service
    public class ISingerSongServiceImpl extends ServiceImpl<ISingerSongDao, SingerSong> implements ISingerSongService {
    
        @Resource
        private ISingerSongDao  singerSongDao;
    
    
        @Override
        public IPage<SingerSong> pageList(Page<SingerSong> page, SingerSong singerSong) {
            return singerSongDao.pageList(page, singerSong);
        }
    }

  • 接口类ISingerSongService
  • @Service
    public interface ISingerSongService extends IService<SingerSong> {
        IPage<SingerSong> pageList(Page<SingerSong> page, SingerSong singerSong);
    }

  • ISingerSongDao
  • @Mapper
    @Repository
    public interface ISingerSongDao extends BaseMapper<SingerSong> {
        IPage<SingerSong> pageList(@Param("page") Page<SingerSong> page,SingerSong singerSong);
    }

  • SingerSongMapper.xml
  • <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    
    <mapper namespace="com.example.bkapi.modules.music.dao.ISingerSongDao">
    
    <select id="pageList" resultType="Map" >
            select
            <include refid="singerSongColumns"/>
            from music_singer_song a
            <include refid="singerSongJoins"></include>
            <where>
                <if test="singerSong.singerName != null and singerSong.singerName != ''">
                   and  a.singer_name LIKE  '%' || #{singerSong.singerName} || '%'
                </if>
                <if test="singerSong.singerSongName != null and singerSong.singerSongName != ''">
                   and  a.singer_song_name LIKE  '%' || #{singerSong.singerSongName} || '%'
                </if>
            </where>
        </select>
    </mapper>

  • 项目目录如下:

  • controller调用的是ISingerSongService接口,然后去实现类ISingerSongServiceImpl调用dao,最后去调用mapper.xml读数据库

4. spring boot区分生产环境和开发环境

  • 通过配置文件,设置项目的开发环境和生成环境。
  • application-dev.yml是开发环境配置文件,application-prod.yml是生产环境配置文件
  • application-dev.yml
  • server:
      port: 8627
    spring:
      datasource:
        url: jdbc:oracle:thin:@localhost:1521:orcl
        username: zzm
        password: zzm
        driver-class-name: oracle.jdbc.OracleDriver
      mvc:
        view:
          prefix: /WEB-INF/
          suffix: .jsp
      servlet:
        multipart:
          max-file-size: 100Mb
          max-request-size: 250Mb
    #mybatis-plus
    mybatis-plus:
      configuration:
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    
    sys:
      adminPath: /bk
    
    #文件服务器
    minio:
      endpoint: http://127.0.0.1:9001
      accessKey: zheng
      secretKey: zzm
      endpointIn: http://127.0.0.1:9001

  • application.prod.yml
  • server:
      port: 8082
      context-path: /demo

  • application.yml (当前为使用生产环境,如果需要使用开发环境,则将prod改为dev)
  • spring:
      profiles:
        active: prod

  • 可以同时使用开发环境和生产环境,只需将项目打成jar包,然后执行:
  • java -jar xxx.jar spring.profiles.active = prod
    java -jar xxx.jar spring.profiles.active = dev

5. springboot解决跨域问题

  • 过滤器中设置响应头(可以指定域名跨域)
  • 全局配置
  • @CrossOrigin注解
  • 三选一,具体配置:请移步:Springboot2.0解决跨域问题

6. springboot + Swagger2生成接口文档

  • pom.xml加入依赖
  • <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger2</artifactId>
        <version>2.9.2</version>
    </dependency>
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger-ui</artifactId>
        <version>2.9.2</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

  • Swagger2配置
  • Swagger2的配置也是比较容易的,在项目创建成功之后,只需要开发者自己提供一个Docket的Bean即可,如下:
  • package com.example.bkapi.common.config;
    
    import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import springfox.documentation.builders.ApiInfoBuilder;
    import springfox.documentation.builders.PathSelectors;
    import springfox.documentation.builders.RequestHandlerSelectors;
    import springfox.documentation.service.ApiInfo;
    import springfox.documentation.spi.DocumentationType;
    import springfox.documentation.spring.web.plugins.Docket;
    import springfox.documentation.swagger2.annotations.EnableSwagger2;
    import springfox.documentation.service.Contact;
    
    //swagger2的配置文件,在项目的启动类的同级文件建立
    @Configuration
    @EnableSwagger2
    //是否开启swagger,正式环境一般是需要关闭的(避免不必要的漏洞暴露!),可根据springboot的多环境配置进行设置
    @ConditionalOnProperty(name = "swagger.enable",  havingValue = "true")
    public class Swagger2 {
    
        // swagger2的配置文件,这里可以配置swagger2的一些基本的内容,比如扫描的包等等
        @Bean
        public Docket createRestApi() {
            return new Docket(DocumentationType.SWAGGER_2)
                    .apiInfo(apiInfo())
                    .select()
                    // 为当前包路径,这里路径写错,将会报错No operations defined in spec!
                    .apis(RequestHandlerSelectors.basePackage("com.example.bkapi"))
                    .paths(PathSelectors.any())
                    .build();
        }
        // 构建 api文档的详细信息函数,注意这里的注解引用的是哪个
        private ApiInfo apiInfo() {
            return new ApiInfoBuilder()
                    // 页面标题
                    .title("博客 API")
                    // 创建人信息
                    .contact(new Contact("guan",  "http://zhengzemin.cn:3000/",  "1638153584@qq.com"))
                    // 版本号
                    .version("1.0")
                    // 描述
                    .description("API 描述")
                    .build();
        }
    }
  • 在application.yml添加配置
    是否开启swagger,正式环境一般是需要关闭的(避免不必要的漏洞暴露!),可根据springboot的多环境配置进行设置
  • swagger:  
        enable: true

  • 这里提供一个配置类,首先通过@EnableSwagger2注解启用Swagger2,然后配置一个Docket Bean,这个Bean中,配置映射路径和要扫描的接口的位置,在apiInfo中,主要配置一下Swagger2文档网站的信息,例如网站的title,网站的描述,联系人的信息,使用的协议等等。
  • 运行之后发现报错了:

  • Failed to start bean 'documentationPluginsBootstrapper' 

  • 在pom.xml添加包依赖
  • <dependency>
      <groupId>com.google.guava</groupId>
      <artifactId>guava</artifactId>
      <version>20.0</version>
      <exclusions>
    	<exclusion>
    	  <groupId>com.google.guava</groupId>
    	  <artifactId>guava</artifactId>
    	</exclusion>
      </exclusions>
    </dependency>
  • 此时启动项目,输入http://localhost:8080/swagger-ui.html,能够看到如下页面,说明已经配置成功了:

  • 可能会遇到的错误
  • 打开http://localhost:8080/swagger-ui.html#/api/user出现

  • 解决:Unable to infer base url. This is common when using dynamic servlet registration or when the API is

最后总结

springboot是个好框架,我会继续将自己的踩坑之道和大家分享!


参考

SpringBoot @Autowired用在static上:www.kairlec.com/archives/42…

SpringBoot2.0整合Mybatis-Plus及分页查询:blog.csdn.net/IT_hejinron…

SpringBoot+Mybatis-Plus两种分页方法:www.cnblogs.com/tuituji27/p…

springboot中使用mybatis-plus:www.jianshu.com/p/8e63a3bae…

spring boot区分生产环境和开发环境:blog.csdn.net/weixin_3070…

Springboot2.0解决跨域问题:segmentfault.com/a/119000001…

SpringBoot整合Swagger2,再也不用维护接口文档了!:blog.csdn.net/u012702547/…

Failed to start bean 'documentationPluginsBootstrapper' :github.com/springfox/s…

Unable to infer base url. This is common when using dynamic servlet registration or when the API is:blog.csdn.net/just_now_an…