zoukankan      html  css  js  c++  java
  • Spring实战拆书--SpringBean

    代码源码地址:https://github.com/wujiachengSH/springBeanDemo

    概述:本章将讲解Spring对于Bean的管理方案。

    目录:

    1. 准备工作
    2. 自动装配
    3. 处理装配歧义性
    4. bean的作用域
    5. 注入式声明Bean

    代码环境:

    1. Sts
    2. jdk1.8
    3. spring4

    1.准备工作

    请在github上下载源码结合文章阅读,效果更佳

    在创建了SpringBoot项目后,我们首先需要开启组件扫描,如下代码所示。

    @Configuration
    //扫描指定包目录
    @ComponentScan(basePackages="com.wjc")
    public class BeanConfig {
    }

    声明一个测试Bean的接口,全文的主要内容都是通过此接口的实现类完成的

    package com.wjc.spring.bean;
    
    public interface Bird {
    
        void fly();
        void feed();
        void twitter();
        void changeTwiter();
    }

    2.自动装配

      自动装配是最常见的Bean装配形式。

      我们首先写一个Bird接口的实现类来展示自动装配,只需一个“@Component”注解即可完成。

    package com.wjc.spring.bean.impl;
    
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.stereotype.Component;
    
    import com.wjc.spring.bean.Bird;
    //这是知更鸟
    @Component
    public class Robin implements Bird { private String flyStr ="知更鸟起飞"; private String feedStr = "不想吃东西"; private String twiterStr = "啊啊啊"; @Override public void fly() { System.out.println(flyStr); } @Override public void feed() { System.out.println(feedStr); } @Override public void twitter() { System.out.println(twiterStr); } @Override public void changeTwiter() { } }

    在测试时,我们只需要使用“@Autowired”注解,就可以拿到对应的对象了

    通过Junit可以测试装配是否完成

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes=BeanConfig.class)
    public class BeanTest {
    
        @Autowired
        private Bird bird;
        
        //测试1,查看是否自动装配了知更鸟
        //此时bean.impl只有robin
        @Test
        public void BeanTest1() {
            assertNotNull(bird);
        }
    }

    3.处理自动装配的歧义性(@Qualifier)

    代码源码地址:https://github.com/wujiachengSH/springBeanDemo

    试想如果我有2个Bird接口的实现类,spring在装配时是否会因为不知道具体需要哪个实现类而报错?

    此时声明一个“Parrot”,也实现bird接口,运行test方法会如何?

    package com.wjc.spring.bean.impl;
    
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.stereotype.Component;
    
    import com.wjc.spring.bean.Bird;
    //这是鹦鹉
    @Component
    public class Parrot implements Bird {
    
        private String flyStr ="鹦鹉起飞"; 
        private String feedStr = "啥都吃";
        private String twiterStr = "说人话";
        
        @Override
        public void fly() {
            System.out.println(flyStr);
        }
    
        @Override
        public void feed() {
            System.out.println(feedStr);
        }
    
        @Override
        public void twitter() {
            System.out.println(twiterStr);
        }
    
        @Override
        public void changeTwiter() {
            twiterStr = "你好你好";
        }
    
    }

    运行结果如下:

    org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.wjc.spring.test.BeanTest': Unsatisfied dependency expressed through field 'bird'; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.wjc.spring.bean.Bird' available: expected single matching bean but found 5: parrot,quail,robin,Cuckoo1,Cuckoo2
    	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:596)
    

     可以看到,由于Spring并不知道应该将哪一个实现类注入到bird中,报出了 “UnsatisfiedDependencyException”,我们可以通过注解“@Qualifier("parrot")”来解决此问题

    //这是鹦鹉
    @Component
    @Qualifier("parrot")
    public class Parrot implements Bird {

    在获取实现类时使用如下方式,即可获取到自己想要的对象实例了

        @Autowired
        @Qualifier("parrot")
        private Bird parrot;
        
        
        //添加@Qualifier("parrot")来解决声明问题
        @Test
        public void BeanTest3() {
            // 此时鹦鹉添加了@Primary
            parrot.fly();
            assertNotNull(parrot);
        }

    4.Bean的作用域

    已知Spring默认是单例模式,但在多线程高并发的情况下,单例模式其实未必是最佳选择,如果线程A将Bean赋了值,而此时线程B拿取了被A赋值的对象,并返回了对应的结果,此时是不是会出现B返回了预料之外的结果?

    本文简单讨论一下原型模式下Bean的传递,和会发生的问题,具体的各自作用域请百度“spring作用域”

    已知Spring作用域如下:singleton / prototype / request  / session /global session

     我们来看一下如下代码,一个原型模式的对象

    package com.wjc.spring.bean.impl;
    
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.context.annotation.Scope;
    import org.springframework.stereotype.Component;
    
    import com.wjc.spring.bean.Bird;
    
    //这是鹌鹑
    //这个使用原型模式
    @Component
    @Qualifier("Quail")
    @Scope("prototype")
    public class Quail implements Bird {
    
        private String flyStr ="鹌鹑起飞"; 
        private String feedStr = "鹌鹑想吃啥就吃啥";
        private String twiterStr = "鹌鹑不知道怎么叫";
        
        @Override
        public void fly() {
            // TODO Auto-generated method stub
            System.out.println(flyStr);
        }
    
        @Override
        public void feed() {
            // TODO Auto-generated method stub
            System.out.println(feedStr);
        }
    
        @Override
        public void twitter() {
            // TODO Auto-generated method stub
            System.out.println(twiterStr);
        }
    
        public void changeTwiter() {
            twiterStr = "我大鹌鹑今天就是饿死。。。。";
        }
        
    }

    看下在TEST时他的表现如何:

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes=BeanConfig.class)
    public class BeanTest3 {
    
        @Autowired
        @Qualifier("Quail")
        private Bird bird;
        
        @Autowired
        @Qualifier("Quail")
        private Bird bird2;
        
        //测试原型模式与单例的区别
        @Test
        public void BeanTest1() {
            bird.twitter();
            bird.changeTwiter();
            bird.twitter();
            
            bird2.twitter();
            bird2.changeTwiter();
            bird2.twitter();
        }
    }

    运行结果:

    鹌鹑不知道怎么叫
    我大鹌鹑今天就是饿死。。。。
    鹌鹑不知道怎么叫
    我大鹌鹑今天就是饿死。。。。

     spring确实将此Bean对象变成了原型模式。那么作用域是否就这么简单的完成了?

     我们看一下如下代码

    @Service
    public class BirdServiceImpl implements BirdService {
    
        @Autowired
        @Qualifier("Quail")
        private Bird bird;
    
        public void ScopTest() {
            bird.twitter();
            bird.changeTwiter();
            bird.twitter();
        }
    }

    运行测试类:

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes=BeanConfig.class)
    public class ServiceTest {
    
        @Autowired
        private BirdService birdService;
        
        @Autowired
        private BirdService birdService2;
        
        //测试在Service上添加和不添加@Qualifier("Quail")时调用的Bean的区别
        @Test
        public void ServiceTest2() {
            birdService.ScopTest();
            birdService2.ScopTest();
        }
    }

    运行结果:

    鹌鹑不知道怎么叫
    我大鹌鹑今天就是饿死。。。。
    我大鹌鹑今天就是饿死。。。。
    我大鹌鹑今天就是饿死。。。。

    ????????原型模式失效了????

    为什么会发生这种情况?因为在此场景下,“BirdServiceImpl”是单例模式的,对Bean的操作不可避免的变成了单例的,如果添加如下代码结果就会完全不一样

    @Service
    @Scope("prototype")
    public class BirdServiceImpl implements BirdService {
    
        @Autowired
        @Qualifier("Quail")
        private Bird bird;
    
        public void ScopTest() {
            bird.twitter();
            bird.changeTwiter();
            bird.twitter();
        }
    }

    再次运行时:

    鹌鹑不知道怎么叫
    我大鹌鹑今天就是饿死。。。。
    鹌鹑不知道怎么叫
    我大鹌鹑今天就是饿死。。。。

     假设“ServiceTest”方法为Control层,“BirdServiceImpl”方法为Service层,“Quail”为Bean,在实际应用时,应该考虑Scop注解是否会可以成功生效。

    如下为测试后的结果

        //当Service上有@Scope("prototype"),Bean上有@Scope("prototype")时 返回不同对象
        //当Service上有@Scope("prototype"),Bean上无@Scope("prototype")时 返回相同对象
        //当Service上无@Scope("prototype"),Bean上有@Scope("prototype")时 返回相同对象
        //当Service上无@Scope("prototype"),Bean上无@Scope("prototype")时 返回相同对象

    5.注入式声明Bean

    在上述代码中,我都是通过硬编码的形式在输入一些内容的,那么能否通过读取配置文件的方式完成输出内容呢?(实际运用场景:获取数据库连接对象Session)

    我们首先定义一个对象,可以看到我没有添加任何注解,因为此对象不需要在这里进行装配!

    package com.wjc.spring.bean.impl;
    
    import com.wjc.spring.bean.Bird;
    //这是杜鹃
    public class Cuckoo implements Bird {
    
        private String flyStr = "fly" ; 
        private String feedStr = "feed";
        private String twiterStr = "twiter";
        
        
        public Cuckoo(String flyStr, String feedStr, String twiterStr) {
            super();
            this.flyStr = flyStr;
            this.feedStr = feedStr;
            this.twiterStr = twiterStr;
        }
    
        @Override
        public void fly() {
            // TODO Auto-generated method stub
            System.out.println(flyStr);
        }
    
        @Override
        public void feed() {
            // TODO Auto-generated method stub
            System.out.println(feedStr);
        }
    
        @Override
        public void twitter() {
            // TODO Auto-generated method stub
            System.out.println(twiterStr);
        }
    
        @Override
        public void changeTwiter() {
            // TODO Auto-generated method stub
            twiterStr = "杜鹃";
        }
    
    }

    我们将Config改造一下,由他来负责装配对象

    package com.wjc.spring.config;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.PropertySource;
    import org.springframework.core.env.Environment;
    
    import com.wjc.spring.bean.impl.Cuckoo;
    
    
    @Configuration
    //扫描指定包目录
    @ComponentScan(basePackages="com.wjc")
    @PropertySource("classpath:Cuckoo.properties")
    public class BeanConfig {
        //开启组件扫描
        //获取资源
        @Autowired
        private Environment env;
        
        //通过配置文件装配Cuckoo
        @Bean(name="Cuckoo1")
        public Cuckoo getbird() {
            
              return new Cuckoo(env.getProperty("flyStr","fly"),
              env.getProperty("feedStr","feed"), env.getProperty("twiterStr","twiter"));
             
            //return new Cuckoo("fly","feed", "twiter");
            
        }
        @Bean(name="Cuckoo2")
        public Cuckoo getbird2() {
             
            return new Cuckoo("fly","feed", "twiter");
            
        }
        
    }

    可以看到我声明了2个"Cuckoo"对象实例,分别叫“Cuckoo1”,“Cuckoo2”

    使用Test方法来执行一下

    package com.wjc.spring.test;
    
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    import com.wjc.spring.bean.impl.Cuckoo;
    import com.wjc.spring.config.BeanConfig;
    
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes=BeanConfig.class)
    public class BeanTest4 {
    
        @Autowired
        @Qualifier("Cuckoo2")
        private Cuckoo Cuckoo;
        
        @Autowired
        @Qualifier("Cuckoo1")
        private Cuckoo Cuckoo1;
        //测试通过配置文件装配Bean
        @Test
        public void BeanTest1() {
            Cuckoo1.fly();
            Cuckoo1.feed();
            Cuckoo1.twitter();
            Cuckoo.fly();
            Cuckoo.feed();
            Cuckoo.twitter();
        }
        
    }

    执行结果

    cuckoo fly
    cuckoo feed
    cuckoo twiter
    fly
    feed
    twiter

    可以看到成功的声明了对象。

    本文章为在下在查看SPRING实战一书和工作上发生过的问题结合来完成的。希望各位看官指点错误和不合理的地方。

    代码源码地址:https://github.com/wujiachengSH/springBeanDemo

  • 相关阅读:
    应用js改变问章字体大小
    在北京实习的日子
    php算法
    SQL Server 查询处理中的各个阶段(SQL执行顺序)
    130 个你需要了解的 vim 命令
    分享一个检测用户是否用手机(Mobile)访问网站的 PHP 类
    使用apache自带日志分割模块rotatelogs,分割日志
    centos下lvs配置
    分享codeigniter框架,在zend studio 环境下的代码提示
    vi 常用命令
  • 原文地址:https://www.cnblogs.com/wujc/p/10466268.html
Copyright © 2011-2022 走看看