超全的SpringIOC和DI實踐

這裏的環境是在史上超詳細的SpringMVC框架搭建基礎上搭建的,如有不懂之處,望請參考。

一、配置SpringIOC容器

在web項目中配置Spring的Ioc容器其實就是創建web應用的上下文

在web.xml配置springIOC容器

  <!-- springIOC容器的配置  -->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:config/spring-context.xml</param-value>
  </context-param>
  
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener> 

在src下config包裏創建spring-context.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-3.2.xsd">
		
</beans>

二、基於XML的配置方式

DI依賴注入,有構造器注入和setter注入兩種方式

創建User類和Address類作爲Bean

public class User {
	private String name;
	private Address address;
	private String[] books;
	private List<String> courses;
	private Map<String,String> cards;
	private Set<String> games;
	private Properties properties;
	public User(String name,Address address){
		this.name=name;
		this.address=address;
	}
	//由於篇幅原因,setter方法省略
}
public class Address {
	private String name;

	public void setName(String name) {
		this.name = name;
	}
	@Override
	public String toString() {
		return "Address [name=" +name + "]";
	}
}

(1)構造器和setter屬性的注入

	<!-- 先創建一個空的對象,再用對象來注入,類裏面的構造函數必須是無參的 -->
	<!-- setter注入要求類裏面必須要有注入屬性的set方法 -->
	<bean id="address" class="com.my.model.Address">
	<property name="name" value="洛陽市"/>
	</bean>
	
	<!-- 構造器注入 -->
	<!-- 構造器注入不要求有注入屬性的set方法 -->
	<bean id="user" class="com.my.model.User">
	<constructor-arg name="name" value="王雷"></constructor-arg>
	<constructor-arg name="address" ref="address"></constructor-arg>
	</bean>

(2)集合類型setter屬性的注入

<bean id="myuser" class="com.my.model.User">
		<property name="name" value="王雷"></property>
		<!-- 數組注入 -->
		<property name="books">
			<array>
				<value>小白書</value>
				<value>白皮書</value>
				<value>小紅書</value>
			</array>
		</property>
		<!-- List 注入 -->
		<property name="courses">
			<list>
				<value>java從入門到精通</value>
				<value>java從精通到精闢</value>
			</list>
		</property>
		<!-- Map注入 -->
		<property name="cards">
			<map>
				<entry>
					<key><value>ICBC</value></key>
					<value>工商銀行</value>
				</entry>
				<entry key="ABC">
					<value>農業銀行</value>
				</entry>
			</map>
		</property>
		<!-- Set注入 -->
		<property name="games">
			<set>
				<value>王者榮耀</value>
				<value>LOL</value>
				<value>dota</value>
			</set>
		</property>
		<!-- 直接賦值爲null -->
		<!-- <property name="wife"><value>null</value></property> -->
		<!-- null注入 -->
		<property name="wife"><null /></property>
		<!-- properties 注入 -->
		<property name="properties">
			<props>
				<prop key="driver">com.mysql.jdbc.Driver</prop>
				<prop key="url">jdbc:mysql://localhost:3306/mybatis</prop>
				<prop key="username">root</prop>
				<prop key="password">root</prop>
			</props>
		</property>

	</bean>

三、基於註解的配置方式

常用定義Bean的註解有四種

  1. @Component用於Spring中的Bean(普通的類)
  2. @Repository 用於對DAO實現類進行標註
  3. @Service 用於對Service實現類進行標註
  4. @Controller 用於對Controller實現類進行標註

它們的功能都表示定義一個bean,只是用名字來劃清界限

@Resource和@Autowired都是做bean的注入時使用
共同點
兩者都可以寫在字段和setter方法上。兩者如果都寫在字段上,那麼就不需要再寫setter方法。
在這裏插入圖片描述
不同點
(1)Resource由J2EE提供,需要導入包javax.annotation.Resource,Autowired爲Spring提供的註解。
(2)@Resource默認按照ByName自動注入 。
@Resource有兩個重要的屬性:name和type。如果使用name屬性,則使用byName的自動注入策略,而使用type屬性時則使用byType自動注入策略。如果既不制定name也不制定type屬性,這時將通過反射機制使用byName自動注入策略。
@Autowired默認是按照類型byType裝配依賴對象。
@Autowired默認情況下它要求依賴對象必須存在,如果允許null值,可以設置它的required屬性爲false。如果我們想使用按照名稱(byName)來裝配,可以結合@Qualifier註解一起使用。

@Service
public class TestServiceImpl {

	public void print(){
		System.out.println("我是TestServiceImpl的print()");
	}
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:config/spring-*.xml"})
public class TestAnnotation {

	@Resource
	private TestServiceImpl my;
	
	@Test
	public void test(){
		my.print();
	}
}

運行結果爲:
在這裏插入圖片描述

需要注意的是,不能直接用@Test做單元測試,否則會出現空指針異常,大致原因如下:
junit單元測試是一個獨立的單元測試,它跟你的上下文沒有關係,原因據說是因爲spring爲了考慮安全性問題,在多線程情況下,不支持直接使用 @Resouce 註解方式進行直接的bean注入,也就是說,如果在多線程調用該注入實例化的變量時,將會報NullPointerException 。
解決方案是在需要測試的類上面添加Spring提供的單元測試

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:config/spring-*.xml"})

除了需要導入單元測試的那兩個包之外,還需要導入,如果不會單元測試請參考Java之JUnit4單元測試
在這裏插入圖片描述
細心的同學會發現,@Resource默認按照ByName的方式進行自動裝配,那麼爲什麼上面的測試代碼把名字命名爲my也可以運行成功,探究@Resource的原理就知道了。
既不指定name屬性,也不指定type屬性,則自動按byName方式進行查找。如果沒有找到符合的bean,則回退爲一個原始類型進行進行查找,如果找到就注入。也就是說,如果按byName找不到,則按byType去找。

四、獲取IOC容器的方式

獲取IOC容器,其實就是獲取Spring的上下文的容器,即ApplicationContext對象,通過該對象可以得到容器中定義的Bean。

方法一: 直接加載配置IOC上下文的XML文件

ApplicationContext context=new ClassPathXmlApplicationContext("config/spring-context.xml");

方法二: 通過Spring提供的工具類獲取容器對象

		ServletContext application=request.getServletContext();
		ApplicationContext context1 = WebApplicationContextUtils.getRequiredWebApplicationContext(application);
		ApplicationContext context2 = WebApplicationContextUtils.getWebApplicationContext(application);

這種方式適合於採用Spring框架的B/S系統,通過ServletContext對象獲取ApplicationContext對象,然後在通過它獲取需要的類實例。
上面兩個工具方式的區別是,前者在獲取失敗時拋出異常,後者返回null。
由於spring是注入的對象放在ServletContext中的,所以可以直接在ServletContext取出 WebApplicationContext 對象:

WebApplicationContext context = (WebApplicationContext) servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);

方法三: 繼承自抽象類ApplicationObjectSupport
抽象類ApplicationObjectSupport提供getApplicationContext()方法,可以方便的獲取到ApplicationContext。 Spring初始化時,會通過該抽象類的setApplicationContext(ApplicationContext context)方法將ApplicationContext 對象注入。
方法四: 繼承自抽象類WebApplicationObjectSupport
類似上面方法,調用getWebApplicationContext()獲取WebApplicationContext
方法五: 實現接口ApplicationContextAware
實現該接口的setApplicationContext(ApplicationContext context)方法,並保存ApplicationContext 對象。 Spring初始化時,會通過該方法將ApplicationContext對象注入。

獲取ioc容器之後,就可以根據需要得到容器裏面的bean對象了,可以通過ApplicationContext對象的getBean方法得到

		Address address=(Address) context.getBean("address");
		User user=(User) context.getBean(User.class);

第一種方式是通過bean的ID得到的,第二種方式是通過bean的類型得到的,如果得到的結果不是唯一,則會發生異常。

參考鏈接
https://blog.csdn.net/m_q_x/article/details/77881529
https://blog.csdn.net/Changui_/article/details/78208315
https://blog.csdn.net/khandudu/article/details/81206400

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