什么是Spring
是一个支持快速开发JavaEE应用程序的开源框架,同时在Spring Framework的基础上也诞生了 Spring Boot等等一系列项目。
IoC容器
容器:为某种特定组件的运行提供必要的软件环境支持。例如,Tomcat是一个Servlet容器,可以为Servlet的运行提供环境。
Spring的核心就是提供了一个IoC容器,可以用于管理所有轻量级的JavaBean组件,同时也提供了一些底层服务,例如组件的生命周期、配置和组装服务、AOP支持(面向切面编程,是OOP的补充和完善,在开发中主要用于解决一些系统层面上的问题,例如日志,事务、权限等),以及建立在AOP上的声明式事务服务等。
那么,IoC是如何对组件进行生命周期管理和配置组装服务呢。
IoC原理
IoC:Inversion of Control,直译为控制反转。
如果一个系统有大量的组件,其生命周期和相互之间的依赖关系如果由组件自身来维护,不但大大增加了系统的复杂度,而且会导致组件之间极为紧密的耦合,继而给测试和维护带来了极大的困难。
看看如下两段代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public class BookService { private HikariConfig config = new HikariConfig(); private DataSource dataSource = new HikariDataSource(config);
public Book getBook(long bookId) { try (Connection conn = dataSource.getConnection()) { ... return book; } } }
public class BookService { private DataSource dataSource;
public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; } }
|
在第二段代码中,我们不直接new一个DataSource,而是写了一个实例化dataSource的函数,这样,BookService不用再去关心如何创建 DataSource,而且这个dataSource由于是被注入到BookService中的,因此也可以与其他对象共享这个dataSource组件。
因此,IoC又被称为依赖注入,它将组件的创建+配置与组件的使用相分离,并且由IoC来管理组件的生命周期。
除了这种set()方法外,还可以通过构造方法注入,例如:
1 2 3 4 5 6 7
| public class BookService { private DataSource dataSource;
public BookService(DataSource dataSource) { this.dataSource = dataSource; } }
|
IoC是一个无侵入容器,即应用程序的组件无需实现Spring的特定接口,或者说,组件根本不知道自己在Spring的容器中运行。
装配Bean
如何使用IoC容器、装配好的Bean如何使用。
编写一个特定的application.xml文件,来告诉Spring的IoC容器应该如何创建并组装Bean。
1 2 3 4 5 6 7 8 9 10 11 12
| <?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">
<bean id="userService" class="com.itranswarp.learnjava.service.UserService"> <property name="mailService" ref="mailService" /> </bean>
<bean id="mailService" class="com.itranswarp.learnjava.service.MailService" /> </beans>
|
- 每个bean都有一个id标识,相当于Bean的唯一ID
- 在userService中,通过< property name=”…” ref=”…” />注入了另一个Bean
- Bean的顺序不重要,Spring根据依赖关系会自动正确初始化
如果注入的不是bean,而是int、boolean这样的类型,则通过value注入,例如:
1 2 3 4 5 6 7
| <bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource"> <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test" /> <property name="username" value="root" /> <property name="password" value="password" /> <property name="maximumPoolSize" value="10" /> <property name="autoCommit" value="true" /> </bean>
|
最后一步,需要创建一个Spring的IoC容器实例,然后加载配置文件,让Spring容器为我们创建并装配好配置文件中指定的所有Bean。
1
| ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
|
接下来,就可以从容器中取出装配好的Bean然后使用它。
1 2 3 4
| UserService userService = context.getBean(UserService.class);
User user = userService.login("bob@example.com", "password");
|
使用Annotation配置
每次增加一个组件,都要在配置文件中添加这个bean,很麻烦。
可以直接使用Annotation配置。
1 2 3 4
| @Component public class MailService { ... }
|
这个Component就相当于定义了一个bean,它有一个可选的名称,默认是mailService,即小写字母开头的类名。
然后添加一个Autowired注释:
1 2 3 4 5 6 7
| @Component public class UserService { @Autowired MailService mailService;
... }
|
这个注释就相当于将指定类型的bean注入到指定的字段中,这个注释可以写在set()方法上、字段上、构造方法中。
最后,编写一个AppConfig类启动容器:
1 2 3 4 5 6 7 8 9 10
| @Configuration @ComponentScan public class AppConfig { public static void main(String[] args) { ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); UserService userService = context.getBean(UserService.class); User user = userService.login("bob@example.com", "password"); System.out.println(user.getName()); } }
|
标注的ComponentScan,它告诉容器,自动搜索当前类所在的包以及子包,将所有标注为component的bean自动创建出来,并根据autowired进行装配。