一、概念
IoC:Inversion of Controller,即控制反转,不是一种技术,而是一种设计思想。
IoC是指在程序开发中,实例的创建不再由调用者管理,而是由spring容器管理。Spring容器负责控制程序之间的关系,而不是由程序代码直接控制。控制权由程序代码转移到了Spring容器,控制权发生了反转,这就是Spring的IoC思想。
二、案例
2.1、创建项目,添加pom依赖
<dependencies>
<!-- 单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- spring依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
</dependencies>
<build>
<plugins>
<!-- 编译插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
2.2、创建一个实体类
public class Team {
private Integer id;
private String name;
private String location;
public Team(){
System.out.println("Team的默认构造方法被调用:id="+id+",name="+name+",location="+location);
}
}
2.3、添加一个配置文件application.xml
<?xml version="1.0" encoding="UTF-8"?>
<!--spring的配置文件 1、beans: 根标签,spring中java的对象成为bean
2、spring-beans.xsd 是约束文件(约束XML文件中能编写哪些标签)-->
<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">
<!--创建对象:声明bean,通知spring要创建哪个类的对象
一个bean标签声明一个对象: id="自定义的对象名称" ,要求唯一
class="类的完全限定名" 包名+类名,spring底层是反射机制创建对象,所以必须使用类名
相当于 Team team1=new Team();
创建好的对象放入一个集合Map中 例如:springMap.put("team1",new Team()); -->
<bean id="team1" class="com.jsonliu.test.bean.Team"/>
<!-- 创建非自定义对象 -->
<bean id="date" class="java.util.Date"/>
</beans>
2.4、获取Spring容器
Spring提供了两种容器,BeanFactory和ApplicationContext
2.4.1、BeanFactory
BeanFactory的基础类型是IoC容器,是管理Bean的工厂,它负责初始化各种Bean,并调用它们的生命周期方法。
BeanFactory接口有多个实现类,最常见的是org.springframework.beans.factory.xml.XmlBeanFactory,它是根据XML中的定义装配Bean。
创建对象时机:调用getBean时创建对象
BeanFactory beanFactory = new XmlBeanFactory(new FileSystemResource("application.xml"));
2.4.2、ApplicationContext
ApplicationContext是BeanFactory的子接口,也称为应用上下文。不仅提供了BeanFactory的所有功能,还添加了对i18n(国际化)、资源访问、事件传播等方面的良好支持。
创建对象时机:初始化applicationContext容器时
ApplicationContext接口的两个常用类:
ClassPathXmlApplicationContext,从类路径ClassPath中查找指定xml配置文件,找到后装载完成ApplicationContext的实例化。
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
FileSystemXmlApplicationContext,它与ClassPathXmlApplicationContext的区别在于:读取Spring配置文件时,FileSystemXmlApplicationContext不再从类路径中读取配置文件,而是通过参数指定配置文件位置,它可以获取类路径之外的资源,如:“D:\application.xml”
ApplicationContext applicationContext2 = new FileSystemXmlApplicationContext("application.xml");
2.5、通过上下文获取容器中对象
@Test
public void test(){
//使用spring容器创建对象
//1、指定spring配置文件名称
String springConfig="application.xml";
//2、创建spring容器对象
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(springConfig);
//3、获取容器中的对象
Team team1 = (Team) applicationContext.getBean("team1");
//4、容器的其他API
int count = applicationContext.getBeanDefinitionCount();
System.out.println("容器中对象个数:"+count);
String[] names = applicationContext.getBeanDefinitionNames();
for(String name:names){
System.out.println("容器中对象的名称:"+name);
}
//5、获取日期
Date date = (Date) applicationContext.getBean("date");
System.out.println("当前时间:"+date);
}
三、Bean的标签属性
属性 | 说明 |
---|---|
class | 指定bean对应类的全路径 |
name | bean对应对象的一个标识 |
scope | bean对象的创建模式和生命周期,singleton和prototype |
id | bean对应对象的唯一标识,不能添加特别字符 |
lazy-init | 是否延迟加载,默认false。延迟加载,指对象被调用时才会加载,使用时需要通过getbean()方法获得对象。不延迟加载时,只需加载配置文件即可。 |
init-method | 加载配置文件即可进行的对象初始化的方法 |
destory-method | 对象销毁时调用方法 |
Team中补充方法:
public void init(){
System.out.println("初始化...");
}
public void destroy(){
System.out.println("销毁...");
}
application.xml中添加:
<!--bean标签的属性:
id="自定义的对象名称" ,要求唯一
name="bean对于的一个标识“,一般使用id居多
class="类的完全限定名"
scope="singleton/prototype" 单例/多例
singleton:默认值, 单例:在容器启动的时候就已经创建了对象,而且整个容器只有唯一 的一个对象
prototype:多例,在使用对象的时候才创建对象,每次使用都创建新的对象
lazy-init="true/false" 是否延迟创建对象,只针对单例有效
true:不延迟创建对象,容器加载的时候立即创建 false:默认加载,使用对象的时候才去创建对象
init-method="创建对象之后执行的初始化方法"
destroy-method="对象销毁方法,调用容器destroy方法的时候执行" -->
<bean id="team2" class="com.jsonliu.test.bean.Team" scope="singleton" lazy-init="true" init-method="init" destroy-method="destroy"/>
<bean id="team3" class="com.jsonliu.test.bean.Team" scope="prototype"/>
修改测试方法:
@Test
public void test(){
//使用spring容器创建对象
//1、指定spring配置文件名称
String springConfig="application.xml";
//2、创建spring容器对象
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(springConfig);
System.out.println("----------容器初始化创建对象------------");
//3、获取容器中的对象
Team team1 = (Team) applicationContext.getBean("team1");
Team team11 = (Team) applicationContext.getBean("team1");
System.out.println(team1);
System.out.println(team11);
//4、容器的其他API
int count = applicationContext.getBeanDefinitionCount();
System.out.println("容器中对象个数:"+count);
String[] names = applicationContext.getBeanDefinitionNames();
for(String name:names){
System.out.println("容器中对象的名称:"+name);
}
//5、获取日期
Date date = (Date) applicationContext.getBean("date");
System.out.println("当前时间:"+date);
System.out.println("----------调用时创建对象------------");
Team team2 = (Team) applicationContext.getBean("team2");
Team team22 = (Team) applicationContext.getBean("team2");
System.out.println(team2);
System.out.println(team22);
System.out.println("----------调用时创建对象:创建了多个对象------------");
Team team3 = (Team) applicationContext.getBean("team3");
Team team33 = (Team) applicationContext.getBean("team3");
System.out.println(team2);
System.out.println(team22);
applicationContext.close();
}
四、Spring容器创建对象方式
- 使用默认构造方法
- 使用带参构造方法
- 使用工厂类
4.1、创建实体类Team
public class Team {
private Integer id;
private String name;
private String location;
public Team(){
System.out.println("Team的默认构造方法被调用:id="+id+",name="+name+",location="+location);
}
public Team(Integer id, String name, String location) {
this.id = id;
this.name = name;
this.location = location;
System.out.println("Team的全参数构造方法被调用:id="+id+",name="+name+",location="+location);
}
}
4.2、创建工厂类
public class MyFactory {
public Team instanceFun(){
System.out.println("MyFactory-------instanceFun");
return new Team(1,"皇家马德里","马德里");
}
public static Team staticFun(){
System.out.println("MyFactory-------staticFun");
return new Team(2,"拜仁慕尼黑","慕尼黑");
}
/**
* 测试两个方法
* @param args
*/
public static void main(String[] args) {
MyFactory myFactory=new MyFactory();
Team team = myFactory.instanceFun();
Team team1 = MyFactory.staticFun();
}
}
4.3、createType.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">
<!--
spring容器创建对象方法:
1、无参构造方法
2、有参构造方法
3、工厂类:实例方法、静态方法
-->
<!--1、默认构造方法-->
<bean id="team1" class="com.jsonliu.test.bean.Team"/>
<!--2、有参构造方法-->
<bean id="team2" class="com.jsonliu.test.bean.Team">
<constructor-arg name="id" value="3"/>
<constructor-arg name="name" value="巴塞罗那"/>
<constructor-arg name="location" value="巴塞罗那"/>
</bean>
<!--3、工厂类:静态方法-->
<bean id="staticTeam" class="com.jsonliu.test.config.MyFactory" factory-method="staticFun"/>
<!--4、工厂类:实例方法-->
<bean id="myFactory" class="com.jsonliu.test.config.MyFactory" />
<bean id="instanceTeam" factory-bean="myFactory" factory-method="instanceFun" />
</beans>
4.4、测试类
public class CreateType {
@Test
public void test(){
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("createType.xml");
}
}
运行结果:
五、基于XML的DI
DI-Dependency Injection,依赖注入。组件之间的依赖关系由容器在运行期间决定,即容器动态的将某个依赖关系注入到组件中。依赖注入的目的并非为软件系统带来更多的功能,而是提升组件重用频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要简单的配置,而无需任何代码就可以指定目标需要的资源,完成自身业务逻辑,不需要关系具体资源来于何处,由谁实现。
IoC是一种思想,一个概念,实现方式多种多样。依赖注入就是其中用的比较多的一种方式。
IoC和DI是同一个概念的不同角度描述。IoC是一个概念,一种思想,DI是实现它的手段。Spring框架使用依赖注入实现IoC。
Spring容器是一个超级大工厂,负责创建、管理所有java对象,这些java对象被称为bean。Spring容器管理着Bean之间的依赖关系(使用依赖注入方式),使用IoC实现对象之间解耦。
5.1、注入分类
bean实例再调用无参构造器创建对象后,就要对bean对象的属性进行初始化。初始化由容器自动完成,被称为注入。
5.1.1、通过set方法
set注入也称为设值注入,通过setter方法传入被调用者实例。这种方法简单直观,因而在Spring依赖注入中大量使用。
5.1.2、通过构造方法
构造注入:在构造调用者实例同时,完成被调用者实例化。使用构造器设置依赖关系。
5.1.3、自动注入
对于引用类型属性的注入,也可以不再配置文件中显示注入。可以通过为标签设置autowire属性值,为引用类型属性进行隐式注入(默认不自动注入引用类型属性)根据自动注入判断标准不同,可以分为:
- byName:根据名称自动注入
- byType:根据类型自动注入
1、byName:当配置文件中被调用者bean的id与代码中调用者bean类属性名相同时,使用byName方式,让容器自动将被调用者bean注入给调用者bean。容器是通过调用者bean类属性名与配置文件中被调用者bean的id进行比较而实现自动注入的。
2、byType:要求配置文件中被调用者bean的class指定的类,要与代码中调用者bean类的某引用类型属性的类型同源。要么相同,要么is-a关系(子类或实现类)。但这样的同源的被调用bean只能有一个,超过一个会出现错误。
5.2、示例
5.2.1、创建TeamDao
public class TeamDao {
public void add(){
System.out.println("TeamDao---------add");
}
}
5.2.2、创建TeamService
public class TeamService {
private TeamDao teamDao; //=new TeamDao();
public void add(){
System.out.println("TeamService------------add");
teamDao.add();
}
public TeamService() {
}
public TeamService(TeamDao teamDao) {
this.teamDao = teamDao;
}
public void setTeamDao(TeamDao teamDao) {
this.teamDao = teamDao;
}
}
5.2.3、DI.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">
<bean id="teamDao" class="com.jsonliu.test.dao.TeamDao"/>
<bean id="teamService" class="com.jsonliu.test.service.TeamService">
<!-- 使用set方法注入属性值 -->
<property name="teamDao" ref="teamDao"/>
</bean>
<bean id="teamService1" class="com.jsonliu.test.service.TeamService">
<!-- 使用构造方法注入属性值 -->
<constructor-arg name="teamDao" ref="teamDao"/>
</bean>
<!-- 按照名称自动注入:查找容器中id名与属性名一致的对象进行注入 -->
<bean id="teamService2" class="com.jsonliu.test.service.TeamService" autowire="byName"/>
<!-- 按照类型自动注入:查找容器中类型与属性类型相同或者符合is-a关系的对象进行注入,但是要求类型相同的对象唯一,否则抛出异常:不知道匹配哪一个 -->
<bean id="teamService3" class="com.jsonliu.test.service.TeamService" autowire="byType"/>
</beans>
5.2.4、测试类
public class DITest {
@Test
public void test(){
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("DI.xml");
TeamService teamService = (TeamService) applicationContext.getBean("teamService");
teamService.add();
TeamService teamService1 = (TeamService) applicationContext.getBean("teamService1");
teamService1.add();
TeamService teamService2 = (TeamService) applicationContext.getBean("teamService2");
teamService2.add();
TeamService teamService3 = (TeamService) applicationContext.getBean("teamService3");
teamService3.add();
}
}
六、基于注解实现IoC
对于DI使用注解,将不再需要在Spring配置文件中声明bean实例。Spring中使用注解,需要在原有Spring运行环境基础上做一些改变。
6.1、声明Bean的注解@Component
在类上添加注解@Component表示该类创建对象的权限交给Spring容器,注解的value属性用于指定bean的id值,可以省略。
@Component不指定value属性,bean的id是类名首字母小写。
//@Component标识在类上,表示该对象由Spring容器创建 value属性表示创建的id值,value可以省略,值也可以省略,默认为类名的首字母小写
@Component("teamDao")
//相当于xml配置文件中:<bean id="teamDao" class="com.jsonliu.test.dao.TeamDao" />
public class TeamDao {
public void add() {
System.out.println("TeamDao---------add");
}
public TeamDao() {
System.out.println("TeamDao默认构造方法");
}
}
@Component标识属于“万能”注解,Spring针对不同类型的类还提供了其他注解:
- @Repository:用于dao实现类注解
- @Service:用于service实现类注解
- @Controller:用于controller实现类注解
@Repository、@Service、@Controller注解是对@Component注解的细化,标注数据持久层、业务实现层、访问控制层的注解。
@Repository
public class TeamDao {
public void add() {
System.out.println("TeamDao---------add");
}
public TeamDao() {
System.out.println("TeamDao默认构造方法");
}
}
@Service
public class TeamService {
private TeamDao teamDao; //=new TeamDao();
public void add(){
System.out.println("TeamService------------add");
teamDao.add();
}
public TeamService() {
}
public TeamService(TeamDao teamDao) {
this.teamDao = teamDao;
}
public void setTeamDao(TeamDao teamDao) {
this.teamDao = teamDao;
}
}
@Controller
public class TeamController {
public TeamController() {
System.out.println("TeamController默认的构造方法");
}
}
6.2、包扫描
需要在Spring配置文件中配置组件扫描器,用于指定在基本包中扫描注解。如果没有进行包扫描,添加的注解是不生效的。
多个包扫描方式:
<?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
http://www.springframework.org/schema/context/spring-context.xsd">
<!--
context:component-scan 告知spring要扫描的包
这些包以及子包如果添加了@Component注解,这些添加了注解的类就由spring进行对象创建
注意:需要在beans标签中添加 xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
-->
<!-- 包扫描方式1:使用多个component-scan -->
<context:component-scan base-package="com.jsonliu.test.dao" />
<context:component-scan base-package="com.jsonliu.test.service" />
<context:component-scan base-package="com.jsonliu.test.controller" />
<!-- 包扫描方式2:使用分隔符 , ; 空格 其中空格不推荐 -->
<context:component-scan base-package="com.jsonliu.test.dao,com.jsonliu.test.service,com.jsonliu.test.controller"/>
<!-- 包扫描方式3:扩大包扫描范围,扫描上层的父包: 不建议使用顶级父包,扫描路径变多,容易导致容器启动变慢,指定到注解所在包是比较合适的 -->
<context:component-scan base-package="com.jsonliu.test"/>
</beans>
6.3、属性注入@Value
在属性上使用注解@Value,该注解的value属性用于指定需要注入的值。使用该注解进行注入,类中无需setter方法;如果存在setter方法,注解添加到setter方法上也可以。
6.4、byType自动注入@Autowired
在引用属性上加@Autowired,该注解默认按使用类型进行装配Bean。使用该注解完成属性注入,类中无需setter。当然,若属性有setter,也可以将其加入到setter上。
6.5、byName自动注入@Autowired和@Qualifier
需要在属性上联合使用注解@Autowired和@Qualifier,@Qualifier的属性value用于指定要匹配的bean的id。类中无需setter方法,也可加到setter方法上。
@Autowired还有一个属性required,默认值为true,表示匹配失败后,会终止程序运行。若值为false,表示匹配失败后,将会忽略,未匹配值为null。
6.6、自动注入@Resource
jdk中提供了@Resource注解,该注解即可按照名称匹配Bean,也可以按照类型匹配Bean。 默认按照名称注入。 要求JDK6+ @Resource可以在属性上,也可以在set方法上。
6.6.1、byType注入引用类型属性
@Resource不带任何参数,默认按照名称的方式注入,按照名称不能注入再按照类型进行Bean匹配注入。
6.6.2、byName注入引用类型属性
@Resource指定其name属性,则按照name属性匹配Bean的id。