spring通過註解註冊bean的方式+spring生命週期

她的開呀發表於2021-12-02

spring容器通過註解註冊bean的方式

  1. @ComponentScan + 元件標註註解 (@Component/@Service...)
    @ComponentScan(value = "com.example.demo.annotation")
    
    spring會將com.example.demo.annotation目錄下標註了spring能識別的註解的類註冊為bean
    @ComponentScan 還可以指定排除和包含規則
    • excludeFilters: 指定排除規則,排除哪些元件
    • includeFilters: 指定只需要包含哪些元件,需要設定 useDefaultFilters = false
    • FilterType.ANNOTATION 基於註解過濾
    • FilterType.ASSIGNABLE_TYPE : 基於給定的型別過濾
    • ...
    • FilterType.CUSTOM: 自定義規則過濾
    @ComponentScan(value = "com.example.demo.annotation",
            /*excludeFilters = {
                    @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class})
            },*/
            includeFilters = {
                    @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Configuration.class}),
                    @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,classes = {TestController.class}),
                    @ComponentScan.Filter(type = FilterType.CUSTOM,classes = {MyTypeFilter.class})
            },useDefaultFilters = false
    )
    
    其中CUSTOM自定義規則中的MyTypeFilter需要實現TypeFilter介面,舉例如下
    public class MyTypeFilter implements TypeFilter {
        @Override
        public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
            String className = metadataReader.getClassMetadata().getClassName();
            System.out.println("------>"+className);
            if (className.contains("er")){
                return true;
            }
            return false;
        }
    }
    
  2. @Bean (可以將第三方包中的類註冊為bean)
    @Bean
    Person person() {
        return new Person("zhang");
    }
    
  3. @Import
    @Import({Color.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class})
    
    • 匯入一個普通類,容器會自動註冊這個元件,元件的id預設是類的全類名
    • 匯入ImportSelector :返回需要註冊的元件
    public class MyImportSelector implements ImportSelector {
        @Override
        public String[] selectImports(AnnotationMetadata annotationMetadata) {
            return new String[]{"com.example.demo.annotation.bean.Red"};
        }
    }
    
    • 匯入ImportBeanDefinitionRegistrar
    public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    
        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
            boolean b = registry.containsBeanDefinition("com.example.demo.annotation.bean.Color");
            boolean b1 = registry.containsBeanDefinition("com.example.demo.annotation.bean.Red");
            if (b && b1){
                registry.registerBeanDefinition("rainBow",new RootBeanDefinition(RainBow.class));
            }
        }
    }
    
  4. 使用spring提供的 FactoryBean
    public class ColorFactoryBean implements FactoryBean<Color> {
        @Override
        public Color getObject() throws Exception {
            return new Color();
        }
    
        @Override
        public Class<?> getObjectType() {
            return Color.class;
        }
    }
    
    @Bean
    ColorFactoryBean colorFactoryBean() {
        return new ColorFactoryBean();
    }
    
    applicationContext.getBean("colorFactoryBean") 預設獲取到的是FactoryBean呼叫getObject方法返回的物件
    要獲取FactoryBean本身,需要在id前面加個& (&colorFactoryBean)

當滿足某種條件時才註冊bean,使用@Conditional

舉例:在windows和linux上分別註冊不同的bean

    @Conditional({WindowsConditional.class})
    @Bean("windows")
    Person person1() {
        return new Person("windows");
    }

    @Bean("linux")
    @Conditional({LinuxConditional.class})
    Person person2() {
        return new Person("linux");
    }

    public class WindowsConditional implements Condition {
        @Override
        public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
            Environment environment = conditionContext.getEnvironment();
            String property = environment.getProperty("os.name");
            return property.toLowerCase().contains("windows");
        }
    }

    public class LinuxConditional implements Condition {
        @Override
        public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
            Environment environment = conditionContext.getEnvironment();
            String property = environment.getProperty("os.name");
            return property.toLowerCase().contains("linux");
        }
    }

bean的生命週期

spring容器管理bean的生命週期:建立--》初始化--》銷燬

我們可以自定義初始化和銷燬方法,容器在bean進行到當前生命週期時來呼叫我們自定義的初始化和銷燬方法

  1. 建立物件:
    單例項:在容器啟動時建立物件
    多例項:在每次獲取bean的時候建立物件

    每個BeanPostProcessor的 postProcessBeforeInitialization 方法會在初始化之前執行

  2. 初始化: 物件建立好,呼叫初始化方法
    每個BeanPostProcessor的 postProcessAfterInitialization 方法會在初始化之後執行

  3. 銷燬:
    單例項:容器關閉時銷燬
    多例項:容器會幫助建立這個bean,但不會管理這個bean,所以容器不會呼叫銷燬方法,可以手動呼叫銷燬方法

指定初始化和銷燬的方法:

  1. 在 @Bean註解指定(initMethod = "",destroyMethod = "")
  2. 通過讓bean實現 InitializingBean(定義初始化邏輯) 和 DisposableBean(定義銷燬時邏輯)
  3. 使用JSR250: @PostConstruct (定義初始化邏輯) @PreDestroy(在容器銷燬bean之前通知進行清理工作)

bean的後置處理器 BeanPostProcessor,其有如下兩個方法,在bean的初始化前後做一些處理:

  • postProcessBeforeInitialization:在初始化之前工作
  • postProcessAfterInitialization:在初始化之後工作
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
    private final ApplicationContext applicationContext;

    public MyBeanPostProcessor(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessBeforeInitialization--->"+beanName);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessAfterInitialization--->"+beanName);
        return bean;
    }
}

如果自定義元件想要使用spring容器底層的元件(ApplicationContext,BeanFactory,***),自定義元件可以實現 ***Aware,
在建立bean的時候,相關BeanPostProcessor會呼叫介面規定的方法注入相關元件

例如:

如果自定義bean 實現了ApplicationContextAware 介面,在ApplicationContextAwareProcessor中會呼叫ApplicationContextAware的
setApplicationContext方法,注入ApplicationContext元件

相關文章