日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學(xué)無先后,達(dá)者為師

網(wǎng)站首頁 編程語言 正文

利用ImportBeanDefinitionRegistrar手動(dòng)向Spring容器注入Bean

作者:文盲青年 更新時(shí)間: 2023-09-12 編程語言

一、注入方式

向Spring容器中注入Bean的方法很多,比如:

  • 利用<Bean>...<Bean>Xml文件描述來注入
  • 利用JavaConfig的@Configuration@Bean注入
  • 利用springboot的自動(dòng)裝配,即實(shí)現(xiàn)ImportSelector來批量注入
  • 利用ImportBeanDefinitionRegistrar來實(shí)現(xiàn)注入

二、@Enable注解簡介

我們常常使用@EnableScheduling@EnableWebMvc等,@Enable注解的主要作用就是利用@Import注解來實(shí)現(xiàn)IoC對象注入,比如Spring自帶的@EnableScheduling,分析一下:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Import({SchedulingConfiguration.class})
@Documented
public @interface EnableScheduling {
}

可以看到其實(shí)就是導(dǎo)入了一個(gè)配置類@Import({SchedulingConfiguration.class}),

@Configuration(proxyBeanMethods = false)
@Role(2)
public class SchedulingConfiguration {
    public SchedulingConfiguration() {
    }

    @Bean(name = {"org.springframework.context.annotation.internalScheduledAnnotationProcessor"})
    @Role(2)
    public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() {
        return new ScheduledAnnotationBeanPostProcessor();
    }
}

這里就是用的@Configuration@Bean注入。

再例如springboot啟用類注解@SpringBootApplication里面的@EnableAutoConfiguration

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

	/**
	 * Exclude specific auto-configuration classes such that they will never be applied.
	 * @return the classes to exclude
	 */
	@AliasFor(annotation = EnableAutoConfiguration.class)
	Class<?>[] exclude() default {};

}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

	/**
	 * Environment property that can be used to override when auto-configuration is
	 * enabled.
	 */
	String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

	/**
	 * Exclude specific auto-configuration classes such that they will never be applied.
	 * @return the classes to exclude
	 */
	Class<?>[] exclude() default {};

	/**
	 * Exclude specific auto-configuration class names such that they will never be
	 * applied.
	 * @return the class names to exclude
	 * @since 1.3.0
	 */
	String[] excludeName() default {};

}

其實(shí)就是AutoConfigurationImportSelector類來實(shí)現(xiàn)了自動(dòng)裝配的邏輯。

三、利用ImportBeanDefinitionRegistrar接口來裝配

本文講的是ImportBeanDefinitionRegistrar接口結(jié)合@Enable注解來實(shí)現(xiàn)注入:

首先我們要自己寫一個(gè)@Enable注解@EnableMyBean

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MyDeanDefinitionRegister.class)
public @interface EnableMyBean {

}

寫出我們的待注入對象:

public class MyBean {

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "MyBean{" +
                "name='" + name + '\'' +
                '}';
    }
}

編寫@Import的類MyDeanDefinitionRegister(實(shí)現(xiàn)ImportBeanDefinitionRegistrar接口):

public class MyDeanDefinitionRegister implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        RootBeanDefinition rootBeanDefinition = new RootBeanDefinition();
        rootBeanDefinition.setBeanClass(MyBean.class);
        String className = MyBean.class.getName();
        MutablePropertyValues propertyValues = new MutablePropertyValues();
        propertyValues.add("name", "lisi");
        //添加屬性
        rootBeanDefinition.setPropertyValues(propertyValues);
        //注入到spring容器
        registry.registerBeanDefinition(className, rootBeanDefinition);
    }
}

springboot啟動(dòng)類,加上我們自己寫的注解:

@SpringBootApplication
@EnableMyBean
public class Applicaiton {

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

寫個(gè)單元測試:

@SpringBootTest
class Test1 {

    @Autowired
    MyBean bean;

    @Autowired
    ApplicationContext applicationContext;


    @Test
    void test1() {
        System.out.println(bean);
    }
}

結(jié)果如下:

MyBean{name='lisi'}

可見我們已經(jīng)成功將MyBean注入到Spring容器了。

四、帶條件的注入方式

假設(shè)我們有以下批量注入需求(同時(shí)滿足一下條件)才能注入:

1、要求把某個(gè)包下的類都加到容器
2、要加一個(gè)自定義注解,只有在這個(gè)包下加了自定義注解的才能被加到容器!

首先注解上面加入?yún)?shù):

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MyDeanDefinitionRegister.class)
public @interface EnableMyBean {

    String[] basePackages() default {};
}

再寫一個(gè)自定義的條件注解:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface Meeting {
}

啟動(dòng)類修改,添加包掃描:

@SpringBootApplication
@EnableMyBean(basePackages = {"com.my.test.meeting"})
public class Applicaiton {

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

待注入的類:

@Meeting
public class Money {
}

@Meeting
public class Work {
}

//這個(gè)類就不加@Meeting注解了,不想它加入到容器,太狗了
public class Dog {

}

修改類MyDeanDefinitionRegister(實(shí)現(xiàn)ImportBeanDefinitionRegistrar接口)邏輯:

public class MyDeanDefinitionRegister implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        //獲取@EnableMyBean注解的屬性
        Map<String, Object> attributes = importingClassMetadata.getAnnotationAttributes(EnableMyBean.class.getName());

        //獲取包掃描
        ClassPathScanningCandidateComponentProvider pathScanningCandidateComponentProvider = new ClassPathScanningCandidateComponentProvider(false);


        //添加過濾,帶有@Meeting注解的類
        pathScanningCandidateComponentProvider.addIncludeFilter(new AnnotationTypeFilter(Meeting.class));

        String[] basePackages = (String[]) attributes.get("basePackages");

        LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet<>();
        for (String basePackage : basePackages) {
            //選擇該包下的
            candidateComponents.addAll(pathScanningCandidateComponentProvider.findCandidateComponents(basePackage));
        }

        //注入Bean
        for (BeanDefinition candidateComponent : candidateComponents) {
            registry.registerBeanDefinition(candidateComponent.getBeanClassName(), candidateComponent);
        }
    }

	//如果想要掃描到接口,則需要調(diào)用該方法獲取掃描器
 	private ClassPathScanningCandidateComponentProvider getScanner() {
        return new ClassPathScanningCandidateComponentProvider(false, this.environment) {
            @Override
            protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
                boolean isCandidate = false;
                if (beanDefinition.getMetadata().isIndependent()) {
                    if (!beanDefinition.getMetadata().isAnnotation()) {
                        isCandidate = true;
                    }
                }
                return isCandidate;
            }
        };
    }
}

單元測試一下:

@SpringBootTest
class Test1 {

    @Autowired
    ApplicationContext applicationContext;


    @Test
    void test1() {
        Arrays.stream(applicationContext.getBeanDefinitionNames()).forEach(System.out::println);
    }
}

結(jié)果:

com.my.test.meeting.Money
com.my.test.meeting.Work

可以看到Money、Work都成功注入了,而沒有Dog,因?yàn)樗鼪]有@Meeting注解

五、直接利用beanFactory注入

這里作為額外補(bǔ)充:

public class Compont2
{
}
@Component
public class ContextAware implements ApplicationContextAware {


    @Autowired
    private DefaultListableBeanFactory beanFactory;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        Class<Compont2> c2 = Compont2.class;
        beanFactory.createBean(c2);
        String name = c2.getName();
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(c2);
        beanFactory.registerBeanDefinition(name, beanDefinitionBuilder.getBeanDefinition());
    }
}

至此已經(jīng)注入完畢,可以被依賴了:

@Component
public class ContextInitializingBean1 implements InitializingBean {

    @Autowired
    private Compont2 compont2;

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("ContextInitializingBean1  " + compont2);
    }
}

原文鏈接:https://rabbit.blog.csdn.net/article/details/130133788

  • 上一篇:沒有了
  • 下一篇:沒有了
欄目分類
最近更新