zoukankan      html  css  js  c++  java
  • Spring学习之旅(四)--高级装配Bean

    条件化 bean

    有时候我们要满足某种情况才将bean 初始化放入容器中。

    基于环境初始化不同的 bean

    1.申明接口并创建两个实现类

    public interface Teacher {
    
        void startWorking();
    }
    
    public class JavaTeacher implements Teacher {
    
        public void startWorking() {
            System.out.println("开始 Java 教学");
        }
    }
    
    public class SqlTeacher implements Teacher {
    
        public void startWorking() {
            System.out.println("开始 SQL 教学");
        }
    }
    

    2.JavaConfig 显式装配两个实现类

    @Configuration
    public class ApplicationConfig {
    
    
        @Bean(name = "teacher")
        @Profile("Monday")
        public Teacher sqlTeacher() {
            return new SqlTeacher();
        }
    
    
        @Bean(name = "teacher")
        @Profile("Tuesday")
        public Teacher javaTeacher() {
            return new JavaTeacher();
        }
    
    }
    

    注:可以看到两个 bean 都取名为 teacher,但是 @Profile 值不同。

    3.单元测试

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = ApplicationConfig.class)
    @ActiveProfiles("Monday")
    //@ActiveProfiles("Tuesday")
    public class ConditionTest {
        @Autowired(required =  false)
        private Teacher teacher;
    
        @Test
        public void test01(){
            if(teacher != null){
                teacher.startWorking();
            }
        }
    }
    

    执行单元测试,查看控制台输出:

    开始 SQL 教学
    

    通过 @ActiveProfiles 配置当前的环境,我们就能做到 周一 SQL教学 周二 Java 教学 了。

    激活 Profile 方式

    Spring 激活 Profile 依赖两个属性:

    • spring.profiles.active
    • spring.profiles.default

    active 优先级比 default 高,如果两个属性都没有设置的话,将只会创建没有定义 profilebean

    除了 @ActiveProfiles 配置外,我们还能通过很多种方式来设置属性:

    • 作为 DispatcherServlet 的初始化参数;
    • 作为 Web 应用的上下文参数;
    • 作为 JNDI 条目;
    • 作为环境变量;
    • 作为 JVM 的系统属性;

    自定义提交

    除了环境之外,我们也可以自定义条件来决定初始化那个 Bean。

    例如刚刚的实例,老师开始 Java教学 ,除了时间是周二之外还应该有学生在,才能完成教学。

    @Configuration
    public class ApplicationConfig {
    
        @Bean(name = "teacher")
        @Conditional(MyCondition.class)
        public Teacher javaTeacher() {
            return new JavaTeacher();
        }
    }
    

    通过 @Conditional 注解来自定义条件,MyCondition 是我们实现 Condition 接口完成的自定义条件判断

    public class MyCondition implements Condition {
    
        public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
            Environment environment = conditionContext.getEnvironment();
            // 1.获取属性值
            List activeProfiles = Arrays.asList(environment.getActiveProfiles());
            List defaultProfiles = Arrays.asList(environment.getDefaultProfiles());
            // 2.判断属性是否存在
            boolean activeFlag = activeProfiles.contains("Tuesday") && activeProfiles.contains("students");
            boolean defaultFlag = defaultProfiles.contains("Tuesday") && defaultProfiles.contains("students");
            return activeFlag || defaultFlag;
        }
    }
    

    单元测试:

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = ApplicationConfig.class)
    @ActiveProfiles({"Tuesday","students"})
    public class ConditionTest {
        @Autowired(required =  false)
        private Teacher teacher;
    
        @Test
        public void test01(){
            if(teacher!=null){
                teacher.startWorking();
            }
        }
    }
    

    控制台输出:

    开始 Java 教学
    

    ConditionContext 方法

    public interface ConditionContext {
    
        BeanDefinitionRegistry getRegistry();
    
        ConfigurableListableBeanFactory getBeanFactory();
    
        Environment getEnvironment();
    
        ResourceLoader getResourceLoader();
    
        ClassLoader getClassLoader();
    }
    
    • getRegistry():返回 BeanDefinitionRegistry 检查 bean 定义;
    • getBeanFactory():返回 ConfigurableListableBeanFactory 检查 bean 是否存在和它的属性;
    • getEnvironment():返回 Environment 检查环境变量是否存在以及它的值;
    • getResourceLoader():返回 ResourceLoader 所加载的资源;
    • getClassLoader():返回 ClassLoader 加载并检查类是否存在

    处理自动装配歧义性

    在之前我们讲到自动装配如果匹配到多个 bean 的话,Spring 会抛出一个 NoUniqueBeanDefinitionException 异常,表示没有明确指明使用哪个 bean 来进行自动装配。

    标识首选 bean

    通过 @Primart 注解可以将一个 bean 设置为首选,但是要注意的是,如果出现多个 @Primart 也还是会抛出异常。

    限定自动装配的 bean

    @Primart 注解不能将可选范围缩小到唯一一个无歧义的选项中。我们可以通过限定符的形式进行范围的缩小,直到唯一为止。

    @Qualifier 注解是使用限定符的主要方式。它可以与 @Autowired 注解协同使用,在注入 bean 的时候指定注入的是那个 bean

    public interface Teacher {
    
        void startWorking();
    }
    
    @Component
    public class SqlTeacher implements Teacher {
    
        public void startWorking() {
            System.out.println("开始 SQL 教学");
        }
    }
    
    @Component
    public class JavaTeacher implements Teacher {
    
        public void startWorking() {
            System.out.println("开始 Java 教学");
        }
    }
    
    @Autowired
    @Qualifier("javaTeacher")
    private Teacher teacher;
    
    @Test
    public void test01() {
        teacher.startWorking();
    }
    

    @Qualifier 注解的参数就是想要注入的 beanid。如果 @Component 没有显示指定 bean 的 Id ,那么默认的 Id 是类型首字母小写。这跟类名就有耦合性了,我们可以在类上加 @Qualifier(name) 的形式设置别名避免和类名耦合。

    自定义限定符

    要注意的是 @Qualifier 也可能出现重复,我们可以通过增加多个条件来进一步缩小范围,但是 Java 不支持在同一个条目上出现相同类型的多个注解,我们可以通过自定义注解的形式来解决这个问题。

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

    我们定义了一个 Monday 注解,我们来使用一下它:

    @Component
    @Monday
    public class JavaTeacher implements Teacher {
    
        public void startWorking() {
            System.out.println("开始 Java 教学");
        }
    }
    

    使用:

    @Autowired
    @Monday
    private Teacher teacher;
    

    bean 作用域

    默认情况下 Spring 应用上下文中的所有 bean 都是以单例形式注入,但是在某些情况下可能就不太适用。比如购物车组件这种有状态值的,针对每一个用户应该是不同的实例。

    Spring 定义了多种作用域:

    • 单例(Singleton): 在整个应用中,只创建 bean 的一个实例;
    • 原型(Prototype):在每次注入或者通过 Spring 应用上下文获取的时候,都会创建一个实例;
    • 会话(Session):在 Web 应用中,为每个会话创建一个 bean 的实例;
    • 请求(Rquest):在 Web 应用中,为每一个请求创建一个 bean 的实例。

    如何指定作用域

    通过使用 @Scope 注解或 scope标签 可以配置作用域。

    运行时注入属性

    Spring 提供了两种在运行时赋值的方式:

    • 属性占位符(Property plaveholder)
    • Spring 表达式语言(SpEL)

    它们都可以在应用运行时在获取值,避免硬编码的存在。

  • 相关阅读:
    http://www.reg007.com/
    快速入门:十分钟学会Python(转)
    Python入门教程 超详细1小时学会Python(转)
    值得关注的10个python语言博客(转)
    【.NET特供-第三季】ASP.NET MVC系列:MVC与三层图形对照
    LeetCode——Spiral Matrix
    HTML中Select的使用具体解释
    为什么没有好用的Android游戏引擎?
    Map.EntrySet的使用方法
    jquery 仅仅读
  • 原文地址:https://www.cnblogs.com/markLogZhu/p/11400356.html
Copyright © 2011-2022 走看看