对于前面不使用spring的例子,是时候将其改造成spring应用了。
从之前的练习模块spring-00-demo-without-spring
拷贝一份,修改为spring-01-demo-with-spring
,在此基础上应用Spring框架的功能。
首先引入spring相关的依赖,因为父模块是Spring源码工程,引入spring核心模块就变得很简单了,只要引入spring-context
模块即可:
spring-01-demo-with-spring\build.gradle
dependencies {
compile(project(":spring-context"))
}
接下来我们重点看spring如何配置bean(POJO),在源码包resources下新建这样几个xml文件:
文件命名没有特别要求,一般以-context
结尾,表明是Spring IoC容器要加载的上下文配置。
application-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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- id为该实例在IoC容器中的标识字段 -->
<bean id="funnyBook" class="com.xiaoma.spring.example.reading.Book">
<property name="name" value="金瓶梅" />
<property name="type" value="文学" />
</bean>
<bean id="boringBook" class="com.xiaoma.spring.example.reading.Book">
<property name="name" value="Spring入门" />
<property name="type" value="IT" />
</bean>
<bean id="dabao" class="com.xiaoma.spring.example.reading.Child">
<property name="name" value="大宝" />
<!-- 通过ref属性引用其他bean -->
<property name="book" ref="funnyBook" />
</bean>
<bean id="erbao" class="com.xiaoma.spring.example.reading.Child">
<property name="name" value="二宝" />
<property name="book" ref="boringBook" />
</bean>
<bean id="bookStoreApp" class="com.xiaoma.spring.example.bookstore.BookStoreApp">
<property name="bookService" ref="bookService" />
</bean>
<!-- 当一个工程模块很多时可以使用<import />标签按模块将bean的声明维护到不同的xml片段中 -->
<import resource="dao-context.xml" />
<import resource="service-context.xml" />
</beans>
最外层的<beans>
标签用来管理内部定义的<bean>
标签。这里我们为每个<bean>
标签指定了class
和id
属性,表明我们要对哪种bean的类型进行实例化,实例化后的对象叫什么名字。<bean>
标签内部可以指定<property>
属性来为bean的属性赋值,也叫做注入。<property>
标签的value
属性可以赋基本类型的值,而ref
属性则用于对依赖的bean对象进行注入,也就是给引用类型的属性赋值。
xml中定义的配置还可以以碎片文件的形式,通过<import>
标签导入到当前文件中。比如这里我们将dao
和service
层的bean定义分散到各自的xml片段文件中,并通过<import>
标签导入进来。
dao-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans ...>
<bean id="bookDao" class="com.xiaoma.spring.example.bookstore.dao.impl.BookDaoImpl" />
<!-- <bean id="bookDao" class="com.xiaoma.spring.example.bookstore.dao.impl.BookDaoMockImpl" />-->
</beans>
以上我们的id为bookDao
的bean是一个BookDaoImpl
类型的实例,当然我们也可以把它替换为BookDaoMockImpl
类型,只要把第一个注释掉,把第二个注释放开即可。
service-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans ...>
<bean id="bookService" class="com.xiaoma.spring.example.bookstore.service.impl.BookServiceImpl">
<property name="bookDao" ref="bookDao" />
</bean>
</beans>
声明service的bean组件时,指定了对bookDao
属性进行注入时引用的是哪一个bean,ref
的值为beanName
,也就是依赖的bean的名称,要确保ref
指向的bean定义必须存在。
这种xml声明bean的形式相比我们之前在应用启动方法中手动的new
实例并进行依赖关系的组装,我们会发现,开发人员无需再关注于如何构建实例,包括构造实例的细节,需要传什么样的实参,从哪里取这些实参,以及需要设置的属性为引用bean的情况,如何匹配合适的类型,不存在相应类型的bean以及多个同类型的bean如何做决定等等。对开发人员来说,仅仅只需要声明bean的创建形式,声明属性注入的形式,实例化bean、依赖注入这些交给容器来完成,做到了声明bean和实例化bean解耦。
spring的IoC容器负责加载开发人员声明的bean定义文件,容器的启动工作和对bean的管理对开发人员来说是透明的。我们要做的只是在容器正常启动后从中获取我们想要的bean来完成业务操作。看调整后的相关类:
ReadingApp.java
package com.xiaoma.spring.example.reading;
import ...
@Slf4j
public class ReadingApp {
public void run() {
// 根据指定的配置文件启动spring IoC容器
ApplicationContext context = new ClassPathXmlApplicationContext("application-context.xml");
// 根据标识名称和类型从IoC容器获取Bean
Book funnyBook = context.getBean("funnyBook", Book.class);
log.info("funnyBook: {}", funnyBook);
Child dabao = context.getBean("dabao", Child.class);
dabao.read();
Child erbao = context.getBean("erbao", Child.class);
erbao.read();
}
}
BookStoreAppTest.java
package com.xiaoma.spring.example.bookstore;
import ...
class BookStoreAppTest {
@Test
void saveBook() {
// 根据指定的配置文件启动spring IoC容器
ApplicationContext context = new ClassPathXmlApplicationContext("application-context.xml");
BookStoreApp bookStoreApp = context.getBean("bookStoreApp", BookStoreApp.class);
bookStoreApp.saveBook("金瓶梅", "文学");
}
}
运行单元测试,能得到和上一节一样的执行结果。
通过本小节的学习,我们体会到spring框架引入容器来帮我们完成bean的管理,而我们要做的就是索取bean并执行业务方法,生活变得美好起来了。