Bean 注册

Spring的配置方案

自动化装配

自动化装配指的是通过一系列描述性代码,描述Bean对象之间的依赖关系,从而交由Spring自动组装出来,目前有两种方式:

@Component注解

@Component // 将当前类对象存入容器
public class Tea {...}

Spring自动注入

@Component
  public class Cup {

    private Tea tea;

    @Autowired
    public Cup(Tea tea) {
        this.tea = tea;
    }
  }

其他功能一样,但语义不同的注解:

@ComponentScan注解

创建一个组件扫描配置:

@ComponentScan(basePackages = "wang.ismy.spring")
public class Config {}

指定扫描策略

@ComponentScan(value = "*",
        excludeFilters = {
                @Filter(type = FilterType.ANNOTATION,
                        classes = {Controller.class, Repository.class}),
                @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = User.class)
        })
  1. 根据注解来排除(type = FilterType.ANNOTATION),这些注解的类型为classes = {Controller.class, Repository.class}。即Controller和Repository注解标注的类不再被纳入到IOC容器中。

  2. 根据指定类型类排除(type = FilterType.ASSIGNABLE_TYPE),排除类型为User.class,其子类,实现类都会被排除。

自定义扫描策略

@ComponentScan(value = "*",
        excludeFilters = {
            @Filter(type = FilterType.CUSTOM, classes = MyTypeFilter.class)
        })
...
public class MyTypeFilter implements TypeFilter {
    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) {...}
}

获取context使用bean

AnnotationConfigApplicationContext context = 
                new AnnotationConfigApplicationContext(Config.class);
context.getBean(Bean.class).say();

处理自动装配歧义性

@Bean
@Primary // 当有多个可选项时,将优先使用这个bean
public Bean1 bean1(){
    Bean1 bean1 = new Bean1();
    bean1.setName("pro");
    return bean1;
}
@Autowired
@Qualifier("bean1f") // 当有多个可选项时,将使用名为bean1f的bean
public void setBean1(Bean1 bean1){}

@Qualifier也可以用在方法参数上

导入

@ImportResource("classpath:spring.xml") // 导入xml配置
@Import(Config.class) // 导入java代码配置
@Import(MyImportSelector.class) // 自定义导入策略
public class Config {}

public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{
                "*",
                "*",
                "*"
        };
    }
}

使用FactoryBean注册组件

public class CherryFactoryBean implements FactoryBean<Cherry> {
    @Override
    public Cherry getObject() {
        return new Cherry();
    }

    @Override
    public Class<?> getObjectType() {
        return Cherry.class;
    }

    @Override
    public boolean isSingleton() {
        return false;
    }
}

如果我们要获取cherryFactoryBean本身,则可以这样做:

context.getBean("&cherryFactoryBean");

@Profile

通过编写不同的profile,以此达到切换不同的profile,装配不同Bean的功能

@Configuration
@Profile("pro")
public class Config1 {
  @Bean
  public Bean1 bean1(){
      Bean1 bean1 = new Bean1();
      bean1.setName("pro");
      return bean1;
  }
}
@Configuration
@Profile("dev")
public class Config {
  @Bean
  public Bean1 bean1(){
      Bean1 bean1 = new Bean1();
      bean1.setName("dev");
      return bean1;
  }
}
System.setProperty("spring.profiles.active","pro");
AnnotationConfigApplicationContext context =
        new AnnotationConfigApplicationContext(MasterConfig.class);
context.getBean(Bean1.class).run();

条件化装配

@Conditional注解

@Bean
  @Conditional(MyConditional.class)
  public Bean1 bean1(){
      Bean1 bean1 = new Bean1();
      bean1.setName("pro");
      return bean1;
  }

只要自定实现Condition接口,就可以控制bean的装配:

public class MyConditional implements Condition {

  @Override
  public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
      // ...
      return true;
  }
}

JAVA代码装配

由于某些类来源于外部,我们无法修改其源码如在类上加上@Component注解来注入容器,所以可以使用java代码的方式创建后注入

@Configuration
public class Config {

    @Bean // 默认值是方法名称
    public wang.ismy.spring.Bean bean(Bean1 bean1){return new wang.ismy.spring.Bean(bean1);}

    @Bean
    public Bean1 bean1(){return new Bean1();}
}

作用域

使用

@Component
@Scope("prototype")
public class Bean { }
<bean scope="prototype" class="wang.ismy.spring.Bean"/>

懒加载@Lazy

使用该注解后,Bean只有在真正需要时才会被初始化,而非在容器启动后就被初始化

使用XML配置

由于spring早期大量使用xml来配置,所以这节的内容还是需要了解一下的。 不过对于新项目,还是推荐使用注解或者java配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

</beans>
<!--方式1-->
<bean class="wang.ismy.spring.Bean"/>
<!--方式2:普通工厂创建-->
<bean id="factory" class="wang.ismy.spring.Factory"/>
<bean factory-bean="factory" factory-method="get"/>
<!--方式3:静态工厂-->
<bean class="wang.ismy.spring.Factory" factory-method="get"/>
<!--只有作用范围是单例时destroy才会被调用-->
<bean scope="singleton" class="wang.ismy.spring.Bean" 
init-method="init" destroy-method="destroy"/>
public class Bean1 {

    private String name;

    public void setName(String name){ this.name = name;}
}
<bean class="wang.ismy.spring.Bean1">
        <property name="name" value="hello"/>
</bean>
<bean class="wang.ismy.spring.Bean">
    <constructor-arg name="name" value="abc"/>
    <constructor-arg name="value" value="-1"/>
</bean>

<bean name="bean1" class="wang.ismy.spring.Bean1"/>
<bean class="wang.ismy.spring.Bean">
    <constructor-arg ref="bean1"/>
</bean>
<bean class="wang.ismy.spring.Bean">
        <!--注入数组-->
        <property name="array">
            <array> <value>AAA</value> <value>BBB</value> <value>CCC</value> </array>
        </property>
        <!--注入集合-->
        <property name="set">
            <set> <value>AAA</value> <value>BBB</value> <value>CCC</value> </set>
        </property>
        <!--注入list-->
        <property name="list">
            <list> <value>AAA</value> <value>BBB</value> <value>CCC</value> </list>
        </property>
        <!--注入map-->
        <property name="map">
            <map>
                <entry key="a" value="a"/>
                <entry key="b" value="b"/>
            </map>
        </property>
        <!--注入properties-->
        <property name="prop">
            <props>
                <prop key="a">a</prop>
                <prop key="b">b</prop>
            </props>
        </property>
    </bean>
ClassPathXmlApplicationContext context =
                new ClassPathXmlApplicationContext("classpath:spring.xml");
context.getBean(Bean1.class).run();

运行时值注入

@Configuration
@ComponentScan(basePackages = "wang.ismy.spring")
@PropertySource("classpath:config.properties")
public class Config { }

config.properties

name=my
@Component
public class Bean {
  @Value("${name}")
  String name;
}

Spring EL表达式

@Component
public class Bean {
  @Value("#{T(System).currentTimeMillis()}")
  long time;

  public void run(){
      System.out.println(time);
  }
}

循环依赖

当一个对象依赖的对象间接或直接又依赖其本身时,就是循环依赖。

针对于这种依赖情况,对于属性注入或者方法注入,Spring通过先创建对象实例,后填充其属性的方式来解决循环依赖,但对于通过构造器注入的情况,Spring则无法解决。

202153193741

202153193815