
Spring 源碼分析(一)之 Spring IOC 容器基礎(chǔ)介紹
Spring IOC 容器在項(xiàng)目中的作用
- 將對(duì)象的構(gòu)建統(tǒng)一解決
- 并自動(dòng)維護(hù)對(duì)象的依賴關(guān)系,從而降低實(shí)現(xiàn)成本
- ...

IOC(Inversion of Control) 控制反轉(zhuǎn)
是面向?qū)ο缶幊讨械囊环N設(shè)計(jì)原則,可以用來減低計(jì)算機(jī)代碼之間的耦合度。其中最常見的方式叫做依賴注入(Dependency Injection,簡(jiǎn)稱DI),還有一種方式叫“依賴查找”(Dependency Lookup)。
系統(tǒng)中通過引入實(shí)現(xiàn)了IoC模式的IoC容器,即可由IoC容器來管理對(duì)象的生命周期、依賴關(guān)系等,從而使得應(yīng)用程序的配置和依賴性規(guī)范與實(shí)際的應(yīng)用程序代碼分離。其中一個(gè)特點(diǎn)就是通過文本的配置文件進(jìn)行應(yīng)用程序組件間相互關(guān)系的配置,而不用重新修改并編譯具體的代碼。
在我們開發(fā)中,經(jīng)常會(huì)A類引用B類、C類、D類...,如果全部交給我們自己來維護(hù)這些類的依賴關(guān)系,工作量比較大而且很容易出錯(cuò),而IOC容器的出現(xiàn),正式為解決這一問題,其可以將對(duì)象的構(gòu)建方法進(jìn)行統(tǒng)一,并自動(dòng)維護(hù)對(duì)象的依賴關(guān)系,從而降低系統(tǒng)的實(shí)現(xiàn)成本。實(shí)現(xiàn)方式:提前對(duì)目標(biāo)對(duì)象基于XML進(jìn)行聲明,或使用注解進(jìn)行聲明。
具體demo如下。
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.demo.spring</groupId>
<artifactId>spring-test</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<spring.version>4.3.8.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>
</dependencies>
</project>
實(shí)現(xiàn)實(shí)體Bean的構(gòu)建
- 基于ClassName的構(gòu)建
- 基于構(gòu)造方法的構(gòu)建
- 靜態(tài)工廠的方法創(chuàng)建
- FactoryBean創(chuàng)建
spring.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.xsd">
<!-- 默認(rèn)構(gòu)造函數(shù)構(gòu)建 基于反射構(gòu)建-->
<bean class="com.demo.spring.HelloSpring"></bean>
<!-- 指定構(gòu)造函數(shù)構(gòu)建 基于反射構(gòu)建-->
<bean class="com.demo.spring.HelloSpring">
<constructor-arg index="0" value="zyy"/>
<constructor-arg name="age" value="18"/>
</bean>
<!-- 靜態(tài)工廠方法構(gòu)建-->
<bean id="helloSpring" class="com.demo.spring.HelloSpring" factory-method="buid">
<constructor-arg name="type" value="A"></constructor-arg>
</bean>
<!-- FactoryBean創(chuàng)建 自定義創(chuàng)建bean-->
<bean id="driver" class="com.demo.spring.DriverFactoryBean" >
<property name="jdbcUrl" value="jdbc:mysql://192.168.5.104:3306"></property>
</bean>
</beans>
HelloSpring,java
package com.demo.spring;
/**
* com.demo.spring
*
* @author Zyy
* @date 2019/2/12 15:50
*/
public class HelloSpring {
private String name;
private int age;
public HelloSpring() {
}
public HelloSpring(String name, int age) {
this.name = name;
this.age = age;
}
//靜態(tài)工廠以及AB測(cè)試
public static HelloSpring buid(String type) {
if ("A".equals(type)) {
return new HelloSpring("zyy",18);
} else if ("B".equals(type)) {
return new HelloSpring("xxx",20);
} else {
throw new IllegalArgumentException("argument must A or B");
}
}
}
DriverFactoryBean.java
package com.demo.spring;
import org.springframework.beans.factory.FactoryBean;
import java.sql.Driver;
import java.sql.DriverManager;
/**
* com.demo.spring
*
* @author Zyy
* @date 2019/2/12 16:13
*/
public class DriverFactoryBean implements FactoryBean {
private String jdbcUrl;
public Object getObject() throws Exception {
return DriverManager.getDriver(jdbcUrl);
}
public Class<?> getObjectType() {
return Driver.class;
}
public boolean isSingleton() {
return true;
}
public String getJdbcUrl() {
return jdbcUrl;
}
public void setJdbcUrl(String jdbcUrl) {
this.jdbcUrl = jdbcUrl;
}
}
bean的基本特性
- 作用范圍
- 生命周期
- 裝載機(jī)制
作用范圍
bean很多是無狀態(tài)的,對(duì)于無狀態(tài)的對(duì)象可以采取單例,而有狀態(tài)的對(duì)象則必須用是多例模式進(jìn)行創(chuàng)建,通過scope可以進(jìn)行設(shè)置
scope="prototype"
scope="singleton"
例如
<bean id="di" class="com.demo.spring.DI" scope="singleton"/>
如果一個(gè)bean設(shè)置成prototype 則可以通過可以BeanFactoryAware 獲取BeanFactory對(duì)象,即可每次獲取都是新對(duì)象。
生命周期
Bean對(duì)象的創(chuàng)建、初始化、銷毀即是Bean的生命周期。通過init-method、destroy-method屬性可以分別指定構(gòu)建方法和初始方法。
<bean id="di" class="com.demo.spring.DI" scope="singleton" init-method="init" destroy-method="destroy"/>
或者可以讓Bean去實(shí)現(xiàn)initializingBean.afterPropertiesSet()、DisposableBean.destroy()方法。分別對(duì)應(yīng)初始方法和銷毀方法。
加載機(jī)制
指定Bean在何時(shí)進(jìn)行加載。設(shè)置lazy-init。
當(dāng)為true:懶加載,用到的時(shí)候才創(chuàng)建對(duì)象
當(dāng)為false:容器啟動(dòng)時(shí),就創(chuàng)建對(duì)象
<bean id="di" lazy-init="true" class="com.demo.spring.DI" scope="singleton" init-method="init" destroy-method="destroy"/>
什么時(shí)候使用懶加載?
懶加載會(huì)使容器啟動(dòng)更快,而非懶加載可以讓容器啟動(dòng)時(shí)更快的發(fā)現(xiàn)程序中的錯(cuò)誤,一般我們選擇非懶加載。
spring依賴注入
一個(gè)bean依賴其他的bean由springIOC容器統(tǒng)一的管理進(jìn)行注入,無需自己外部傳入,內(nèi)部創(chuàng)建進(jìn)行處理。
依賴注入的幾種方式
- set方法注入
- 構(gòu)造方法注入
- 自動(dòng)注入(byName,byType)
- 依賴方法注入(lookup-method)
自動(dòng)注入(byName,byType)
DI.java
package com.demo.spring;
/**
* com.demo.spring
*
* @author Zyy
* @date 2019/2/12 16:35
*/
public class DI {
}
HelloSpring.java
package com.demo.spring;
/**
* com.demo.spring
*
* @author Zyy
* @date 2019/2/12 15:50
*/
public class HelloSpring {
private String name;
private int age;
private DI di;
public HelloSpring() {
}
public HelloSpring(String name, int age) {
this.name = name;
this.age = age;
}
public DI getDi() {
return di;
}
public void setDi(DI di) {
this.di = di;
}
//靜態(tài)工廠以及AB測(cè)試
public static HelloSpring buid(String type) {
if ("A".equals(type)) {
return new HelloSpring("zyy",18);
} else if ("B".equals(type)) {
return new HelloSpring("xxx",20);
} else {
throw new IllegalArgumentException("argument must A or B");
}
}
}
spring.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.xsd">
<!-- 默認(rèn)構(gòu)造函數(shù)構(gòu)建 基于反射構(gòu)建-->
<bean class="com.demo.spring.HelloSpring"></bean>
<!-- 指定構(gòu)造函數(shù)構(gòu)建 基于反射構(gòu)建-->
<bean id="helloByName" class="com.demo.spring.HelloSpring" autowire="byName">
<constructor-arg index="0" value="zyy"/>
<constructor-arg name="age" value="18"/>
</bean>
<!-- byName-->
<bean id="di" class="com.demo.spring.DI"/>
<!-- byType時(shí),如果bean相同 可以指定primary為true 來選擇要注入哪一個(gè)-->
<bean class="com.demo.spring.DI" primary="true"/>
<bean class="com.demo.spring.DI"/>
<!-- 靜態(tài)工廠方法構(gòu)建-->
<bean id="helloSpring" class="com.demo.spring.HelloSpring" factory-method="buid">
<constructor-arg name="type" value="A"></constructor-arg>
</bean>
<!-- FactoryBean創(chuàng)建 自定義創(chuàng)建bean-->
<bean id="driver" class="com.demo.spring.DriverFactoryBean" >
<property name="jdbcUrl" value="jdbc:mysql://192.168.5.104:3306"></property>
</bean>
</beans>
依賴方法注入(lookup-method)
一個(gè)單例的bean依賴了一個(gè)多例的bean,為了每次獲取多例的bean是不同的
DI.java
package com.demo.spring;
/**
* com.demo.spring
*
* @author Zyy
* @date 2019/2/12 16:35
*/
public class DI {
public void inject() {
System.out.println(" inject ");
}
public void init() {
}
public void destroy () {
}
}
LookUpTest.java
package com.demo.spring;
/**
* com.demo.spring
*
* @author Zyy
* @date 2019/2/12 18:20
*/
public abstract class LookUpTest {
public void create() {
getDi().inject();
}
//這個(gè)抽象方法由spring采用cglib(動(dòng)態(tài)字節(jié)碼)進(jìn)行實(shí)現(xiàn)
public abstract DI getDi();
}
spring.xml
<bean class="com.demo.spring.LookUpTest" >
<lookup-method name="getDi"/>
</bean>
該操作的原理是基于動(dòng)態(tài)代理技術(shù),重新生成一個(gè)類繼承目標(biāo)類(cglib),然后重寫抽象方法達(dá)到注入目的。
對(duì)于單例bean依賴多例bean這種情也可以通過實(shí)現(xiàn)ApplicationContextAware、BeanFactoryAware接口獲取BeanFactory實(shí)例,從而調(diào)用geBean()方法獲取新的實(shí)例,推薦使用這種方法,比lookup-method邏輯清晰。
DI.java
package com.demo.spring;
/**
* com.demo.spring
*
* @author Zyy
* @date 2019/2/12 16:35
*/
public class DI {
public void inject() {
System.out.println(" inject ");
}
public void init() {
}
public void destroy () {
}
}
BeanFactoryAwareTest.java
package com.demo.spring;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
/**
* com.demo.spring
*
* @author Zyy
* @date 2019/2/12 22:24
*/
public class BeanFactoryAwareTest implements BeanFactoryAware {
private BeanFactory beanFactory;
public void create() {
beanFactory.getBean(DI.class).inject();
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
}
測(cè)試
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* PACKAGE_NAME
*
* @author Zyy
* @date 2019/2/12 16:02
*/
public class SpringIOCTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
//context.getBean("helloSpring");
//context.getBean("driver");
context.getBean("helloByName");
}
}
如有問題,歡迎留言:)