Spring—IoC控制反转
- spring核心技术:ioc,aop。能实现模块之间的解耦合
- 依赖:calssA中使用classB的属性或者方法,叫做classA依赖classB
一、ioc的概念(spring的一个核心功能)
-
ioc:控制反转,是一个理论,概念,思想
-
描述:把对象的创建,赋值,管理工作,都交给代码之外的容器实现,也就是对象的创建是有其他外部资源完成的。
-
控制:创建对象,对象的属性赋值,对象之间的关系管理。
-
反转:把原来的开发人员管理,创建对象的权限转移给代码之外的容器实现。由容器代替开发人员管理对象。创建对象,给属性赋值
-
正转:有开发人员在代码中,使用new构造方法创建对象,开发人员主动管理对象。
public static void main(string args[]){ Student student = new Student();//在代码中,创建对象。————正转 }
-
容器:是一个服务器软件,一个框架(spring)
-
使用ioc的目的:减少对代码的改动,也能实现不用的功能。实现耦合。
二、ioc的技术实现
- DI是ioc 的技术实现
- DI:依赖注入,只需要在程序中提供要使用的对象名称就可以,至于对象如何在容器中创建,赋值,查找都是由容器内部实现。
- spring是使用的DI实现了ioc的功能,spring底层创建对象,使用的是反射机制。
- spring是一个容器,管理对象,给属性赋值,底层是反射创建对象。
Ⅰ、DI的实现(两种)
- 在spring的配置文件中,使用标签和属性完成,叫做基于XML的di实现
- 使用spring中的注解,完成属性的赋值,叫做基于注解的id实现
Ⅱ、DI的语法分类
注入就是赋值的意思
- set注入(设置注入):spring调用类的set方法,在set方法可以实现属性的赋值(80%都是使用set注入)
- 构造注入:spring调用类的有参构造方法,创建对象。在构造方法中完成赋值
1、实现步骤
-
创建maven项目
-
加入maven的依赖
spring的依赖
junit依赖
-
创建类(接口和他的实现类)和没有使用框架一样,就是普通的类
通过spring的语法完成属性的赋值
-
创建spring需要使用的配置文件
声明类的信息,这些类由spring创建和管理
-
测试spring创建的对象
2、bean标签
Ⅰ、spring的配置文件
- beans:是根标签,spring把Java对象成为bean
- spring-beans.xsd 是约束文件,和mybatis指定 dtd是一样的。
Ⅱ、告诉spring创建对象
-
声明bean,就是spring要创建某个类的对象
-
id:对象的自定义名称,唯一值。spring通过这个名称找到对象
-
class:类的全限定名称(不能是接口,因为spring是反射机制创建对象,必须使用类)
-
spring就完成SomeService someService = new SomeServiceImpl();
-
spring是把创建好的对象放入到map中,spring框架有一个map对象的
springMap.put( id的值 , 对象 );
eg : springMap.put ( “someService” , new SomeServiceImpl () ) ;
-
一个bean标签只能声明一个对象
<bean id = "someService" class = "nuc.gjq.service.impl.SomeServiceImpl" />
3、使用spring容器创建的对象
spring默认创建对象的时间:在创建spring的容器时,会创建配置文件中的所有对象
public void test(){
//使用spring容器创建的对象
//1.指定spring配置文件的名称
String config = "beans.xml";
//2.创建表示spring容器的对象,ApplicationContext
//ApplicationContext就是表示Spring容器,通过容器获取对象
//ClassPathXmlApplicationContext:表示从类路径中加载spring的配置文件
ApplicationContext ac = new ClassPathXmlApplicationContext(config);
//3.从容器中获取某个对象,你要调用对象的方法
//getBean("配置文件中的bean的id值")
SomeService service = (service) ac.getBean("someService");
//4.使用spring创建好的对象
service.doSome();
}
4、获取spring容器中Java对象的信息
public void test(){
String config = "beans.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(config);
//使用spring提供的方法,获取容器中定义的对象数量
int nums = ac.getBeanDefinitionCount();
System.out.print("容器中定义的对象数量:"+nums);
//容器中每个定义的对象的名称
String names[] = ac.getBeanDefinitionNames();
for(String name:names){
System.out.print(name);
}
}
5、获取一个非自定义的类对象
spring创建对象:默认调用的是无参构造方法
<!-- 创建一个存在的某个类对象 -->
< bean id = "mydate" class = "java.util.Date" />
public void test(){
String config = "beans.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(config);
//使用getBean();
Date my = (Date) ac.getBean("mydate");
System.out.print("Date:"+my);
}
三、基于XML的DI
1.从容器中获取Student对象
public void test(){
String config = "applicationContext.xml";//xml文件名为固定格式
ApplicationContext ac = new ClassPathXmlApplicationContext(config);
//从容器中获取student对象
student myStudent = (Student) ac.getBean("myStudent");
System.out.print("student对象:"+myStudent);
}
2.注入分类
Ⅰ、set注入
- spring调用类的set方法,用set方法完成属性赋值
- 不管是自己写的类,还是本身有的类,只要有set方法就能使用(例如:非自定义的类对象)
(1)简单类型的set注入
简单类型:Java的基本数据类型+String
格式:
<bean id = "xx" class = "yyy">
<property name = "属性名字" value = "此属性的值" />
<!-- 一个property只能给一个属性赋值,如果有多个属性需要多个property -->
</bean>
public class student {
private String name;
private String age;
public student(){
}
public void setName(String name) {
this.name = name;
}
public void setAge(String age) {
this.age = age;
}
}
<bean id = "myStudent" class = "nuc.gjq.Student">
<property name = "name" value = "zs" /> <!-- setName("zs") -->
<property name = "age" value = "23" /> <!-- setAge(23) -->
</bean>
(2)引用类型的set注入
格式:
<bean id = "xx" class = "yyy">
<property name = "属性名字" ref = "bean的id(对象的名称)" />
<!-- 一个property只能给一个属性赋值,如果有多个属性需要多个property -->
</bean>
private String name;
private String age;
private School school;
public Student() {
}
public void setName(String name) {
this.name = name;
}
public void setAge(String age) {
this.age = age;
}
public void setSchool(School school) {
this.school = school;
}
<!-- 声明School对象 -->
<bean id = "myShool" class = "nuc.gjq.School">
<property name = "name" value = "中北大学" />
<property name = "address" ref = "山西太原" />
</bean>
<!-- 声明Student对象 -->
<bean id = "myStudent" class = "nuc.gjq.Student">
<property name = "name" value = "zs" /> <!-- setName("zs") -->
<property name = "age" value = "23" /> <!-- setAge(23) -->
<property name = "school" ref = "myShool" />
</bean>
Ⅱ、构造注入
- spring调用类有参数构造方法,在创建对象的同时,在构造对象的同时,在构造方法中给属性赋值
- 构造注入使用< constructor - arg > 标签
- < constructor - arg > 标签是属性
- name:表示构造方法的形参名
- index:表示构造方法的参数位置,参数从左往右位置是0,1,2的顺序
- value:构造方法的形参类型是简单类型的,使用vlaue
- ref:构造方法的形参类型是引用类型的,使用ref
(1)使用name属性
<!-- 声明School对象 -->
<bean id = "myShool" class = "nuc.gjq.School">
<property name = "name" value = "中北大学" />
<property name = "address" ref = "山西太原" />
</bean>
<!-- 声明Student对象 -->
<bean id = "myStudent" class = "nuc.gjq.Student">
< constructor-arg name = "name" value = "zs" >
< constructor-arg name = "age" value = "23" >
< constructor-arg "school" ref = "myShool" >
</bean>
(2)使用index属性
<!-- 0,1,2的顺序可以改变 -->
<bean id = "myStudent" class = "nuc.gjq.Student">
< constructor-arg index = "0" value = "zs" >
< constructor-arg index = "1" value = "23" >
< constructor-arg index = "2" ref = "myShool" >
</bean>
<!-- 也可以省略index -->
<bean id = "myStudent" class = "nuc.gjq.Student">
< constructor-arg value = "zs" >
< constructor-arg value = "23" >
</bean>
3、引用类型属性自动注入
spring框架根据某些规则可以给引用类型赋值,不用手动赋值
Ⅰ、byName方式自动注入(按名称注入)
-
Java类中引用类型的属性名和spring容器中(配置文件)的id名称一样且数据类型是一致的,这样的容器中的bean,spring能够赋值给引用类型
-
语法:
<bean id="xx" class="yyy" autowire="byName"> 简单类型属性赋值 </bean>
<!-- byname自动注入 --> <bean id = "myStudent" class = "nuc.gjq.student.Student" autowire="byName"> <property name="name" value="zs"/> <property name="age" value="23"/> </bean> <bean id="school" class="nuc.gjq.student.School"> <property name="name" value="中北大学"/> <property name="address" value="山西太原"/> </bean>
Ⅱ、byType方式自动注入
-
Java类中引用类型的数据类型和spring容器中(配置文件) 的class属性是同源关系的,这样的bean能够赋值给引用类型
-
同源(一类的意思)
- Java类中引用类型的数据类型和bean的class的值是一样的
- Java类中引用类型的数据类型和bean的class的值是父子关系的
- Java类中引用类型的数据类型和bean的class的值是接口和实现类关系的
-
语法
<bean id="xx" class="yyy" autowire="byType"> 简单类型属性赋值 </bean>
<bean id = "myStudent" class = "nuc.gjq.student.Student" autowire="byType"> <property name="name" value="zs"/> <property name="age" value="23"/> </bean> <bean id="myShool" class="nuc.gjq.student.School"> <property name="name" value="中北大学"/> <property name="address" value="山西太原"/> </bean>
4、为应用指定多个spring配置文件
-
分配方式
- 按功能模块,一个模块一个配置文件
- 按类的功能,数据库相关的配置一个配置文件,做事务的功能一个配置文件,做service功能的一个配置文件等
-
包含关系的配置文件
-
spring-total表示主配置文件 :包含其他的配置文件的,主配置文件一般是不定义对象的
-
语法
<import resource="其他配置文件路径"/>
-
关键字:"classpath:"表示类路径(class文件所在的目录)
在spring的配置文件中要指定其他文件的位置,需要使用classpath,告诉spring到哪里去加载读取文件
<import resource="classpath:02/spring-school.xml"/> <import resource="classpath:02/spring-student.xml"/>
-
通配符
- 在包含关系的配置文件中,可以用通配符(*:表示任意字符)
- 注意:主的配置文件名称不能包含在通配符的范围中,(不能叫做spring-total.xml)
- 配置文件文件必须放在同一个目录中
<import resource="classpath:02/spring-*.xml"/>
-
四、基于注解的DI
通过注解完成Java对象的创建,属性赋值
1、使用注解的步骤
- 加入maven依赖 spring-context,加入依赖的同时,间接加入spring-aop的依赖,使用注解必须使用spring-aop依赖
- 在类中加入spring的注解(多个不同功能的注解)
- 在spring的配置文件中,加入一组件扫描器的标签,说明注解在项目中的位置
2、学习的注解
- @Component
- @Respotory
- @Service
- @Controller
- @Value
- @Autowired
- @Resource
3、实现步骤
-
加入依赖
-
创建类,在类中加入注解
-
创建spring的配置文件
声明组件扫描器的标签,指明注解在你的项目中的位置
-
使用注解创建对象,创建容器ApplicationContext
4、@Component
- 用于创建对象,等同于的功能
- 属性:value 就是对象的名称,也就是bean的id值,value的值是唯一的,创建的对象在整个spring容器中就一个
- 位置:在类的上面
- @Component(value = “myStudent”)等同于
- @Repository,@Service,@Controller 使用语法和 @Component 一样,都能创建对象,但是这三个注解还有额外的功能
- @Repository(用在持久层类的上面):放在dao的实现类的上面,表示创建dao对象,dao对象是能访问数据库的。
- @Service(用在业务层类的上面):放在service的实验室类上面,创建service对象,service对象是做业务处理,可以由事务等功能的。
- @Controller(用在控制器的上面):放在控制器(处理器)类的上面,创建控制器对象的,控制器对象,能够接受用户提交的参数,显示请求的处理结果
- @Repository,@Service,@Controller是给项目分层的
Ⅰ、使用方法
1、java代码(不在持久层,不在业务层,不在控制器层,eg:entity层)
//使用value属性,指定对象名称
@Component(value = "myStudent")
//最常用,省略value
@Component("myStudent")
//不指定对象名称,由spring提供的默认名称:类名的首字母小写
@Component
2、声明组件扫描器(component-scan)
<context:component-scan base-package="nuc.gjq.ba01"/>
- 组件就是Java对象
- base-package:指定注解在你的项目中的包名
- component-scan工作方式:spring会扫描遍历base-package指定的包
- 把包中和子包中的所有类,找到类中的注解,按照注解的功能创建对象,或给属性赋值
- 调用无参构造方法,创建对象
Ⅱ、指定多个包的三种方式
-
<!--第一种:使用多次组件扫描器,指定不同的包--> <context:component-scan base-package="nuc.gjq.ba01"/> <context:component-scan base-package="nuc.gjq.ba02"/>
-
<!--第二种:使用分隔符(;或,)分隔多个包名--> <context:component-scan base-package="nuc.gjq.ba01;nuc.gjq.ba02"/>
-
<!--第三种方式:指定父包--> <context:component-scan base-package="nuc.gjq"/>
5、简单类型属性注入@Value
Ⅰ、属性
- value是String类型的,表示简单类型的属性值
Ⅱ、位置:
- 在属性定义的上面,无需set方法,推荐使用
- 在set方法上面
@Value(value = "zs")
private String name;
@Value(value = "23")
private int age;
6、引用类型注入@Autowired
- spring 框架提供的注解,实现引用类的赋值
- spring中通过注解给引用类型赋值,使用的是自动注入原理,支持byName,byType
- @Autowired:默认使用byType自动注入
Ⅰ、属性
- required 是一个 boolean 类型的,默认 ture
1.required = true:表示引用类型赋值失败,程序报错,并终止执行
2.required = false:引用类型如果赋值失败,程序正常执行,引用类型是null
Ⅱ、位置
- 在属性定义的上面,无需set方法,推荐使用
- 在set方法上面
Ⅲ、byType方法(默认方法)
@Autowired
private School school;
public class School {
@Value(value = "中北大学")
private String name;
@Value(value = "山西太原")
private String address;
}
Ⅳ、byName方法
- 在属性上面加入@Autowired
- 在属性上面加入 @Qualifier(value = “bean的id”):表示使用指定名称的bean完成赋值(value可以省略)
@Autowired
@Qualifier("mySchool")
private School school;
@Component("mySchool")
public class School {
@Value(value = "中北大学")
private String name;
@Value(value = "山西太原")
private String address;
}
7.JDK注解@Resource自动注入
- @Resource:来自jdk中的注解,spring框架提供了对这个注解的支持,可以使用它给应引用类型赋值
- 使用的是自动注入原理,支持byName,byType,默认是byName
- 默认是byName:先使用byName自动注入,如果byName赋值失败,再使用byType
Ⅰ、位置
- 在属性定义的上面,无需set方法,推荐使用
- 在set方法上面
@Resource
private School school;
Ⅱ、@Resource只使用byName的方式
- 需要增加一个属性name
- name的值是bean的id(名称)
@Resource(name = "mySchool")
private School school;
8、导入文件的形式(${})
-
先创建test.properties.file
myname = “张三” myage = 23
-
加载属性配置文件
<context:component-scan base-package="nuc.gjq.ba02"/> <context:property-placeholder location="classpath:test.properties"/>
-
赋值
@Value("${myname}") private String name; @Value("${myage}") private int age;
五、单元测试
-
一个工具类库,做测试方法使用
-
单元:指定的是方法,一个类中有很多方法,一个方法称为单元
-
使用单元测试
-
需要加入junit依赖
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency>
-
创建测试作用的类:叫做测试类
src/test/java目录中创建类
-
创建测试方法
1)public方法
2)没有返回值 (void)
3)方法名称自定义,建议名称是test+测试的方法名
4)方法没有参数
5)方法的上面加入@Test,这样的方法是可以单独执行的,不用使用main方法
-
public void test(){
String config = "nuc.gjq.applicationContext.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(config);
//从容器中获取student对象
student myStudent = (Student) ac.getBean("myStudent");
System.out.print("student对象:"+myStudent);
}
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
2. 创建测试作用的类:叫做测试类
src/test/java目录中创建类
3. 创建测试方法
1)public方法
2)没有返回值 (void)
3)方法名称自定义,建议名称是test+测试的方法名
4)方法没有参数
5)方法的上面加入@Test,这样的方法是可以单独执行的,不用使用main方法
public void test(){
String config = "nuc.gjq.applicationContext.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(config);
//从容器中获取student对象
student myStudent = (Student) ac.getBean("myStudent");
System.out.print("student对象:"+myStudent);
}