读书笔记-spring高级装配bean

2 篇文章 0 订阅

1.profile

spring会等到运行时根据环境决定需要创建哪个bean,所以一个war包可以适用于所有环境,不需要进行重新构建。

1.1配置profile

用@Profile注解指定某个bean属于哪个profile。

@Configuration
@Profile("dev")
public class DevConfig{
   @Bean
   public DataSource dataSource() {
      return new ...
      }
}

@Profile用在类级别上,表明类里面的bean只有dev profile激活时才会创建。
@Profile也可用在方法上。
XML配置如下:

<beans profile="dev">
   <bean id="devBean"/>
</beans>

<beans profile="prod"
   <bean id="prodBean"/>
</beans>

1.2激活profile

2个相关属性:spring.profiles.active/spring.profiles.default
有多种方式设置这2个属性:

  • 作为DispatcherServlet的初始化参数;
  • 作为Web应用的上下文参数;
  • 作为JNDI条目;
  • 作为环境变量;
  • 作为JVM的系统属性;
  • 在集成测试类上,使用@ActiveProfiles注解设置;
    例如在web.xml中设置默认profile:
<!--为上下文设置默认profile-->
<context-param>
  <param-name>spring.profiles.default></param-name>
  <param-value>dev</param-value>
</context-param>

<!--为servlet设置默认profile-->
<init-param>
  <param-name>spring.profiles.default></param-name>
  <param-value>dev</param-value>
</init-parm>

使用profile进行测试
spring提供了@ActiveProfiles,指定运行测试时要激活哪个profile。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath*:httpclient-context.xml"})
@ActiveProfiles("dev")
public class CommandTest {

2 条件化的bean

当希望在某些特定条件下才创建bean,如某个bean只有在另外某个特定bean声明之后才创建,或者某个特定环境变量设置之后才会创建某个bean。
可以使用spring的@Conditional 注解。

    @Bean
    @Conditional(ExistsCondition.class)
    public SgtPeppers sgtPeppers(){
        return new SgtPeppers();
    }

设置给@Conditional的类可以是实现了Condition接口的类型:

public class ExistsCondition implements Condition {
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        Environment env = conditionContext.getEnvironment();
        return env.containsProperty("propertyName");//检查propertyName属性
    }
}

ConditionContext是一个接口:

public interface ConditionContext {
    BeanDefinitionRegistry getRegistry();//可以检查bean定义

    ConfigurableListableBeanFactory getBeanFactory();//用于检查bean是否存在

    Environment getEnvironment();//检查环境变量是否存在以及值是什么

    ResourceLoader getResourceLoader();//读取并探查ResourceLoader加载的资源

    ClassLoader getClassLoader();//用于加载并检查类是否存在
}

AnnotatedTypeMetadata也是接口,能够让我们检查带有@Bean注解的方法上还有其他注解:

public interface AnnotatedTypeMetadata {
    boolean isAnnotated(String var1);//用于判断带有@Bean注解的方法是否还有其他特定注解

    Map<String, Object> getAnnotationAttributes(String var1);

    Map<String, Object> getAnnotationAttributes(String var1, boolean var2);

    MultiValueMap<String, Object> getAllAnnotationAttributes(String var1);

    MultiValueMap<String, Object> getAllAnnotationAttributes(String var1, boolean var2);
}

3处理自动装配的歧义性

如果某个bean要装配时,有多个实现,会造成冲突:

@Autowired
    public void setCompact(CompactDisc compact) {
        this.cd = compact;
    }
public interface CompactDisc {
    void play();
}
@Component
public class CompactImpl1 implements CompactDisc {
@Component
public class CompactImpl2 implements CompactDisc {

解决办法有:

3.1 设置首选的bean

@Component
@Primary
public class CompactImpl1 implements CompactDisc {

3.2限定自动装配的bean

@Qualifier注解

@Autowired
    @Qualifier("compactImpl1")
    public void setCompact(CompactDisc compact) {
        this.cd = compact;
    }

@Component注解声明的CompactImpl1类会创建为id为compactImpl1的bean(首字母小写),
@Qualifier(“compactImpl1”)指向组件扫描是创建的bean,即compactImpl1。
也可以为bean直接设置自己的限定符

@Component  //或者是@Bean
@Qualifier("impl1")
public class CompactImpl1 implements CompactDisc {

自定义注解
当不想用@Qualifier或者出现@Qualifier定义重复时,可以自定义注解:

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

这样,只需要添加@impl1注解

@Component //@Bean
@Impl1
public class CompactImpl1 implements CompactDisc {

在注入时,也只需要添加@Impl1注解:

   @Autowired
    @Impl1
    public void setCompact(CompactDisc compact) {
        this.cd = compact;
    }

为了创建自定义的限定符注解,可以创建一个新的注解并添加上@Qualifier注解,这种技术可以用到很多的spring注解中。

4 bean的作用域

spring有多种作用域,用@Scope注解,默认单例:
单例(Singleton):整个应用中只有1个实例;
原型(Prototype):每次注入或者spring应用上下文获取时创建1个;
会话(Session):web应用中每个会话创建1个;如购物车;
请求(Request):web应用中每个请求创建1个;

@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)//使用@Scope("prototype")也可以,但SCOPE_PROTOTYPE常量更加安全不易出错
public class CompactImpl1

XML配置:

<bean class="chapter2.CompactImpl1" scope="prototype"/>

4.1 会话和请求作用域

会话作用域如下:

@Component
    @Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.INTERFACES)
    public Shopping shop(){}

proxyMode的作用如下:
例如想把Shopping注入到单例Service的bean中,

@Component
public class Service {
    Shopping shopping;
    @Autowired
    public void setShopping(Shopping shopping){
        this.shopping = shopping;
    }
}

因为Service是单例bean,spring上下文加载时就会创建,但此时Shopping是会话作用域的,还不存在,知道某个用户进入系统时才会创建,而且系统里会有多个Shopping实例,spring应该是service的bean处理购物车功能时,使用的Shopping实例刚好是当前会话的bean。
所以spring会注入一个Shopping bean的代理,如下图,代理会暴露与Shopping bean相同的方法,当service真正调用Shopping方法时,代理会对其进行懒解析,将调用委托给会话作用域内真正的Shopping bean。
proxyMode = ScopedProxyMode.INTERFACES 表明创建的代理是基于接口的代理,代理要实现Shopping接口,Shopping需要是一个接口;
如果Shopping是一个具体类,此时,只能使用CGLib生成基于类的代理,此时proxyMode要设置成 ScopedProxyMode.TARGET_CLASS
这里写图片描述
请求作用域也是同样道理。

4.2 XML中声明作用域代理

<bean class="chapter2.Shopping" scope="session"><aop:scoped-proxy/></bean>

功能和@Scope注解的proxyMode属性功能相同,会告诉spring为bean创建一个作用域代理,默认使用CGLib创建目标类的代理,设置后会生成基于接口的代理。

5 运行时值注入

spring提供的两种运行时求值方式:

  • 属性占位符
  • spring表达式语言(SpEL)

5.1 注入外部的值

注入外部值:使用@PropertySource注解和Environment。

@Configuration
@PropertySource("classpath:app.properties")
public class CDplayerConfig {
    @Autowired
    Environment env;

    @Bean
    public CompactDisc disc() {
        return new SgtPeppers(env.getProperty("title"));
    }
}

app.properties属性文件会加载到Environment中。

spring的Environment

getProperty()有四种重载方法:
这里写图片描述
第四种可以指定返回值类型和默认值,如下:

int result = env.getProperty("db.connection", Integer.class, 10);

将属性解析为类:
如果文件中没有定义该属性,得到的值是null。
env.getRequiredProperty()可以要求属性必须定义,否则抛出IllegalStateException异常。

解析属性占位符

spring配置中使用${….}
如果用组件扫描和自动装配创建和初始化bean时,没有指定占位符的配置文件和类了,此时可以使用@Value注解

@Bean
    public CompactDisc disc(@Value("${title}") String title) {
        this.title = title;
    }

5.2

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值