第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表达式语言
????????????????

原文地址:https://www.cnblogs.com/Xmingzi/p/8856728.html