简介
用过Spring
都晓得想注入一个Bean很简略!
@ResourceCustomBean customBean;
大略像这样就能够注入一个曾经存在容器里的Bean,但前提是你把这个Bean退出容器、退出也很简略,只有在类的下面@Component
,@Service
等等一些框架提供的注解就能够!
当初 咱们要本人写一个组件,咱们要本人治理咱们的组件,应用本人的注解管本人的Bean。
这时候ImportBeanDefinitionRegistrar
就退场了!
基本功
step1
咱们也有模有样的起个Enable结尾的启动注解、
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)@Documented@Import(ImportCustomBeanDefinitionRegistrar.class)//联合@Import注解,这里参可参考其余Blogpublic @interface EnableCustomBean {}
step2
而后把这个注解放到启动类上,平时都是看大佬们Enable这样用,今个儿我本人也能开发个!
@SpringBootApplication@EnableCustomBeanpublic class BootApplication { public static void main(String[] args) { SpringApplication.run(BootApplication.class, args); }}
step3
再自定义一个Bean,
public class CustomBean { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "CustomBean{" + "name='" + name + '\'' + '}'; }}
好了,看起来所有很完满呢,只有把CustomBean
这个类注册到容器就OK!
step4
怎么把CustomBean
退出容器呢?尽管说咱们是本人定义的类,但还是运行在Spring
里,有一些初始工作还是要Spring
帮咱们一把。这里实现ImportBeanDefinitionRegistrar
接口,明天的角儿!办法registerBeanDefinitions
在Spring启动的时候会运行到!在博客@Import里有解释。
public class ImportCustomBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata im portingClassMetadata, BeanDefinitionRegistry registry) { //到这里就很明确了, String simpleName = CustomBean.class.getName(); RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(); rootBeanDefinition.setBeanClass(CustomBean.class); MutablePropertyValues propertyValues = new MutablePropertyValues(); propertyValues.add("name", "Savey"); rootBeanDefinition.setPropertyValues(propertyValues); registry.registerBeanDefinition(simpleName, rootBeanDefinition); }}
step5
跑个Test看下吧
@SpringBootTestpublic class ImportBeanDefinitionRegistrarTest { //只管注入即可, @Resource CustomBean bean;// @Resource ApplicationContext applicationContext; @Test public void test_register_custom_bean() { System.out.println(bean); }}
不出意外的话打印出CustomBean{name='Savey'}
,阐明咱们CustomBean曾经被加载到容器里了!
进阶
咱们要求把某个包下的类都加到容器、另外呢,我还要加一个自定义注解,只有在这个包下加了自定义注解的能力被加到容器!
其实,有了下面的基本功,想实现上面这个性能就太简略多了!
step1
自定义个注解
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)@Documentedpublic @interface Savey {}
step2
在EnableCustomBean
注解里加个属性嘛、
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)@Documented@Import(ImportCustomBeanDefinitionRegistrar.class)public @interface EnableCustomBean { /** * 定义要扫描的包 */ String[] basePackages() default {};}
step3
批改启动类、
@SpringBootApplication@EnableCustomBean(basePackages = {"com.boot.boot.savey"})public class BootApplication { public static void main(String[] args) { SpringApplication.run(BootApplication.class, args); }}
这里我传递一个basePackages属性,让指定包下的类才退出容器!!
step4
创立com.boot.boot.savey
目录,在目录下创立一些要自定义Bean,
留神: 给类加上 Savey
注解!!
@Savey@Getter@Setter@ToStringpublic class Money {}@Savey@Getter@Setter@ToStringpublic class Work {}//这个类就不加Savey注解了,不想它退出到容器,不想玩游戏,只想工作!所以不加了!@Getter@Setter@ToStringpublic class PlayGame {}
step5
批改ImportCustomBeanDefinitionRegistrar
@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { //获取EnableCustoBean正文的属性 final Map<String, Object> attributes = importingClassMetadata.getAnnotationAttributes(EnableCustomBean.class.getName()); //获取包扫描 ClassPathScanningCandidateComponentProvider pathScanningCandidateComponentProvider = new ClassPathScanningCandidateComponentProvider(false); //增加过滤 带有Savey这个注解的类 pathScanningCandidateComponentProvider.addIncludeFilter(new AnnotationTypeFilter(Savey.class)); LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet<>(); for (String basePackages : (String[]) attributes.get("basePackages")) { candidateComponents.addAll(pathScanningCandidateComponentProvider.findCandidateComponents(basePackages)); } //注册Bean for (BeanDefinition candidateComponent : candidateComponents) { registry.registerBeanDefinition(candidateComponent.getBeanClassName(), candidateComponent); }}
打个Test吧
@SpringBootTestpublic class ImportCustomScanPackagesDefinitionRegisterTest { @Resource ApplicationContext applicationContext; @Test public void test_scan_packages() { Arrays.stream(applicationContext.getBeanDefinitionNames()).forEach(System.out::println); }}
最初只有它俩退出了容器,PlayGame
却没有!
com.boot.boot.savey.Moneycom.boot.boot.savey.Work
代码在GitHub