敞开生长之旅!这是我参与「日新方案 2 月更文挑战」的第 29 天,点击检查活动概况

前言

装备注解,指@Configuration,@ComponentScan,@Scope,@Lazy,@Conditional,@Import等注解,本篇文章将对这些注解的运用进行具体总结。

Springboot版别:2.4.1

Spring版别:5.3.2

正文

一. @Configuration

@Configuration注解润饰的类便是装备类,合作@Bean注解运用,能够用于向容器注册bean。如下是示例(一切类悉数放在同一包目录下)。

事务类如下所示。

public class MyService {}

由@Configuration注解润饰的事务类如下所示。

@Configuration
public class MyConfig {
    @Bean
    public MyService myService() {
        return new MyService();
    }
}

测验类如下所示。

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext applicationContext
                = new AnnotationConfigApplicationContext(MyConfig.class);
    }
}

运转测验程序,能够将MyServicebean注册到容器中,并且bean名称为myService,日志打印如下所示。

超详细总结Spring的配置注解

二. @ComponentScan

@ComponentScan注解用于扫描并注册bean到容器中。如下是示例(一切类悉数放在同一包目录下)。

事务类如下所示。

@Component
public class MyDao {}
@Component
public class MyService {}

装备类如下所示。

@Configuration
@ComponentScan(value = "com.learn.spring.annotation.componentscan", 
        useDefaultFilters = true)
public class MyConfig {}

测验类如下所示。

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext applicationContext
                = new AnnotationConfigApplicationContext(MyConfig.class);
    }
}

运转测验程序后,会把com.learn.spring.annotation.componentscan包目录下的MyServiceMyDaobean注册到容器中。上面的装备类的@ComponentScan注解的value指示扫描哪个包目录及其子目录下的类,同时useDefaultFilters字段为true(不显示指定也为true)表明运用默许过滤规矩,默许过滤规矩便是将指定途径下一切由@Controller,@Service,@Repository和@Component注解润饰的类的bean注册到容器中

现在将useDefaultFilters装备为true,并编写一个自界说过滤器MyTypeFilter,如下所示。

public class MyTypeFilter implements TypeFilter {
    /**
     * @param metadataReader 能够获取当时正在操作的类的信息
     * @param metadataReaderFactory 能够获取上下文中一切类的信息
     */
    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
            throws IOException {
        // 获取当时类的一切注解信息
        AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
        // 获取当时类的类信息
        ClassMetadata classMetadata = metadataReader.getClassMetadata();
        // 获取当时类的一切资源信息
        Resource resource = metadataReader.getResource();
        // 完成功能:将类名不包括"Dao"字符串的类注册到容器中
        String className = classMetadata.getClassName();
        return !className.contains("Dao");
    }
}

MyConfig进行如下修正。

@Configuration
@ComponentScan(value = "com.learn.spring.annotation.componentscan",
        includeFilters = {@Filter(type = FilterType.CUSTOM, value = {MyTypeFilter.class})},
        useDefaultFilters = false)
public class MyConfig {}

再次运转测验程序,打印如下。

超详细总结Spring的配置注解

MyDaobean没有被注册到容器中。

三. @Scope

@Scope注解用于设置bean效果域。示例如下(一切类悉数放在同一包目录下)。

事务类如下所示。

public class MyService {}

装备类如下所示。

@Configuration
public class MyConfig {
    @Bean
    @Scope(value = "prototype")
    public MyService myService() {
        return new MyService();
    }
}

测验类如下所示。

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext applicationContext
                = new AnnotationConfigApplicationContext(MyConfig.class);
        MyService myService1 = applicationContext.getBean(MyService.class);
        MyService myService2 = applicationContext.getBean(MyService.class);
        System.out.println(myService1 == myService2);
    }
}

运转测验程序,会打印false,这是因为将MyServicebean效果域设置为了prototype,表明原型形式(多例)。

Springbean的效果域如下所示。

效果域 阐明
Singleton(单例) 容器中仅有一个bean实例
Prototype(多例) 每次从容器获取到的都是新的bean实例,容器将bean交给获取方后,就不再管理该bean的生命周期
RequestHTTP恳求) 每次HTTP恳求都会产生一个新的bean实例
Session(会话) 同一次会话共享同一个bean实例,不同会话bean实例不同

四. @Lazy

@Lazy注解用于设置bean推迟加载,即初始化容器的时候不会初始化推迟加载的bean,比及运用时才初始化bean。示例如下(一切类悉数放在同一包目录下)。

事务类如下所示。

public class MyService {
    public MyService() {
        System.out.println("MyService init.");
    }
}

装备类如下所示。

@Configuration
public class MyConfig {
    @Lazy
    @Bean
    public MyService myService() {
        return new MyService();
    }
}

测验类如下所示。

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext applicationContext
                = new AnnotationConfigApplicationContext(MyConfig.class);
        System.out.println("ApplicationContext initialization complete.");
        MyService myService = applicationContext
                .getBean(MyService.class);
    }
}

运转测验程序,打印如下。

超详细总结Spring的配置注解

特别注意:延时加载只针对单例生效。

五. @Conditional

@Conditional表明满意一定条件时才会注册为容器中的bean。示例如下(一切类悉数放在同一包目录下)。

两个事务类如下所示。

public class MyWindowsService {}
public class MyLinuxService {}

两个条件类如下所示。

public class WindowsCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Environment environment = context.getEnvironment();
        String osName = environment.getProperty("os.name");
        return osName != null && osName.contains("Windows");
    }
}
public class LinuxCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Environment environment = context.getEnvironment();
        String osName = environment.getProperty("os.name");
        return osName != null && osName.contains("Linux");
    }
}

装备类如下所示。

@Configuration
public class MyConfig {
    @Bean
    @Conditional(WindowsCondition.class)
    public MyWindowsService myWindowsService() {
        return new MyWindowsService();
    }
    @Bean
    @Conditional(LinuxCondition.class)
    public MyLinuxService myLinuxService() {
        return new MyLinuxService();
    }
}

测验类如下所示。

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext applicationContext
                = new AnnotationConfigApplicationContext(MyConfig.class);
    }
}

运转测验程序,打印如下。

超详细总结Spring的配置注解

可见由于MyWindowsService满意条件,所以容器初始化时将MyWindowsServicebean注入到了容器中。

六. @Import

@Import注解能够向容器注册bean,有如下三种方法(一切类悉数放在同一包目录下)。

1. 直接注册bean

事务类如下所示。

public class MyService {}

装备类如下所示。

@Configuration
@Import({MyService.class})
public class MyConfig {}

测验类如下所示。

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext applicationContext
                = new AnnotationConfigApplicationContext(MyConfig.class);
    }
}

运转测验程序,打印如下。

超详细总结Spring的配置注解

可见直接将MyService注册为了容器中的bean

2. 经过ImportSelector注册bean

新增事务类如下所示。

public class MyDao {}

自界说一个ImportSelector的完成类,如下所示。

public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[] {
                "com.learn.spring.annotation.importt.MyDao"
        };
    }
}

装备类修正如下所示。

@Configuration
@Import({MyService.class, MyImportSelector.class})
public class MyConfig {}

运转测验程序,打印成果如下。

超详细总结Spring的配置注解

可见经过MyImportSelectorMyDao注册到了容器中。

3. 经过ImportBeanDefinitionRegistrar注册bean

新增事务类如下所示。

public class MyController {}

自界说一个ImportBeanDefinitionRegistrar的完成类,如下所示。

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    /**
     * @param importingClassMetadata 当时类的注解信息
     * @param registry 能够注册BeanDefinition
     *
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
                                        BeanDefinitionRegistry registry) {
        BeanDefinition beanDefinition = new RootBeanDefinition(MyController.class);
        registry.registerBeanDefinition("myController", beanDefinition);
    }
}

装备类修正如下所示。

@Configuration
@Import({MyService.class, MyImportSelector.class,
        MyImportBeanDefinitionRegistrar.class})
public class MyConfig {}

运转测验程序,打印如下。

超详细总结Spring的配置注解

可见将MyController注册到了容器中。

七. FactoryBean

FactoryBean并不是装备注解,可是经过FactoryBean能够协助创立杂乱的bean,例如像MyBatis整合Spring中,就大量运用到了FactoryBean。示例如下(一切类悉数放在同一包目录下)。

事务类如下所示。

public class MyService {}

界说一个FactoryBean的完成类,如下所示。

public class MyFactoryBean implements FactoryBean<MyService> {
    @Override
    public MyService getObject() {
        return new MyService();
    }
    @Override
    public Class<?> getObjectType() {
        return MyService.class;
    }
    @Override
    public boolean isSingleton() {
        return true;
    }
}

装备类如下所示。

@Configuration
public class MyConfig {
    @Bean
    public MyFactoryBean myFactoryBean() {
        return new MyFactoryBean();
    }
}

测验类如下所示。

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext applicationContext
                = new AnnotationConfigApplicationContext(MyConfig.class);
        Object myService = applicationContext.getBean("myFactoryBean");
        System.out.println(myService);
        Object myFactoryBean = applicationContext.getBean("&myFactoryBean");
        System.out.println(myFactoryBean);
    }
}

运转测验程序,打印如下。

超详细总结Spring的配置注解

在装备类中,将MyFactoryBean注册成了容器中的bean,可是经过姓名myFactoryBean无法将MyFactoryBeanbean从容器中获取出来,而获取出来的是MyServicebean。假如需求将MyFactoryBeanbean从容器中获取出来,需求在bean的姓名前面加&符号。

经过FactoryBean向容器注册的bean实际上是保存在FactoryBeanRegistrySupportfactoryBeanObjectCache字段中,factoryBeanObjectCache字段是一个MapkeyFactoryBean自己的bean的姓名,valueFactoryBean创立的bean的实例,以此来完成单例。

总结

Spring的装备注解,更多的是为如何向容器注册bean服务,那么这里给出容器注册bean的总结。

  1. 装备类中运用@Bean注解向容器注册bean
  2. 运用@ComponentScan注解扫描指定包途径下的类并注册,默许扫描@Controller,@Service,@Component和@Repository注解润饰的类;
  3. 运用@Import注解,有如下三种方法
    1. 运用@Import(类.class)直接向容器注册bean
    2. 经过界说ImportSelector接口的完成类;
    3. 经过界说ImportBeanDefinitionRegistrar接口的完成类.
  4. 运用BeanFactory封装需求注册的bean的类。

敞开生长之旅!这是我参与「日新方案 2 月更文挑战」的第 29 天,点击检查活动概况