SpringBoot的一般使用流程及常见问题记录

1. 搭建流程

1.引入Spring-Boot依赖、Spring-Boot整合MyBatis的插件依赖、MyBatis由数据库表逆向生成JavaBean和Mapper文件的mybatis-generator插件等:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.glodon.tot.library</groupId>
    <artifactId>mybatis-tot-library</artifactId>
    <version>1.0-SNAPSHOT</version>

    <!--SpringBoot的基本父级依赖-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.4.5.RELEASE</version>
    </parent>

    <!--全局属性模板配置-->
    <properties>
        <java.version>1.8</java.version>
        <mybatis.version>3.4.0</mybatis.version>
        <mybatis.generator.version>1.3.2</mybatis.generator.version>
        <mybatis.springboot.version>1.1.1</mybatis.springboot.version>
    </properties>

    <dependencies>
        <!--springboot对web项目的支持-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>${mybatis.springboot.version}</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
    </dependencies>


    <build>
        <plugins>
            <!--SpringBoot整合Maven的依赖-->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <!--Maven的逆向工程插件-->
            <plugin>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-maven-plugin</artifactId>
                <version>1.3.2</version>
                <executions>
                    <execution>
                        <id>Generate MyBatis Artifacts</id>
                        <goals>
                            <goal>generate</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <verbose>true</verbose>
                    <overwrite>true</overwrite>
                </configuration>
                <dependencies>
                    <dependency>
                        <groupId>mysql</groupId>
                        <artifactId>mysql-connector-java</artifactId>
                        <version>${mysql.version}</version>
                    </dependency>
                    <dependency>
                        <groupId>org.mybatis.generator</groupId>
                        <artifactId>mybatis-generator-core</artifactId>
                        <version>${mybatis.generator.version}</version>
                    </dependency>
                    <dependency>
                        <groupId>org.mybatis</groupId>
                        <artifactId>mybatis</artifactId>
                        <version>${mybatis.version}</version>
                    </dependency>
                </dependencies>
            </plugin>
        </plugins>
    </build>

</project>

2.resources目录下配置SpringBoot的配置文件和逆向工程插件mybatis-generator的配置文件,主要如下:

[application.yml]

spring:
  application:
    name: mybatis-spring-demo-tot
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/glodon_test
    username: root
    password: root

[generatorConfig.xml]

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>
    <!--导入所需的jar,我们的jar都在Module的路径下,这一句可以省去
    <classPathEntry location="/Program Files/IBM/SQLLIB/java/db2java.zip" />
    -->

    <!--这里的targetRuntime是指定生成SQL映射文件的类型:
        MyBatis3:可以生成动态增删改查的映射文件;
        MyBatis3Simple:可以生成简单带有增删改查的Mapper的SQL标签
        其他值看文档
    -->
    <context id="DB2Tables" targetRuntime="MyBatis3Simple">
        <!--jdbcConnection指定如何连到数据库,就是配置数据库连接-->
        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/glodon_test"
                        userId="root"
                        password="root">
        </jdbcConnection>

        <!--Java类型解析器,一般使用默认-->
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false" />
        </javaTypeResolver>

        <!--指定JavaBean的生成策略
            targetPackage:指定生成JavaBean的目标包名
            targetProject:指定目标工程
        -->
        <javaModelGenerator targetPackage="com.glodon.tot.models" targetProject="src/main/java">
            <property name="enableSubPackages" value="true" />
            <property name="trimStrings" value="true" />
        </javaModelGenerator>

        <!--SQL映射文件的生成策略,即mapper文件
            targetPackage:目标包,不需要手动创建
            targetProject:目标工程
            targetPackage包名必须和下面javaClientGenerator标签中targetPackage一致
        -->
        <sqlMapGenerator targetPackage="com.glodon.tot.mappers"  targetProject="src/main/resources">
            <property name="enableSubPackages" value="true" />
        </sqlMapGenerator>

        <!--指定接口生成策略,即Dao层-->
        <javaClientGenerator type="XMLMAPPER" targetPackage="com.glodon.tot.mappers"  targetProject="src/main/java">
            <property name="enableSubPackages" value="true"/>
        </javaClientGenerator>


        <!--指定逆向分析哪些表,创建JavaBean
            domainObjectName:指定对应表的JavaBean的类名
        -->
        <table tableName="books" domainObjectName="Books"></table>
        <table tableName="buser" domainObjectName="Buser"></table>
        <table tableName="bookrecords" domainObjectName="BookRecords"></table>

    </context>
</generatorConfiguration>

上面是我之前的基本配置(如果mybatis的maven插件报红重启IDEA即可),然后通过侧边菜单栏Maven Projects中Plugins中的mybatis-generator插件运行即可生成数据库中表的mapper接口和接口对应的映射文件和javaBean对象,的下面有一个差不多的配置文件,也可参照:
[generatorConfig.xml]

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>

    <context id="DB2Tables" defaultModelType="flat" targetRuntime="MyBatis3Simple">
        <commentGenerator>
            <!-- 抑制警告 -->
            <property name="suppressTypeWarnings" value="true"/>
            <property name="suppressDate" value="true"/>
            <property name="suppressAllComments" value="false"/>
            <property name="javaFileEncoding" value="UTF-8"/>
        </commentGenerator>
        <!--数据库链接地址账号密码-->
        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
                        connectionURL="jdbc:mysql://10.1.83.41:3306/demo?useUnicode=true&amp;characterEncoding=utf8"
                        userId="root" password="root">
        </jdbcConnection>
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false"/>
        </javaTypeResolver>
        <!--生成Model类存放位置-->
        <javaModelGenerator targetPackage="com.glodon.demo.mybatis.models" targetProject="src/main/java">
            <property name="enableSubPackages" value="true"/>
            <property name="trimStrings" value="true"/>
            <!--<property name="constructorBased" value="true"/>-->
        </javaModelGenerator>
        <!--生成映射文件存放位置-->
        <sqlMapGenerator targetPackage="com.glodon.demo.mybatis.mappers" targetProject="src/main/resources">
            <property name="enableSubPackages" value="true"/>
        </sqlMapGenerator>
        <!--生成Dao类存放位置-->
        <!-- 客户端代码,生成易于使用的针对Model对象和XML配置文件 的代码
                type="ANNOTATEDMAPPER",生成Java Model 和基于注解的Mapper对象
                type="MIXEDMAPPER",生成基于注解的Java Model 和相应的Mapper对象
                type="XMLMAPPER",生成SQLMap XML文件和独立的Mapper接口
        -->
        <javaClientGenerator type="XMLMAPPER" targetPackage="com.glodon.demo.mybatis.mappers" targetProject="src/main/java">
            <property name="enableSubPackages" value="true"/>
        </javaClientGenerator>
        <table tableName="user" domainObjectName="User"
               enableCountByExample="true"
               enableUpdateByExample="true"
               enableDeleteByExample="true"
               enableSelectByExample="true"
               selectByExampleQueryId="true">
            <generatedKey column="id" sqlStatement="JDBC"/>
            <columnOverride column="sex" javaType="com.glodon.demo.mybatis.models.Sex" />
        </table>
    </context>
</generatorConfiguration>

3.创建SpringBoot的入口类,该类会自动扫描它所在目录及子级目录,所以注意一下它的位置。

@SpringBootApplication
@MapperScan("com.glodon.tot.mappers") //指定mapper接口的扫描路径
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

4.利用maven插件生成对应的JavaBean和Mapper接口以及对应的mapper文件,在创建好数据的表后,直接在Maven Projects侧边菜单栏找到Plugins–>mybatis-generator–>mybatis-generator:generate运行即可生成。

5.根据业务需求进行开发,注解啥的都跟Spring和SpringMVC一样,但是Controller类上的注解由@Controller变成了@RestController,其他一样。

2. 常见问题

2.1 Springboot创建非web应用

 SpringBoot除了可以用来写API,也可以用来直接作应用,只是这个时候需要改动Spring-Boot的启动类,继承CommandLineRunner类,只是不用写Controller层了,如下:

/**
 * @author liuwg-a
 * @date 2018/12/18 20:32
 * @description
 */
@SpringBootApplication
public class Application implements CommandLineRunner {

    @Autowired
    DownloadService downloadService;

    public static void main(String[] args) {
		// 这里的args一定不能掉,否则无法获取参数
        SpringApplication.run(Application.class, args);
    }

	// 接受命令行中的参数
    @Override
    public void run(String... args) {
        if (args.length!=1) {
            throw new IllegalArgumentException("Please input only one path that you want to store!");
        }
        // 调用的时候直接调用Service层即可,即不用再写Controller层了
        downloadService.getAllPublicRfas(args);
        exit(0);
    }
}

2.2 打包Springboot项目,运行jar包提示“jar中没有主清单属性”

 引入插件即可解决,可以自动添加清单,如下:

 <plugin>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-maven-plugin</artifactId>
     <version>2.0.3.RELEASE</version>
     <executions>
         <execution>
             <goals>
                 <goal>repackage</goal>
             </goals>
         </execution>
     </executions>
 </plugin>

2.3 让springboot在控制台打印MyBatis的日志

 在springboot的配置文件application.properties(如果是yml文件具体更改格式)中写入:

# 让MyBatis打印日志
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

指定某个模块使用其他多个模块中的mapper.xml文件,在配置文件中application.properties(如果是yml文件具体更改格式)中写入:

# mybatis
mybatis.mapperLocations=classpath:com/goujianwu/portal/common/mapper/*.xml
mybatis.typeAliasesPackage=com.goujianwu.portal.common.model

# 引用多个模块的mapper文件
mybatis.mapperLocations=classpath*:mapper/*.xml

2.4 springboot时间修改时区后接口返回时间不变

 在使用VOD时,UTC转成北京时间时,由于序列化的原因时间不论怎么转,控制台打印出来的是北京时间,但PostMan使用得到的还是UTC的时间,主要原因是Spring默认的序列化框架FastJson的问题,所以建议配置文件中加入:

# 配置序列化时时区
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8

2.5 springboot中Bean的循环依赖

 在一次项目中出现这样下面的情况:

@Service("aService")
public class AServiceImpl implements AService {
	@Autowired
	private BService;
	//...
} 

@Service("bService")
public class BServiceImpl implements BService {
	@Autowired
	private AService;
	//...
} 

启动项目时出现:

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'testCallbackController': Unsatisfied dependency expressed through field 'AService'; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'AService': Bean with name 'AService' has been injected into other beans [bService] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:584)
	at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:90)
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:370)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1336)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:572)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:495)
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:317)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:315)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:759)
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:548)
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:142)
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754)
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:386)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:307)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1242)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1230)
	at com.goujianwu.task.Application.main(Application.java:23)
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'AService': Bean with name 'AService' has been injected into other beans [bService] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:602)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:495)
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:317)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:315)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
	at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:251)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1135)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1062)
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:581)

出现提示AService和BService循环依赖,项目无法启动,有点类似于死锁的场景,Spring在初始化容器时会将所有Bean对象进行初始化,在初始化AService对象,发现需要初始化BService,但在初始化时发现又要初始化AService,Spring就有点懵逼了,这特么到底要宝宝先初始化哪个Bean啊,所以会出现上述的启动异常。

解决方案:通过将其中一个Bean设成懒加载即可,如:

@Lazy
@Service("aService")
public class AServiceImpl implements AService {
	@Autowired
	private BService bService;
	//...
} 

@Service("bService")
public class BServiceImpl implements BService {
	@Autowired
	private AService aService;
	//...
} 

但通常不会这么简单,我们发现有很多Controller用到了aServicebService,就算将上述两个Service的Bean设成懒加载了,在Spring的IOC容器初始化后实例化Controller的Bean时,发现不少Controller通过@Autowired注入了aServicebService,所以上述的方法极少会解决问题,更好的方式应该在出现循环依赖的Bean中注入另一个Bean时同时指定为懒加载,另一个不变:

@Service("aService")
public class AServiceImpl implements AService {
	@Lazy
	@Autowired
	private BService bService;
	//...
} 

@Service("bService")
public class BServiceImpl implements BService {
	@Autowired
	private AService aService;
	//...
} 

这样一来就算加载Controller时注入aServicebService,就不会有影响,比如初始化aService时,并不会去初始化bService,只有在调用aService中的某个涉及bService的具体方法时才会去初始化bService,至此问题解决(其实还是建议尽量减少代理之间的耦合度,良好的代码设计才是规避循环依赖的最好方式)。

2.6 springboot中开启定时任务支持

SpringBoot启动类上添加@EnableScheduling注解开启支持:

@SpringBootApplication
@EnableScheduling
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}

使用的时候只需要在Bean中的方法上加上注解@Scheduled注解即可:

@Scheduled(cron = "0/1 0 20 * * *")
protected void run() {
	...
}

该注解支持多种参数,上面是仅是利用的cron表达式cron = x x x x x x x,个人觉得在简单场景下使用较为方便,表达式中的7个x从前至后分别表示秒、分、时、日、月、星期几、年份(可选参数),下面提供cron参数的说明:

参数说明
第1位x表示秒,取值范围0-59
第2位x表示分,取值范围0-59
第3位x表示时,取值范围0-23
第4位x表示日,取值范围1-31
第5位x表示月份,取值范围1-12
第6位x表示星期几,取值范围1-7(注:从周日开始算,即表示周日-周六)
第7位x表示年份(可选),取值范围1970-2099

注:

此外,上述7位参数还支持如下取值:

  • *:可以匹配任意值,具体范围根据所处参数位置判断,可以理解为每秒,每分,每时…
  • ?:表示不确定的值,它只能出现在日和星期几中,即第4、6位参数中;
  • -:表示取值区间,包含首尾,如2-5,表示了23454个值;
  • ,:表示一个列表,可以理解为-一个意思,只是它可以取到间断不连续的值,比如2,4,6
  • /:语法为x/yx表示从x开始,y表示步进值;
  • #:表示第几个,只能出现星期几中,即第6位参数,比如1#2,表示第2周中的周日;

下面是示例:

0 0 3 * * ?     每天3点触发任务
0 5 3 * * ?     每天3点5分触发任务
0 5 3 ? * *     每天3点5分触发任务,其中日使用?表示不确定值,因为可能为28、30、31
0 5/10 3 * * ?  每天3点5分触发任务,并且期间每隔10分钟周期性执行一次,直到3点55结束当天任务,次日再次重复
0 10 3 ? * 1    每周星期日的3点10分触发任务  
0 10 3 ? * 1#3  每个月的第三周中的星期天触发任务,#号只能出现在星期几的位置上;

啰嗦一句,不要写代码写傻了,和我一样想不通@Scheduled(cron = "0/1 0 20 * * *")为什么在晚上8点触发任务后,每一秒都执行一次,为什么只能执行到20:00:59,不继续往下运行了,因为分钟位置上限制了为0,即20:01:00后就不在触发任务了,触发将其改作0-1等(其他表达式效果一样即可)。

2.7 springboot打包成jar运行找不到 resources 下文件

SpringBoot 打包成 jar 后无法读取 resources 下的文件,必须以流的方式读取,否则会抛文件未找到的异常,示例代码如下:

    public String uEditorOption() {
        // springboot打包后只能以流的方式读取jar包classes下的文件
        StringBuilder config = new StringBuilder();
        try {
            InputStream stream = this.getClass().getClassLoader().getResourceAsStream("ueditor/config.json");
            if (stream == null) {
                return null;
            }
            BufferedReader br = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8));
            String line;
            while ((line = br.readLine()) != null) {
                config.append(line);
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return config.toString();
    }

2.8 springboot 中获取上下文

 实现 ApplicationContextAware 类即可,在实现方法 setApplicationContext(ApplicationContext applicationContext) 时,可以将其拷贝到自己本地,示例如下:

public class ApplicationContextHolder implements ApplicationContextAware {

    private static ApplicationContext contextContainer;

    private static final Logger LOGGER = LoggerFactory.getLogger(ApplicationContextHolder.class);

    public static ApplicationContext getContextContainer() {
        LOGGER.info(">> spring context has init.");
        return contextContainer;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        contextContainer = applicationContext;
    }
}

2.9 springboot 中获取带有指定注解的 Bean 对象

 通过上下文的 getBeansWithAnnotation() 方法可以获取带有指定注解的 Bean 对象,然后通过 Spring 的 AnnotationUtils.findAnnotation(x,x)
方法获取注解对象,最后通过这个注解对象获取上面的值。千万不要用 AnnotationUtils.getAnnotation 方法去获取,那样获取的是代理对象,无法获取实际 Bean 对象,正确示例如下:

Map<String, Object> beansWithAnnotation = springApplicationContext.getBeansWithAnnotation(EnableExcelImportSupport.class);
        if (CollectionUtils.isEmpty(beansWithAnnotation)) {
            return;
        }

        beansWithAnnotation.forEach((key, value) -> {
            Class<?> aClass = value.getClass();
            EnableExcelImportSupport annotation = AnnotationUtils.findAnnotation(aClass, EnableExcelImportSupport.class);
            if (annotation != null && annotation.types() != null && annotation.types().length > 0) {
                Arrays.asList(annotation.types()).forEach(o -> templateTypes.put(o.getType(), o));
            }
        });

2.10 组合注解

 这个其实是基础内容,不完全是 spring 的内容,某些时候我们需要将自定义的注解和已有的注解进行组合,以便让这个注解可以具备多个注解的功能,比如当我们自定义的注解,但若同时想让 Spring 来管理带有这个注解的类的实例,那我们需要自己去实现 Spring 的扩展接口来扫描自定义注解来让带有自定义注解的类的实例加入 IOC 容器,但此时若我们可以将自定义注解和Bean对象的注解如:@Bean@Component等组合一下,就不用那么复杂自己去实现扩展进行扫描,它就会自动交给 IOC 容器了。示例如下:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Component // 和Spring的Bean管理注解合并,不能少
public @interface CustomExcelExportHandler {
    ExcelExportTaskBizType type();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值