Spring 高级装配详解

一、环境与profile

​ 在3.1版本中,Spring引入了bean profile的功能。要使用profile,首先要将所有不同的bean定义整理到一个或者多个pofile之中,再将应用部署到每个环境时,确保对应的profile处于激活状态。

  • 在Java配置中,可以使用@Profile注解来指定某个bean属于哪一个profile。

    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Profile;
    import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
    import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
    
    import javax.sql.DataSource;
    
    @Configuration
    public class DevelopmentProfileConfig {
        @Profile("dev")
        @Bean()
        public DataSource dataSource() {
            return new EmbeddedDatabaseBuilder()
                    .setType(EmbeddedDatabaseType.H2)
                    .addScript("classpath:schema.sql")
                    .addScript("classpath:test-data.sql")
                    .build();
        }
    }
    
  • 在XML中配置profile

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

    或者

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

注意:

​ Spring确定那个profile处于激活状态,需要依赖两个独立的属性:

  • spring.profiles.active
  • spring.profiles.default

二、条件化的bean

@Conditional来源于spring-context包下的一个注解。Conditional中文是条件的意思,@Conditional注解它的作用是按照一定的条件进行判断,满足条件给容器注册bean。

三、处理自动装配的歧义性

1. 自动装配的歧义性

​ 例如,我们创建一个接口和三个实现该接口的类,并通过隐式的bean发现和自动装配机制进行注入bean。

// Dessert接口
public interface Dessert {
    void cook();
}

// Cake类
@Component
public class Cake implements Dessert{
    private String name = "蛋糕";
    private String description = "水果";

    @Override
    public void cook() {
        System.out.println(name + "加了一些" + description);
    }
}

// Cookies类
@Component
public class Cookies implements Dessert{
    private String name = "饼干";
    private String description = "巧克力豆";


    @Override
    public void cook() {
        System.out.println(name + "加了一些" + description);
    }
}

// IceCream类
@Component
public class IceCream implements Dessert{
    private String name = "冰淇淋";
    private String description = "奥利奥碎屑";


    @Override
    public void cook() {
        System.out.println(name + "加了一些" + description);
    }
}

// 测试类
@Autowired
private Dessert dessert;

@Test
public void compactDiscTest() {
    dessert.cook();
}

​ 此时,由于 CakeCookies IceCream 均为 Dessert,自动装配在此时会遇到歧义性,导致Spring无法做出选择,从而抛出org.springframework.beans.factory.UnsatisfiedDependencyException错误。

2. 进行处理

​ 当确实发生歧义性的时候,Spring提供了多种解决方案来解决遮掩的个问题。包括:

  • 将可选bean中的某一个设置为首选(primary)的bean;
  • 使用限定符(qualifier)来帮助Spring将可选的bean的方位缩小到只有一个bean。
1)@Primary
  1. 与@Component组合

    @Component
    @Primary
    public class Cookies implements Dessert{
        ......
    }
    
  2. 与@Bean方法组合

    @Configuration
    public class DessertConfig {
        
        @Bean
        @Primary
        public Dessert dessert() {
            return new IceCream();
        }
        
    }
    
  3. <bean>元素中使用

    <bean id="iceCream"
          class="com.shiftycat.dessert.IceCream"
          primary="true">
    
2)@Qualifier

​ 在使用@Primary来表选首选bean时,如果标示了两个及以上的首选bean,那么该机制就会失效。为了解决这个问题,我们可以使用@Qualifier来规定限制条件以缩小满足要求的bean数量。

//方法1
@Component
@Qualifier
public class IceCream implements Dessert{
    ......
}
//方法2
@Autowired
@Qualifier("iceCream")
private Dessert dessert;

@Test
public void compactDiscTest() {
    dessert.cook();
}

当然,我们也可以创建自定义的限定符,例如:

@Component
@Qualifier("clod")
public class IceCream implements Dessert{
    ......
}
@Autowired
@Qualifier("clod")
private Dessert dessert;

@Test
public void compactDiscTest() {
    dessert.cook();
}

在Java配置显式定义bean的时候,@Qualifier也可以与@Bean注解一起使用。但是,此时,如果有两个bean都使用@Qualifier进行标记,也会出现错误。例如:

@Component
@Qualifier("cold")
public class IceCream implements Dessert{
    private String name = "冰淇淋";
    private String description = "奥利奥碎屑";


    @Override
    public void cook() {
        System.out.println(name + "加了一些" + description);
    }
}

@Component
@Qualifier("cold")
public class Popsicle implements Dessert{
    private String name = "棒冰";
    private String description = "巧克力豆";

    @Override
    public void cook() {
        System.out.println(name + "加了一些" + description);
    }
}

同时,由于Java不允许在同一个条目上重复出现相同类型的多个注解,因此,使用多个@Qualifier注解编译器会提示错误。

// 编译错误
@Component
@Qualifier("cold")
@Qualifier("creamy")
public class IceCream implements Dessert{
		......
}

因此,我们可以使用自定义的限定符注解,从而可以更便捷地进行限定。

// 自定义限定注解
@Target({ElementType.TYPE, ElementType.CONSTRUCTOR,
        ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Cold {
}

@Target({ElementType.TYPE, ElementType.CONSTRUCTOR,
        ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Creamy {
}

@Target({ElementType.TYPE, ElementType.CONSTRUCTOR,
        ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Fruity {
}
@Component
@Cold
@Fruity
public class Popsicle implements Dessert{
    ......
}

@Component
@Cold
@Creamy
public class IceCream implements Dessert{
    ......
}
@Autowired
@Cold
@Fruity
private Dessert dessert;

@Test
public void compactDiscTest() {
    dessert.cook();
}

四、bean的作用域

​ 在默认情况下,Spring应用上下文中所有的bean都是以单例(singleton)的形式创建的。而Spring定义了多种作用域,可以基于这些作用域创建bean,包括:

  • 单例(singleton):在整个应用中,只创建bean的一个实例。
  • 原型(prototype):每次注入或者通过Spring应用上下文获取的时候,都会创建一个新的bean实例。
  • 会话(Session):在Web应用中,为每个会话创建一个bean实例。
  • 请求(Request)在Web应用中,为每个请求创建一个bean实例。

bean的作用域可以使用@Scope或者<bean>元素中的scope属性进行设置。

@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class Cake implements Dessert{
    ......
}
<bean id="cake"
      class="com.shiftycat.dessert.Cake"
      scope="prototype">

在Web应用中,例如有一个bean代表用户的购物车,此时它的作用域一定是会话作用域。

《Spring实战(第4版)》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值