附上我的github项目源码: https://github.com/hzprivate/studyAll 查看 springboot-beanNameGenerator项目
阅读spring官方文档时,看到了一个beanNameGenerator:
当一个组件作为扫描过程的一部分被自动检测时,它的bean名称由该扫描程序所知道的BeanNameGenerator策略生成。默认情况下,任何包含名称值的Spring构造型注释(@Component、@Repository、@Service和@Controller)都将该名称提供给相应的bean定义。
如果这样的注释不包含名称值或任何其他检测到的组件(例如由自定义过滤器发现的组件),则默认bean名称生成器将返回未大写的非限定类名。例如,如果下面的组件类
于是根据官方文档的例子,顺便翻了下beanNameGenerator源码,看了下如何生成默认bean名称
关键这四行代码 简单易懂。
于是马上新建springboot项目,手写自己的命名规则。
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
/**
* 自定义 组件命名规则 返回全限定名称
* @author hz
* @create 2020-04-09
*/
public class MyNameGenerator implements BeanNameGenerator {
@Override
public String generateBeanName(BeanDefinition beanDefinition, BeanDefinitionRegistry beanDefinitionRegistry) {
//获取全限定名称
String beanClassName = beanDefinition.getBeanClassName();
// //获取类名
// String shortClassName = ClassUtils.getShortName(beanClassName);
//小写首字母
// String decapitalize = Introspector.decapitalize(shortClassName);
return beanClassName;
}
}
import com.springboot.beanname.generator.MyNameGenerator;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
* @author hz
* @create 2020-04-10
*/
@Configuration
@ComponentScan(basePackages = "com.springboot.beanname.bean.bean1",nameGenerator = MyNameGenerator.class)
public class AppConfig {
}
美滋滋的开启我的项目,打印所有的bean对象看下是不是生成了自己想要的规则名称。然后exceuse me? 为什么产生了两个bean对象,我的规则事实证明成功了,但是为什么默认规则的也执行了。这就很尴尬了。如果这样。那我们反过来想。项目里有两个同名类。并且都随容器生成bean对象。那就会报beanName对象重复错误。那这个自定义就没有任何意义。
最后调试了半天 BeanNameGenerator 源码,我以为在这接口前又执行了什么生成bean对象操作。最后把问题定位到了启动类里的 @SpringBootApplication注解。 点开进去。发现原来。他这里有扫描注解。哎、头疼。启动类执行时根据默认命名规则生成bean对象到容器。我的配置类 AppConfig 也会随着项目启动根据自己的命名规则生成Student bean对象到容器。所以容器产生了不同的两个Student对象名称。
继续修改代码。在启动类上用自己的ComponentScan。excludeFilters :用自定义过滤规则MyTypeFilter 过滤掉com.springboot.beanname.bean.bean1下的扫描对象Student
import com.springboot.beanname.filter.MyTypeFilter;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
//@SpringBootApplication //(相当于如下三个注解)
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @ComponentScan.Filter(type = FilterType.CUSTOM, classes = MyTypeFilter.class)})
public class BeannameApplication {
public static void main(String[] args) {
//启动Spring Boot项目的唯一入口
SpringApplication springApplication =new SpringApplication(BeannameApplication.class);
springApplication.addListeners(new ApplicationStartup());
springApplication.run(args);
}
}
import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;
import java.io.IOException;
/**
* ComponentScan 扫描 指定过滤规则
* @author hz
* @create 2020-04-13
*/
public class MyTypeFilter implements TypeFilter {
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
throws IOException {
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
ClassMetadata classMetadata = metadataReader.getClassMetadata();
Resource resource = metadataReader.getResource();
String className = classMetadata.getClassName();
// 检测名字包含 com.springboot.beanname.bean.bean1 的bean
if(className.contains("com.springboot.beanname.bean.bean1")){
return true;
}
return false;
}
}
再次启动。大功告成。com.springboot.beanname.bean.bean1下的对象 用自定义规则。其余的用默认规则。并且两个同名Student 也不会报beanName重复错误了