Inversion of Control“控制反转
降低代码之间的耦合度
其中最常见的的方式叫做依赖注入简称DI
🎀什么是ioc
不是技术,而是一种设计思想。在Java开发中,IOC意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。把对象创建和对象之间的调用交给Spring管理。
🎨可控制反转是什么意思呢?
谁控制谁?
控制什么?
为何是反转(有反转就应该有正转了),哪些方面反转了?什么又是在正转呢?
- 谁控制谁,控制什么:一般来说,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IOC是有专门一个容器来创建这些对象,即由IOC容器来控制对象的创建;
谁控制谁?是IOC容器控制了对象;
控制什么?那就是主要控制了外部资源获取(不只是对象包括比如文件等)
- 为何是反转,哪些方面反转了:有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;
为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转
哪些方面反转了?依赖对象的获取被反转了。(主动变被动)
🧨使用IOC的目的:为了降低耦合度
一、IOC底层原理:
xml 工厂模式 反射
工厂模式,仍是存在耦合度的,进一步解耦
让耦合度降低到最低----IOC
1、IOC过程
第一步:xml配置文件 ,配置创建的对象
<bean id="dao" class= "com.yer.UserDao"></bean>
第二步 有dao 类 和service类,创建工厂类
class UserFactory{ public static UserDao getDao(){ String classValue = class属性值 //1.由xml解析 Class clazz = Class.forName(classValue)//2.通过反射创建 return (UserDao)clazz.newInstance(); } }
2、IOC接口
🍕 ioc思想基于ioc容器完成,ioc容器就是对象工厂
🍤Spring提供ioc容器实现两种方式(两个接口)
- BeanFactory :Ioc容器基本实现,是Spring内部的使用接口,不提供开发人员进行使用
*加载配置文件的时候不回去创建对象,在使用对象的时候才回去创建对象
- AppplicationContext:BeanFactory的子接口,提供更多更强大功能,一般由开发人员使用
*加载配置文件的时候就会把配置文件对象创建
在耗时耗资源的过程在服务器启动的时候就完成–verygood!
ApplicationContext接口有实现类:
ClassPathXmlApplicationContext
实际开发中都是读取类路径,
它是用于读取类路径下的配置文件
FileSystemXmlApplicationContext 它是用于读取系统文件目录中的配置文件
二、ioc容器 bean管理xml方式(创建对象和set注入)
🍳什么是bean管理?
指的是两个操作
创建对象
注入属性
🥩bean 管理操作有两种方式
- 基于xml配置文件方式实现
- 基于注解实现方式实现
三、ioc操作bean管理(基于xml方式)
🥨基于xml方式创建对象
<bean id="user" class="com.yer.spring5.User"></bean>
在spring配置文件中使用bean标签,标签里面可以添加对应属性,就可以实现对象的创建
在bean标签中有很多属性:
- id:唯一标识
- class:类全路径
创建对象的也是默认执行无参构造方法
🥠基于xml方式注入属性
DI依赖注入 ,就是注入属性
先创建对象 再注入属性
什么是DI?
DI—Dependency Injection“依赖注入”
是组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。
依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。
理解DI的关键是:“谁依赖谁,为什么需要依赖,谁注入谁,注入了什么”,那我们来深入分析一下:
- 谁依赖于谁:当然是应用程序依赖于IOC容器
- 为什么需要依赖:应用程序需要IOC容器来提供对象需要的外部资源
- 谁注入谁:很明显是IOC容器注入给应用程序 某个对象,应用程序依赖的对象(IOC容器把应用
程序所依赖的对象注入进应用程序中)
- 注入了什么:就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)。
🌭 第一种注入方式:使用set方法进行注入
(1)创建类,定义属性和对应的set方法
(2)在spring配置文件配置对象创建配置属性注入
<bean id="book" class="com.yer.spring5.Book"> <property name="bname" value="java一学就会"></property> <property name="bname" value="mysql一学就会"></property> </bean>
🍣 第二种注入方式:使用有参构造进行注入
(1)创建类:定义属性,创建属性对应有参构造方法
(2)在spring配置文件中进行配置
//有参数构造注入属性 <bean id="oders" class="com.yer.spring5.Orders"> <constructor-arg name="oname" value="小可爱"></constructor-arg> <constructor-arg name="address" value="中国"></constructor-arg> </bean>
🍚p名称空间注入(了解)
使用p名称空间注入可以简化基于xml配置方式
第一步:添加p名称空间在配置文件中
xmlns:p="http://www.springframework.org/schema/p"
第二步:进行属性注入,在bean标签里面进行操作
<bean id="book" calss="com.yer.spring.Book" p:bname="" p:bauthor=""></bean>
实体类中必须有set方法;
实体类中必须有无参构造器(默认存在);
四、IOC操作Bean管理(xml注入其他类型属性)
🧀字面量
(1)null值
<property name="address"> <null/> </property>
(2)属性值包含特殊符号
<property name="address"> <value><![CDATA[<<北京>>]]></value> </property>
五、IOC操作Bean管理(注入属性- 外部bean ,内部bean,级联)
1、通过service去调用dao —引入外部bean
public class UserService { //创建UserDao类型属性,生成set方法 private UserDao userDao; public void setUserDao(UsesrDao userDao){ this.userDao = userDao; } public void add(){ System.out.println("sevice add......") //原始方式创建UserDao对象 //UserDao userDao = new UserDaoImpl(); //userDao.update(); } }
bean.xml
<bean id="userservice" class="com.yer.spring5.service.UserService"> //注入userDao对象 //name属性:类里面属性名称 //ref属性:创建userDao对象bean标签id值 ---userDapImpl <property id="userDao" ref="userDapImpl"></property> </bean> <bean id="userDaoImpl" class="com.yer.spring5.service.UserDaoImpl"> </bean>
用ref 外部bean 注入进来
2、内部bean ------ 一对多 (部门 和 员工)
<bean id="emp" class="com.yer.spring5.bean.Emp"> <!-- 设置两个普通属性--> <property name="ename" value="西西"></property> <property name="eage" value="18"></property> <!-- 设置对象类型属性--> <property name="dept" > <bean id="dept" class="com.yer.spring5.Dept"> <property name="dname" value="金融部"></property> </bean> </property> </bean>
property中写bean
3、 级联赋值
第一种
<!--级联赋值--> <bean id="emp" class="com.yer.spring5.bean.Emp"> <!-- 设置两个普通属性--> <property name="ename" value="西西"></property> <property name="eage" value="18"></property> <!--级联赋值--> <property name="dept" ref="dept"></property> </bean> <bean id="dept" class="com.yer.spring5.Dept"> <property name="dname" value="金融部"></property> </bean>
相比外部 有属性
<!--级联赋值--> <bean id="emp" class="com.yer.spring5.bean.Emp"> <!-- 设置两个普通属性--> <property name="ename" value="西西"></property> <property name="eage" value="18"></property> <!--级联赋值--> <property name="dept" ref="dept"></property> <property name="dept.dname" value="技术部"></property> </bean> <bean id="dept" class="com.yer.spring5.Dept"> <property name="dname" value="金融部"></property> </bean> <!--得到dept的dname属性 ,我们要生成get方法 -->
如果没有get方法会报红的(dept.name)
创建对象,get拿到属性才能赋值
六、IOC操作Bean管理(xml 注入集合属性)
🍱 注入数组类型属性
<!--数组类型属性注入--> <property name="courses"> <array> <value>java</value> <value>mysql</value> </array> </property>
🍔List集合
<!--list类型属性注入--> <property name="list"> <list> <value>三三</value> <value>四四</value> </list> </property>
🥞Map集合
<!--map类型属性注入--> <property name="maps"> <map> <entry key="JAVA" value="java-map"/> <entry key="myBatis" value="myBatis-map"/> </map> </property>
🌭set类型
<!--set类型属性注入--> <property name="sets"> <set> <value>mysql</value> <value>redis</value> </set> </property>
通过ref 标签的 bean 可以创建对象
集合中可以是一个个的对象
🧈把集合注入部分提取出来
1.在spring配置中引入命名空间util
<?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:util="http://www.springframework.org/schema/util" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.1.xsd"> </beans>
使用util:list
<!--1、提取list集合类型属性注入--> <util:list id="bookList"> <value>java</value> <value>python</value> <value>mysql</value> </util:list> <!--2、提取list集合类型属性注入--> <bean id="book" class="com.yer.spring5.Book"> <property name="list" ref="bookList"/> </bean>
七、IOC 操作 Bean管理 (FactoryBean)
** spring中有两种bean ,一种普通bean,一种工厂bean**
🍍普通bean :在配置文件中定义的bean类型就是返回的类型
🥧工厂bean:在配置文件定义的bean类型和返回的类型可以不一样
第一步,创建一个类,让这个类作为工厂bean,实现接口FactoryBean
第二步,实现接口,在实现的方法中定义返回的bean类型
(总结一下FactoryBean和BeanFactory 待更新…)
八、IOC 操作 Bean管理 (bean 作用域)
- singleton(单实例):默认,每个容器中只有一个bean的实例,单例的模式由BeanFactory自身来维护。
- prototype(多实例):为每一个bean请求提供一个实例。
- request:为每一个网络请求创建一个实例,在请求完成以后,bean会失效并被垃圾回收器回 收。
- session:与request范围类似,确保每个session中有一个bean的实例,在session过期后, bean会随之失效
- global-session:全局作用域,global-session和Portlet应用相关。当你的应用部署在Portlet 容器中工作时,它包含很多portlet。如果你想要声明让所有的portlet共用全局的存储变量的话,那 么这全局变量需要存储在global-session中。全局作用域与Servlet中的session作用域效果相同。
🍛在spring中,可以设置创建的bean是单实例还是多实例,如何设置单实例还是多实例?
- 在spring配置文件bean标签中,有属性(scope)用于设置单/多实例
- scope属性值:
默认值singleton 表示单实例对象
prototype ,表示多实例对象
<bean id= "book" class="com.yer.spring5-1.Book" scope="prototype"> <property name="list" ref="bookList"></property> </bean>
🧁 singleton 和prototype的区别
- singleton单实例,prototype多实例
- 设置scope值是singleton的时候,加载spring配置文件的时候就会创建单实例对象为prototype时,是在调用getBean方法的时候创建多实例对象,而不是在加载spring配置文件时
九、IOC 操作 Bean管理 (bean 生命周期)
首先说一下Servlet的生命周期:实例化,初始init,接收请求service,销毁destroy;
Spring上下文中的Bean生命周期也类似
生命周期:从对象创建到对象销毁的过程
bean 生命周期
- 创建bean实例,通过构造器创建bean实例(无参数构造)
- 为bean中相关属性设置值,和对其他bean的引用(调用set方法)
- 调用bean的初始化方法(需要进行配置初始化的方法)
- bean可以使用了(对象获取到,可以使用)
- 当容器关闭的时候,调用bean的销毁方法(需要进行配置销毁的方法)
init-method 调初始化方法
destory 销毁方法
除了这五步还有两步 bean 的后置处理器
在3的初始化之前 和初始化之后
把bean的实例传递给bean后置处理器的方法
BeanPostProcessor 接口中有两个方法
在第三步之前 在初始化之前 postProcessBeforeInitialization
在第三步之后 初始化之后 postProcessAfterInitialization
手动销毁 调用销毁方法
生命周期七步
十、IOC 操作 Bean管理 (xml 自动装配)
什么是自动装配:根据指定装配规则
bean标签属性autowire,配置自动装配
<bean id="emp" class="ocom.yer.spring5-1.Emp" autowire="byType"> </bean>
autowire 属性通常用两个值
byType 根据属性类型
byName 根据属性名称
十一、IOC 操作bean管理(外部属性文件)
- 直接配置数据库信息
配置德鲁伊连接池
- 引入外部属性文件配置数据库
- 创建外部属性文件 properties 文件
把外部properties属性文件引入到spring配置文件中
- 引入命名空间context
<?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/contexthttp://www.springframewor .org/schema/context/spring-context.xsd"> </bean>
十二、IOC 操作bean管理(基于注解管理)
1.什么是注解
(1) 注解是代码特殊标记,格式:@注解名称(属性名称=属性值,属性名称=属性值)
(2)注解在什么地方用呢? 注解作用在: 类 属性 方法
(3)使用注解目的:简化xml配置
2.在spring中针对bean管理 中 创建对象提供注解四个
- @Component
- @Service
- Controller
- Repository
四个注解功能都一样,都可以用来创建bean实例
3.基于注解方式实现对象创建
第一步:引入依赖 aop
第二步:开启组件扫描
第三步:创建类,在类上面添加创建对象注解
注解中vaule属性值可以省略不写 默认是类名称的首字母小写 UserService-userService
注解原理是与aop有关—重点
开启组件扫描
如果扫描多个包,使用逗号隔开
要记得扫描包的上层目录
<context:component-scan base-package="con.yer"></context:component-scan>
首先加载配置文件 bean.xml
bean.xml 中只有一段代码,开启注解扫描
发现了相关注解@Component (可默认为类名首字母小写)
4.开启组件扫描细节配置
<!--示例1: use-default-filters="flase"表示现在不是用默认filter,自己配置filter context:include-filter,设置去扫描哪些内容--> <context:component-scan base-package="con.yer" use-default-filters="flase"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan> <!--示例2:扫描所有内容 context:exclude-filter:设置哪些内容不进行扫描--> <context:component-scan base-package="con.yer" > <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan>
5.基于注解方式实现属性注入
(1)@Autowired:根据属性类型自动进行装配
(2)@Qualifier:根据属性名称
(3)@Resource: 可以根据类型,可以根据名称
(4)@Value:注入普通类型属性
@Autowired :根据属性类型进行自动装配
第一步,把service和dao的对象进行创建,在service和dao类添加创建对象注解
package com.yer.dao; public interface UserDao { void add(); }
package com.yer.dao; import org.springframework.stereotype.Repository; @Repository public class UserDaoImpl implements UserDao{ @Override public void add() { System.out.println("UserDaoImpl add..."); } }
package com.yer.sevice; import com.yer.dao.UserDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class UserService { //定义dao类型属性 //不需要添加set方法 //添加注入属性注解 @Autowired private UserDao userDao; public void add(){ System.out.println("UserService add...."); userDao.add(); } }
<context:conponent-scan base-package="com.yer"></context:conponent-scan>
@Qualifier :根据名称注入
@Qualifier 的使用是与@Autowired一起使用
@Resource 根据类型注入
@Resource(name="userDaoImpl22") private UserDao userDao; public void add(){ System.out.println("UserService add...."); userDao.add(); } }
@Repository(value="userDaoImpl22") public class UserDaoImpl implements UserDao{ @Override public void add() { System.out.println("UserDaoImpl add"); } }
@Resource是 javax.annotation.Resource包下的
@Value( value="月月") private String name;
6.完全注解开发
(1)创建配置类,代替xml配置文件
package com.yer.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; /** * @author Darling * @create 2022-02-28-16:58 */ @Configuration //作为配置类,代替xml文件 @ComponentScan(basePackages = {"com.yer"}) public class SpringConfig { }
package com.yer.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; /** * @author Darling * @create 2022-02-28-16:58 */ @Configuration //作为配置类,代替xml文件 @ComponentScan(basePackages = {"com.yer"})//扫描包 public class SpringConfig { }
(2)编写测试类
package com.yer.testdemo; import com.yer.config.SpringConfig; import com.yer.sevice.UserService; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.testng.annotations.Test; /** * @author Darling * @create 2022-02-28-16:20 */ public class TestSpring5 { @Test public void testService(){ ApplicationContext context = // new ClassPathXmlApplicationContext("bean.xml"); new AnnotationConfigApplicationContext(SpringConfig.class); UserService userService = context.getBean("userService",UserService.class); System.out.println(userService); userService.add(); } }
SLF4J: Failed to load class “org.slf4j.impl.StaticLoggerBinder”.
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
SLF4J: Failed to load class “org.slf4j.impl.StaticLoggerBinder”.
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
报错了
https://blog.csdn.net/weixin_39548940/article/details/100015174
官网给出的解决思路如下:
This error is reported when the org.slf4j.impl.StaticLoggerBinder class could not be loaded into memory. This happens when no appropriate SLF4J binding could be found on the class path. Placing one (and only one) of slf4j-nop.jar, slf4j-simple.jar, slf4j-log4j12.jar, slf4j-jdk14.jar or logback-classic.jar on the class path should solve the problem.
翻译成汉语如下(我是直接英汉互译翻译过来的):
此错误在组织slf4j.inf.strestcoperbinder类无法装入内存时报告。当在类路径上找不到合适的slf4j绑定时,就会发生这种情况。slf4j-nop.jar放置一个(且只有一个), slf4j-simple.jar, slf4j-log4j12.jar, slf4j-jdk14.jar 或 logback-classic.jar 的类路径应该解决这个问题。
解决方案:
在Maven工程的pom文件中,新增一个上述的包文件之一的依赖配置,项目就可以正常编译运行了。
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-nop</artifactId> <version>1.7.2</version> </dependency>
SLF4J binding could be found on the class path. Placing one (and only one) of slf4j-nop.jar, slf4j-simple.jar, slf4j-log4j12.jar, slf4j-jdk14.jar or logback-classic.jar on the class path should solve the problem.
翻译成汉语如下(我是直接英汉互译翻译过来的):
此错误在组织slf4j.inf.strestcoperbinder类无法装入内存时报告。当在类路径上找不到合适的slf4j绑定时,就会发生这种情况。slf4j-nop.jar放置一个(且只有一个), slf4j-simple.jar, slf4j-log4j12.jar, slf4j-jdk14.jar 或 logback-classic.jar 的类路径应该解决这个问题。
解决方案:
在Maven工程的pom文件中,新增一个上述的包文件之一的依赖配置,项目就可以正常编译运行了。
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-nop</artifactId> <version>1.7.2</version> </dependency>