第3章 高级装配
Posted 笑明子
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第3章 高级装配相关的知识,希望对你有一定的参考价值。
1、"profile bean"(Spring为那些和运行环境有关系的bean提供的配置。配置后,Spring会根据运行环境选取相应的bean)
(需要将这些bean放到profile bean中,并确保profile处于active状态)
2、使用java配置profile bean(使用@Profile注解指定bean属于哪个profile文件中)
例如:
//在类级别上使用profile bean @Configuration @profile("dev") public class 配置类{ @Bean public 需要的Bean 方法名(){ new 需要的Bean(); } }
(同理:可以再创建一个类指定不同环境需要装配的bean)
//上面这个bean只有在dev profile文件被激活时才创建。如果dev 不是激活状态则忽略创建。(根据profile是否激活,装配不同的bean)
3、在方法级别上使用profile bean (Spring 3.2 开始可以再方法级别上使用Profile bean)
@Configuration public class 配置类名{ @Bean @Profile("dev") //dev profile 装配的bean public 开发环境需要的Bean 方法名(){ return new 开发环境需要的Bean(); } @Bean @Profile("prod") //prod profile 装配的bean public 运行环境需要的Bean 方法名(){ return new 运行环境需要的Bean(); } }
4、在XML中配置Profile
1:每一个xml配置文件的<beans>元素添加 profile = ""
例如:
<beans xmlns = ""
...
profile = "dev">
<bean>...<bean>
</bean>
2:使用嵌套<beans>
例如:
<beans> <beans profile = "dev"> <bean>...<bean> </beans> <beans profile = "prod"> <bean>...<bean> </beans> <beans>
5、激活profile
配置profile的两个属性:
1:active(Spring.profiles.active):指定那个profile处于激活状态
2:default(Spring.profiles.default):如果没有配置active,会找default配置的值。
//如果都active和default都没有配置:不装配profile中的bean。
设置active和default属性:
1:作为DispatcherServlet的参数初始化
在web的Servlet配置中加:
<web-app ...>
<context-param>
<param-name>spring.profiles.default</param-name>
<param-value>dev</param-value>
</context-parm>
</web-app>
2:作为Web应用的上下文
3:作为JNDI条目
4:作为环境变量
5:作为JVM的系统属性
6:在集成测试类上,使用@activeProfiles注解设置
6、条件化的bean
需求:希望一个bean在含有某个外部的jar是才创建,希望某个特定的bean声明后才创建,(总之就是有条件的创建bean)
在Spring 4之前很难做到。但是Spring 4 引入了 @Conditional 注解,他可以用在带有@Bean的注解方法上。
@Conditional:
如果给定的计算结果为true则创建bean,否则bean被忽略。
例如:
//假设有一个名为 MagicBean 的类,我们希望只有设置了环境属性 magic 时才实例化这个bean。
1:条件话创建bean:
@bean @Conditional(MagicExistsCondition.class) //设置条件。 public MagicBean magicBean(){ return new MagicBean(); } //传给@Conditional的参数需要是Condition接口的实现。实现Condition接口只需要实现matches()方法, //matches()方法返回值就是判断条件的结果。
2:判断条件:
public class MagicExistsCondition implements Condition{ public boolean matches(ConditionContext context , AnnotatedTypeMetadata metadata){ Environment env = context.getEnvironment(); return env.containsProperty("magic"); } } //本例中判断环境中是否有magic。
注意:
上面的例子只是使用了ConditionContext获取到了环境属性。实际开发中获取会根据更为负责的内容判断(但来源基本都是matches的参数):
ConditionContext:
1:使用getRegistry()方法返回的BeanDefinitionRegistry检查bean的定义。
2:使用getBeanFactory()方法返回的ConfigurableListtableBeanFactory()检查bean是否存在,探测bean的属性。
3:使用getEnvironment()方法返回的ResourceLoader加载资源。
4:使用getClassLoader()返回的ClassLoader加载并检测类是否存在。
AnnotatedTypeMetadata:
这个接口可以检查带有@Bean的方法上还含有什么注解。
1:使用isAnnotated()方法可以判断带有@Bean的方法是不是还有别的注解。
2:借助别的方法可以检查方法上的其他注解及其属性。
7、处理自动装配的歧义性
自动装配时:只有一个Bean匹配到所需结果时才能装配成功,匹配到多个Bean阻碍了自动装配。
例如:
//需要自动装配的bean @Autowired public void setDessert(Dessert dessert){ this.dessert = dessert; }
//Dessert 是一个接口。并且有三个类实现了该接口并且都是用@Component指定了该类作为bean: @Component public class Cake implements Dessert{...} @Component public class Cookies implements Dessert{...} @Component public class IceCream implements Dessert{...}
//当Spring试图给setDessert进行自动注入时:无法匹配到唯一的bean,抛出异常(NoUniqueBeanDefinitionException)。
虽然出现这种歧义性的概率非常小,当发生这种情况时我们可以通过
1:给其中一个bean设置首选。2:使用限定符将可选的bean缩小的一个。
8、标注首选的bean
在声明bean的时候可以使用@Primary设置一个设置首选的bean,当出现自动装配的歧义性时会选择这个bean。
1:@Component 和 @Primary组合声明首选:
@Component @primary public class IceCream implements Dessert{...}
2:Bean配置 和 Primary组合声明首选:
2.1:使用javaConfig配置:
@Bean @Primary public Dessert iceCream(){ return new IceCream(); }
2.2:使用xml配置bean(使用XML配置bean同样可以实现:)
<bean id = "iceCream" class = "" primary = "true" />
注意:问题来了:如果同时配置了两个Primary,依然产生歧义。(无法最终锁定到一个bean上)
9、通过限定符将bean范围缩小。
????????????????
10、bean作用域:
Spring 应用上下文中所有的bean都是以单例的形式创建的,但是每次都调用一个实例是很不现实的(对象状态改变)。
Spring提供了多种作用域:可以基于这些作用域创建bean:
1:单例:在整个项目中只有一个bean实例(默认)
2:原型:每次注入或者通过Spring上下文获取的时候都会创建新的实例
3:会话:在Web应用中为每一个会话创建一个实例
4:请求:在Web应用中为每一个请求创建一个实例
11、选择bean的作用域:
1:@Component 和 @ Scope 结合
例如:
@Component @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) //声明作用域为 原型 public class xxx(...)
2:@Bean 和 @Scope 结合(在javaConfig中配置)
例如:
@Bean @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) public 需要的Bean 方法名(){ return new 需要的Bean(); }
使用Xml配置bean的情况:
<bean id = "" class = "" scope = "prototype" />
12、使用会话和请求作用域
?????????????????
13、在xml中声明作用域代理
?????????????????
14、运行时值注入:
之前描述bean时使用了:
@Bean
public 需要的Bean 方法名(){return new 需要的Bean();}
<bean id ="" class = ""><property name = "属性名称" value = "一个字符串"></bean>
无论是使用什么方法注入,其实都是将关联的内容写死了。这种硬编码有时候是行不通的。
Spring提供了两种运行时求值的方法。:
1:属性占位符
2:Spring表达式语言
15、注入外部的值:
在Spring中处理外部值的最简单的办法是声明属性源,并且使用Spring的Environment检索。
例如:
@Configuration @PropertySource("classPath:/com/xxx/yyy/app.properties") //声明属性源 public class 配置类名{ @Autowired Environment env; @Bean public 需要的Bean 方法名(){ return new 需要的Bean(env.getProperty("需要的属性")); //检索指定的属性 } }
16、Spring的Environment
Environment中的getProperty()方法有四个重载的方法:
1:String getProperty(String key)
2:String gegtProperty(String key , String defaultValue) //添加了默认值
3:T getProperty(String key , Class<T> type) //将获取到的值转化为T类型(例如String -> Integer)
4:T getProperty(String key , Class<T> type , T defaultValue)
如果没有找到key也没有指定默认值,这种情况需要报告给程序,抛出异常:
5:getRequiredProperty()
例如:
@Configuration @PropertySource("classPath:/com/xxx/yyy/app.properties") //声明属性源 public class 配置类名{ @Autowired Environment env; @Bean public 需要的Bean 方法名(){ return new 需要的Bean(env.getRequiredProperty("key")); //如果找不到,抛出异常(IllegalStateException) } }
如果想要将属性解析为类:
6:getPropertyAsClass()
例如:
Class<需要的类> clazz = evn.getPropertyAsClass("key" , 需要的类.class);
Environment还提供了检索哪些profile处于active状态(具体看书)。
17、解析属性占位符:
1:在xml中配置:
<bean id = "" class = "" c:_title = "${key1}" c:_title = "${key2}" /> //${key}将从某一个源获取。
2:如果使用组件扫描和自动装配(不配置xml了):
public BlankDisc (@Value("${key}") String title){ this.title = title; }
注意:
为了使用占位符,需要配置一个
PropertyPlaceholderConfigurer bean
或者
PropertySourcesPlaceholderConfigurer bean (推荐)
(具体看书)
18、使用Spring表达式语言
????????????????
以上是关于第3章 高级装配的主要内容,如果未能解决你的问题,请参考以下文章