這裏的環境是在史上超詳細的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的註解有四種
- @Component用於Spring中的Bean(普通的類)
- @Repository 用於對DAO實現類進行標註
- @Service 用於對Service實現類進行標註
- @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