定义
依赖倒置原则(DIP)
具体实现依赖抽象,下层依赖上层
IOC控制反转
我们原本创建对象时可以随意安排创建对象的时机,进行对象创建,但是在引入spring-ioc容器后,对象创建的工作转交给了容器进行,创建对象的控制权在容器手中,由容器控制在什么时机,用什么方式去创建,控制权从开发者手中转交由容器进行操作控制,这就是控制反转的基本概念
DI依赖注入
创建对象时原本是使用new自己创建,而使用spring-ioc后,创建对象的工作转交给spring-ioc容器进行操作,对象的创建是需要依赖spring-ioc容器的注入才行,就是依赖注入的基础概念
IOC是DIP的设计原理,DI是IOC的具体实现
使用IOC带来的好处
- 解耦
- 将对象的创建进行集中管理
- 提高了功能的复用
- 提高了程序整个体系的可维护性,灵活性,扩展性
IOC配置详情
ApplicationContext接口是Spring IoC容器实现的代表,它负责实例化,配置和组装Bean
继承类图
ApplicationContext配置元数据的方式有三种
- 使用xml
最基础的使用方式,相对繁琐,需要有配置文件支持,一般使用ClassPathXMLApplicationContext进行实例化容器,但是内容最为清晰直观.
- 使用java注释
Spring 2.5 支持基于注解的元数据配置.使用包扫扫描(context:component‐scan)等功能将被注释的类加载到容器中,还是结合xml文件进行配置.
- 使用java代码
从 Spring 3.0开始, 由Spring JavaConfig项目提供的功能已经成为Spring核心框架的一部分,你可以使用Java配置来代替XML配置定义外部bean,使用AnnotationConfigApplicationContext进行实例化容器
容器的使用
使用ApplicationContext/BeanFactory接口来进行bean的创建,进行容器对象加载可以使用getBean方法
- 现阶段一般使用ApplicationContext接口进行对象创建,因其内部有更多的方法封装,其包含所有BeanFactory的方法以及
- BeanFactory是实现IOC的核心接口,其中IOC的功能都是由BeanFactory定义的,核心为getBean系列方法
- BeanFactory和ApplicationContext最大的不同是,加载方式,单例作用域下,BeanFactory默认为懒加载,而ApplicationContext是饿加载,需要手动设置才会为懒加载
XML配置的IOC相关功能具体示例和说明
(1)基础准备工作
0.引入依赖,pom.xml中引入spring-context即可将关联的依赖jar都引入其中
org.springframework
spring-context
5.2.6.RELEASE
compile
1.创建容器对象
xml下容器对象一般使用ClassPathXMLApplicationContext来进行创建容器
常用容器实现类有
- ClassPathXMLApplicationContext 根据项目路径的xml来配置实例化spring容器
- FileSystemXmlApplicationContext 根据磁盘路径的xml来配置实例化spring容器
- AnnotationConfigApplicationContext 根据JavaConfig来配置实例化Spring容器
ApplicationContext ioc =new ClassPathXmlApplicationContext("spring-ioc.xml")
2.定义spring的xml配置文件
spring-ioc.xml配置文件的操作基本都在beans中进行配置,需要引入必要的依赖约束内容(配置文件是自定义名称的并无强制要求,为了明确基础意义,设定为spring-ioc.xml)
3.定义所需示例类,主要有Person,Wife,Child,三个,Wife和Child继承Person,以及一个工厂Person的工厂类
package com.learn.beans;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* @author wangxing
* @version 2020/6/26 16:35 Administrator
*/
public class Person {
private Integer id;
private String name;
private String gender;
private Date birthday;
private List hobbies;
private Map course;
private Wife wife;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public void setIdxxx(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public List getHobbies() {
return hobbies;
}
public void setHobbies(List hobbies) {
this.hobbies = hobbies;
}
public Map getCourse() {
return course;
}
public void setCourse(Map course) {
this.course = course;
}
public Wife getWife() {
return wife;
}
public void setWife(Wife wife) {
this.wife = wife;
}
@Override
public String toString() {
return "Person{" +
"id=" + id +
", name='" + name + '\'' +
", gender='" + gender + '\'' +
", birthday=" + birthday +
", hobbies=" + hobbies +
", course=" + course +
", wife=" + wife +
'}';
}
public Person() {
System.out.println(" load person");
}
public Person(Integer id, String name, String gender, Date birthday, List hobbies,
Map course, Wife wife) {
this.id = id;
this.name = name;
this.gender = gender;
this.birthday = birthday;
this.hobbies = hobbies;
this.course = course;
this.wife = wife;
}
public Person(String name) {
this.name = name;
}
public static Person createPersonFactory(String type){
if ("wife".equals(type)) {
return new Wife();
} else if ("child".equals(type)) {
return new Child();
}else{
return new Person();
}
}
}
package com.learn.beans;
/**
* @author wangxing
* @version 2020/6/26 16:38 Administrator
*/
public class Wife extends Person {
private Integer id;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Wife{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
public Wife() {
System.out.println("load wife"+this);
}
public Wife(Integer id, String name) {
this.id = id;
this.name = name;
System.out.println("load wife"+this);
}
}
package com.learn.beans;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
/**
* @author wangxing
* @version 2020/6/26 17:57 Administrator
*/
public class Child extends Person implements InitializingBean, DisposableBean {
public void initByConfig(){
System.out.println("通过配置实现的初始化方法");
}
public void destroyByConfig(){
System.out.println("通过配置实现的销毁方法");
}
public void destroy() throws Exception {
System.out.println("通过实现DisposableBean接口实现的销毁方法");
}
public void afterPropertiesSet() throws Exception {
System.out.println("通过实现InitializingBean接口实现的初始化方法");
}
}
package com.learn.beans.factory;
import com.learn.beans.Child;
import com.learn.beans.Person;
import com.learn.beans.Wife;
/**
* @author wangxing
* @version 2020/6/26 17:59 Administrator
*/
public class PersonFactory {
public Person createPersonFactory(){
return new Person();
}
public Person createPersonFactory(String type){
if ("wife".equals(type)) {
return new Wife();
} else if ("child".equals(type)) {
return new Child();
}else{
return new Person();
}
}
}
(2)学习spring-ioc的基础功能
0.
用来描述一个类是用来做什么的
bean标签在spring-beans.xsd中的定义
bean标签的属性
1.在方法中通过IOC容器进行类加载的方式
/*
1.通过类来获取bean
当配置文件中有多个相同类的配置时会报错,一般使用方法二和三
*/
Pesron user = (User)context.getBean(Pesron.class);
/*
2.通过xml配置文件中名称来获取bean
需要进行强制转型
*/
Pesron user = (Pesron)context.getBean("user");
/*
3.通过名字+类来获取bean
通过名称和类型两个参数进行确认,保证不会加载错误
*/
Pesronuser = context.getBean("user",Pesron.class);
2.进行类信息的注入
主要有两种注入方式
- setter方法注入
基于setter方法的依赖注入
- 属性必须声明了set方法
- name是根据set方法的名字来的 比如方法名字是: setIdxx ‐> name="idxx"
可以使用p:标签来进行注入setter方法,但是不能注入集合
使用p标签需要引入对应标签
xmlns:p="http://www.springframework.org/schema/p"
- 构造方法注入
基于构造函数的依赖注入
- 将会调用自定义构造函数来实例化对象,就不会调用默认的无参构造函数
- name是根据构造函数的参数名来的, 比如:User(String idxx) ‐> name="idxx"
- name属性可以省略 但是要注意参数的位置
- 如果非要把位置错开 可以使用 name 或者 index 或者 type
- index 是下标 从0开始
- type 在位置错开情况下只能在类型不一样的时候指定才有明显效果
可以使用c:标签来进行构造方法的参数注入
使用c:标签构造方法进行参数注入需要引入对应标签
xmlns:c="http://www.springframework.org/schema/c"
3.各种类型数据的配置方式
标签进行属性配置
标签配置list对象
标签配置set对象
阅读
运动
4.控制依赖加载顺序使用bean:depends-on属性来控制加载依赖于某个类
5.将对象设置为懒加载,使用bean:lazy‐init属性,设置为true时类的加载变更为懒加载,只在调用时进行加载
6.自动注入使用bean:autowire属性来进行控制
autowire的值包括四种
- default/no:不自动装配
- byType 按照类型进行装配,以属性的类型作为查找依据去容器中找到这个组件,如果有多个类型相同的bean对象,那么会报异常,如果找不到则装配null
- byName 按照名字进行装配,以属性名作为id去容器中查找组件,进行赋值,如果找不到则装配null
- constructor:按照构造器进行装配,先按照有参构造器参数的类型进行装配,没有就直接装配null;如果按照类型找到了多个,那么就使用参数名作为id继续匹配,找到就装配,找不到就装配null
//通过将autowire-candidate 属性设置为false,避免对bean定义进行自动装配
//通过将其 元素的primary属性设置为 true,将单个bean定义指定为主要候选项
7.bean的作用域设置使用bean:scope属性进行配置
| singleton | (默认) 每一Spring IOC容器都拥有唯一的实例对象。
| prototype | 一个Bean定义可以创建任意多个实例对象.
| request | 将单个bean定义范围限定为单个HTTP请求的生命周期。 也就是说,每个HTTP请求都有自己的bean实例,它是在单个bean定义的后面创建的。 只有基于Web的SpringApplicationContext
的才可用。
| session | 将单个bean定义范围限定为HTTPSession
的生命周期。 只有基于Web的SpringApplicationContext
的才可用。
| application | 将单个bean定义范围限定为ServletContext
的生命周期。 只有基于Web的SpringApplicationContext
的才可用。
| websocket | 将单个bean定义范围限定为WebSocket
的生命周期。 只有基于Web的SpringApplicationContext
的才可用。
注意
多线程下单例模式可能会产生线程安全问题,单例模式的对象中控制尽量不要有数据属性,只保留操作方法,如果一定要有属性,那么记得加锁
8.bean的生命周期控制
在对象的加载前和销毁后执行我们定义的方法
实现方式有两种
- 1使用接口实现的方式来实现生命周期的回调
初始化方法: 实现接口: InitializingBean 重写afterPropertiesSet方法初始化会自动调用的方法
销毁的方法: 实现接口: DisposableBean 重写destroy 方法 销毁的时候自动调用方法
-2 使用指定具体方法的方式实现生命周期的回调,在对应的bean里面创建对应的两个方法init‐method="init" destroy‐method="destroy"
注意:
销毁是在spring容器关闭的时候
package com.learn.beans;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
/**
* @author wangxing
* @version 2020/6/26 17:57 Administrator
*/
public class Child extends Person implements InitializingBean, DisposableBean {
public void initByConfig(){
System.out.println("通过配置实现的初始化方法");
}
public void destroyByConfig(){
System.out.println("通过配置实现的销毁方法");
}
public void destroy() throws Exception {
System.out.println("通过实现DisposableBean接口实现的销毁方法");
}
public void afterPropertiesSet() throws Exception {
System.out.println("通过实现InitializingBean接口实现的初始化方法");
}
}
9.向配置文件中引入配置文件使用context:property-placeholder标签
使用context:property-placeholder标签需要引入对应引用
xmlns:context="http://www.springframework.org/schema/context"
定义配置文件
mysql.username=xxx
mysql.password=xxx
mysql.url=jdbc:mysql://192.168.1.150:3306/mall
mysql.driver=com.mysql.jdbc.Driver
xml用引入配置文件,使用第三方类
总结
定义
DIP/IOC/DI
IOC的好处
IOC配置
ApplicationContext的三种配置方式
XML配置方式的学习整理
spring-ioc的xml配置基础标签
1.
2. bean的注入方式
3. 各类数据类型的装配方式
4. 对类注入进行依赖控制depends-on
5. 将类注入设置为懒加载lazy‐init
6. 自动注入bean:autowire
7. bean的作用域bean:scope
8. bean生命周期控制init‐method="init" destroy‐method="destroy"
9. 引入外部配置文件context:property-placeholder