Spring Boot类无法被扫描到(Class is never used)
导致问题的主要原因是不了解SpringBoot注解扫描范围约定,将启动文件Application.java在package中定义的层次过深。
SpringBoot注解扫描范围约定
SpringBoot项目的注解扫描默认规则是根据Application类所在的包位置从上往下扫描!“Application类”是指SpringBoot项目入口类。这个类的位置很关键。如果Application类所在的包为:com.makeronly,则只会扫描com.makeronly包及其所有子包,如果controller、service或dao所在包不在com.makeronly及其子包下,则不会被扫描!
如果Application类放在com.makeronly.pay包中,那么与pay的同级包、父级包是不会被扫描的。
springboot项目scanBasePackages和exclude 迁移到配置文件
在实际开发中想把@SpringBootApplication 扫描包 scanBasePackages 和 排除功能exclude 放到配置文件 @Configuration中,方便打jar包(不要@SpringBootApplication)……搜索查询了很久,没有找到方法 手动捂脸…… 最后……如下
@SpringBootApplication(scanBasePackages = { "com.demo.workflow", "com.demo.xiaoxiao.swagger"}, ,exclude = {SecurityAutoConfiguration.class}) 迁移到config配置文件
注意 如下方式迁移是无效的
@ComponentScan(basePackages = {"com.demo.workflow", "com.demo.skull","org.flowable.ui.modeler", "org.flowable.ui.common"}
, excludeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = SecurityAutoConfiguration.class)
)
为什么这么迁移? 其实 很简单,我们看下注解源码就知道了,截取一部分
scanBasePackages 对应的刚好是 @ComponentScan
exclude 对应的刚好是 @EnableAutoConfiguration(exclude
所以将启动类的配置更改到配置中心如上
SpringBoot 获取扫描包路径
其中 AnnotationMetadata metadata 是最初的 @SpringBootApplication 标签,故 new PackageImport(metadata).getPackageName() 就可获得 @SpringBootApplication 标签所在的包路径。
总结 :SpringBoot会将 当前启动类所在包 下面的所有组件扫描到容器中。
springboot快速入门及@SpringBootApplication注解分析
简单demo
使用 maven 构建项目,官方现在稳定版本是1.5.4,第一个入门demo不是web项目,pom依赖如下:
实体 User 类:
配置类:
入口类 Application :
项目结构目录
启动程序,以 main 方法启动:
打印出正确的结果。
来分析一下流程,为何 Runnable 类, User , Map 会纳入spring容器。
首先我们分析的就是入口类 Application 的启动注解 @SpringBootApplication ,进入源码:
发现 @SpringBootApplication 是一个复合注解,包括 @ComponentScan ,和 @SpringBootConfiguration , @EnableAutoConfiguration 。
根据上面的理解,上面的入口类 Application ,我们可以使用:
使用 @ComponentScan 注解代替 @SpringBootApplication 注解,也可以正常运行程序。原因是 @SpringBootApplication 中包含 @ComponentScan ,并且 springboot 会将入口类看作是一个 @SpringBootConfiguration 标记的配置类,所以定义在入口类 Application 中的 Runnable 也可以纳入到容器管理。
看一个demo学会使用这些参数配置
在包下com.zhihao.miao.springboot定义一个启动应用类(加上@SpringBootApplication注解)
在com.zhihao.miao.beans包下定义一个实体类,并且想将其纳入到spring容器中,
启动启动类,打印结果如下:
说明Cat类并没有纳入到spring容器中,这个结果也如我们所想,因为@SpringBootApplication只会扫描@SpringBootApplication注解标记类包下及其子包的类(特定注解标记,比如说@Controller,@Service,@Component,@Configuration和@Bean注解等等)纳入到spring容器,很显然MyConfig不在@SpringBootApplication注解标记类相同包下及其子包的类,所以需要我们去配置一下扫包路径。
修改启动类,@SpringBootApplication(scanBasePackages = "com.zhihao.miao"),指定扫描路径:
启动并打印:
当然使用@SpringBootApplication(scanBasePackageClasses = MyConfig.class),指定scanBasePackageClasses参数的value值是你需要扫描的类也可以,结果一样,不过如果多个配置类不在当前包及其子包下,则需要指定多个。
再看一个列子,
在上面的列子的相同包下(com.zhihao.miao.springboot)配置了People,并将其纳入到spring容器中(@Component),我们知道@SpringBootApplication注解会扫描当前包及其子包,所以People类会纳入到spring容器中去,我们需要将其排除在spring容器中,如何操作?
可以使用@SpringBootApplication的另外二个参数(exclude或excludeName)
启动类,
启动并打印结果:
然后修改@SpringBootApplication配置,
很明显启动报错。使用@excludeName注解也可以。如下,
@SpringBootApplication(excludeName = {"com.zhihao.miao.springboot.People"})
参考文档:
Springboot1.5.4官方文档
springboot 启动排除某些bean 的注入
问题:
最近做项目的时候,需要引入其他的jar。然后还需要扫描这些jar里的某些bean。于是使用注解:@ComponentScan
这个注解直接指定包名就可以,它会去扫描这个包下所有的class,然后判断是否解析:
@ComponentScan(basePackages = {"your.pkg", "other.pkg"})
public class Application {
}
其他的jar中定义了 redissonConfig 这个bean。然后我自己的项目也定义了redissonConfig 这个bean。导致项目启动报错。所以使用如下方式,排除jar 中的RedissonConfig.class。
@ComponentScan(basePackages = {"com.xx.xx.*"}, excludeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {RedissonConfig.class}))
@ComponentScan注解。扫描或解析的bean只能是Spring内部所定义的,比如@Component、@Service、@Controller或@Repository。如果有一些自定义的注解,比如@Consumer、这个注解修饰的类是不会被扫描到的。这个时候我们就得自定义扫描器完成这个操作。
配置文件中使用的:component-scan标签底层使用ClassPathBeanDefinitionScanner这个类完成扫描工作的。@ComponentScan注解配合@Configuration注解使用,底层使用ComponentScanAnnotationParser解析器完成解析工作。