目录
IoC 控制反转
概述
控制反转,是一个概念,一种思想。
就是将对象的创建、赋值、管理,交给代码之外的容器来实现。
控制:创建对象、对象的属性赋值、对象之间关系的管理。
反转:把原来开发人员创建对象、对象的属性赋值…的权限,转移给代码之外的容器实现。
正转:由开发人员在代码中,new出一个对象,主动管理对象。
容器:是一个软件、容器。spring就是这么一个容器。
Why IoC?:减少对代码的改动,也能实现不同的功能。解耦。
IoC的技术实现:DI: 依赖注入(Dependency Injection,简称DI) :只需要在程序内部提供要使用的对象名称就可以。
依赖注入:
当某个角色(可能是一个Java实例,调用者)需要另一个角色(另一个Java实例,被调用者)的协助时,在 传统的程序设计过程中,通常由调用者来创建被调用者的实例。但在Spring里,创建被调用者的工作不再由调用者来完成,因此称为控制反转;创建被调用者 实例的工作通常由Spring容器来完成,然后注入调用者,因此也称为\注入。
依赖注入有两种:设值注入、构造注入
所谓依赖注入**,是指程序运行过程中,如果需要调用另一个对象协助时,无须在代码中创建被调用者,而是依赖于外部的注入。Spring的依赖注入对调用者和被调用者几乎没有任何要求,完全支持对POJO之间**依赖关系的管理。
spring是使用的di实现的控制反转,底层通过反射机制来创建对象。
创建对象
1、引入依赖
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
2、创建一个接口和其实现类
3、在resources创建spring配置文件
4、在配置文件中 配置创建对象
<?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:就是告诉spring创建某个类的对象。
id:对象的自定义名称,唯一值,spring通过id找到对象
class:类的权限定名称,不能是接口,因为spring是通过反射机制创建的对象
此时spring就会完成创建操作:
SomeService someService = new SomeServiceImpl();
spring是将创建好的对象放入到map集合中,方便存取,spring有专门一个map是存放对象的
例如:springMap.put("someService", new SomeServiceImpl());
一个bean标签声明一个对象。
-->
<bean id="someService" class="com.wm.service.impl.SomeServiceImpl"></bean>
</beans>
<!--
beans:根标签,bean即为一个java对象
spring-beans.xsd:xml约束文件
-->
5、编写代码创建对象
@Test
public void testDoSome(){
//指定spring的配置文件路径、名称
String config = "beans.xml";
/*
创建spring的容器对象ApplicationContext,通过它就能获取java对象了
ApplicationContext是一个接口,
ClassPathXmlApplicationContext是常用的一个其实现类
表示从类路径中加载配置文件
* */
ApplicationContext appContext = new ClassPathXmlApplicationContext(config);
/*
从容器中获取对象
getBean( 配置文件中,bean的id值 );
**/
SomeServiceImpl someService = (SomeServiceImpl) appContext.getBean("someService");
someService.doSome();
}
6、创建对象的时间:在创建spring的容器时,就会创建配置文件中配置的所有对象
即ApplicationContext appContext = new ClassPathXmlApplicationContext(config);
执行时,就已经创建好对象了。
而之后的getBean()
都是从容器中取 已经创建好的对象
7、创建非自定义类的对象
如:Date对象、String对象…
同创建自定义对象的方法一样。
查看容器中对象的信息
@Test
public void test01(){
String config = "beans.xml";
ApplicationContext appContext = new ClassPathXmlApplicationContext(config);
//获取容器中对象的数量
int count = appContext.getBeanDefinitionCount();
System.out.println("对象数量"+count);
//获取容器中所有对象的名称
String[] names = appContext.getBeanDefinitionNames();
for (String name : names) {
System.out.println("对象名称"+name);
}
}
给对象赋值
(DI:依赖注入)
XML方式
set注入
spring通过调用对象的set方法给其属性赋值
简单类型
通过配置文件中bean标签中的property标签进行配置,name就是对象的属性名,value就是赋的值
<bean id="student" class="com.wm.ba01.Student">
<property name="name" value="张三"/>
<property name="age" value="20"/>
</bean>
@Test
public void test01(){
String config = "ba01/applicationContext.xml";
ApplicationContext appContext = new ClassPathXmlApplicationContext(config);
Student student = (Student) appContext.getBean("student");
System.out.println(student);
}
}
执行结果:
Student{name='张三', age=20}
引用类型
配置文件:
<!--
di:给属性赋值
1、set注入:spring调用对象的set方法给其赋值
引用类型:
通过 ref 来确定
这个ref的值就是自定义的bean的id
-->
<bean id="school" class="com.wm.ba02.School">
<property name="name" value="第一小学"/>
</bean>
<bean id="student" class="com.wm.ba02.Student">
<property name="name" value="张三"/>
<property name="age" value="20"/>
<property name="school" ref="school"/><!-- 这里通过ref来给赋值 -->
</bean>
@Test
public void test01(){
String config = "ba02/applicationContext.xml";
ApplicationContext appContext = new ClassPathXmlApplicationContext(config);
Student student = (Student) appContext.getBean("student");
System.out.println(student);
}
执行结果为:
Student{name='张三', age=20, school=School{name='第一小学'}}
构造注入
通过有参构造方法赋值
public Student(String name, int age, School school) {
System.out.println("有参构造执行");
this.name = name;
this.age = age;
this.school = school;
}
<!--
di:给属性赋值
2、构造注入:spring调用有参数构造方法给其赋值
通过<constructor-arg>标签实现。
一个<constructor-arg>标签表示构造方法的一个参数
<constructor-arg name="" index="" value="" ref="">
name:表示形参名
index:表示形参的位置,从0开始
value:简单类型的参数值
ref:引用类型
如果是按照形参顺序从上到下给index 0-n 的参数赋值,则index可以省略
-->
<bean id="student" class="com.wm.ba03.Student">
<constructor-arg name="name" value="张三"/>
<constructor-arg name="age" value="12"/>
<constructor-arg name="school" ref="school"/>
</bean>
<bean id="school" class="com.wm.ba03.School">
<property name="name" value="第一小学"/>
</bean>
@Test
public void test02(){
String config = "ba03/applicationContext.xml";
ApplicationContext appContext = new ClassPathXmlApplicationContext(config);
Student student = (Student) appContext.getBean("student");
System.out.println(student);
}
执行结果
有参构造执行
Student{name='张三', age=12, school=School{name='第一小学'}}
自动注入引用类型
ByName
要求配置文件中 bean的id,和java实体类中的引用类型的属性名一致,且数据类型一致,这样spring能够自动为其赋值
<bean id="student" class="com.wm.ba04.Student" autowire="byName">
<property name="name" value="张三"/>
<property name="age" value="12"/>
</bean>
<bean id="school" class="com.wm.ba04.School">
<property name="name" value="第一小学"/>
</bean>
ByType
要求配置文件中bean的class的值和java实体类的属性是同一类型(包括父子、接口实现类关系)。
<bean id="student" class="com.wm.ba04.Student" autowire="byType">
<property name="name" value="张三"/>
<property name="age" value="12"/>
</bean>
<bean id="school" class="com.wm.ba04.School">
<property name="name" value="第一小学"/>
</bean>
多个配置文件
按模块:如登录模块一个、查询成绩一个…
按功能:如操作数据库的一个、做service的一个,做事务的一个…
每个模块的配置文件只定义这个模块的对象,所以此时则需要一个主配置文件将它们串联起来
<?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">
<!--
主配置文件:一般不定义对象,只需将模块配置文件引入即可
classpath:ba05/spring_student.xml
classpath表示类路径,即编译后target/class下的路径
-->
<import resource="classpath:ba05/spring_student.xml"/>
<import resource="classpath:ba05/spring_school.xml"/>
</beans>
注解方式
@Component
、@Respotory、@Service、@Controller
创建对象
/*
* @Component:创建对象的。相当于<bean>标签
* value:对象名,相当于<bean>的id
* value值是唯一的,创建的对象在容器中只有一个
* 相当于:<bean id="student" class="com.wm.ba01.Student">
* spring中和@Component一样创建对象的还有
* @Repository:用在dao类上面(持久层
* @Service:用在service类上面(业务逻辑层
* @Controller:用在controller类上面(控制器层
* 这三个对象是给项目分层的。
* 这个类不是以上三种类,且需要创建对象的时候,使用@Component注解
* */
@Component(value = "student")
public class Student {
}
<!--
声明组件扫描器component-scan:组件就是java对象
base-package:指定注解在你项目中的包名
之后,spring会扫描你指定包和其子包中的所有类,找到注解,创建对象或者赋值
-->
<context:component-scan base-package="com.wm.ba01"/>
<!--
扫描多个包的方式:
1、创建多个标签
2、包之间用;或,分隔
3、扫描他们的父包
-->
@Value
简单类型赋值
/*
* @Value:简单类型的属性赋值
* value:赋的值,“value=”可以省略,只需写赋的值即可@Value("张三")
* 位置:1、在属性上方:无需set方法,推荐使用
* 2、在set方法上方
* */
@Component(value = "student")
public class Student {
@Value("张三")
String name;
@Value("12")
int age;
}
@Autowired
@Component(value = "student")
public class Student {
@Value("张三")
String name;
@Value("12")
int age;
/*
* @Autowired:给引用数据类型赋值
* 和xml中的自动注入相同原理,默认是byType方式
* 位置:在属性上方,无需set方法,推荐使用。或在set方法上方。
* @Autowired(required = true):required默认为true:表示如果赋值失败就终止执行并报错
* (比如,@Qualifier(value = "")的value写错)
* 若为false:表示如果赋值失败,则继续执行,赋值为null
* 推荐使用true,bug尽早暴露,方便修改
* 使用byName的方式:需要在属性上方加入@Qualifier(value = "")
* value的值是bean的id值,即@Component(value = "")的value值。
* */
@Autowired(required = true)
@Qualifier(value = "school")
School school;
}
@Component("school")
public class School {
@Value("第二小学")
String name;
}
@Resource
是jdk中的注解,spring保留了对其的支持。使用的也是自动注入的原理。
放在属性上方,无需set方法,推荐使用。或在set方法上方。
默认方式是byName:若byName赋值失败,则会继续使用byType。
只想使用byName的话:@Resource(name = " ")name的值为bean的id
两种方式的比较
xml的对象创建、赋值是和代码分离的,注解还需在类中编写,各有优缺点,因此:
若对象需要经常修改,则使用xml,若不经常修改则使用注解。
不过,现在的开发大多使用注解方式。