1. 程式人生 > >Spring使用之IOC

Spring使用之IOC

      這一段純屬廢話,可以不用看........,兌現諾言,每週寫一篇部落格。最近除了學習演算法,還有想更全面瞭解Spring框架的使用。於是開始了我的學習之旅。由於本人工作中接觸的是一個非常非常老的專案,所以對Spring的瞭解僅僅停留在一些基本的使用上。為加深瞭解Spring提供的功能,於是就果斷買了《Spring實戰》,初略看下來,收穫還是有的,但總感覺也不是特別特別優秀的書。接下來,我就來談談我對Spring的一點淺薄理解。

一、Spring第一印象

      對於Java後端開發來說,Spring基本是再熟悉不過的框架了,甚至可以說,現在不用Spring框架進行開發的JAVA後端專案應該基本是鳳毛麟角(個人猜測)。相信一提到Spring,大家想到的就是DI(Dependency Injection)/IOC(Inversion of Control)、AOP這些名詞了,沒錯,這就是Spring提供的核心功能。DI、IOC說的就是同一個功能,翻譯過來分別是依賴注入、控制反轉,其實就是我們把物件的建立、銷燬、維護交給Spring,讓它幫我們管理這些物件,但我們需要的時候,讓它直接給我們一個,不需要我們自己去new。AOP就是面向切面程式設計。額額,看著這些名詞一臉懵逼.................................

接下來就來個小例子看看這到底能幹嘛,能給我們程式設計提供什麼樣的幫助。。

在給小例子之前我們得先了解兩個概念:

1、既然要讓Spring幫我們建立管理物件、那我們就得先告訴Spring怎麼去建立這些物件,還有物件之間是什麼關係。Spring提供了3個方案來實現:1、XML中顯式配置。2、Java中顯式配置。3、自動包掃描

2、Spring提供了倆種容器型別,分別為BeanFactory、ApplicationContext。BeanFactory只提供了基本的DI支援,ApplicationContext基於BeanFactory構建,能提供更多服務。

Spring上下文,就是一種容器,繼承自ApplicationContext,提供更多功能,如常見的,

AnnotationConfigApplicationContext可從JAVA配置類中載入上下文

ClassPathXmlApplicationContext 從類路徑下的XML檔案中載入上下文。

FileSystemXmlApplilcationContext 從檔案系統路徑中載入上下文

例子

接下來我們用Idea編輯器,通過maven工具建立一個maven專案來體驗下Spring。建立完之後我們首先要引入Spring jar包,通過maven,我們不需要自己去下載,只需要在配置檔案中配置我們需要用到的jar包。專案結構如下圖


之後我們在pom.xml檔案中配置需要用到的jar檔案,我們將spring的幾個核心包配置進來。

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>cn.springstudy</groupId>
  <artifactId>luckyspring</artifactId>
  <version>1.0-SNAPSHOT</version>

  <name>luckyspring</name>
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.7</maven.compiler.source>
    <maven.compiler.target>1.7</maven.compiler.target>
    <!-- spring 版本  -->
    <org.springframework.version>4.0.0.RELEASE</org.springframework.version>
  </properties>
  <dependencies>
    <!-- 當前專案用到的3個核心spring jar包 -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${org.springframework.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>${org.springframework.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
      <version>${org.springframework.version}</version>
    </dependency

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

</project>
複製程式碼

現在假設我們有一個公司,一個公司裡面得有僱員來工作,才能讓公司提供服務,於是我們建立僱員類

package cn.springstudy.vo;

public class Employee {

    public void work(){
        System.out.println("Employ start to Work");
    };
}複製程式碼

再建立公司類

package cn.springstudy.vo;

public class Company {

    //公司類有個僱員物件
    private Employee employee;

    public void  supportService(){
        employee.work();
    }

    public Employee getEmployee() {
        return employee;
    }

    public void setEmployee(Employee employee) {
        this.employee = employee;
    }
}複製程式碼

前面我們說過告訴Spring怎麼去建立物件有三種方式

方式一XML配置來告訴spring怎麼去建立我們的僱員還有公司類,在建立一個applicationContext.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">
    <!-- 配置一個物件 指定該物件id為emplayee-->  
    <bean id="employee" class="cn.springstudy.vo.Employee"></bean>
    <!--配置物件,之後往物件中放入一個Employee物件
       ref="employee"表示注入的物件為spring容器中id為employee的物件,這樣配置後建立Company
       物件後,會自動呼叫setEmployee()方法放入一個Employee物件
    -->    
    <bean id="company" class="cn.springstudy.vo.Company">
        <property name="employee" ref="employee"></property>
    </bean>
</beans>複製程式碼

接下來我們建立一個main函式作為程式的入口,在main函式中去建立Spring容器

package cn.springstudy.spring;

import cn.springstudy.vo.Company;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringStudy {
    public static void main(String arg[]){
        //方式一,XML檔案中載入上下文,即Spring容器
        ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        //從容器中獲取一個Company物件
        Company company = (Company) classPathXmlApplicationContext.getBean("company");
        company.supportService();
    }
}複製程式碼

執行一下,控制檯列印如下


我們可以看到我們執行company.supportService()居然沒報空指標,要知道我們可沒在Company類中去建立Empoyee物件,說明什麼???Spring容器自動幫我們建立Employee物件並set到Company物件中了。

方式二

建立一個配置Java類

package cn.springstudy.spring;

import cn.springstudy.vo.Company;
import cn.springstudy.vo.Employee;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

//註解,表明這個是一個Spring配置類
@Configuration
public class SpringConfig {

 //Spring容器管理的類,通過Bean註解,方法名即預設為bean的id,也可在Bean註解後加引數指定Bean的ID
    @Bean
    public Employee employee(){
        return  new Employee();
    }
    //Spring容器管理的類,通過Bean註解
    @Bean
    public Company company(){
        Company company =  new Company();
        company.setEmployee(employee());
        return  company;
    }
}
複製程式碼

main函式,程式入口,執行結果同上

package cn.springstudy.spring;

import cn.springstudy.vo.Company;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringStudy {

    public static void main(String arg[]){

        //方式二,預設bean的名稱即為配置方法的名稱,需要用到AnnotationConfigApplicationContext容器
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
        annotationConfigApplicationContext.register(SpringConfig.class);
        annotationConfigApplicationContext.refresh();
        Company company1 = (Company) annotationConfigApplicationContext.getBean("company");
        company1.supportService();
    }
}複製程式碼

方式三、

方式三序藉助方式一或者方式二,我們首先需要在需要被Spring容器管理的類上面加上@Component註解,所以我們在Empoyee加上註解

package cn.springstudy.vo;

import org.springframework.stereotype.Component;

@Component
public class Employee {

    public void work(){
        System.out.println("Employ start to Work");
    };
}複製程式碼

Company類上加上註解,需自動分配的物件上加@Autowired註解

package cn.springstudy.vo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class Company {

    //Autowired,幫我分配個Employee物件過來。
    @Autowired
    private Employee employee;

    public void  supportService(){
        employee.work();
    }

    public Employee getEmployee() {
        return employee;
    }

    public void setEmployee(Employee employee) {
        this.employee = employee;
    }
}複製程式碼

在配置類上加上包自動掃描路徑

package cn.springstudy.spring;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

//cn.springstudy.vo 為掃描的基礎包路徑
@Configuration
@ComponentScan("cn.springstudy.vo")
public class SpringConfigScan {
}複製程式碼

測試main方法

package cn.springstudy.spring;

import cn.springstudy.vo.Company;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringStudy {

    public static void main(String arg[]){    
        //方式三,需藉助方式2
        AnnotationConfigApplicationContext annotationConfigApplicationContext2 = new AnnotationConfigApplicationContext();
        annotationConfigApplicationContext2.register(SpringConfig.class);
        annotationConfigApplicationContext2.refresh();
        Company company2 = (Company) annotationConfigApplicationContext.getBean("company");
        company2.supportService();
    }
}複製程式碼

執行結果同上。

XML、Java配置檔案混著使用

當然XML配置方式和Java配置檔案還能混著用。

1、在XML配置中混用Java配置檔案只需在配置檔案中增加一個<bean></bea>即可將Java配置類匯入XML配置中。2、XML中匯入其他XML配置,只需用<import>指定其他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">
    <!--匯入其他XML配置檔案  -->
    <import resource="applicationContext2.xml"></import>
    <!--Spring Java配置類-->
    <bean class="cn.springstudy.spring.SpringConfig"></bean>
    
    <bean id="employee" class="cn.springstudy.vo.Employee"></bean>
    <bean id="company" class="cn.springstudy.vo.Company">
        <property name="employee" ref="employee"></property>
    </bean>
    
</beans>複製程式碼

1、Java配置檔案混著XML配置檔案,只需在Java類中加個註解即可,2、Java配置類匯入其他配置類,只需加@Import註解,指定配置類位置。

package cn.springstudy.spring;

import cn.springstudy.vo.Company;
import cn.springstudy.vo.Employee;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;

@Configuration
//匯入其他XML配置檔案
@ImportResource("classpath:applicationContext.xml")
//匯入其他配置類
@Import({SpringConfigScan.class})
public class SpringConfig {    
    @Bean
    public Employee employee(){
        return  new Employee();
    }

    @Bean
    public Company company(){
        Company company =  new Company();
        company.setEmployee(employee());
        return  company;
    }
}複製程式碼

Spring DI初步使用方法已經如上,接下來我們主要通過XML配置的方式來說明其他功能。

構造方法注入依賴物件

以上的小例子中Company中的Employee物件是通過setEmployee方法配置進去的。我們還可以通過構造方法的形式配置進去。

給Company類增加一個構造方法

package cn.springstudy.vo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

public class Company {

    private Employee employee;
    private String name;

    public Company(Employee employee,String name){
        this.employee = employee;
        this.name = name;
    }
    public void  supportService(){
        employee.work();
    }

    public Employee getEmployee() {
        return employee;
    }

    public void setEmployee(Employee employee) {
        this.employee = employee;
    }
}複製程式碼

配置檔案配置如下

<?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:c="http://www.springframework.org/schema/c" 
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/c
       http://www.springframework.org/schema/c/spring-c.xsd">
    <!--在上面beans中宣告spring中c名稱空間-->

    <bean id="employee" class="cn.springstudy.vo.Employee"></bean>
   <!-- 構造方法中注入Employee物件,還有一個名稱其中
    c:為名稱空間字首
    emplayee、name分別為建構函式中引數的名稱
     -ref表示注入的是spring容器中的物件
    "emplayee"表示引入的是id為emplayee的物件,name沒有帶-ref則是注入字串juejin.com
   -->
    <bean id="company" class="cn.springstudy.vo.Company"
        c:employee-ref="employee"
        c:name="juejin.com">
    </bean>

</beans>複製程式碼

工廠方法模式建立物件

以上說明Spring的依賴注入的基本使用。我們在程式設計時候可能不是通過new來建立物件,有時也會使用靜態方法(靜態工廠模式)來建立物件,那麼Spring是否支援呢?答案是肯定的。

public class ClientService {
    private ClientService() {}

    public static ClientService createInstance() {
        return new ClientService()
    }
}複製程式碼

<bean id="clientService"
    class="examples.ClientService"
    factory-method="createInstance"/>複製程式碼

當然工廠方法模式是通過非通過靜態方法建立物件的,Spring也是支援的,如下

public class DefaultServiceLocator {

    private static ClientService clientService = new ClientServiceImpl();

    public ClientService createClientServiceInstance() {
        return clientService;
    }
}複製程式碼

<!-- 工廠類  -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
</bean>
<!--  ClientService類配置 -->
<bean id="clientService"
    factory-bean="serviceLocator"
    factory-method="createClientServiceInstance"/>複製程式碼

Spring中bean的作用域

預設情況下,Spring容器中的bean都是單例的,可以通過配置修改。spring定義了多種作用域:

1、Singleton(預設) 單例

2、Prototype 原型,每次通過容器獲取物件都會建立一個新物件、

3、Session 會話,一個web會話中使用一個物件

4、Request ,每次web請求使用一個物件

通過XML配置可在bean中指定scrop屬性指定

Java配置類可在方法上加註解@Scope("prototype")

自動掃描的類可在類上加註解@Scope("prototype")

<!-- scope 屬性指定作用域 -->
<bean id="employee" class="cn.springstudy.vo.Employee"  scope="prototype"></bean>複製程式碼

@Component
@Scope("prototype")
public class Company {.....}複製程式碼

@Configuration
public class SpringConfig {
    @Bean
    @Scope("prototype")
    public Employee employee(){
        return  new Employee();
    }
}複製程式碼


最後,感覺這篇寫得有點亂,也很基礎,還有很多東西沒說到,不知道看到這裡的人兒有無收穫,說好的一週一篇,額,那就先發出來吧,以後可能還做修改,待續。。