• Spring使用笔记(三) 高级装配


    高级装配

    一、环境与Profile

    一)配置profile bean

    环境的改变导致配置改变(需求:通过环境决定使用哪个bean),可以通过Spring的Profile解决。

    Profile可以在程序运行时根据环境的改变决定使用哪个bean。所以一个部署单元能适应所有环境。

    1.在Java文件中配置 

    @Configuration
    public class UserConfig {
        @Bean
        @Profile("dev") //dev激活时才创建使用该bean
        public User devUser() {
            return new User("devUser", 110);
        }
        @Bean
        @Profile("pro")
        public User proUser(){
            return new User("proUser", 110);
        }
    }
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = UserConfig.class)
    @ActiveProfiles("dev") //指定测试时使用的bean
    public class TestAll {
        @Autowired
        private User user;
        @Test
        public void doSth() {
            System.out.println(user.getName() + " : " + user.getPhone()); //devUser : 110
        }
    }

    2.在xml中配置

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           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" profile="dev">
        <bean class="entity.User">
            <constructor-arg name="name" value="devUserXml"/>
            <constructor-arg name="phone" value="111"/>
        </bean>
    </beans>

    方式二:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           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">
        <beans profile="dev">
            <bean class="entity.User">
                <constructor-arg name="name" value="devUserXml2"/>
                <constructor-arg name="phone" value="11111"/>
            </bean>
        </beans>
        <beans profile="pro">
            <bean class="entity.User">
                <constructor-arg name="name" value="proUserXml2"/>
                <constructor-arg name="phone" value="222"/>
            </bean>
        </beans>
    </beans>

    二)激活Profile

    首先要配置两个独立的属性:

    1.spring.profiles.active

    2.Spring.profiles.default

    有多种方式设置这两种属性,具体设置方式请查阅相关文档,注意:

    如果设置成了spring.profiles.active后,spring.profiles.default设置成什么值都无所谓,系统优先使用前者的设置。

    二、条件化的bean

    如果我们要求:一个或者多个bean

    1.在类路径下包含特定的的库时

    2.在某个特定的bean声明之后

    3.特定的环境变量设置之后

    才创建。显然profile不能满足要求。

    我们可以使用@Condtional注解。

    @Configuration
    public class UserConfigWithCondition {
        @Bean
        //如果给定类中的matches方法返回true就创建此bean
        @Conditional(MyCondition.class)
        public User conditionUser() {
            return new User("conditionUser", 22);
        }
    }
    /*
    * 必须实现Condition接口
    * */
    public class MyCondition implements Condition{
        public boolean matches(ConditionContext conditionContext,
                               AnnotatedTypeMetadata annotatedTypeMetadata) {
            return true;
        }
    }
    public interface ConditionContext {
        //返回的类可检查bean的定义
        BeanDefinitionRegistry getRegistry();
        //返回类可检查bean是否存在,甚至探测其属性
        @Nullable
        ConfigurableListableBeanFactory getBeanFactory();
        //检查环境变量
        Environment getEnvironment();
        //探查ResourceLoader所加载的资源
        ResourceLoader getResourceLoader();
        @Nullable
        ClassLoader getClassLoader();
    }
    public interface AnnotatedTypeMetadata {
        //是否有给定名字的注解
        boolean isAnnotated(String var1);
        @Nullable
        Map<String, Object> getAnnotationAttributes(String var1);
        @Nullable
        Map<String, Object> getAnnotationAttributes(String var1, boolean var2);
        @Nullable
        MultiValueMap<String, Object> getAllAnnotationAttributes(String var1);
        @Nullable
        MultiValueMap<String, Object> getAllAnnotationAttributes(String var1, boolean var2);
    }

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

    如果有多个bean能够匹配结果的话,这种歧义性会阻碍Springle的自动装配。

    解决方案:

    1.指定首选的bean:

    多个bean情况

    @Configuration
    public class UserConfig {
        @Bean
        public User user() {
            return new User("firstUser", 1);
        }
        @Bean
        @Primary
        public User user2() {
            return new User("secondUser", 2);
        }
        @Bean
        public User user3() {
            return new User("thirdUser", 3);
        }
    }
    @Autowired
    private User user;
    @Test
    public void doSth() {
        System.out.println(user.getName() + " : " + user.getPhone()); //secondUser : 2
    }

    多个实现类的情况:

    public interface Schooler {
        void introduce();
    }
    
    @Component
    @Primary
    public class Teacher implements Schooler{
        public void introduce() {
            System.out.println("I'm a teacher.");
        }
    }
    
    @Component
    public class Student implements Schooler{
        public void introduce() {
            System.out.println("I'm a student.");
        }
    }
    
    @Component
    public class Worker implements Schooler{
        public void introduce() {
            System.out.println("I'm a worker." );
        }
    }
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = AutoConfig.class)
    public class TestAll {
        @Autowired
        private Schooler schooler;
        @Test
        public void t() {
            schooler.introduce(); //I'm a teacher.
        }
    }

    2.限定自动装配的Bean 

    1)根据bean的id

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = AutoConfig.class)
    public class TestAll {
        @Autowired
        @Qualifier("student") //根据bean的id来注入,这里使用的是默认id
        private Schooler schooler;
        @Test
        public void t() {
            schooler.introduce(); //I'm a student.
        }
    }

    创建自定义限定符:

    @Configuration
    public class UserConfig {
        @Bean
        @Qualifier("First")
        public User user() {
            return new User("firstUser", 1);
        }
        @Bean
        @Qualifier("Second")
        public User user2() {
            return new User("secondUser", 2);
        }
        @Bean
        @Qualifier("Third")
        public User user3() {
            return new User("thirdUser", 3);
        }
    }
    @Autowired
    @Qualifier("First")
    private User user;
    @Test
    public void test() {
        System.out.println(user.getName()); //firstUser
    }

    三)Bean的作用域

    默认情况下,Spring应用中所有bean都是作为单例形式创建的。

    Spring创建了多种作用域,可以基于这些作用域创建bean。

    单例(Singleton):

    原型(Prototype):每次注入或者通过上下文获取的时候,都会创建一个新的实例。

    会话(Session):在Web应用中,为每个会话创建一个bean实例。

    请求(request):在Web应用中,为每个请求创建一个bean实例。

    其他略

    四)运行时注入

    为了注入字面量,且要避免硬编码(让这些值在运行时确定),Spring提供了两种运行时求值得方法:

    1)属性占位符

    2)Spring表达式语言(SpEl)

    1.处理外部值

    处理外部值最简单的方式是声明属性源并通过Spring的Environment来检索属性。

    app.properties:

    name = tang
    @Configuration
    @PropertySource("classpath:/app.properties")
    public class UserConfig {
        @Autowired
        private Environment e;
        @Bean
        public User user() {
            return new User(e.getProperty("name"), 22);
        }
    }
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = UserConfig.class)
    public class TestAll {
        @Autowired
        User user;
        @Test
        public void t() {
            System.out.println(user.getName()); //tang
        }
    }

    xml中配置:

    <bean id="propertyConfigurer"
          class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations">
            <list>
                <value>app.properties</value>
            </list>
        </property>
    </bean>
    <bean class="entity.User" c:name="${aName}" c:phone="22"/>

    如果我们使用组件扫描和和自动装配的话,可以使用@Value来实现:

    @Component
    @PropertySource("classpath:/app.properties")
    public class Worker implements Schooler{
        private String name;
        private int phone;
        public Worker(@Value("${name}") String name, @Value("${phone}") int phone) {
            this.name = name;
            this.phone = phone;
        }
        public void introduce() {
            System.out.println("I'm a worker. And my name is " + name);
        }
    }
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = AutoConfig.class)
    public class TestAll {
        @Autowired
        Worker worker;
        @Test
        public void t() {
            worker.introduce(); //I'm a worker. And my name is tang
        }
    }

    2.使用Spring表达式语言进行装配 

    Spring Expression Language(SqEl)能将值装配到bean属性和构造器参数中,表达式会在运行时得到值。

    在装配bean的时候如何使用这些表达式:

    1)通过XML使用:

    <bean id="aUser" class="entity.User">
        <constructor-arg name="name" value="Peter"/>
        <constructor-arg name="id" value="222"/>
    </bean>
    <bean id="sys" class="entity.System">
        <property name="name" value="aSystem"/>
        <property name="user"  value="#{aUser}"/>
    </bean>

    2)通过自动扫描:

    @Component("userBean")
    public class User {
        @Value("tom")
        private String name;
        @Value("111")
        private int id;
    
        public User(String name, int id) {
            this.name = name;
            this.id = id;
        }
    }
    @Component("sys")
    public class System {
        @Value("#{userBean}")
        private User user;
        @Value("TarSys")
        private String name;
    
        @Override
        public String toString() {
            return "System " + name + " have a user named " + user.getName();
        }
    }

    基础表达式:

    1)表示字面量

    #{3.14159}

    #{9.18E4}

    #{‘Hello’}

    #{true}

    2)引用bean的属性方法

    #{user.name}

    #{user.getName()}

    #{user.getName().toUpperCase()}

    #{user.getName().?toUpperCase()}   //保证左边的值不为null,如果为null不调用右边的方法,直接返回null

    3)表达式中使用类型

    #{T(java.lang.Math).random()}

    4)spEl运算符

     略

  • 相关阅读:
    react 组件间通信,父子间通信
    微信公众号配置及微信jsAPI支付
    Vue 幸运大转盘
    Hystrix断路器配置属性解析
    使用熔断器仪表盘监控(hystrix)
    使用熔断器防止服务雪崩
    创建服务消费者(Feign)
    Spring Cloud Ribbon配置详解
    创建服务消费者(Ribbon)
    创建服务提供者
  • 原文地址:https://www.cnblogs.com/Shadowplay/p/10112690.html
Copyright © 2020-2023  润新知