【Spring】史上最全的spring IoC使用講解

目錄

一、配置spring容器(將bean裝配到spring容器中)

1.1、XML方式

1.2、註解方式

1.3、JavaConfig

1.4、混合使用

1.5、實現bean的單獨註冊

1.6 插一句

二、自動裝配

2.1、使用XML來完成自動裝配

2.1.1、byType

2.1.2、byName

2.1.3、no和default

2.1.4、constructor

2.2、使用JavaConfig和註解來進行自動裝配

2.2.1 @Autowired

2.2.2 @Resource

三、spring懶加載

3.1 使用XML設置

3.2 使用註解設置

3.3 懶加載與非懶加載的優缺點

四、spring作用域

4.1 Singleton Beans with Prototype-bean Dependencies

4.1.1 ApplicationContextAware接口

4.1.2 Lookup Method Injection

五、spring生命週期和回調

5.1 Initialization Callbacks(初始化回調)

5.2 Destruction Callbacks(銷燬回調)

5.3 Default Initialization and Destroy Methods(默認初始化和銷燬方法)

5.3.1 XML(init-method和destroy-method)

5.3 2 註解(@PostConstruct和@PreDestory)

六、其他

6.1 @ComponentScan標籤的其他用法

6.2 byType多個同類型的Bean衝突解決辦法

6.2.1、@Primary

6.2.2、第二種方法使用限定註解@Qualifier

6.3 加速spring掃描類的速度

6.4 @Bean註解

配置類

6.5 @Profile標籤


一、配置spring容器(將bean裝配到spring容器中)

使用ioc首先就要配置好spring容器,要告訴容器我需要它管理的bean是什麼,以及bean之間的依賴關係

配置spring(初始化spring環境)的方式有三種(spring的編程風格):

  1. schemal-based-------xml
  2. annotation-based-----annotation
  3. java-based----java Configuration

 

我們首先創建好項目

IndexDao接口:

package priv.cy.dao;
public interface IndexDao {
    public void test();
}

 

IndexDao實現類:

package priv.cy.dao;
public class IndexDaoImpl implements IndexDao {
    private String str;

    @Override
    public void test() {
        System.out.println(str);
        System.out.println("Impl");
    }
    public void setStr(String str) {
        this.str = str;
    }
    public String getStr() {
        return str;
    }
}

Service類:

package priv.cy.dao;
public class IndexService {
    private IndexDao dao;

    public IndexService(IndexDao dao) {
        this.dao = dao;
    }
    public void service() {
        dao.test();
    }
}

 

 

1.1XML方式

首先講第一種,使用XML

先在resources創建一個XML,XML保存到resource包中,XML文件的名字可以任意,這裏命名爲spring.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"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <!--上面這些叫做命名空間-->

       <!--在bean標籤中指定要裝配給spring容器的類,在class屬性寫好類的全限定名,設置其id,如果不設置name默認name和id相同-->
    <bean id="dao" class="priv.cy.dao.IndexDaoImpl">
        <property name="str" value="aaa"></property>
    </bean>

    <bean id="service" class="priv.cy.dao.IndexService">
        <!--使用構造方法注入-->
        <constructor-arg ref="dao"></constructor-arg>
	        <!--使用set方法注入,這裏配置的是service這個類依賴的對象,ref中寫的是xml文件中定義的該對象的id-->
                   <property name="dao" ref="dao"></property>
    </bean>
</beans>

 

在這個XML文件中來指定要委託給spring容器管理的對象,然後指定對象之間的依賴關係,依賴關係是通過注入的方式來實現的,這裏再講一下注入方式,一共有三種

  1. 構造方法注入。
  2. setter方法注入。
  3. 接口注入

接口注入在spring3提供的,現在的spring版本已經取消了

 

上面展示了setter方法注入(使用<property>標籤)和構造方法注入(使用<constructor-arg>標籤),這兩種選一種使用就可以。注意,如果依賴的是已經交給spring容器管理的bean(即已經在xml中配置過的bean),依賴關係用ref,等號右邊寫定義的name。配置bean的時候如果只定義了id,那麼它的name默認和id一樣。

 

如果注入的不是被spring容器管理的bean,就不要用ref,比如indexDaoImpl依賴一個String對象,就用value後面直接寫String的值就可以了。

 

通過上面的配置,spring容器已經接管了我們的bean

創建測試類進行測試,注意創建spring容器對象時構造方法傳入的是classpath:spring.xml。

package priv.cy.dao;
public class Test {
    public static void main(String[] args) {
        /**
         * XML是放到resource包下的,這樣編譯後纔會被複制到classpath路徑下,XML的編譯就是原樣複製,.java的編譯時編程.class再放到classpath目錄中
         */
        // 如果使用了XML,就需要用這個類來獲取Spring容器,通過該對象來獲取bean
        ClassPathXmlApplicationContext classPathXmlApplicationContext
                = new ClassPathXmlApplicationContext("classpath:spring.xml");

        IndexService service = (IndexService) classPathXmlApplicationContext.getBean("service");
        service.service();
    }
}

通過spring容器獲取bean,實現了IOC控制反轉

 

1.2、註解方式

再講第二種。使用註解的方式來配置spring容器

首先註解的方式依舊還是需要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"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <!--上面這些網址叫做命名空間-->

    <!--剛剛使用的是XML的方式來管理spring容器,下面講解用註解的方式管理容器-->
    <!--還可以使用註解來管理spring容器,但是spring默認是關閉註解支持的,並且不會去掃描註解,這麼做是爲了節約性能-->
    <!--開啓支持註解解析-->
    <context:annotation-config></context:annotation-config>
    <!--開啓掃描com包下面的所有註解-->
    <context:component-scan base-package="priv"></context:component-scan>

      <!--以前spring要使用註解管理spring容器這兩條必須要寫,但是現在把第一條合併到第二條了,所以現在寫下面這一條就可以了-->
 
</beans>

 

Dao類:

通過註解,將IndexDaoImpl裝配到Spring容器中去,可以通過@Value標籤給依賴的String對象注入常量值

package priv.cy.dao;
import org.springframework.stereotype.Component;

// 這裏可以指定放入容器之後這個類的id是dao,如果不寫的話默認就是類名
@Component("dao")
public class IndexDaoImpl implements IndexDao {
    @Value("aaa")
    private String str;
    @Override
    public void test() {
        System.out.println(str);
        System.out.println("Impl  0");
    }
    public void setStr(String str) {
        this.str = str;
    }
    public String getStr() {
        return str;
    }
}

 

Service類:

通過註解將該類裝配到Spring容器中,並且直接通過@Autuwired註解將依賴的對象注入給IndexDaoImpl

package priv.cy.dao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
// 可以在括號裏指定他的id,如果不指定id默認就是類名
@Service
public class IndexService {
    @Autowired // 默認使用的是構造方法進行注入
    private IndexDao dao;

    public IndexService(IndexDao dao) {
        this.dao = dao;
    }
    public void service() {
        dao.test();
    }
}

 

測試類:

也就是使用解析XML的那個類來獲取spring容器對象,進而來獲取bean

package priv.cy.dao;
public class Test {
    public static void main(String[] args) {
        /**
         * XML是放到resource包下的,這樣編譯後纔會被複制到classpath路徑下,XML的編譯就是原樣複製,.java的編譯時編程.class再放到classpath目錄中
         */
        // 如果使用了XML,就需要用這個類來獲取Spring容器,通過該對象來獲取bean
        ClassPathXmlApplicationContext classPathXmlApplicationContext
                = new ClassPathXmlApplicationContext("classpath:spring.xml");

        IndexService service = (IndexService) classPathXmlApplicationContext.getBean("service");
        service.service();
    }
}

AnnotationConfigApplicationContext和ClassPathXmlApplicationContext這兩個對象的getBean方法傳入的參數可以是beanname,也可以是xxx.class,即可以通過name來獲取bean,也可以通過type來獲取bean。如果傳入的是xx.class,那麼返回的對象就是這個類型,不需要強轉,如果傳入的是name,那麼返回的類型就是Object,需要強轉,除非在傳入的參數中添加一個class參數指名bean的類型。例如:

IndexDaodao = annotationConfigApplicationContext.getBean("indexDao", IndexDao.class);

 

1.3JavaConfig

最後講一下javaConfig的當時來管理spring容器,因爲從上面兩種可以看出,都是需要有XML文件的,即使是註解方式,也需要有XML文件來開啓支持註解。但是javaConfig方法,就可以完全不需要XML文件。

package priv.cy.dao;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
 * 前面講的是XML和註解的管理spring的方式,可是註解的方式本身還需要通過XML來開啓註解支持,所以本質還是離不開XML
 * 所以就可以使用Javaconfig的方式,直接創建一個Java類來管理spring,完全不需要XML
 */
// 將該類設置爲一個配置類,該類就相當於一個XML
@Configuration
// 開啓掃描,指定要掃描哪些包,就和XML中開啓掃描的作用是一樣的,如果不開啓就沒有辦法使用註解。必須要指定掃描位置
@ComponentScan("priv")
public class Spring {
}

通過這個JavaConfig類,也就開啓了註解支持,後面的描述bean之間的依賴關係就可以直接使用註解了,完全不需要XML

 

在獲取spring容器對象的時候會有些不同,因爲spring解析XML和解析javaConfig功能的實現是使用的不同的類,所以通過javaConfig配置spring容器的話,獲取spring容器對象的時候需要使用另一個對象,注意傳入的是spring.class。

AnnotationConfigApplicationContext(通過註解的方式初始化應用程序上下文)這個類本身是支持解析註解的功能的,之前的ClassPathXmlApplicationContext(通過XML的方式初始化應用程序上下文)不支持解析註解,所以在之前如果要使用註解,還需要在XML開啓支持解析註解但是AnnotationConfigApplicationContext類本身就支持解析註解,所以也就不用再從XML中開啓了。只需要在javaConfig類中指定要掃描的位置就可以了

測試類:

package priv.cy.dao;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
    public static void main(String[] args) {
        //如果沒有使用XML,使用的Javaconfig的方式來進行管理spring的,就不能使用上面的方法來獲取Spring容器了,要使用這個類來獲取
        AnnotationConfigApplicationContext annotationConfigApplicationContext
                = new AnnotationConfigApplicationContext(Spring.class);
        IndexService service1 = (IndexService) annotationConfigApplicationContext.getBean("service");
        service.service();
    }
}

 

1.4、混合使用

也可以混合使用,即用Javaconfig也用XML,不用註解將indexDaoImpl裝載到spring容器了,也不用註解來標識各個bean之間的依賴關係,而是直接通過XML來用bean標籤進行裝載

然後在Javaconfig那個類上再加一個註解@ImportResource("classpath:spring.xml"),表示也根據xml的配置來管理spring容器,解析這個XML,進行混合使用

package priv.cy.dao;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan("priv")
@ImportResource("classpath:spring.xml")
public class Spring {
}

我不是很喜歡寫XML,所以我一般就是使用javaConfig的方式和註解的方式來配置spring容器

 

 

1.5、實現bean的單獨註冊

所謂的初始化spring環境就是將我們交給spring管理的類實例化

這裏註冊的意思其實就是裝配

 

我們已經講了上面幾個初始化spring的環境的方法

1、xml    ClassPathXmlApplicationContext   提供了類的掃描和單獨bean的註冊(就是在xml定義一個類的bean標籤,spring容器就會完成這個類的聲明和註冊,也就是實例化)這兩個功能,xml可以不用掃描代碼就可以完成對指定bean的裝配,只要是在xml聲明瞭,它就會對這個類完成註冊,實現實例化。

2、annotation  使用註解

3、javaConfig  AnnotationConfigAoolicationContext    類的掃描    類的定義(Java代碼)     不能單獨註冊一個類,就是說必須進行掃描,纔可以實現類的定義,類的掃描和類的定義是不能分開的。從代碼層面就是說如果我們不開啓掃描註解,我們添加的註解就是無效的。

 

但實際上spring給Annotation提供了單獨註冊方法,只不過用的很少,可以不使用掃描直接去將指定的類在spring容器中註冊,完成bean的裝配。我們可以使用register這個方法來完成對bean的單獨註冊

 

先說一下register這個方法:

javaConfig:

@Configuration
@ComponentScan("priv")
public class AppConfig {
}

 

service:

@Service
public class IndexService {
    public void service() {
        System.out.println("service");
    }
}

 

測試類:

public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext
                = new AnnotationConfigApplicationContext();

        // 實現類的單獨註冊,不需要掃描註解就可以將其來spring容器中實例化
         annotationConfigApplicationContext.register(AppConfig.class);
        // 使用register方法來實現類的單獨註冊必須要refresh
        annotationConfigApplicationContext.refresh();

        System.out.println(annotationConfigApplicationContext.getBean(IndexService.class).getClass().getName());
    }
}

 執行結果:

 

我們發現直接將配置JavaConfig配置文件直接註冊進register方法也是可以實現相同的效果,掃描註解,實現裝配,但是使用register方法進行註冊必須要手動refreshsh。這些都可以從源碼中找到原因。

 

這個是AnnotationConfigApplicationContext類的構造方法源碼,也就是我們平時將javaConfig的class傳入進行spring容器配置的方法。

public  AnnotationConfigApplicationContext(Class<?>...annotatedClasses) {
	this();
	register(annotatedClasses);
	refresh();
}

我們發現原有的構造方法本質也是調用的register方法實現的配置文件註冊,並且幫我們refresh了,所以如果我們直接調用register方法,我們還需要自己手動refresh。

那麼如何使用register實現單獨bean註冊呢,很簡單

我們不傳入javaConfig類,並且將service類上的@Service註解去掉,看看如何在無註解和不進行掃描的情況下手動將我們的bean裝配到spring容器中

public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext
                = new AnnotationConfigApplicationContext();

        // 實現類的單獨註冊,不需要掃描註解就可以將其來spring容器中實例化
        annotationConfigApplicationContext.register(IndexService.class);
        // 使用register方法來實現類的單獨註冊必須要refresh
        annotationConfigApplicationContext.refresh();

        System.out.println(annotationConfigApplicationContext.getBean(IndexService.class).getClass().getName());
    }
}

執行結果:

很簡單,我們只需要將要裝配的類的class傳入到register方法中就實現了單獨註冊裝配,沒有添加javaConfig配置,不需要掃描,也不需要加註解。

如果閱讀了register源碼,我們就能發現register支持註冊兩種bean,一個是普通bean,一個就是javaConfig的bean,所以它不光支持讀取我們的配置類,也支持讓我們單獨註冊bean到spring容器當中

 

1.6 插一句:

XML中還有一個depends-on 這個可以規定bean在容器中的創建順序。

<bean id="beanOne" class="ExampleBean" depends-on="manager"/>
<bean id="manager" class="ManagerBean" />

比如上面的這種配置,通過depends-on配置,那麼manager就會先於beanOne創建,但是這兩個bean之間是沒有依賴關係的。這種操作的應用場景就是如果beanOne的初始化需要用到manager的一些信息或者beanOne中的一些靜態方法需要用到manager的信息,那麼就得保證manager先於beanOne創建。但是這兩個bean之間並沒有依賴關係,所以只能使用depends-on的方法

 

二、自動裝配

下面我們再說一下自動裝配這個問題

定義還是要定義的,就是說要將哪個類作爲bean裝配給spring容器管理,但是通過自動裝配的技術bean之間的依賴關係不需要我們來設置了,比如如果使用XML來配置,我們就不需和上面一樣還要在bean標籤中再嵌套設置該bean所依賴的對象了。

只需要把所有要裝配給spring容器的對象在XML文件中設置好或者用註解指定好,然後spring會直接根據定義類時的代碼獲取到依賴關係,自動給需要依賴的對象進行裝配,這就大大減少了描述依賴關係的配置。

 

手動裝配的優先級高於自動裝配

自動裝配的優點:

  • 大大減少我們描述依賴關係是的大篇幅配置
  • 如果對象依賴關係更新,也不需要重新配置

 

自動裝配的方法:

  • no/default
  • byName
  • byType
  • constructor

 

指定自動裝配的方式有兩種:

  • 全局配置:全局都用指定的自動裝配方法,如全局都用byName
  • 局部配置:指定的自動裝配方法只在局部有效

 

2.1、使用XML來完成自動裝配

全局指定,直接在XML頭部設置自動裝配方式

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"
        default-autowire="byType">

    <bean id="dao" class="priv.cy.dao.IndexDaoImpl"></bean>

    <bean id="service" class="priv.cy.dao.IndexService"></bean>
</beans>

 

2.1.1byType

就是通過類型來查找相應的依賴關係

 

一上面的XML配置文件爲例

開啓了自動裝配之後,首先spring先掃描XML文件中設置的要裝配給spring容器管理的bean,然後掃描到了indexService這個bean之後,會先去掃描定義這個類的代碼,發現裏面有一個類型是IndexDao的依賴對象,然後就會返回來掃描XML配置文件,查看有沒有交給spring容器管理的這個類型的bean,最後發現了id爲dao的這個bean,然後就直接將這個bean注入給indexService對象了,這就是通過類型來進行自動裝配。

 

XML中配置的bean,底層都會講他們添加到一個map中,所以上面掃描bean的過程在底層就是遍歷這個map。

 

但是上述方法有一個問題,剛纔的例子只是將一種IndexDao接口的實現類裝配到spring容器中去管理,如果將兩種IndexDao接口的實現類裝配到spring容器中,就會出現報錯,因爲spring容器發現這兩種類型都可以注入給IndexService,不知道應該使用哪個了

 

例如定義兩個IndexDao實現類:

package priv.cy.dao;

public class IndexDaoImpl implements IndexDao {
    @Override
    public void test() {
        System.out.println("Impl 0");
    }

}

package priv.cy.dao;

public class IndexDaoImpl1 implements IndexDao {
    @Override
    public void test() {
        System.out.println("Impl 1");
    }
}

 

將其交給spring容器管理:

<?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"
        default-autowire="byType">

    <bean id="dao" class="priv.cy.dao.IndexDaoImpl"></bean>
    <bean id="dao1" class="priv.cy.dao.IndexDaoImpl1"></bean>

    <bean id="service" class="priv.cy.dao.IndexService"></bean>
</beans>

運行時報錯如下:

Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'priv.cy.dao.IndexDao' available: expected single matching bean but found 2: dao,dao1

上面的意思就是說應該注入IndexDao類型的對象,但是發現了兩個bean,dao和dao1都可以注入,這纔會報錯。這時就需要用byName來解決

而且還有一點要注意,使用自動裝配的時候默認使用的是無參構造方法來創建對象,所以要確保所有的bean都有無參構造方法,要麼不顯示地寫構造方法,要麼顯示寫出無參構造方法(如果寫了有參構造方法必須也寫上無參構造方法,因爲有參構造方法會使默認的隱式無參構造方法失效)

 

 

2.1.2byName

這裏就是通過指定bean的Name來進行自動裝配

<?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"
        default-autowire="byName">

    <bean id="dao" class="priv.cy.dao.IndexDaoImpl"></bean>
    <bean id="dao1" class="priv.cy.dao.IndexDaoImpl1"></bean>
    <bean id="service" class="priv.cy.dao.IndexService"></bean>
</beans>

 

前面說過,如果不指定bean的name,那麼name默認和id一樣,使用byName進行自動裝配的時候,使用的是類的set方法名來進行Name匹配的,如:

package priv.cy.dao;

public class IndexService {
    private IndexDao dao;
    public void service() {
        dao.test();
    }
    public void setDao(IndexDao dao) {
        this.dao = dao;
    }
}

比如上面的service類,setter方法的方法名是setDao,那麼再進行自動裝配的時候就會自動將方法名前面的set去掉,將剩下的第一個字母變小寫,得到的name去和XML中設置的bean的name進行比較,找到name爲dao的這個bean,將其注入到service類中。此時運行service方法,就會輸出Impl 0

所以這裏要識別的名字只和set方法中的哪個後綴名字有關,和service類中定義的依賴對象的名字 private IndexDao dao;,也就是dao是沒有關係的。

 

比如我將 

<bean id="dao1" name="aaa" class="priv.cy.dao.IndexDaoImpl1"></bean>

IndexDaoImpl1這個類的name改成aaa,然後將service中的setter改爲setAaa 

public class IndexService {
    private IndexDao dao;
    public void service() {
        dao.test();
    }
    public void setAaa(IndexDao dao) {
        this.dao = dao;
    }
}

 

這個時候輸出的就是Impl  1而不是Impl  0l了,說明byName只和bean的name值以及setter方法的名字有關係。和定義類的時候給依賴對象起的名字無關。

 

2.1.3nodefault

<?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"
        default-autowire="default">

    <bean id="dao" class="priv.cy.dao.IndexDaoImpl"></bean>
    <bean id="dao1" class="priv.cy.dao.IndexDaoImpl1"></bean>
    <bean id="service" class="priv.cy.dao.IndexService"></bean>
</beans>

這兩個作用一樣,都是使用手動裝配,如果沒有手動裝配的話,會報空指針錯誤

 

 

2.1.4constructor

<?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"
        default-autowire="constructor">

    <bean id="dao" class="priv.cy.dao.IndexDaoImpl"></bean>
    <bean id="dao1" class="priv.cy.dao.IndexDaoImpl1"></bean>
    <bean id="service" class="priv.cy.dao.IndexService"></bean>
</beans>

 

這個和byType差不多,都是依靠類型來進行自動裝配,區別就是byType根據的是定義的成員屬性的類型來進行自動裝配的

public class IndexService {
    private IndexDao dao;
    public void service() {
        dao.test();
    }
    public void setDao(IndexDao dao) {
        this.dao = dao;
    }
}

它是根據裏面private IndexDao dao; 這裏面定義的依賴對象的類型IndexDao來spring容器中的bean進行類型匹配的

 

而constructor使用的是構造方法的類型來進行匹配的,所以使用這種方式要設置好構造方法

public class IndexService {
    private IndexDao dao;

    public IndexService (IndexDao dao) {
        this.dao = dao;
    }

    public void service() {
        dao.test();
    }
    public void setDao(IndexDao dao) {
        this.dao = dao;
    }
}

他就會根據構造方法中傳入的參數類型進行匹配,取得了構造方法中傳入的參數類型是IndexDao,然後再去spring容器中找這個類型的bean,找到後將其注入到IndexService的bean中。

 

注意使用這種方法如果沒有設置好構造方法,就會發生錯誤。

 

上面說的都是全局,自動裝配也可以局部配置

<?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"
        >

    <bean id="dao" class="priv.cy.dao.IndexDaoImpl"></bean>
    <bean id="dao1" class="priv.cy.dao.IndexDaoImpl1"></bean>
    <bean id="service" class="priv.cy.dao.IndexService" autowire="byName"></bean>
</beans>

像這種,在需要自動裝配的bean標籤中,添加autowire屬性,等號右邊是自動裝配的方式。這樣只有service這個bean使用自動裝配,其他的bean都是用手動裝配。

 

但是局部配置的最廣泛的用處還是與全局配置混合使用,局部配置的優先級要高於全局配置

<?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"
        default-autowire="byType">

    <bean id="dao" class="priv.cy.dao.IndexDaoImpl"></bean>
    <bean id="dao1" class="priv.cy.dao.IndexDaoImpl1"></bean>
    <bean id="service" class="priv.cy.dao.IndexService" autowire="byName"></bean>
</beans>

 向這種寫法就可以實現service這個bean使用byName進行自動裝配,其他的bean使用byType進行自動裝配。這樣就可以防止service的bean使用byType時發現有多個相同類型的bean都可以注入到service中,出現衝突報錯了

 

2.2、使用JavaConfig和註解來進行自動裝配

下面我們使用JavaConfig和註解來進行自動裝配

 

這裏就需要說一下使用註解的時候,bean的命名規則:

  1. 在使用@Component、@Repository、@Service、@Controller等註解創建bean時,如果指定bean的name,則是指定的名稱.
  2. 如果不指定bean的name,bean名稱的默認規則是類名的首字母小寫,如SysConfig - sysConfig,Tools - tools。
  3. 如果類名前兩個或以上個字母都是大寫,那麼bean名稱與類名一樣,如RBACUserLog - RBACUserLog,RBACUser - RBACUser,RBACRole - RBACRole。

 

這裏和使用XML時的命名規則有些不同,使用XML來配置spring容器的時候bean標籤寫上id,然後如果不指定name的話,那麼bean的name默認和id一樣。

 

這裏插一條,spring中bean的命名規則是通過BeanNameGenerator這個接口來實現的,我們可以通過實現這個接口來完成自己的命名規則,編寫好命名規則類,然後告訴在告訴spring要掃描位置的同時,指定命名規則類就可以了。配置方法如下:

MyNameGenerator是我們自己編寫的命名規則類

 

javaConfig:

@Configuration
@ComponentScan(basePackages = "org.example", nameGenerator = MyNameGenerator.class)
public class AppConfig {
    // ...
}

XML:

 <beans>
        <context:component-scan base-package="org.example"
            name-generator="org.example.MyNameGenerator" />
  </beans>

 

javaConfig類:

package priv.cy.dao;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan("priv")
public class Spring {
}

 

service:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class IndexService {
    @Autowired
    private IndexDao indexDaoImpl;

    public void service() {
        indexDaoImpl.test();
    }
    public void setDao(IndexDao dao) {
        this.indexDaoImpl = dao;
    }
}

 

dao:

import org.springframework.stereotype.Repository;
@Repository
public class IndexDaoImpl implements IndexDao {
    @Override
    public void test() {
        System.out.println("Impl 0");
    }

}

import org.springframework.stereotype.Repository;
@Repository
public class IndexDaoImpl1 implements IndexDao {
    @Override
    public void test() {
        System.out.println("Impl 1");
    }

}

上面這些代碼通過註解將類交給spring容器去管理,默認的bean name就是他們的首字母小寫的類名

 

先講一下@Autowired@Resource的區別

  • @Autowired默認先使用的是byType,byType使用的是屬性的類型,如果byType會出現報錯的話,再就會再使用byName,這裏使用的name也是屬性的屬性名,用屬性的屬性名來去spring容器中匹配查找相同的bean name
  • @Resource默認是使用的是byName,但是這裏的byName和之前講XML自動裝配的byName不太一樣,之前的byName都是通過setter方法名來匹配name的,但是@Resource使用的是屬性名來匹配name的,和setter方法沒有關係。@Resource可以指定使用byName還是byType。

以上兩種自動裝配的註解都不需要使用setter方法

 

2.2.1 @Autowired

例子:

@Service
public class IndexService {
    @Autowired
    private IndexDao indexDaoImpl;

    public void service() {
        indexDaoImpl.test();
    }
    public void setDao(IndexDao dao) {
        this.indexDaoImpl = dao;
    }
}

按道理說如果使用byType會出現報錯,因爲有兩個IndexDao接口的實現類,但是它不會直接報錯,@Autowired會再換成byName再試一次,這個時候的name使用的是屬性名的name,也就是用這個indexDaoImpl去查找,如果byName也沒有找到,就會報之前那個有兩個bean的衝突錯誤了。

 

所以註解(@Autowired和@Resource)使用byName都是直接使用依賴的對象的屬性名來進行查找的,和setter方法沒有任何關係,比如上面的這個代碼在spring容器中代理的IndexDaoImpl.class這個bean默認的name時indexDaoImpl,上面的代碼中setter方法名時setDao,按之前的道理IndexService取到的依賴類的名字應該是dao纔對,但是這裏依舊能將bean注入,但是此時spring容器中IndexDaoImpl這個類的bean的name是indexDaoImpl(默認命名規則)。說明此時IndexService是用indexDaoImpl這個name來從spring容器中進行查找匹配的(也就是使用的private IndexDao indexDaoImpl 裏面這個對象的名字來進行匹配的,如果此時吧這個對象的名字改成aaa,也就會報錯出現問題了

 

2.2.2 @Resource

使用@Resource的話如果不指定的話,就默認是byName,如果是默認byName,那麼過程和上面的@Autowired一樣,但是@Resource可以指定使用byName還是byType

指定byType:

@Service
public class IndexService {
    @Resource(type = IndexDaoImpl1.class)
    private IndexDao dao;

    public void service() {
        dao.test();
    }
    public void setDao(IndexDao dao) {
        this.dao = dao;
    }
}

直接指定使用byType,並且使用IndexDaoImpl1.class這個類,這樣不管怎麼樣service這個類注入的已經是IndexDaoImpl1這個實現類

 

也可以指定byName

@Service
public class IndexService {
    @Resource(name = "indexDaoImpl1")
    private IndexDao dao;

    public void service() {
        dao.test();
    }
    public void setDao(IndexDao dao) {
        this.dao = dao;
    }
}

這樣就通過bean的name來進行查找,查找spring容器中bean的name是indexDaoImpl1的這個bean,將其注入給IndexService對象

 

三、spring懶加載

bean在調用的時候纔會被加載

使用懶加載的前提是這個bean的作用域必須是單例的

 

3.1 使用XML設置:

用lazy-init。告訴spring容器是否以懶加載的方式創造對象。用的時候才加載構造,不用的時候不加載

取值:true(懶,真正調用到的時候再加載)、false(非懶,已啓動spring容器就創建對象)、default(懶)

 <bean id="dao" class="cn.java.ioc.MW" lazy-init="default" ></bean>

 

3.2 使用註解設置:

@Lazy
@Bean("dao")
public Dao dao() {
    System.out.println("dao");
    return new Dao();
}

 

3.3 懶加載與非懶加載的優缺點:

  • 懶加載:對象使用的時候纔去創建,節省資源,但是不利於提前發現錯誤。
  • 非懶加載:容器啓動的時候立刻創建對象。消耗資源。利於提前發現錯誤。

 

當scope=“prototype” (多例)時,默認以懶加載的方式產生對象。

當scope=“singleton” (單例)時,默認以非懶加載的方式產生對象。

 

 

四、spring作用域

spring容器中的bean可以分爲五個範圍。所有範圍的名稱都是說明的,

1.singleton:這種bean範圍是默認的,這種範圍確保不管接受到多個請求,每個容器中有一個bean的實例,單例模式由bean factory自身來維護。

2.prototype:原先通過範圍與單例範圍相反,爲每一個bean請求提供一個實例。

3.request:在請求bean範圍內會爲每一個來自客戶端的網絡請求創建一個實例,在請求完成之後,bean會失效並被垃圾回收器回收。該屬性僅對HTT請求產生作用,使用該屬性定義Bean時,每次HTTP請求都會創建一個新的Bean,適用於WebApplicationContext環境。

4.session:與請求範圍類似,確保每個session中的bean的實例在session過期後bean會隨之消失。該屬性僅用於HTTP Session,同一個Session共享一個Bean實例。不同Session使用不同的實例。Session

5.global-session:global-session和portlet公用全局存儲變量的話,那麼這全局變量需要存儲在global-session中。該屬性僅用於HTTP Session,同session作用域不同的是,所有的Session共享一個Bean實例。

 

下面重點討論singleton、prototyp作用域,request、session和global-session類作用域放到Spring MVC章節討論,這裏不再做詳細講述。

 

可以通過@Scope(" ")註解來設置

spring容器bean的作用域默認是單例,非懶加載的

 

 

4.1 Singleton Beans with Prototype-bean Dependencies

意思是在Singleton 當中引用了一個Prototype的bean的時候引發的問題,依賴的這個對象的原型作用域失效,也變成了單例了

 

其實這個問題就可以總結爲一個類依賴另一個類,但是這兩個的作用域是不同的,就會出現問題。

 

比如下面這一套代碼:

javaConfig

@Configuration
@ComponentScan("priv")
public class Spring {
}

 

service

@Service
@Scope("singleton")
public class IndexService {
    @Autowired
    private IndexDao dao;

    public void service() {
        System.out.println("service" + this.hashCode());
        System.out.println("dao" + dao.hashCode());
    }

    public void setDao(IndexDao dao) {
        this.dao = dao;
    }
}

 

DAO

@Repository
@Scope("prototype")
public class IndexDaoImpl implements IndexDao {
    @Override
    public void test() {
        System.out.println("Impl 0");
    }
}

 

Test:

public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext
                = new AnnotationConfigApplicationContext(Spring.class);
        IndexService service = (IndexService) annotationConfigApplicationContext.getBean("indexService");
        IndexService service1 = (IndexService) annotationConfigApplicationContext.getBean("indexService");
        IndexService service2 = (IndexService) annotationConfigApplicationContext.getBean("indexService");
        service.service();
        service1.service();
        service2.service();
    }
}

 

結果:

可以發現service是單例的,而它依賴的dao是原型的,但是最終的結果確實dao的原型作用域失效,這就是作用域不同進行依賴錯產生的問題。因爲service是單例的,他在spring容器中只會創建一次,而它依賴的對象是隨着它的加載而加載的,所以service只加載一次,即使dao是原型的,也只會被加載一次。

我們的解決辦法只能是在service調用dao的時候想辦法重新獲取一次新的dao對象,這樣既能保證service對象是單例,也能保證每一次調用dao都是新產生的對象。

如果這個對象要依賴的類和自己的作用域不同,spring提供了兩種解決方法:

 

4.1.1 ApplicationContextAware接口

一種是讓這個對象實現ApplicationContextAware接口,這個還需要覆寫一個set方法,和引入ApplicationContext對象

這樣的解決辦法就是在service類每一次需要使用依賴的這個dao時,就通過ApplicationContext對象重新取一次這個bean,這樣dao就可以依舊保持原型的作用域,每一次使用它就會是一個新bean,而service對象一直保持單例不變。

 

javaConfig

@Configuration
@ComponentScan("priv")
public class Spring {
}

 

service

@Service
@Scope("singleton")
public class IndexService implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    @Autowired   //加不加這個註解都無所謂了,反正最後使用的也不是這個dao對象  用ApplicationContext這個Autowired可加可不加
    private IndexDao dao;

    public void service() {
        System.out.println("service" + this.hashCode());
		//每次調用都使用applicationContext對象重新獲取IndexDaoImpl的bean
        System.out.println("dao" + applicationContext.getBean("indexDaoImpl").hashCode());
    }
    public void setDao(IndexDao dao) {
        this.dao = dao;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

 

DAO

@Repository
@Scope("prototype")
public class IndexDaoImpl implements IndexDao {
    @Override
    public void test() {
        System.out.println("Impl 0");
    }
}

 

Test:

public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext
                = new AnnotationConfigApplicationContext(Spring.class);
        IndexService service = (IndexService) annotationConfigApplicationContext.getBean("indexService");
        IndexService service1 = (IndexService) annotationConfigApplicationContext.getBean("indexService");
        IndexService service2 = (IndexService) annotationConfigApplicationContext.getBean("indexService");
        service.service();
        service1.service();
        service2.service();
    }
}

 

結果:

 

但是這種實現spring接口的方法,會大大增加項目的耦合度,侵入性太強,使代碼嚴重spring的API,spring的設計理念之一就是非侵入性,所以spring還提供了Lookup Method Injection這個方法來解決Singleton Beans with Prototype-bean Dependencies問題

 

 

4.1.2 Lookup Method Injection

使用@Lookup註解,就可以不需要依賴spring的API了

 

其他代碼和之前還是一樣,只需要修改service代碼:

@Service
@Scope("singleton")
public class IndexService {
//  @Autowired     使用Lookup必須要將Autowired去掉,否則會出錯
    private IndexDao dao;

    public void service() {
        System.out.println("service" + this.hashCode());
        System.out.println("dao" + getDao().hashCode());
    }

    @Lookup
    public IndexDao getDao() {
        return null;
    }
}

這裏只需要添加一個get方法,用來每一次調用時獲取全新的dao對象,方法名不重要,自定義就行,返回值就是你要獲取的對象類型,只需要在這個方法上面添加@Lookup註解,這個方法內部什麼邏輯代碼都不需要寫,直接寫一個return null就可以了。在每次獲取dao對象的位置調用這個方法就可以了。還有一點就是因爲是通過這個方法來獲取daobean了,所以也就不需要@Autowired這個註解進行注入了,要將其刪除,不刪除的話會出現報錯

 

@Lookup是通過方法的返回值類型,根據這個類型從spring容器中查找這個bean,如果找到了就直接通過這個方法返回回來。但是這裏就有可能出現前面講的多個相同類型的bean衝突問題,如果IndexDao是一個接口,而這個接口有多個實現類被委託給spring容器進行管理,那麼這個時候如果還通過類型來查找就找到多個bean,進而出現錯誤

Exception in thread "main" org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'priv.cy.dao.IndexDao' available: expected single matching bean but found 2: indexDaoImpl,indexDaoImpl1

 

解決這個問題就還需要通過byName來進行查找,方法就是@Lookup("indexDaoImpl"),直接在這個註解指定bean的name,然後他就會通過這個name去spring容器中查找這個對應的bean,這樣就不會起衝突了

@Service
@Scope("singleton")
public class IndexService {
//    @Autowired   
    private IndexDao dao;
    public void service() {
        System.out.println("service" + this.hashCode());
        System.out.println("dao" + getDao().hashCode());
    }

    @Lookup("indexDaoImpl")
    public IndexDao getDao() {
        return null;
    }
}

 

 

五、spring生命週期和回調

就相當於finally

生命週期回調有三種:

  • Initialization Callbacks(初始化回調)
  • Destruction Callbacks(銷燬回調)
  • Default Initialization and Destroy Methods(默認初始化和銷燬方法)

 

 

5.1 Initialization Callbacks(初始化回調)

在初始化的時候回調,初始化是在實例化方法執行之後,需要實現InitializingBean接口,然後覆寫afterPropertiesSet()方法,這樣每次初始化的時候就會回調afterPropertiesSet方法

代碼:

@Repository
public class IndexDaoImpl1 implements IndexDao , InitializingBean {
    public IndexDaoImpl1() {
        System.out.println("Constructor");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("init");
    }
}

 

測試類:

public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext
                = new AnnotationConfigApplicationContext(Appconfig.class);
    }
}

 

執行結果:

 

 

5.2 Destruction Callbacks(銷燬回調)

當容器被銷燬的時候,會進行回調。需要實現DisposableBean這個接口,然後覆寫afterPropertiesSet()方法,這樣容器被銷燬的時候就會回調afterPropertiesSet方法

代碼:

@Repository
public class IndexDaoImpl1 implements IndexDao , InitializingBean , DisposableBean {
    public IndexDaoImpl1() {
        System.out.println("Constructor");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("init");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("destroy");
    }
}

 

5.3 Default Initialization and Destroy Methods(默認初始化和銷燬方法)

可以自定義回調方法,不需要去實現接口,不依賴spring的API,降低了系統的耦合,非侵入性。

可以指定一個初始化方法和銷燬方法來實現方法回調

 

有兩種設置方式,XML和註解

 

5.3.1 XMLinit-methoddestroy-method

 

dao:

@Repository
public class IndexDaoImpl1 implements IndexDao {
    public IndexDaoImpl1() {
        System.out.println("Constructor");
    }
    public void init() {
        System.out.println("init");
    }
    public void destory() {
        System.out.println("destory");
    }
}

 

test:

public class Test {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext classPathXmlApplicationContext
                = new ClassPathXmlApplicationContext("classpath:spring.xml");
    }
}

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"

       default-init-method="init"
       default-destroy-method="destory" >

    <bean id="dao" class="priv.cy.dao.IndexDaoImpl1"></bean>
</beans>

 就是直接在需要添加回調方法的類中編寫自己想要的方法就行了,方法名自定義,只需要在XML配置文件中指定初始化回調方法和銷燬回調方法的方法名就可以了

 

 

5.3 2 註解(@PostConstruct@PreDestory):

 

也可以直接使用註解,不需要用XML配置

@PostConstruct 由JSR-250提供,在構造函數執行完之後執行,等價於xml配置文件中bean的initMethod

@PreDestory 由JSR-250提供,在Bean銷燬之前執行,等價於xml配置文件中bean的destroyMethod

使用以上兩個註解直接用在回調方法上,也能實現自定義的回調方法

 

例如:

@Repository
public class IndexDaoImpl1 implements IndexDao {
    public IndexDaoImpl1() {
        System.out.println("Constructor");
    }

    @PostConstruct
    public void init() {
        System.out.println("init");
    }

    @PreDestory
    public void destory() {
        System.out.println("destory");
    }
}

 

 

懶加載

只有在調用的時候纔會被加載

使用@Lazy標籤

 

例如將上面的回調方法代碼加上@Lazy標籤:

@Repository
@Lazy
public class IndexDaoImpl1 implements IndexDao {
    public IndexDaoImpl1() {
        System.out.println("Constructor");
    }
    @PostConstruct
    public void init() {
        System.out.println("init");
    }
    public void destory() {
        System.out.println("destory");
    }
}

 

Test:

public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext
                = new AnnotationConfigApplicationContext(Appconfig.class);

        //IndexDaoImpl1 dao = (IndexDaoImpl1) annotationConfigApplicationContext.getBean(IndexDao.class);
    }
}

 

執行結果:

 

這個代碼的構造方法和回調方法都不會執行了,說明只有當調用這個bean的時候這個bean在容器中才會被實例化

 

 

六、其他

6.1 @ComponentScan標籤的其他用法

它不光可以指定要spring要掃描註解的範圍,還可以指定spring不掃描的位置

 

使用註解的時候如果向註解中傳入的參數只有一個,也就是value,那麼就可以不用寫value="",直接在括號裏面寫上參數就可以。但是如果傳入的參數大於1,那麼就必須表明每一個參數要傳給誰

例如:

@Configuration
@ComponentScan(value = "priv", excludeFilters = {@ComponentScan.Filter(type = FilterType.REGEX,pattern = "priv.cy.service.*")})
public class Appconfig {
}

上面的註解意思就是掃描priv下面的所有包,但是priv.cy.service包下的內容除外,如果使用註解來裝配bean的話,priv.cy.service包下的bean就會全部裝配失敗。type表示的是後面pattern表達式的類型,像REGEX類型的表達式後面必須要帶*,其他類型的相關簡介請看官方文檔https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#beans-scanning-filters

 

還有就是可以可以指定必須要掃描哪些位置,使用includeFilters 參數

例如:

@Configuration
@ComponentScan(basePackages = "org.example",
        includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"),
        excludeFilters = @Filter(Repository.class))
public class AppConfig {
    ...
}

它的應用場景比如我對A包裏面除了其中一個子包a需要掃描之外,其餘的都不需要掃描,就需要用到includeFilters。

 

實現以上功能還可以使用XML配置

<beans>
    <context:component-scan base-package="org.example">
        <context:include-filter type="regex"
                expression=".*Stub.*Repository"/>

        <context:exclude-filter type="annotation"
                expression="org.springframework.stereotype.Repository"/>
    </context:component-scan>
</beans>

 

 

6.2 byType多個同類型的Bean衝突解決辦法

再來說一下之前講過使用byType會出現多個相同類型的bean出現衝突無法自動裝配的解決方法,有兩種

 

6.2.1@Primary

這個是主數據源標籤,出現衝突的情況下優先使用這個bean進行裝配

例子:

service:

@Service
public class IndexService {
    @Autowired
    private IndexDao indexDao;
}

dao:

@Repository
public class IndexDaoImpl1 implements IndexDao {
    public IndexDaoImpl1() {
        System.out.println("Constructor 1");
    }
    @PostConstruct
    public void init() {
        System.out.println("init 1");
    }
    public void destory() {
        System.out.println("destory 1");
    }
}

@Repository
public class IndexDaoImpl2 implements IndexDao {
    public IndexDaoImpl2() {
        System.out.println("Constructor 2");
    }
    @PostConstruct
    public void init() {
        System.out.println("init 2");
    }
    public void destory() {
        System.out.println("destory 2");
    }
}

不使用@Primary標籤,報錯

Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'priv.cy.dao.IndexDao' available: expected single matching bean but found 2: indexDaoImpl1,indexDaoImpl2

 

 

對IndexDaoImpl1使用@Primary標籤

@Repository
@Primary
public class IndexDaoImpl1 implements IndexDao {
    public IndexDaoImpl1() {
        System.out.println("Constructor 1");
    }
    @PostConstruct
    public void init() {
        System.out.println("init 1");
    }
    public void destory() {
        System.out.println("destory 1");
    }
}

就可以正常注入了,會優先將IndexDaoImpl1注入到IndexService對象中

 

6.2.2、第二種方法使用限定註解@Qualifier

@Qualifier這個註解可以直接指定要注入哪個bean,它通過name去指定bean

代碼:

@Service
public class IndexService {
    @Qualifier("indexDaoImpl1")
    private IndexDao indexDao;
}

直接指定使用indexDaoImpl1這個name的bean去注入

Java自帶的一些註解可以替代spring的註解來完成相同的功能,比如@Resource,這個就屬於jdk的註解,但是也能實現自動裝配的功能。

 

 

6.3 加速spring掃描類的速度

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-indexer</artifactId>
        <version>5.2.4.RELEASE</version>
        <optional>true</optional>
    </dependency>
</dependencies>

添加了這個之後spring項目在編譯的時候就會將所有要掃描的類添加到一個索引當中,以後再掃描就不需要使用類路徑一個個掃描了,而是直接使用索引進行掃描,這樣大大加快了掃描速度。

 

6.4 @Bean註解

@Bean 註解在方法上,聲明當前方法的返回值爲一個bean,替代xml中的方式(方法上),也就是將方法的返回值所謂一個bean交給spring容器管理

比如一些必須要通過一個方法返回出來的對象想把它交給spring容器去管理,我們就需要用這個辦法,在這個方法上面添加@Bean標籤,這個方法的返回值就交給spring容器管理了。

 

配置類

@Configuration
public class SpringConfiguration {
    /**
     * 在JavaConfig配置類中使用@Bean標籤來完成bean的註冊,效果就和在XML中配置<bean>標籤是一樣的
     */
    @Bean
    public HollowController hollowController(){
        return new HollowController();
    }
}

 

 

6.5 @Profile標籤

這個標籤用來切換環境

dao:

@Repository
@Profile("dao1")
public class IndexDaoImpl1 implements IndexDao {
    public IndexDaoImpl1() {
        System.out.println("Constructor 1");
    }
    @PostConstruct
    public void init() {
        System.out.println("init 1");
    }
    public void destory() {
        System.out.println("destory 1");
    }
}


@Repository
@Profile("dao2")
public class IndexDaoImpl2 implements IndexDao {
    public IndexDaoImpl2() {
        System.out.println("Constructor 2");
    }
    @PostConstruct
    public void init() {
        System.out.println("init 2");
    }
    public void destory() {
        System.out.println("destory 2");
    }
}

 

給兩個dao設置了對應的環境名

測試類:

public class Test {
    public static void main(String[] args) {
        // 創建AnnotationConfigApplicationContext類的時候先不指定配置類
        AnnotationConfigApplicationContext annotationConfigApplicationContext
                = new AnnotationConfigApplicationContext();

        // 獲取annotationConfigApplicationContext對象的環境並對其進行修改
        annotationConfigApplicationContext.getEnvironment().setActiveProfiles("dao2");

        // 指定完環境之後再註冊配置類
        annotationConfigApplicationContext.register(Appconfig.class);

        // 註冊完成後刷新annotationConfigApplicationContext
        annotationConfigApplicationContext.refresh();

        // 再取bean就是在dao2這個環境下獲取了
        annotationConfigApplicationContext.getBean(IndexService.class);
    }
}

此時service注入的就是dao2環境下的bean了

這個註解的用途就是可以設置很多種環境,可以通過切換不同的環境來使不同的bean交給spring容器管理,或者設置多個配置類環境,可以根據需要任意切換。


其他文章:【Spring】面試官,請別再問Spring Bean的生命週期了!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章