Spring.IOC
1 vs. 工厂模式
public class ServiceFactory { public Object getServiceObject (String name) { if (name.equals("bs")) { A a = new A(); B b = new B(a); // 构造器 MyBatiscConnection conn = new MyBatiscConnection(); LogUtil log = new LogUtil(); BookService bookservice = new BookService(); b.setA(a); // setter conn.setB(b); bookservice.setConn(conn); LogUtil.setConn(conn); bookservice.setLogger(log); return bookservice; } } } main () { ServiceFactory sf = new ServiceFactory(); BookService bookservice = sf.getServiceObject("bs"); bookservice.sellBook(); }
将工厂模式切割,分为解析程序和 xml 文件。
xml 暴露出来,用来 描述 工厂需要实例化的类以及他们之间的关系。
<xml> <!-- 之前命令;描述 --> <bean id="a" class="xxx.yyy.A" /> <bean id="b" class="xxx.yyy.B"> <property name="a" bean="a" /> </bean> <bean id="conn" class="xxx.yyy.MyBConn"> <property name="b" bean="b" /> </bean> <bean id="log" class="xxx.yyy.LogUtil"> <property name="conn" bean="conn" /> </bean> <bean id="bookservice" class="xxx.BookService"> <property name="conn" bean="conn" /> <property name="log" bean="log" /> </bean> </xml>
2 接口
最核心的 jar 包:
- spring-core
- spring-bean
- spring-context
最核心的接口是 BeanFactory
,它用来描述 IOC 容器:

它很干净,很纯粹,最主要的方法是 getBean
用来给调用方返回一个实例化好的对象。
在实际运用中,需要一些周边功能,比如加载资源/国际化/等等,Spring 为此提供了 ApplicatinContext
接口。它本身是 BeanFactory 的一个实现:

可以看到,ApplicationContext 除了实现了 BeanFactory,还实现了其他一些 实用的接口。因此,它是在 Spring 中操作一切的核心。
这是 门面模式 的一种典型使用。
3 配置
3.1 传统的方式 xml
这种方式,充分利用了 xml 文件的优势:
- 接受度比较高,语法简单
- 表达能力比较强
- 生态比较完整,基于 xml 的校验、解析等比较完善
所以,最开始的时候,描述工厂里 bean 声明的方式,选用的就是 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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <bean id="bs1" class="learning.spring.BookServiceImpl" /> <bean id="bs2" class="learning.spring.BookService2Impl" /> <!--<bean id="bs2" class="learning.spring.BookServiceImplImpl" />--> <!--<bean id="bs3" class="learning.spring.BookServiceImplImpl" />--> </beans>
但是:
- 很多人不喜欢 xml 这种标签式的语法。写起来麻烦,看起来不舒服
- xml 方式过于重型
- xml 语法校验虽然强大,但不够强大
- xml 虽然灵活,但不够灵活
所以,就产生了很多其他的叛逆的想法
3.2 基于 Java 的方式进行配置
@Configuration public class SuibianSpringConfig { // <bean id="bs1" class="learning.spring.BookServiceImpl" /> @Bean public BookService bs1() { if (Math.random() > 0.5) { // 构造器注入 return new BookService2Impl(bookDAO()); } else { // setter 注入 BookService bs = new BookServiceImpl(); bs.setBookDAO(bookDAO()); return bs; } } @Bean public BookDAO bookDAO () { return new BookDAO(); } }
3.3 混合双打
Java Style 中混入 XML Style:
@Configuration @ImportResource(locations = "learning/spring/my-spring.xml") public class SuibianSpringConfig { // <bean id="bs1" class="learning.spring.BookServiceImpl" /> @Bean public BookService bs1() { System.out.println(bookDAO()); if (Math.random() > 0.5) { return new BookService2Impl(); } else { return new BookServiceImpl(); } } @Bean public BookDAO bookDAO () { return new BookDAO(); } }
XML style 中混入 Java Style:
<?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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <bean id="bs2" class="learning.spring.BookService2Impl" /> <bean class="learning.spring.configuration.SuibianSpringConfig" /> </beans>
4 装配 (Wiring)
创建应用对象之间协作关系的行为,通常称为装配 (Wiring)。这是依赖注入 (DI) 的本质。
装配的基础,是使用配置文件对 Bean 的关系进行声明。
总结起来,在 Spring 中,声明 Bean 一共有三种方式:
- 在 XML Style 的配置中,使用
<bean />
节点 - 在 Java Style 的配置中,使用
@Bean
注解 - 开启 Component 扫描,然后使用相关注解:
@Component/@Controller/@Service/@Repository
4.1 Wiring in XML
装配:
<!-- 最简单的声明 --> <bean class="package.Dog" /> <!-- Class.forName("package.Dog").newInstance(); --> <bean id="xxx" class="包名.类的名字" /> <!-- 对象内部必须要有数据,所以要给他数据 --> <!-- 构造器注入 --> <bean id="donkey" class="learning.components.Donkey"> <constructor-arg index="0" value="得水" /> <constructor-arg index="1" value="333" /> </bean> <!-- 属性注入 --> <bean class="learning.components.Goal" id="goal"> <property name="name" value="多利" /> <property name="countOfLegs" value="4" /> </bean> <!-- 集合的注入 --> <bean class="learning.components.Goal" id="goal"> <property name="name" value="多利" /> <property name="legs"> <list> <!-- map/arrary/set/prop 同理 --> <value>leg1</value> <value>leg2</value> <value>leg3</value> </list> </property> </bean> <!-- Bean 的依赖 --> <bean class="learning.components.Goal" id="goal"> <property name="xxx"> <ref bean="xxx" /> </property> <property name="yyy" ref="yyy" /> <property name="zzz"> <bean class="package.Zzz" /> </property> </bean> <!-- 使用 C/P/UTIL 命名空间简化使用 --> <bean class="learning.components.Goal" id="goal" p:name="多利" p:countOfLegs="4" p:xxx-ref="xxx" p:aliases-ref="aliaslist" /> <util:list id="aliaslist"> <value>kkkk1</value> <value>kkkk2</value> <value>kkkk3</value> </util:list> <!-- 将实例化的工作,委托给工厂 --> <bean class="learning.components.AnimalFactory" id="goalmiemie" factory-method="xxx" /> <bean class="learning.components.AnimalFactory" id="deshui" factory-method="yyy" /> <!-- 包含其他的配置文件到这里 --> <import resource="xxxxx.xml" />
另外:
- denpend-on 定义顺序
- parent 定义继承
- scope 定义初始化策略
- lazy 延迟初始化
- alias 定义别名
- import 引入其他的定义文件
4.2 Wiring in JavaConfig
@Configuration @ComponentScan("包名") @ImportResource("..../xxx.xml") @Import({ShiroConfig.class, MybatisConfig.class, ScheduleConfig.class}) public class SpringConfig { @Bean public Author author () { return new Author("luxun"); } @Bean public Book book1 (Author author) { return new Book("呐喊", author); // 参数注入 } @Bean(name = "book3") public Book book2 () { return new Book("呐喊", author()); // 方法调用注入 } }
4.3 Wiring with Annotation
首先,需要开启 Component Scanning,即组件扫描:
// JavaConfig @Configuration @ComponentScan("xxx.components") public class SpringConfig { // ... } // XMLConfig: // // <context:component-scan base-package="xxx.components" />
声明 Bean 有四个注解:
- @Component
- @Controller
- @Service
- @Repository
在注解的方式中,要确定 Bean 之间的关系,需要使用下面注解进行 “注入”:
- @Autowired
- @Inject
- @Resource
- @Value
注入的注解:
- 可以放在 field 上,称作属性注入
- 可以放在 constructor 上,称作构造器注入
- 可以放在 setter 方法上,称作 setter 注入
@Controller public class BookController { @Autowired // 属性注入 BookDAO bookDAO; @Autowired // 构造器注入 public BookController(BookDAO bookDAO) { this.bookDAO = bookDAO; } @Autowired // setter 注入 public void setBookDAO(BookDAO bookDAO) { this.bookDAO = bookDAO; } }
属性注入的写法是最简单的,但不建议使用。原因:
- …
- …
- …
5 生命周期 (Lifecycle)
如果想在 Bean 实例化过程中,做一些额外的事情, 就需要了解 Bean 从产生,到使用,到最后销毁的整个过程。
也就是 Bean 的生命周期。

一个 Bean 从产生到消亡经历了这些过程:
- Spring 对 bean 进行实例化 (
Instance
) - Spring 对 bean 的值或引用进行注入 (
Populate Properties
) - 若实现了 BeanNameAware 接口,则调用 setBeanName 方法,即
BeanNameAware.setBeanName()
BeanFactoryAware.setBeanFactory()
ApplicationContextAware.setApplicationContext()
BeanPostProcessor.postProcessBeforeInitialization()
InitializingBean.afterPropertiesSet()
。若声明了init-method
方法,调用之BeanPostProcessor.postProcessAfterInitialzation()
- 此时 bean 准备就绪,就可以使用了,直到销毁
DisposableBean.destroy()
。若 bean 声明了 destory-method 属性,调用之