工廠只負責建立對象,而Spring固然不只僅是一個對象工廠;其核心是一個對象容器,因爲具有控制反轉的能力,因此也叫它IOC容器;java
容器能夠理解爲存放對象的地方,固然不只僅是存儲,還有對象的管理,包括-建立-銷燬-裝配; 這樣本來程序要作的事情交給了Spring,因此這屬於IOC,稱之爲IOC容器;mysql
Spring有兩個容器接口ApplicationContext是BeanFactory的子接口。它們均可以做爲Spring的容器;正則表達式
BeanFactory做爲頂級接口主要面向於Spring框架自己,僅提供了基礎基本的容器功能如DIspring
該方式Bean類中必須存在無參構造器sql
<bean id="UserService1" class="com.yyh.serviceimpl.UserServiceImpl"/>
xml配置:api
<bean id="userService" class="com.yyh.serviceimpl.ServiceFactory" factory-method="getService"/>
工廠:session
import com.yyh.service.UserService; public class ServiceFactory { public static UserService getService() { System.out.println("factory static run!"); return new UserServiceImpl(); } }
xml配置:app
<!--工廠Bean--> <bean id="serviceFactory" class="com.yyh.serviceimpl.ServiceFactory"/> <!--Service Bean--> <bean id="userService2" factory-bean="serviceFactory" factory-method="getService2"/>
工廠添加方法:框架
public UserService getService2() { System.out.println("factory instance run!"); return new UserServiceImpl(); }
配置Bean時,可使用 id 或者 name 屬性給bean命名。 id 和 name 屬性做用上同樣,推薦使用id。ide
id取值要求嚴格些,必須知足XML的命名規範。id是惟一的,配置文件中不容許出現兩個id相同的bean。
name取值比較隨意,甚至能夠用數字開頭。在配置文件中容許出現多個name相同的bean,在用getBean()返回實例時,最後的一個Bean將被返回。
注意:在spring5中name和id同樣也不容許有重複的名稱。
若是沒有id,name,則用類的全名做爲name
如 <bean class="test.Test">
,可使用 getBean("test.Test") 返回該實例。
若是存在多個id和name都沒有指定,且類都同樣的,如:
<bean class="com.yh.service.UserService"/> <bean class="com.yh.service.UserService"/> <bean class="com.yh.service.UserService"/>
則能夠經過getBean(「完整類名#索引」)來得到,如:getBean("com.yh.service.UserService#1")
,索引從0開始,若要獲取第一個則能夠忽略索引,直接寫類名
name中可使用分號(「;」)、空格(「 」)或逗號(「,」)來給這個Bean添加多個名稱(至關於別名 alias 的做用)。如:
" name=「a b c d」等同於 name=「a,b,c,d」 這樣寫至關於有 1 2 3 4(4個)個標識符標識當前bean id=「1 2 3 4」 這樣寫至關於有 「1 2 3 4」(1個)個標識符標識當前bean "
而id中的任何字符都被做爲一個總體 ;
若是既配置了 id ,也配置了 name ,則兩個都生效。固然也不能重複;
當註解中出現與xml配置中相同的id或相同name時,優先是用xml中的配置
類別 | 說明 |
---|---|
singleton | 默認值; 在Spring IoC容器中僅存在一個Bean實例,Bean以單例方式存在。 |
prototype | 每次從容器中調用Bean時,都返回一個新的實例; |
request | 每次HTTP請求都會建立一個新的Bean,該做用域僅適用於WebApplicationContext環境 |
session | 同一個HTTP Session 共享一個Bean,不一樣Session使用不一樣Bean,僅適用於WebApplicationContext 環境 |
application | Bean的做用域爲ServletContext ,僅適用於WebApplicationContext環境。 |
做用域就是指做用範圍:單例則表示對象的做用範圍是整個Spring容器,而prototype則表示無論理做用範圍,每次get就直接建立新的
Spring提供了非入侵(不強制類繼承或實現)方式的生命週期方法,能夠在Bean的初始化以及銷燬時作一些額外的操做
<bean id="service" class="com.yh.service.UserService" scope="singleton" init-method="init" destroy-method="destroy"/> <!-- init-method 用於初始化操做 detroy-method 用於銷燬操做
注意:destroy僅在scope爲singleton時有效 由於多例狀況下
執行順序及其含義:
1 構造對象
2 設置屬性
3 瞭解Bean在容器中的name
4 瞭解關聯的beanFactory
5 初始化前處理
6 屬性設置完成
7 自定義初始化方法
8 初始化後處理
9 業務方法
10 Bean銷燬方法
11 自定義銷燬方法
依賴指的是當前對象在運行過程當中須要使用到的其餘參數,Spring能夠幫助咱們來完成這個依賴關係的創建,說的簡單點就是把你須要參數的給你,而你不用管參數怎麼來的,已達到儘量的解耦 ;
舉個例子:
Controller 中須要Service對象,Spring能夠把Service自動丟到你的Controller中,你不須要關係Service是怎麼來的,用就完了;
要使用依賴注入,必須如今須要依賴的一方(Controller)中爲被依賴的一方(Service)定義屬性,用以接收注入;
bean:
public class User2 { private String name; private int age; private Phone phone; public User2(String name, int age, Phone phone) { this.name = name; this.age = age; this.phone = phone; } @Override public String toString() { return "User2{" + "name='" + name + '\'' + ", age=" + age + ", phone=" + phone + '}'; } }
xml:
<!--依賴注入--> <bean id="user" class="com.yh.demo2.User2"> <!--按參數名稱注入 --> <constructor-arg name="name" value="jerry"/> <!--按參數位置注入 --> <constructor-arg index="1" value="18"/> <!--參數類型爲其餘bean對象時value換成ref --> <constructor-arg name="phone" ref="phone"/> <!--type指定類型不經常使用 --> <!--<constructor-arg type="java.lang.String" name="name" value="jerry"/>--> </bean> <!--user須要的依賴phoneBean--> <bean id="phone" class="com.yh.demo2.Phone"/>
依然對上面的User2類的依賴進行注入
<!--setter方法注入(屬性注入) --> <bean id="user2" class="com.yh.demo2.User2"> <property name="name" value="jerry"/> <!--注入常量值--> <property name="age" value="20"/> <property name="phone" ref="phone"/> <!--注入其餘Bean--> </bean>
注意:上述配置要求User2必須存在空參構造器
上面經過嵌套標籤constructor的方式注入依賴,在須要注入的依賴較多時致使xml顯得很臃腫,C名稱空間來簡化xml中<constructor-arg>
標籤的書寫
使用前須要先在xml頭部進行聲明
xmlns:c="http://www.springframework.org/schema/c"
使用:
<!--c命名空間的使用--> <bean id="user3" class="com.yh.demo2.User2" c:name="jerry" c:_1="21" c:phone-ref="phone"></bean> <!-- c:name 指定爲name參數賦值 c:_1 指定爲構造函數的第2個參數賦值 c:phone-ref 指定爲構造函數的第phone參數賦值爲id爲"phone"的Bean -->
一樣的p命名空間則是用於簡化<property>
標籤的書寫
聲明:
xmlns:p="http://www.springframework.org/schema/p"
使用:
<bean id="user4" class="com.yh.demo2.User2" p:name="jerry" p:age="20" p:phone-ref="phone"/> <!-- p:name 指定爲name屬性賦值 p:age 指定爲age屬性賦值 p:phone-ref 爲phone屬性賦值爲id爲"phone"的Bean -->
SpEL即Spring Expression Language的縮寫,與JSTL同樣是表達式語言,能夠支持使用更加複雜的語法注入依賴,包括標準數學運算符,關係運算符,邏輯運算符,條件運算符,集合和正則表達式等;
語法:#{表達式}
用例:
<!--SpEL --> <bean id="user5" class="com.yh.demo2.User2"> <!--<property name="name" value="#{'jerry'}"/>--> <!--字符常量--> <!--<property name="age" value="#{100.0}"/>--> <!--數字常量--> <!--<property name="phone" value="#{phone}"/>--> <!--對象引用--> <!--<property name="name" value="#{phone.model.concat(' jerry')}"/>--> <!--方法調用--> <!--<property name="age" value="#{1+100}"/>--> <!--算數符--> <!--<property name="name" value="#{'11' > '22'}"/>--> <!--比較符--> <!--<property name="name" value="#{true or false}"/>--> <!--邏輯符--> <!--<property name="name" value="#{1 > 0?1:0}"/>--> <!--三目--> </bean>
<!-- 容器數據類型注入--> <bean id="user100" class="com.yh.demo2.User3"> <!--set注入 --> <property name="set"> <set> <value>3</value> <value>3</value> <value>a</value> </set> </property> <!--list注入 --> <property name="list"> <list> <value>3</value> <value>3</value> <value>a</value> </list> </property> <!--map注入 --> <property name="map"> <map> <entry key="name" value="jerry"/> <entry key="age" value="18"/> <entry key="sex" value="man"/> </map> </property> <!--properties注入 --> <property name="properties"> <props> <prop key="jdbc.user">root</prop> <prop key="jdbc.password">admin</prop> <prop key="jdbc.driver">com.mysql.jdbc.Driver</prop> </props> </property> </bean>
強調:Spring的依賴注入要麼經過構造函數,要麼經過setter,什麼接口注入都tm扯犢子;
接口注入不是一種注入方式,只不過因爲OOP的多態,Spring在按照類型注入時,會在容器中查找類型匹配的Bean,若是沒有則查找該類的子類,若是容器中有多個匹配的子類Bean時會拋出異常,坑了一堆人,而後就開始意淫給這個問題取個名字吧.....接口注入.....
通用註解
@Component 用於在Spring中加入Bean
MVC場景下
@Controller 等價於 @Component 標註控制層
@Service 等價於 @Component 標註業務層
@Repository 等價於 @Component 標註數據訪問層(DAO)
在實現上沒有任何不一樣,僅僅是爲了對Bean進行分層是結構更清晰
使用步驟:
1.須要依賴context和aop兩個jar包
2.添加命名空間
3.指定掃描的註解所在的包
<?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 https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--指定要掃描註解的包 --> <context:component-scan base-package="com.yh.demo"/> </beans>
要註冊的Bean:
import org.springframework.stereotype.Component; @Component("userService) public class UserService { public String hello(String name){ return "hello " + name; } }
測試:
public class Tester { @Test public void test(){ ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); UserService service = (UserService) context.getBean("userService"); System.out.println(service.hello("jerry")); } }
若註解中沒有指定id則默認使用簡單類名且小寫開頭,userService
@Value用於對基本類型屬性進行注入
@Autowired將容器中的其餘Bean注入到屬性中
@Qualifier("BeanID") 指定要注入的Bean的ID
準備UserDAO類:
import org.springframework.stereotype.Repository; @Repository("userDAO") public class UserDAO { public void save(){ System.out.println("user saved!"); } }
UserService類:
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @Component("userService") public class UserService { @Value("hello")//基本類型 private String info; //@Autowired(required = false) //默認爲true表示屬性時必須的不能爲空 @Autowired //注入類型匹配的Bean //@Qualifier("userDAO") //明確指定須要的BeanID private UserDAO userDAO; //set/get..... }
測試:
import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Tester { @Test public void test(){ ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); UserService service = (UserService) context.getBean("userService"); System.out.println(service.getInfo());//普通屬性測試 service.getUserDAO().save();//對象屬性測試 } }
Autowired默認自動注入類型一致的Bean;required屬性用於設置屬性是不是必須的默認爲true
Qualifier須要和Autowired搭配使用,用於明確指定要注入的Bean的ID
當Spring中存在多個類型都匹配的Bean時直接報錯
接口:
public interface PersonDao { }
兩個實現類:
@Repository() public class PersonDaoImpl1 implements PersonDao{ }
@Repository() public class PersonDaoImpl2 implements PersonDao{ }
注入:
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component("userService") public class UserService { @Autowired private PersonDao personDao; }
Qualifier和Autowired書寫繁瑣,@Resource可將兩個標籤的功能整合,即注入指定ID的Bean
@Resource標準註解的支持是JSR-250中定義的,因此時使用須要導入擴展包,Maven依賴以下:
<dependency> <groupId>javax.annotation</groupId> <artifactId>javax.annotation-api</artifactId> <version>1.3.2</version> </dependency>
Resource默認按照使用屬性名稱做爲id查找,查找失敗則使用類型查找
能夠利用name屬性指定經過id查找
也可經過type指定類型,當出現相同類型的多個Bean時拋出異常
import javax.annotation.Resource; @Component("userService") public class UserService { //@Resource()//默認按照id/name //@Resource(name="xx")//指定name //@Resource(type = PersonDaoImpl1.class) //指定type @Resource(name="xx",type = PersonDaoImpl1.class)//同時指定name和type private PersonDao personDao; }
用於標註Bean的做用域
@Repository() @Scope("prototype") //每次get都建立新的 public class UserDAO { public void save(){ System.out.println("user saved!"); } }
由於註解的表達能力有限,不少時候沒法知足使用需求;咱們能夠將註解和XML配合使用,讓XML負責管理Bean,註解僅負責屬性注入;