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配置文件的 元素添加 profile = "" 例如:
2:使用嵌套 例如:
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配置中加:
spring.profiles.default dev
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同样可以实现:)
注意:问题来了:如果同时配置了两个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的情况:
12、使用会话和请求作用域 ????????????????? 13、在xml中声明作用域代理 ????????????????? 14、运行时值注入: 之前描述bean时使用了: @Bean public 需要的Bean 方法名(){return new 需要的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 type) //将获取到的值转化为T类型(例如String -> Integer) 4:T getProperty(String key , Class 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中配置:
2:如果使用组件扫描和自动装配(不配置xml了):
public BlankDisc (@Value("${key}") String title){ this.title = title; }
注意: 为了使用占位符,需要配置一个 PropertyPlaceholderConfigurer bean 或者 PropertySourcesPlaceholderConfigurer bean (推荐) (具体看书) 18、使用Spring表达式语言 ????????????????