Spring實戰拆書--SpringBean

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

概述:本章將講解Spring對於Bean的管理方案。java

目錄:git

  1. 準備工做
  2. 自動裝配
  3. 處理裝配歧義性
  4. bean的做用域
  5. 注入式聲明Bean

代碼環境:github

  1. Sts
  2. jdk1.8
  3. spring4

 

1.準備工做

請在github上下載源碼結合文章閱讀,效果更佳spring

在建立了SpringBoot項目後,咱們首先須要開啓組件掃描,以下代碼所示。數據庫

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

聲明一個測試Bean的接口,全文的主要內容都是經過此接口的實現類完成的express

package com.wjc.spring.bean;

public interface Bird {

    void fly();
    void feed();
    void twitter();
    void changeTwiter();
}

 


 

2.自動裝配

  自動裝配是最多見的Bean裝配形式。session

  咱們首先寫一個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能夠測試裝配是否完成ide

@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

相關文章
相關標籤/搜索