前言
先理解IOC和DI的概念以便可以理解Bean和spring原理,以及为什么需要spring
IOC和DI简介
- IOC是Inversion Of Control(控制反转)的缩写,它是一种设计思想,是指将对象的控制权由程序代码反转给外部容器。
- 在Spring中,控制反转是实现Spring容器的指导思想。有了Spring容器,开发人员无需编写管理对象生命周期和依赖关系的代码,此项工作将由Spring容器根据配置自动完成,如此一来,对象的控制权由程序代码反转给Spring容器。
- DI是Dependency Injection(依赖注入)的缩写,它是控制反转的另一种说法,同时也为控制反转提供了实现方法。依赖注入是指调用类对其他类的依赖关系由容器注入,这就避免了调用类对其他类的过度依赖,降低了类与类之间的耦合。
IOC
- IoC是Spring框架的核心内容,使用多种方式完美的实现了IoC,可以使用XML配置,也可以使用注解,新版本的Spring也可以零配置实现IoC。
过程
Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从Ioc容器中取出需要的对象。
采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。
画图讲解IOC底层原理
DI
https://blog.csdn.net/weixin_46168350/article/details/110102737
总结
我对IoC(控制反转)和DI(依赖注入)的理解
在平时的java应用开发中,我们要实现某一个功能或者说是完成某个业务逻辑时至少需要两个或以上的对象来协作完成,在没有使用Spring的时候,每个对象在需要使用他的合作对象时,自己均要使用像new object() 这样的语法来将合作对象创建出来,这个合作对象是由自己主动创建出来的,创建合作对象的主动权在自己手上,自己需要哪个合作对象,就主动去创建,创建合作对象的主动权和创建时机是由自己把控的,而这样就会使得对象间的耦合度高了,A对象需要使用合作对象B来共同完成一件事,A要使用B,那么A就对B产生了依赖,也就是A和B之间存在一种耦合关系,并且是紧密耦合在一起,而使用了Spring之后就不一样了,创建合作对象B的工作是由Spring来做的,Spring创建好B对象,然后存储到一个容器里面,当A对象需要使用B对象时,Spring就从存放对象的那个容器里面取出A要使用的那个B对象,然后交给A对象使用,至于Spring是如何创建那个对象,以及什么时候创建好对象的,A对象不需要关心这些细节问题(你是什么时候生的,怎么生出来的我可不关心,能帮我干活就行),A得到Spring给我们的对象之后,两个人一起协作完成要完成的工作即可。
所以控制反转IoC(Inversion of Control)是说创建对象的控制权进行转移,以前创建对象的主动权和创建时机是由自己把控的,而现在这种权力转移到第三方,比如转移交给了IoC容器,它就是一个专门用来创建对象的工厂,你要什么对象,它就给你什么对象,有了 IoC容器,依赖关系就变了,原先的依赖关系就没了,它们都依赖IoC容器了,通过IoC容器来建立它们之间的关系。
这是我对Spring的IoC(控制反转)的理解。DI(依赖注入)其实就是IOC的另外一种说法,DI是由Martin Fowler 在2004年初的一篇论文中首次提出的。他总结:控制的什么被反转了?就是:获得依赖对象的方式反转了。
Bean的配置管理
Bean的定义
-
通常使用XML文件来实现Bean的配置。XML配置文件的
根元素是<beans>
,元素中有很多<bean>
子元素,每个<bean>
子元素都定义了一个Bean,并且描述了该Bean应该被如何配置到Spring容器中。 -
<bean>
元素提供了一系列属性用于描述Bean的信息,其中常用的如表所示。
除了以上所列举的属性之外,<bean>
元素还包含有一些配置依赖信息的子元素,其中,子元素用于通过属性注入的方式设置值,<constructor-arg>
子元素用于自动寻找Bean的构造函数,并在初始化时将设置的参数注入。
- 注入集合
在以上代码中,<bean>
元素下共有3个<property>
元素,这3个<property>
元素分别包含了<list>
、<map>
、<array>
元素,其中,<list>
元素用于注入List集合,<map>
元素用于注入Map集合,<array>
元素用于注入Array。
BeanFactory 接口
- IOC 思想基于 IOC 容器完成,IOC 容器底层就是对象工厂
- Spring 提供 IOC 容器实现两种方式:(两个接口)
(1)BeanFactory:IOC 容器基本实现,是 Spring 内部的使用接口,不提供开发人员进行使用加载配置文件时候不会创建对象,在获取对象(使用)才去创建对象
(2)ApplicationContext
:BeanFactory 接口的子接口,提供更多更强大的功能,一般由开发人员进行使用 加载配置文件时候就会把在配置文件对象进行创建 - ApplicationContext 接口有实现类
IOC 操作Bean 管理(概念)
(0) Bean 管理指的是两个操作
(1) Spring 创建对象
(2) Spirng 注入属性
Bean 管理操作有两种方式
(1) 基于 xml 配置文件方式实现
(2) 基于注解方式实现
Bean 管理(基于xml 方式)
1、基于 xml 方式创建对象
(1) 在 spring 配置文件中,使用 bean 标签,标签里面添加对应属性,就可以实现对象创建
(2) 在 bean 标签有很多属性,介绍常用的属性
id 属性
:唯一标识class 属性
:类全路径(包类路径)name
:可以设置多个别名,可以用逗号,分号,空格隔开(通过id和name都可以取出该Bean)
(3)创建对象时候,默认也是执行无参数构造方法完成对象创建
- 别名
alias 设置别名 , 为bean设置别名 , 可以设置多个别名
<!--设置别名:在获取Bean的时候可以使用别名获取-->
<alias name="userT" alias="userNew"/>
- Bean的配置
<!--bean就是java对象,由Spring创建和管理-->
<!--
id 是bean的标识符,要唯一,如果没有配置id,name就是默认标识符
如果配置id,又配置了name,那么name是别名
name可以设置多个别名,可以用逗号,分号,空格隔开
如果不配置id和name,可以根据applicationContext.getBean(.class)获取对象;
class是bean的全限定名=包名+类名
-->
<bean id="hello" name="hello2 h2,h3;h4" class="com.kuang.pojo.Hello">
<property name="name" value="Spring"/>
</bean>
name可以设置多个别名,可以用逗号,分号,空格隔开
- import
团队的合作通过import来实现 .
<import resource="{path}/beans.xml"/>
2、基于 xml 方式注入属性
(1)DI:依赖注入,就是注入属性
3、第一种注入方式:使用set 方法进行注入
(1)创建类,定义属性和对应的 set 方法
(2)在 spring 配置文件配置对象创建,配置属性注入
/**
* 演示使用set方法进行注入属性
*/
public class Book {
//创建属性
private String bname;
private String bauthor;
private String address;
//创建属性对应的set方法
public void setBname(String bname) {
this.bname = bname;
}
public void setBauthor(String bauthor) {
this.bauthor = bauthor;
}
}
<!--2 set方法注入属性-->
<bean id="book" class="com.atguigu.spring5.Book">
<!--使用property完成属性注入
name:类里面属性名称
value:向属性注入的值
-->
<property name="bname" value="易筋经"></property>
<property name="bauthor" value="达摩老祖"></property>
</bean>
4、第二种注入方式:使用有参数构造进行注入
(1)创建类,定义属性,创建属性对应有参数构造方法
/**
* 使用有参数构造注入
*/
public class Orders {
//属性
private String oname="";
private String address;
//有参数构造
public Orders(String oname,String address) {
this.oname = oname;
this.address = address;
}
}
(2)在 spring 配置文件中进行配置(三种方式编写)
<!--3 有参数构造注入属性-->
<bean id="orders" class="com.atguigu.spring5.Orders">
<constructor-arg name="oname" value="电脑"></constructor-arg>
<constructor-arg name="address" value="China"></constructor-arg>
</bean>
5、beans.xml 有三种方式编写
<!-- 第一种根据index参数下标设置 -->
<bean id="userT" class="com.kuang.pojo.UserT">
<!-- index指构造方法 , 下标从0开始 -->
<constructor-arg index="0" value="kuangshen2"/>
</bean>
<!-- 第二种根据参数名字设置 -->
<bean id="userT" class="com.kuang.pojo.UserT">
<!-- name指参数名 -->
<constructor-arg name="name" value="kuangshen2"/>
</bean>
<!-- 第三种根据参数类型设置 -->
<bean id="userT" class="com.kuang.pojo.UserT">
<constructor-arg type="java.lang.String" value="kuangshen2"/>
</bean>
6、使用SpEL注入
<!-- personAge年龄为50以内随机数 -->
<bean id="person" class="com.hk.spring.di09.Person">
<property name="personName" value="李四"/>
<property name="personAge" value="#{T(java.lang.Math).random()*50}"/>
</bean>
<!-- myStudent的属性与 person值相同-->
<bean id="myStudent" class="com.hk.spring.di09.Student" autowire="byType">
<!-- person.personName底层调用的是bean的Get方法,bean内必须包含属性的get方法 -->
<property name="name" value="#{person.personName}"/>
<!-- 调用bean的方法 -->
<property name="age" value="#{person.countAge(person.personAge)}"/>
</bean>
7、p 名称空间和c名称空间注入(了解)
(1)使用 p 名称空间注入,可以简化基于 xml 配置方式第一步 添加 p 名称空间在配置文件中
第一步 添加 p 名称空间在配置文件中
第二步 进行属性注入,在 bean 标签里面进行操作
<bean id="book" class="com.atguigu.spring5.Book" p:bname="九阳神功" p:bauthor="无名氏">
</bean>
1、P命名空间注入 : 需要在头文件中加入约束文件
导入约束 : xmlns:p="http://www.springframework.org/schema/p"
<!--P(属性: properties)命名空间 , 属性依然要设置set方法-->
<bean id="user" class="com.kuang.pojo.User" p:name="狂神" p:age="18"/>
2、c 命名空间注入 : 需要在头文件中加入约束文件
导入约束 : xmlns:c="http://www.springframework.org/schema/c"
<!--C(构造: Constructor)命名空间 , 属性依然要设置set方法-->
<bean id="user" class="com.kuang.pojo.User" c:name="狂神" c:age="18"/>
发现问题:爆红了,刚才我们没有写有参构造!
解决:把有参构造器加上,这里也能知道,c 就是所谓的构造器注入!
Bean 管理(xml 注入其他类型属性)
1、字面量
(1)null值
<!--null值-->
<property name="address">
<null/>
</property>
(2)属性值包含特殊符号
<!--属性值包含特殊符号
1 把<>进行转义 < >
2 把带特殊符号内容写到CDATA -->
<property name="address">
<value><![CDATA[<<南京>>]]></value>
</property>
2、注入属性-外部bean
(1)创建两个类 service类和dao类
(2)在service调用dao里面的方法
(3)在spring配置文件中进行配置
public class UserService {
//创建UserDao类型属性,生成set方法
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void add() {
System.out.println("service add...............");
userDao.update();
}
}
<!--1 service和dao对象创建-->
<bean id="userService" class="com.atguigu.spring5.service.UserService">
<!--注入userDao对象
name属性:类里面属性名称
ref属性:创建userDao对象bean标签id值
-->
<property name="userDao" ref="userDaoImpl"></property>
</bean>
<bean id="userDaoImpl" class="com.atguigu.spring5.dao.UserDaoImpl"></bean>
3、注入属性-内部bean
(1)一对多关系:部门和员工一个部门有多个员工,一个员工属于一个部门部门是一,员工是多
(2)在实体类之间表示一对多关系,员工表示所属部门,使用对象类型属性进行表示
public class Dept {
private String dname;
public void setDname(String dname) {
this.dname = dname;
}
}
//员工类
public class Emp {
private String ename;
private String gender;
//员工属于某一个部门,使用对象形式表示
private Dept dept;
//生成dept的get方法
public Dept getDept() {
return dept;
}
public void setDept(Dept dept) {
this.dept = dept;
}
public void setEname(String ename) {
this.ename = ename;
}
public void setGender(String gender) {
this.gender = gender;
}
public void add() {
System.out.println(ename+"::"+gender+"::"+dept);
}
}
(3)在spring配置文件中进行配置
<!--内部bean-->
<bean id="emp" class="com.atguigu.spring5.bean.Emp">
<!--设置两个普通属性-->
<property name="ename" value="lucy"></property>
<property name="gender" value="女"></property>
<!--设置对象类型属性-->
<property name="dept">
<bean id="dept" class="com.atguigu.spring5.bean.Dept">
<property name="dname" value="安保部"></property>
</bean>
</property>
</bean>
意思是:在员工类对象里面生成了部门类对象
4、注入属性-级联赋值
(1)第一种写法
<!--级联赋值-->
<bean id="emp" class="com.atguigu.spring5.bean.Emp">
<!--设置两个普通属性-->
<property name="ename" value="lucy"></property>
<property name="gender" value="女"></property>
<!--级联赋值-->
<property name="dept" ref="dept"></property>
</bean>
<bean id="dept" class="com.atguigu.spring5.bean.Dept">
<property name="dname" value="财务部"></property>
</bean>
(2)第二种写法
<!--级联赋值-->
<bean id="emp" class="com.atguigu.spring5.bean.Emp">
<!--设置两个普通属性-->
<property name="ename" value="lucy"></property>
<property name="gender" value="女"></property>
<!--级联赋值-->
<property name="dept" ref="dept"></property>
<property name="dept.dname" value="技术部"></property>
</bean>
<bean id="dept" class="com.atguigu.spring5.bean.Dept">
<!-- <property name="dname" value="财务部"></property>-->
</bean>
5、注入属性-(Properties)
public class Student {
private Properties info;
//get...set ..略
}
8、Properties注入
<property name="info">
<props>
<prop key="学号">20190604</prop>
<prop key="性别">男</prop>
<prop key="姓名">小明</prop>
</props>
</property>
Bean 管理(xml 注入集合属性)
1、注入数组类型属性
2、注入 List 集合类型属性
3、注入 Map 集合类型属性
(1)创建类,定义数组、list、map、set 类型属性,生成对应 set 方法
public class Stu {
//1 数组类型属性
private String[] courses;
//2 list集合类型属性
private List<String> list;
//3 map集合类型属性
private Map<String, String> maps;
//4 set集合类型属性
private Set<String> sets;
//学生所学多门课程
private List<Course> courseList;
public void setCourseList(List<Course> courseList) {
this.courseList = courseList;
}
public void setSets(Set<String> sets) {
this.sets = sets;
}
public void setCourses(String[] courses) {
this.courses = courses;
}
public void setList(List<String> list) {
this.list = list;
}
public void setMaps(Map<String, String> maps) {
this.maps = maps;
}
public void test() {
System.out.println(Arrays.toString(courses));
System.out.println(list);
System.out.println(maps);
System.out.println(sets);
System.out.println(courseList);
}
}
(2)在 spring 配置文件进行配置
<!--1 集合类型属性注入-->
<bean id="stu" class="com.atguigu.spring5.collectiontype.Stu">
<!--数组类型属性注入-->
<property name="courses">
<array>
<value>java课程</value>
<value>数据库课程</value>
</array>
</property>
<!--list类型属性注入-->
<property name="list">
<list>
<value>张三</value>
<value>小三</value>
</list>
</property>
<!--map类型属性注入-->
<property name="maps">
<map>
<entry key="JAVA" value="java"></entry>
<entry key="PHP" value="php"></entry>
</map>
</property>
<!--set类型属性注入-->
<property name="sets">
<set>
<value>MySQL</value>
<value>Redis</value>
</set>
</property>
<!--注入list集合类型,值是对象-->
<property name="courseList">
<list>
<ref bean="course1"></ref>
<ref bean="course2"></ref>
</list>
</property>
</bean>
4、在集合里面设置对象类型值
(1)Java类
//课程类
public class Course {
private String cname; //课程名称
public void setCname(String cname) {
this.cname = cname;
}
}
import java.util.List;
public class Book {
private List<String> list;
public void setList(List<String> list) {
this.list = list;
}
}
(2)在 spring 配置文件进行配置
<!--创建多个course对象-->
<bean id="course1" class="com.atguigu.spring5.collectiontype.Course">
<property name="cname" value="Spring5框架"></property>
</bean>
<bean id="course2" class="com.atguigu.spring5.collectiontype.Course">
<property name="cname" value="MyBatis框架"></property>
</bean>
<!--注入list集合类型,值是对象-->
<property name="courseList">
<list>
<ref bean="course1"></ref>
<ref bean="course2"></ref> </list>
</property>
5、把集合注入部分提取出来
(1)在 spring 配置文件中引入名称空间 util
(2)使用 util 标签完成 list 集合注入提取
<!--1 提取list集合类型属性注入-->
<util:list id="bookList">
<value>易筋经</value>
<value>九阴真经</value>
<value>九阳神功</value>
</util:list>
<!--2 提取list集合类型属性注入使用-->
<bean id="book" class="com.atguigu.spring5.collectiontype.Book" scope="prototype">
<property name="list" ref="bookList"></property>
</bean>