Spring【依賴注入】就是這麼簡單

Java3y發表於2018-03-14

前言

在Spring的第二篇中主要講解了Spring Core模組的使用IOC容器建立物件的問題,Spring Core模組主要是解決物件的建立和物件之間的依賴關係,因此本博文主要講解如何使用IOC容器來解決物件之間的依賴關係

回顧以前物件依賴

我們來看一下我們以前關於物件依賴,是怎麼的歷程

直接new物件

  • 在最開始,我們是直接new物件給serice的userDao屬性賦值...

class  UserService{
	UserDao userDao = new UserDao();
}

複製程式碼

寫DaoFactory,用字串來維護依賴關係

  • 後來,我們發現service層緊緊耦合了dao層。我們就寫了DaoFactory,在service層只要通過字串就能夠建立對應的dao層的物件了。

  • DaoFactory


public class DaoFactory {

    private static final DaoFactory factory = new DaoFactory();
    private DaoFactory(){}

    public static DaoFactory getInstance(){
        return factory;
    }

    public <T> T createDao(String className,Class<T> clazz){
        try{
            T t = (T) Class.forName(className).newInstance();
            return t;
        }catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

}

複製程式碼
  • serivce
    private CategoryDao categoryDao = DaoFactory.getInstance().createDao("zhongfucheng.dao.impl.CategoryDAOImpl", CategoryDao.class);

    private BookDao bookDao = DaoFactory.getInstance().createDao("zhongfucheng.dao.impl.BookDaoImpl", BookDao.class);

    private UserDao userDao = DaoFactory.getInstance().createDao("zhongfucheng.dao.impl.UserDaoImpl", UserDao.class);

    private OrderDao orderDao = DaoFactory.getInstance().createDao("zhongfucheng.dao.impl.OrderDaoImpl", OrderDao.class);

複製程式碼

DaoFactory讀取配置檔案

  • 再後來,我們發現要修改Dao的實現類,還是得修改service層的原始碼呀..於是我們就在DaoFactory中讀取關於daoImpl的配置檔案,根據配置檔案來建立物件,這樣一來,建立的是哪個daoImpl對service層就是透明的

  • DaoFactory



public class DaoFactory {
	
	private  UserDao userdao = null;
	
	private DaoFactory(){
		try{
			InputStream in = DaoFactory.class.getClassLoader().getResourceAsStream("dao.properties");
			Properties prop = new Properties();
			prop.load(in);
			
			String daoClassName = prop.getProperty("userdao");
			userdao = (UserDao)Class.forName(daoClassName).newInstance();
			
		}catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
	
	private static final DaoFactory instance = new DaoFactory();
	
	public static DaoFactory getInstance(){
		return instance;
	}
	
	
	public UserDao createUserDao(){
		return userdao;
	}
	
}

複製程式碼
  • service
	UserDao dao = DaoFactory.getInstance().createUserDao();
複製程式碼

Spring依賴注入

通過上面的歷程,我們可以清晰地發現:物件之間的依賴關係,其實就是給物件上的屬性賦值!因為物件上有其他物件的變數,因此存在了依賴...

Spring提供了好幾種的方式來給屬性賦值

  • 1) 通過建構函式
  • 2) 通過set方法給屬性注入值
    1. p名稱空間
  • 4)自動裝配(瞭解)
  • 5) 註解

搭建測試環境

  • UserService中使用userDao變數來維護與Dao層之間的依賴關係

  • UserAction中使用userService變數來維護與Service層之間的依賴關係

  • userDao

public class UserDao {

	public void save() {
		System.out.println("DB:儲存使用者");
	}
}

複製程式碼
  • userService

public class UserService {
	
	private UserDao userDao; 

	public void save() {
		userDao.save();
	}
}

複製程式碼
  • userAnction
public class UserAction {

	private UserService userService;

	public String execute() {
		userService.save();
		return null;
	}
}
複製程式碼

建構函式給屬性賦值

其實我們在講解建立帶引數的建構函式的時候已經講過了...我們還是來回顧一下唄..

我們測試service和dao的依賴關係就好了....在serice中加入一個建構函式,引數就是userDao

    public UserService(UserDao userDao) {
        this.userDao = userDao;
		
		//看看有沒有拿到userDao
		System.out.println(userDao);
    }

複製程式碼

applicationContext.xml配置檔案


    <!--建立userDao物件-->
    <bean id="userDao" class="UserDao"/>

    <!--建立userService物件-->
    <bean id="userService" class="UserService">
        <!--要想在userService層中能夠引用到userDao,就必須先建立userDao物件-->
        <constructor-arg index="0" name="userDao" type="UserDao" ref="userDao"></constructor-arg>
    </bean>

複製程式碼
  • 測試:可以成功獲取到userDao物件

        // 建立容器物件
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");

        //得到service物件
        UserService userService = (UserService) ac.getBean("userService");
複製程式碼

這裡寫圖片描述


通過set方法給屬性注入值

我們這裡也是測試service和dao層的依賴關係就好了...在service層通過set方法來把userDao注入到UserService中

  • 為UserService新增set方法

public class UserService {

    private UserDao userDao;


    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;

        //看看有沒有拿到userDao
        System.out.println(userDao);
    }

    public void save() {
        userDao.save();
    }
}

複製程式碼

applicationContext.xml配置檔案:通過property節點來給屬性賦值

  • 引用型別使用ref屬性
  • 基本型別使用value屬性

    <!--建立userDao物件-->
    <bean id="userDao" class="UserDao"/>

    <!--建立userService物件-->
    <bean id="userService" class="UserService">
        <property name="userDao" ref="userDao"/>
    </bean>

複製程式碼
  • 測試:

這裡寫圖片描述


內部Bean

我們剛才是先建立userDao物件,再由userService對userDao物件進行引用...我們還有另一種思維:先建立userService,發現userService需要userDao的屬性,再建立userDao...我們來看看這種思維方式是怎麼配置的:

applicationContext.xml配置檔案:property節點內建bean節點


    <!--
        1.建立userService,看到有userDao這個屬性
        2.而userDao這個屬性又是一個物件
        3.在property屬性下又內建了一個bean
        4.建立userDao
    -->
    <bean id="userService" class="UserService">
        <property name="userDao">
            <bean id="userDao" class="UserDao"/>
        </property>
    </bean>


複製程式碼
  • 測試

這裡寫圖片描述

我們發現這種思維方式和伺服器訪問的執行順序是一樣的,但是如果userDao要多次被其他service使用的話,就要多次配置了...

p 名稱空間注入屬性值

p名稱控制元件這種方式其實就是set方法的一種優化,優化了配置而已...p名稱空間這個內容需要在Spring3版本以上才能使用...我們來看看:

applicationContext.xml配置檔案:使用p名稱空間


    <bean id="userDao" class="UserDao"/>
    
    <!--不用寫property節點了,直接使用p名稱空間-->
    <bean id="userService" class="UserService" p:userDao-ref="userDao"/>

複製程式碼
  • 測試

這裡寫圖片描述


自動裝配

Spring還提供了自動裝配的功能,能夠非常簡化我們的配置

自動裝載預設是不開啟的,自動裝配常用的可分為兩種:

  • 根據名字來裝配
  • 根據型別類裝配

XML配置根據名字

applicationContext.xml配置檔案:使用自動裝配,根據名字


    <bean id="userDao" class="UserDao"/>

    <!--
        1.通過名字來自動裝配
        2.發現userService中有個叫userDao的屬性
        3.看看IOC容器中沒有叫userDao的物件
        4.如果有,就裝配進去
    -->
    <bean id="userService" class="UserService" autowire="byName"/>

複製程式碼
  • 測試

這裡寫圖片描述


XML配置根據型別

applicationContext.xml配置檔案:使用自動裝配,根據型別

值得注意的是:如果使用了根據型別來自動裝配,那麼在IOC容器中只能有一個這樣的型別,否則就會報錯!


    <bean id="userDao" class="UserDao"/>

    <!--
        1.通過名字來自動裝配
        2.發現userService中有個叫userDao的屬性
        3.看看IOC容器UserDao型別的物件
        4.如果有,就裝配進去
    -->
    <bean id="userService" class="UserService" autowire="byType"/>

複製程式碼
  • 測試:

這裡寫圖片描述


我們這只是測試兩個物件之間的依賴關係,如果我們有很多物件,我們也可以使用預設自動分配

這裡寫圖片描述


###使用註解來實現自動裝配###

@Autowired註解來實現自動裝配:

  • 可以在構造器上修飾
  • 也可以在setter方法上修飾
  • 來自java的@Inject的和@AutoWired有相同的功能

如果沒有匹配到bean,又為了避免異常的出現,我們可以使用required屬性上設定為false。【謹慎對待】

  • 測試程式碼
@Component
public class UserService {

    private UserDao userDao ;


    @Autowired
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
}
複製程式碼

順利拿到userDao的引用

這裡寫圖片描述

使用JavaConfig配置類實現物件依賴

在有兩種方法(但我測試不出來,如果會的請在評論去告訴我.....)

  • 第一種(測試不出來)

import org.springframework.context.annotation.Bean;

@org.springframework.context.annotation.Configuration
public class Configuration {

    @Bean()
    public UserDao userDao() {

        return new UserDao();
    }

    @Bean
    public UserService userService() {

        //直接呼叫@bean的方法
        return new UserService(userDao());
    }

}
複製程式碼

  • 第二種(測試不出來)

import org.springframework.context.annotation.Bean;

@org.springframework.context.annotation.Configuration
public class Configuration {

    @Bean()
    public UserDao userDao() {

        return new UserDao();
    }

    @Bean
    public UserService userService(UserDao userDao) {

        //通過建構函式依賴注入
        return new UserService(userDao);
    }

}

複製程式碼

這裡寫圖片描述


  • 如果我直接通過構造器傳入的話,那麼報錯了

import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.context.annotation.Bean;

@org.springframework.context.annotation.Configuration
public class Configuration {

    @Bean()
    public UserDao userDao() {

        return new UserDao();
    }

    @Bean(autowire = Autowire.BY_TYPE)
    public UserService userService(UserDao userDao) {

        return new UserService(userDao);
    }

}
複製程式碼

這裡寫圖片描述


  • 我測試中只有通過這種方法才能拿到userDao的引用。
public class Configuration {

    @Bean()
    public UserDao userDao() {

        return new UserDao();
    }

    @Bean(autowire = Autowire.BY_TYPE)
    public UserService userService() {

        return new UserService(userDao());
    }

}


複製程式碼

這裡寫圖片描述


當然了,最簡單直觀的方法還有一種:在UserService中加入setUser()方法,那麼只要set進去就行了..

  • UserService

public class UserService {

    private UserDao userDao ;

    public UserService() {
    }

    public UserService(UserDao userDao) {


    }

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
}
複製程式碼
  • Config

import org.springframework.context.annotation.Bean;

@org.springframework.context.annotation.Configuration
public class Config1 {

    @Bean(name = "userDao")
    public UserDao userDao() {

        return new UserDao();
    }


    @Bean(name="userService")
    public UserService userService() {

        UserService userService = new UserService();

        userService.setUserDao(userDao());

        return userService;
    }

}
複製程式碼

這裡寫圖片描述


最後

擴充套件閱讀:

如果文章有錯的地方歡迎指正,大家互相交流。習慣在微信看技術文章,想要獲取更多的Java資源的同學,可以關注微信公眾號:Java3y

相關文章