經過上篇文章大概知道ioc、DI的概念了,下面咱們詳細介紹一下java
IOC經過上文的介紹做用是控制建立對象的解釋權,咱們把代碼從新看一下mysql
//User.java public class User { private String username; private String password; /** * 省略有參無參構造器,getter/setter方法 */ }
咱們也不建立dao、service類了,直接就用這個User類來測試spring
配置文件:applicationContext.xmlsql
<?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:建立的bean的惟一標識,做用是區分其它的bean class:建立的對象的全限定名(包名+類名) --> <bean id="user" class="com.ty.bean.User"></bean> </beans>
//測試文件 public class MyTest { ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml"); @Test public void test1(){ User user = (User) context.getBean("user"); System.out.println(user); } }
這是IOC建立對象的第一種方式即經過bean的id獲取IOC容器中的對象:context.bean("配置文件bean中的id")數據庫
還有另外兩種方式 :session
@Test public void test2(){ User user = context.getBean(User.class); System.out.println(user); }
這種注入方法有劣勢,即:配置文件中有多個同類型的bean(即class重複)時,注入對象就會報錯,例如:app
<?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:建立的bean的惟一標識,做用是區分其它的bean class:建立的對象的全限定名(包名+類名) --> <bean id="user" class="com.ty.bean.User"></bean> <bean id="use2" class="com.ty.bean.User"></bean> </beans>
再接着運行測試文件的時候就會報NoUniqueBeanDefinitionException異常ide
@Test public void test3(){ User user = context.getBean("user",User.class); System.out.println(user); }
光建立對象沒用,最重要的仍是爲對象賦值,咱們說過DI是IOC的實現,DI更能經過代碼體現出來,接下開咱們依次介紹幾種賦值方式post
<bean id="user" class="com.ty.bean.User"> <!-- property:表示屬性注入 name:屬性的名字,實際上是常規bean中set以後的名字(首字母小寫) value:屬性值 --> <property name="username" value="root"></property> <property name="password" value="123456"></property> </bean>
public class Address { private String city; /** * 省略有參無參構造器,getter/setter方法 */ } public class User { private String username; private String password; private String[] hobby; private Address address; private List list; private Set set; private Map map; private Properties properties; /** * 省略有參無參構造器,getter/setter/toString方法 */ }
<!-- private String[] hobby --> <property name="hobby"> <array> <value>吃飯</value> <value>看電影</value> <value>打遊戲</value> <!-- 表示賦空值,能夠起佔位做用--> <null></null> </array> </property> <!--private Address address- 能夠有2種注入方式,一種方式在property標籤嵌套一個bean--> <!-- <property name="address">--> <!-- <bean class="com.ty.bean.Address">--> <!-- <property name="city" value="大連"></property>--> <!-- </bean>--> <!-- </property>--> <!--ref:引用的bean的id--> <property name="address" ref="address"></property> <!-- private List list--> <property name="list"> <list> <value>a</value> <value>b</value> <value>c</value> </list> </property> <!-- private Set set--> <property name="set"> <set> <value>redio</value> <value>book</value> <value>game</value> </set> </property> <!-- private Map map--> <property name="map"> <map> <entry key="1" value="北京"></entry> <entry key="2" value="上海"></entry> <entry key="3" value-ref="address"></entry> </map> </property> <!-- private Properties properties--> <property name="properties"> <props> <prop key="username">root</prop> <prop key="password">root</prop> </props> </property> </bean> <bean id="address" class="com.ty.bean.Address"> <property name="city" value="大連"></property> </bean>
List,Set,Map,Properties中還有一種注入方式,即util命名方式,主要的做用:方便別人引用測試
<!-- private List list--> <property name="list" ref="mylist"></property> <!-- private Set set--> <property name="set" ref="myset"></property> <!-- private Map map--> <property name="map" ref="mymap"></property> <!-- private Properties properties--> <property name="properties" ref="myproperties"></property> </bean> <util:list id="mylist"> <list> <value>a</value> <value>b</value> <value>c</value> </list> </util:list> <util:set id="myset"> <set> <value>redio</value> <value>book</value> <value>game</value> </set> </util:set> <util:map id="mymap"> <entry key="1" value="北京"></entry> <entry key="2" value="上海"></entry> <entry key="3" value-ref="address"></entry> </util:map> <util:properties id="myproperties"> <prop key="username">root</prop> <prop key="password">root</prop> </util:properties>
除了上方util命名空間方式以外搭配着還有一個P命名空間
<bean id="user2" class="com.ty.bean.User" p:username="root" p:password="123456" p:address-ref="address" p:list-ref="mylist"></bean>
能夠按照必定規則進行裝配注入,不用具體指定爲某個屬性賦值,在工廠中查找一個bean,爲屬性注入屬性值。
<bean id="address" class="com.ty.bean.Address"> <property name="city" value="上海"></property> </bean> <bean id="person" class="com.ty.bean.Person" autowire="byName"></bean>
byName:按照bean的id進行裝配
byType:按照bean的類型來進行注入,可是若是有多個相同類型,就會報錯,不知道選擇哪個具體的類型
public class User { private String username; private String password; /** * 省略有參無參構造器,getter/setter方法 */ }
<bean id="user3" class="com.ty.bean.User"> <!--構造注入的constructor-arg標籤裏還有幾個屬性 name:形參列表的名稱 value:實參的值 type:參數類型 index:參數索引,從0開始 --> <constructor-arg name="username" value="root"></constructor-arg> <constructor-arg name="password" value="123456"></constructor-arg> </bean>
屬性注入有P命名空間,構造器注入有C命名空間
<bean id="user4" class="com.ty.bean.User" c:username="root" c:password="123456"></bean>
<bean id="person" class="com.ty.bean.Person" autowire="constructor"></bean>
<bean id="parent" class="com.ty.bean.User"> <property name="username" value="root"></property> </bean> <!--parent:指定繼承的bean信息--> <bean id="son" class="com.ty.bean.User" parent="parent"></bean>
這個是在繼承關係的父bean裏添加abstract屬性,這個做用就是該bean是否能實例化
<bean id="parent" class="com.ty.bean.User" abstract="true"> <property name="username" value="root"></property> </bean>
這個就是bean的前後建立順序,depends-on依賴哪一個bean就誰先建立誰,而後再建立本對象
<bean id="user5" class="com.ty.bean.User" depends-on="address"></bean> <bean id="address" class="com.ty.bean.Address"> <property name="city" value="大連"></property> </bean>
經過scope屬性指定當前bean的做用域,有四個值
singleton:單例模式,從IOC容器中獲取的都是同一個對象,默認的做用域
prototype:多例模式,從IOC容器中獲取的對象每次都是新建立
request:每次發送請求都會有一個新的對象
session:每一次會話都會有一個新的對象
<bean id="user5" class="com.ty.bean.User" scope="singleton"></bean>
ps:singleton做用域:每次在建立IOC容器完成以前此對象已經建立完成,即:
ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
prototype做用域:每次是在須要用到此對象的時候纔會建立,即
User user = (User) context.getBean("user");
工廠自己不須要建立對象,可是能夠經過靜態方法調用,對象=工廠類.靜態工廠方法名();
public class PersonStaticFactory { public static Person getInstance(String name){ Person person=new Person(); person.setId(1001); person.setAge(18); person.setName(name); return person; } }
<!--靜態工廠:類名.靜態方法()--> <bean id="person" class="com.ty.factory.PersonStaticFactory" factory-method="getInstance"> <constructor-arg value="jack"></constructor-arg> </bean>
工廠自己須要建立對象,工廠類 工廠對象=new 工廠類;工廠對象.get對象名();
public class PersonInstanceFactory { public Person getInstance(String name){ Person person=new Person(); person.setId(1001); person.setAge(18); person.setName(name); return person; } }
<!--實例工廠:先建立工廠實例,而後調用工廠實例的方法 factory-bean:表示具體工廠類的實例 factory-method:表示具體工廠實例的方法 --> <bean id="factory" class="com.ty.factory.PersonInstanceFactory"></bean> <bean id="person" class="com.ty.bean.Person" factory-bean="factory" factory-method="getInstance"> <constructor-arg value="jack"></constructor-arg> </bean>
FactoryBean是Spring規定的一個接口,當前接口的實現類,Spring都會將其做爲一個工廠,可是在ioc容器啓動的時候不會建立實例,只有在使用的時候纔會建立對象
public class MyFactoryBean implements FactoryBean { /** * 此方式是spring建立bean方式的一種補充,用戶能夠按照需求建立對象, 建立的對象交由spring IOC容器來進行管理,不管是不是單例,都是在用到的時候纔會建立該對象,不用該對象不會建立 */ @Override public Person getObject() throws Exception { //建立的對象 Person person=new Person(); person.setId(1001); person.setName("jack"); person.setAge(20); return person; } @Override public Class<?> getObjectType() { //返回對象的類型 return Person.class; } @Override public boolean isSingleton() { //是否單例 return true; } }
<bean id="factoryBean" class="com.ty.factory.MyFactoryBean"></bean>
在對象建立完成以後會調用初始化方法
實現InitializingBean接口
//實現這個方法,完成初始化操做 @Override public void afterPropertiesSet() throws Exception { System.out.println("afterPropertiesSet"); }
對象中提供一個普通的方法同時配置Spring配置文件:
public void init(){ System.out.println("init"); }
<bean id="p" class="com.ty.bean.Person" init-method="init"></bean>
順序:對象的注入-》InitializingBean-》普通init-method
@Override public void destroy() throws Exception { System.out.println("destory"); }
public void mydestory(){ System.out.println("mydestory"); }
<bean id="p" class="com.ty.bean.Person" scope="prototype" init-method="init" destroy-method="mydestory"> <property name="id" value="1001"></property> </bean>
銷燬方法只會在scope="singleton"時纔會調用,並且須要對象關閉,例如:context.close()
spring中包含一個BeanPostProcessor的接口,能夠在bean的初始化方法的先後調用該方法,若是配置了初始化方法的前置和後置處理器,不管是否包含初始化方法,都會進行調用
public class MyBeanPostProcessor implements BeanPostProcessor { //對象初始化以前執行 @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("postProcessBeforeInitialization"+beanName); return bean; } //對象初始化對象以後運行 @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("postProcessAfterInitialization"+beanName); return bean; } }
<bean class="com.ty.bean.MyBeanPostProcessor"></bean> <bean id="p" class="com.ty.bean.Person" scope="prototype" init-method="init" destroy-method="mydestory"> <property name="id" value="1001"></property> </bean>
就是把Spring配置文件中須要常常修改的字符串信息,轉移到一個更小的配置文件中,方便後期的維護
例如:數據庫鏈接
<context:property-placeholder location="classpath:db.properties"></context:property-placeholder> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="username" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> <property name="url" value="${jdbc.url}"></property> <property name="driverClassName" value="${jdbc.driverClassName}"></property> </bean>
jdbc.username=root jdbc.password=root jdbc.driverClassName=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/demo
//測試 @Test public void test11() throws SQLException { ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml"); DruidDataSource dataSource = context.getBean("dataSource", DruidDataSource.class); System.out.println(dataSource); System.out.println(dataSource.getConnection()); }