Spring IoC介绍
IoC(Inversion of Control) 主要是针对对象的创建和调用控制而言的,也就是说,当应用程序需要使用一个对象时,不再是应用程序直接创建该对象,而是由 IoC 容器来创建和管理,即控制权由应用程序转移到 IoC 容器中,也就是“反转”了控制权。这种方式基本上是通过依赖查找的方式来实现的,即 IoC 容器维护着构成应用程序的对象,并负责创建这些对象。
Spring IoC实现步骤
配置 配置元数据,编写交给SpringIoC容器管理组件的信息,配置方式有三种。分别是XML方式,注解方式,配置类方式。
实例化IoC容器 选择一个合适的容器实现类,进行IoC容器的实例化工作。
1
2
//实例化ioc容器,读取外部配置文件,最终会在容器内进行ioc和di动作
ApplicationContext context = new ClassPathXmlApplicationContext ( "services.xml" , "daos.xml" );
类型名
简介
ClassPathXmlApplicationContext
通过读取类路径下的 XML 格式的配置文件创建 IOC 容器对象
FileSystemXmlApplicationContext
通过文件系统路径读取 XML 格式的配置文件创建 IOC 容器对象
AnnotationConfigApplicationContext
通过读取Java配置类创建 IOC 容器对象
WebApplicationContext
专门为 Web 应用准备,基于 Web 环境创建 IOC 容器对象,并将对象引入存入 ServletContext 域中。
获取Bean
1
2
//获取ioc容器的组件对象
PetStoreService service = context . getBean ( "petStore" , PetStoreService . class );
XML方式
1.配置元数据
1
2
3
4
5
6
7
8
9
package com.atguigu.ioc ;
public class HappyComponent {
public void doWork () {
System . out . println ( "HappyComponent.doWork" );
}
}
1
<bean id= "happyComponent" class= "com.atguigu.ioc.HappyComponent" />
1
2
3
4
5
6
7
8
9
public class ClientService {
private static ClientService clientService = new ClientService ();
private ClientService () {}
public static ClientService createInstance () {
return clientService ;
}
}
1
<bean id= "clientService" class= "examples.ClientService" factory-method= "createInstance" />
1
2
3
4
5
6
7
public class DefaultServiceLocator {
private static ClientServiceImplclientService = new ClientServiceImpl ();
public ClientService createClientServiceInstance () {
return clientService ;
}
}
1
2
3
4
5
6
7
8
9
10
<!-- 将工厂类进行ioc配置 -->
<bean id= "serviceLocator" class= "examples.DefaultServiceLocator" >
</bean>
<!-- 根据工厂对象的实例工厂方法进行实例化组件对象 -->
<!-- factory-bean属性:指定当前容器中工厂Bean 的名称。 -->
<!-- factory-method: 指定实例工厂方法名。-->
<bean id= "clientService"
factory-bean= "serviceLocator"
factory-method= "createClientServiceInstance" />
1
2
3
4
5
6
7
8
9
10
11
public class UserDao {
}
// #----------------------# //
public class UserService {
private UserDao userDao ;
public UserService ( UserDao userDao ) {
this . userDao = userDao ;
}
}
1
2
3
4
5
6
7
8
9
<beans>
<!-- 引用类bean声明 -->
<bean id= "userService" class= "x.y.UserService" >
<!-- 构造函数引用 -->
<constructor-arg ref= "userDao" />
</bean>
<!-- 被引用类bean声明 -->
<bean id= "userDao" class= "x.y.UserDao" />
</beans>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class UserDao {
}
public class UserService {
private UserDao userDao ;
private int age ;
private String name ;
public UserService ( int age , String name , UserDao userDao ) {
this . userDao = userDao ;
this . age = age ;
this . name = name ;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<!-- 场景1: 多参数,可以按照相应构造函数的顺序注入数据 -->
<beans>
<bean id= "userService" class= "x.y.UserService" >
<!-- value直接注入基本类型值 -->
<constructor-arg value= "18" />
<constructor-arg value= "赵伟风" />
<constructor-arg ref= "userDao" />
</bean>
<!-- 被引用类bean声明 -->
<bean id= "userDao" class= "x.y.UserDao" />
</beans>
<!-- 场景2: 多参数,可以按照相应构造函数的名称注入数据 -->
<beans>
<bean id= "userService" class= "x.y.UserService" >
<!-- value直接注入基本类型值 -->
<constructor-arg name= "name" value= "赵伟风" />
<constructor-arg name= "userDao" ref= "userDao" />
<constructor-arg name= "age" value= "18" />
</bean>
<!-- 被引用类bean声明 -->
<bean id= "userDao" class= "x.y.UserDao" />
</beans>
<!-- 场景2: 多参数,可以按照相应构造函数的角标注入数据
index从0开始 构造函数(0,1,2....)-->
<beans>
<bean id= "userService" class= "x.y.UserService" >
<!-- value直接注入基本类型值 -->
<constructor-arg index= "1" value= "赵伟风" />
<constructor-arg index= "2" ref= "userDao" />
<constructor-arg index= "0" value= "18" />
</bean>
<!-- 被引用类bean声明 -->
<bean id= "userDao" class= "x.y.UserDao" />
</beans>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public Class MovieFinder {
}
public class SimpleMovieLister {
private MovieFinder movieFinder ;
private String movieName ;
public void setMovieFinder ( MovieFinder movieFinder ) {
this . movieFinder = movieFinder ;
}
public void setMovieName ( String movieName ){
this . movieName = movieName ;
}
}
1
2
3
4
5
6
7
8
9
10
<bean id= "simpleMovieLister" class= "examples.SimpleMovieLister" >
<!-- setter方法,注入movieFinder对象的标识id
property标签: 可以给setter方法对应的属性赋值
name = 属性名 ref = 引用bean的id值-->
<property name= "movieFinder" ref= "movieFinder" />
<!-- setter方法,注入基本数据类型movieName
name = 属性名 value= 基本类型值-->
<property name= "movieName" value= "消失的她" />
</bean>
<bean id= "movieFinder" class= "examples.MovieFinder" />
2.创建IoC容器与读取Bean
创建IoC容器
1
2
3
4
5
6
7
8
9
10
11
12
//方式1:实例化并且指定配置文件
//参数:String...locations 传入一个或者多个配置文件
ApplicationContext context =
new ClassPathXmlApplicationContext ( "services.xml" , "daos.xml" );
//方式2:先实例化,再指定配置文件,最后刷新容器触发Bean实例化动作 [springmvc源码和contextLoadListener源码方式]
ApplicationContext context =
new ClassPathXmlApplicationContext ();
//设置配置配置文件,方法参数为可变参数,可以设置一个或者多个配置
iocContainer1 . setConfigLocations ( "services.xml" , "daos.xml" );
//后配置的文件,需要调用refresh方法,触发刷新配置
iocContainer1 . refresh ();
读取Bean
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//方式1: 根据id获取
//没有指定类型,返回为Object,需要类型转化!
HappyComponent happyComponent =
( HappyComponent ) iocContainer . getBean ( "bean的id标识" );
//使用组件对象
happyComponent . doWork ();
//方式2: 根据类型获取
//根据类型获取,但是要求,同类型(当前类,或者之类,或者接口的实现类)只能有一个对象交给IoC容器管理
//配置两个或者以上出现: org.springframework.beans.factory.NoUniqueBeanDefinitionException 问题
HappyComponent happyComponent = iocContainer . getBean ( HappyComponent . class );
happyComponent . doWork ();
//方式3: 根据id和类型获取
HappyComponent happyComponent = iocContainer . getBean ( "bean的id标识" , HappyComponent . class );
happyComponent . doWork ();
根据类型来获取 bean 时,在满足 bean 唯一性的前提下,其实只是看:『对象 instanceof 指定的类型』的返回结果,
只要返回的是 true 就可以认定为和类型匹配,能够获取到。
3.组件周期方法与作用域
周期方法
在组件类中定义方法,然后当IoC容器实例化和销毁组件对象的时候进行调用。
1
2
3
4
5
6
7
8
9
10
11
public class BeanOne {
//周期方法要求: 方法命名随意,但是要求方法必须是 public void 无形参列表
public void init () {
// 初始化逻辑
}
}
public class BeanTwo {
public void cleanup () {
// 释放资源逻辑
}
}
1
2
3
4
<beans>
<bean id= "beanOne" class= "examples.BeanOne" init-method= "init" />
<bean id= "beanTwo" class= "examples.BeanTwo" destroy-method= "cleanup" />
</beans>
作用域
<bean
标签声明Bean,只是将Bean的信息配置给SpringIoC容器!
在IoC容器中,这些<bean
标签对应的信息转成Spring内部 BeanDefinition
对象,BeanDefinition
对象内,包含定义的信息(id,class,属性等等)!
这意味着,BeanDefinition
与类
概念一样,SpringIoC容器可以可以根据BeanDefinition
对象反射创建多个Bean对象实例。
具体创建多少个Bean的实例对象,由Bean的作用域Scope属性指定!
取值
含义
创建对象的时机
默认值
singleton
在 IOC 容器中,这个 bean 的对象始终为单实例
IOC 容器初始化时
是
prototype
这个 bean 在 IOC 容器中有多个实例
获取 bean 时
否
1
2
3
4
5
6
7
8
9
10
11
<!--bean的作用域
准备两个引用关系的组件类即可!!-->
<!-- scope属性:取值singleton(默认值),bean在IOC容器中只有一个实例,IOC容器初始化时创建对象 -->
<!-- scope属性:取值prototype,bean在IOC容器中可以有多个实例,getBean()时创建对象 -->
<bean id= "happyMachine8" scope= "prototype" class= "com.atguigu.ioc.HappyMachine" >
<property name= "machineName" value= "happyMachine" />
</bean>
<bean id= "happyComponent8" scope= "singleton" class= "com.atguigu.ioc.HappyComponent" >
<property name= "componentName" value= "happyComponent" />
</bean>
注解方式
添加注解。
Spring 提供了以下多个注解,这些注解可以直接标注在 Java 类上,将它们定义成 Spring Bean。
注解
说明
@Component
该注解用于描述 Spring 中的 Bean,它是一个泛化的概念,仅仅表示容器中的一个组件(Bean),并且可以作用在应用的任何层次,例如 Service 层、Dao 层等。 使用时只需将该注解标注在相应类上即可。
@Repository
该注解用于将数据访问层(Dao 层)的类标识为 Spring 中的 Bean,其功能与 @Component 相同。
@Service
该注解通常作用在业务层(Service 层),用于将业务层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。
@Controller
该注解通常作用在控制层(如SpringMVC 的 Controller),用于将控制层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* projectName: com.atguigu.components
* description: controller类型组件
*/
@Scope ( scopeName = ConfigurableBeanFactory . SCOPE_SINGLETON ) //单例,默认值
@Scope ( scopeName = ConfigurableBeanFactory . SCOPE_PROTOTYPE ) //多例 二选一
@Controller ( value = "指定值BeanName" )
public class XxxController {
//周期方法要求: 方法命名随意,但是要求方法必须是 public void 无形参列表
@PostConstruct //注解制指定初始化方法
public void init () {
// 初始化逻辑
}
}
public class BeanTwo {
@PreDestroy //注解指定销毁方法
public void cleanup () {
// 释放资源逻辑
}
}
配置文件确定扫描范围
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns= "http://www.springframework.org/schema/beans"
xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
xmlns:context= "http://www.springframework.org/schema/context"
xsi:schemaLocation= "http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd" >
<context:component-scan base-package= "com.atguigu.components" />
</beans>
<!--############################分割线####################################### -->
<!-- 情况二:指定不扫描的组件 -->
<context:component-scan base-package= "com.atguigu.components" >
<!-- context:exclude-filter标签:指定排除规则 -->
<!-- type属性:指定根据什么来进行排除,annotation取值表示根据注解来排除 -->
<!-- expression属性:指定排除规则的表达式,对于注解来说指定全类名即可 -->
<context:exclude-filter type= "annotation" expression= "org.springframework.stereotype.Controller" />
</context:component-scan>
<!--############################分割线####################################### -->
<!-- 情况三:仅扫描指定的组件 -->
<!-- 仅扫描 = 关闭默认规则 + 追加规则 -->
<!-- use-default-filters属性:取值false表示关闭默认扫描规则 -->
<context:component-scan base-package= "com.atguigu.ioc.components" use-default-filters= "false" >
<!-- context:include-filter标签:指定在原有扫描规则的基础上追加的规则 -->
<context:include-filter type= "annotation" expression= "org.springframework.stereotype.Controller" />
</context:component-scan>
依赖注入
1
2
3
4
5
6
7
8
@Controller ( value = "tianDog" )
public class SoldierController {
@Autowired
@Qualifier ( value = "maomiService222" )
//@Resource(name='test') == @Autowired + @Qualifier(value='test') JSR-250注解
// 根据面向接口编程思想,使用接口类型引入Service组件
private ISoldierService soldierService ;
}
1
2
# 声明外部配置文件application.properties
catalog.name = MovieCatalog
1
<context:property-placeholder location= "application.properties" />
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Component
public class CommonComponent {
/**
* 情况1: ${key} 取外部配置key对应的值!
* 情况2: ${key:defaultValue} 没有key,可以给与默认值
*/
@Value ( "${catalog:deFault}" )
private String name ;
public String getName () {
return name ;
}
public void setName ( String name ) {
this . name = name ;
}
}
运行测试
1
2
3
4
5
6
7
8
public class ControllerTest {
@Test
public void testRun (){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext ( "spring-ioc.xml" );
StudentController studentController = applicationContext . getBean ( StudentController . class );
studentController . findAll ();
}
}
配置类方式
配置类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//标注当前类是配置类,替代application.xml
@Configuration
//引入jdbc.properties文件
@PropertySource ({ "classpath:application.properties" , "classpath:jdbc.properties" })
@ComponentScan ( basePackages = { "com.atguigu.components" })
public class MyConfiguration {
//如果第三方类进行IoC管理,无法直接使用@Component相关注解
//解决方案: xml方式可以使用<bean标签
//解决方案: 配置类方式,可以使用方法返回值+@Bean注解
@Bean ( name = "myThing" destroyMethod = "cleanup" )
@Scope ( "prototype" )
public DataSource createDataSource ( @Value ( "${jdbc.user}" ) String username ,
@Value ( "${jdbc.password}" ) String password ,
@Value ( "${jdbc.url}" ) String url ,
@Value ( "${jdbc.driver}" ) String driverClassName ){
//使用Java代码实例化
DruidDataSource dataSource = new DruidDataSource ();
dataSource . setUsername ( username );
dataSource . setPassword ( password );
dataSource . setUrl ( url );
dataSource . setDriverClassName ( driverClassName );
//返回结果即可
return dataSource ;
}
}
@Configuration指定一个类为配置类,可以添加配置注解,替代配置xml文件
@ComponentScan(basePackages = {“包”,“包”}) 替代<context:component-scan标签实现注解扫描
@PropertySource(“classpath:配置文件地址”) 替代 <context:property-placeholder标签
IoC容器声明
1
2
3
// AnnotationConfigApplicationContext 根据配置类创建 IOC 容器对象
ApplicationContext iocContainerAnnotation =
new AnnotationConfigApplicationContext ( MyConfiguration . class );
总结
XML方式配置总结
所有内容写到xml格式配置文件中
声明bean通过<bean标签
<bean标签包含基本信息(id,class)和属性信息 <property name value / ref
引入外部的properties文件可以通过<context:property-placeholder
IoC具体容器实现选择ClassPathXmlApplicationContext对象
XML+注解方式配置总结
注解负责标记IoC的类和进行属性装配
xml文件依然需要,需要通过<context:component-scan标签指定注解范围
标记IoC注解:@Component,@Service,@Controller,@Repository
标记DI注解:@Autowired @Qualifier @Resource @Value
IoC具体容器实现选择ClassPathXmlApplicationContext对象
完全注解方式配置总结
完全注解方式指的是去掉xml文件,使用配置类 + 注解实现
xml文件替换成使用@Configuration注解标记的类
标记IoC注解:@Component,@Service,@Controller,@Repository
标记DI注解:@Autowired @Qualifier @Resource @Value
<context:component-scan标签指定注解范围使用@ComponentScan(basePackages = {“com.atguigu.components”})替代
<context:property-placeholder引入外部配置文件使用@PropertySource({“classpath:application.properties”,“classpath:jdbc.properties”})替代
<bean 标签使用@Bean注解和方法实现
IoC具体容器实现选择AnnotationConfigApplicationContext对象