spring IOC

一:IOC容器

1.1 IOC思想

IOC:Inversion of Control,翻译过来是反转控制。

①获取资源的传统方式

自己做饭:买菜、洗菜、择菜、改刀、炒菜,全过程参与,费时费力,必须清楚了解资源创建整个过程 中的全部细节且熟练掌握。

在应用程序中的组件需要获取资源时,传统的方式是组件主动的从容器中获取所需要的资源,在这样的 模式下开发人员往往需要知道在具体容器中特定资源的获取方式,增加了学习成本,同时降低了开发效 率。

②反转控制方式获取资源

点外卖:下单、等、吃,省时省力,不必关心资源创建过程的所有细节。

反转控制的思想完全颠覆了应用程序组件获取资源的传统方式:反转了资源的获取方向——改由容器主 动的将资源推送给需要的组件,开发人员不需要知道容器是如何创建资源对象的,只需要提供接收资源 的方式即可,极大的降低了学习成本,提高了开发的效率。这种行为也称为查找的被动形式。

③DI

DI:Dependency Injection,翻译过来是依赖注入。
DI 是 IOC 的另一种表述方式:即组件以一些预先定义好的方式(例如:setter 方法)接受来自于容器 的资源注入。相对于IOC而言,这种表述更直接。

Spring容器是一个超级大工厂,负责创建、管理所有java对象,这些java对象被称为Bean。Spring容器管理着容器中Bean之间的依赖关系,Spring使用“依赖注入”的方式来管理Bean之间的依赖关系。使用IOC实现对象之间的解耦合。

1.2 IOC容器在Spring中的实现

Spring 的 IOC 容器就是 IOC 思想的一个落地的产品实现。IOC 容器中管理的组件也叫做 bean。在创建 bean 之前,首先需要创建 IOC 容器。Spring 提供了 IOC 容器的两种实现方式:

①BeanFactory

这是 IOC 容器的基本实现,是 Spring 内部使用的接口。面向 Spring 本身,不提供给开发人员使用。

②ApplicationContext

BeanFactory 的子接口,提供了更多高级特性。面向 Spring 的使用者,几乎所有场合都使用 ApplicationContext 而不是底层的 BeanFactory。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

二:基于XML管理bean

2.1 入门案例

①创建Maven项目

在这里插入图片描述

②引入maven依赖pom.xml

<dependencies>
        <!-- 基于Maven依赖传递性,导入spring-context依赖即可导入当前所需所有jar包 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.1</version> </dependency>
        <!-- junit测试 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

③定义接口和实体类

在这里插入图片描述

④创建Spring配置文件

在src/main/resource/目录下面创建一个xml文件,文件名可以随意,单spring建议的名称为applicationContixt.xml
定义测试类

在这里插入图片描述

⑥创建测试类测试

在这里插入图片描述

⑦思路

在这里插入图片描述

⑧注意

Spring 底层默认通过反射技术调用组件类的无参构造器来创建组件对象,这一点需要注意。如果在需要 无参构造器时,没有无参构造器,则会抛出下面的异常:

2.2 获取bean

在这里插入图片描述

在这里插入图片描述

①方式一:根据id获取

由于 id 属性指定了 bean 的唯一标识,所以根据 bean 标签的 id 属性可以精确获取到一个组件对象。

②方式二:根据类型获取


```java
@Test
    public void testHelloWorld(){
        ApplicationContext ac = new
                ClassPathXmlApplicationContext("applicationContext.xml");
        HelloWorld bean = ac.getBean(HelloWorld.class);
        bean.sayHello();
    }

③方式三:根据id和类型

 @Test 
    public void testHelloWorld(){ 
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        HelloWorld bean = ac.getBean("helloworld", HelloWorld.class); 
        bean.sayHello(); 
    }

④注意

当根据类型获取bean时,要求IOC容器中指定类型的bean有且只能有一个

当IOC容器中一共配置了两个: 根据类型获取时会抛出异常:

  • org.springframework.beans.factory.NoUniqueBeanDefinitionException:
    No qualifying bean of type ‘com.atguigu.spring.bean.HelloWorld’
    available: expected single matching bean but found 2:
    helloworldOne,helloworldTwo

如果组件类实现了接口,根据接口类型可以获取 bean 吗?

可以,前提是bean唯一

如果一个接口有多个实现类,这些实现类都配置了 bean, 根据接口类型可以获取 bean 吗?

不行,因为bean不唯一

⑥结论

根据类型来获取bean时,在满足bean唯一性的前提下,其实只是看:『对象 instanceof 指定的类 型』 的返回结果,只要返回的是true就可以认定为和类型匹配,能够获取到。

若没有一个类型匹配的bean,此时抛出异常:NoSuchBeanDefinitionException
若有多个类型匹配的bean,此时抛出异常:NoUniqueBeanfinitionException

2.3 依赖注入之setter注入

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

2.4 依赖注入之构造器注入

在这里插入图片描述

注意:
constructor-arg标签还有两个属性可以进一步描述构造器参数:
index属性:指定参数所在位置的索引(从0开始)
name属性:指定参数名

在这里插入图片描述

2.5 特殊值处理

①字面量赋值

什么是字面量? int a = 10; 声明一个变量a,初始化为10,此时a就不代表字母a了,而是作为一个变量的名字。当我们引用a的时候,我们实际上拿到的值是10。 而如果a是带引号的:‘a’,那么它现在不是一个变量,它就是代表a这个字母本身,这就是字面量。

所以字面量没有引申含义,就是我们看到的这个数据本身。

<!--使用value属性给bean的属性赋值,spring会把value的属性值看做字面量-->
<property name="name" value="张三"/>

②null值

<property name="name">
	<null/>
</property>

注意:

 <property name="name" value=“”null“”></property> 

以上写法,为name所赋的值是字符串null

③xml实体

<!-- 小于号在XML文档中用来定义标签的开始,不能随便使用 --> 
<!-- 解决方案一:使用XML实体来代替 --> 
<property name="expression" value="a &lt; b"/>

④CDATA节

<property name="expression">
<!-- 解决方案二:使用CDATA节 --> 
<!-- CDATA中的C代表Character,是文本、字符的含义,CDATA就表示纯文本数据 --> 
<!-- XML解析器看到CDATA节就知道这里是纯文本,就不会当作XML标签或属性来解析 --> 
<!-- 所以CDATA节中写什么符号都随意 --> 
<value><![CDATA[a < b]]></value>
</property>

2.6 为引用类型属性赋值

方式一:引用外部已声明的bean,用ref属性

 <bean id="studentFour" class="com.atguigu.spring.bean.Student">
        <property name="id" value="1004"></property>
        <property name="name" value="赵六"></property>
        <property name="age" value="26"></property>
        <property name="sex" value="女"></property>
        <!-- ref属性:引用IOC容器中某个bean的id,将所对应的bean为属性赋值 --> 
        <property name="clazz" ref="clazzOne"></property>
    </bean>

在这里插入图片描述

方式二:内部bean

==内部bean只能用于给属性赋值,不能在外部通过IOC容器获取,因此可以省略id属性 ==

    <bean id="studentFour" class="com.atguigu.spring.bean.Student"> 
        <property name="id" value="1004"></property> 
        <property name="name" value="赵六"></property> 
        <property name="age" value="26"></property> 
        <property name="sex" value="女"></property> 
        <property name="clazz"> 
            <!-- 在一个bean中再声明一个bean就是内部bean --> 
            <!-- 内部bean只能用于给属性赋值,不能在外部通过IOC容器获取,因此可以省略id属性 --> 
            <bean id="clazzInner" class="com.atguigu.spring.bean.Clazz">
                <property name="clazzId" value="2222">
                </property>
                <property name="clazzName" value="远大前程班"></property>
            </bean> 
        </property>

方式三:级联属性赋值

需要提前为ref关联的bean进行设置,然后通过级联方式,这相当于修改

    <bean id="studentFour" class="com.atguigu.spring.bean.Student"> 
        <property name="id" value="1004"></property> 
        <property name="name" value="赵六"></property> 
        <property name="age" value="26"></property> 
        <property name="sex" value="女"></property> 
        <!-- 一定先引用某个bean为属性赋值,才可以使用级联方式更新属性 -->
        <property name="clazz" ref="clazzOne"></property> 
        <property name="clazz.clazzId" value="3333"></property>
        <property name="clazz.clazzName" value="最强王者班"></property>
    </bean>

   <bean id="clazzOne" class="com.atguigu.spring.bean.Clazz">
        <property name="clazzId" value="1111"></property> 
        <property name="clazzName" value="财源滚滚班"></property> 
    </bean>

2.7 为数组类型属性赋值

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

2.8 为集合类型的属性赋值

①为List、set 集合类型属性赋值

在这里插入图片描述

在这里插入图片描述

②为Map集合类型属性赋值

在这里插入图片描述

<bean id="teacherOne" class="com.atguigu.spring.bean.Teacher">
	<property name="teacherId" value="10010"></property>
	<property name="teacherName" value="大宝"></property>
</bean>
<bean id="teacherTwo" class="com.atguigu.spring.bean.Teacher">
	<property name="teacherId" value="10086"></property>
	<property name="teacherName" value="二宝"></property>
</bean>
<bean id="studentFour" class="com.atguigu.spring.bean.Student">
	<property name="id" value="1004"></property>
	<property name="name" value="赵六"></property>
	<property name="age" value="26"></property>
	<property name="sex" value="女"></property>
	<!-- ref属性:引用IOC容器中某个bean的id,将所对应的bean为属性赋值 -->
	<property name="clazz" ref="clazzOne"></property>
	<property name="hobbies">
    	<array>
       	 	<value>抽烟</value>
        	<value>喝酒</value>
       	 <value>烫头</value>
   		 </array>
	</property>
	<property name="teacherMap">
    	<map>
        	<entry>
            	<key>
               		 <value>10010</value>
            	</key>
            	<ref bean="teacherOne"></ref>
        	</entry>
       		 <entry>
            	<key>
                	<value>10086</value>
            	</key>
            	<ref bean="teacherTwo"></ref>
        	</entry>
    </map>
</property>
</bean>

③引用集合类型的bean

使用util:list、util:map标签必须引入相应的命名空间,可以通过idea的提示功能选择

 <!--list集合类型的bean-->
<util:list id="students">
<ref bean="studentOne"></ref>
<ref bean="studentTwo"></ref>
<ref bean="studentThree"></ref>
</util:list>


<!--map集合类型的bean-->
<util:map id="teacherMap">
<entry>
    <key>
        <value>10010</value>
    </key>
    <ref bean="teacherOne"></ref>
</entry>
<entry>
    <key>
        <value>10086</value>
    </key>
    <ref bean="teacherTwo"></ref>
</entry>
</util:map>


2.9 p命名空间

引入p命名空间后,可以通过以下方式为bean的各个属性赋值
在这里插入图片描述

2.10 引入外部属性文件properties

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

2.11 bean的作用域

①概念
在Spring中可以通过配置bean标签的scope属性来指定bean的作用域范围,各取值含义参加下表:
在这里插入图片描述

如果是在WebApplicationContext环境下还会有另外两个作用域(但不常用):

在这里插入图片描述
在这里插入图片描述

scope:设置bean的作用域
scope = “singleton | prototype ”
singleton 单例:表示获取bean所对应的对象都是同一个
prototype 多例:表示获取bean对应的对象都不是同一个

2.12 bean的生命周期

①具体的生命周期过程

bean对象创建(调用无参构造器)
给bean对象设置属性
bean对象初始化之前操作(由bean的后置处理器负责)
bean对象初始化(需在配置bean时指定初始化方法)
bean对象初始化之后操作(由bean的后置处理器负责)
bean对象就绪可以使用
bean对象销毁(需在配置bean时指定销毁方法)
IOC容器关闭

2.13 FactoryBean

①简介
FactoryBean是Spring提供的一种整合第三方框架的常用机制。和普通的bean不同,配置一个FactoryBean类型的bean,在获取bean的时候得到的并不是class属性中配置的这个类的对象,而是getObject()方法的返回值。通过这种机制,Spring可以帮我们把复杂组件创建的详细过程和繁琐细节都屏蔽起来,只把最简洁的使用界面展示给我们。将来我们整合Mybatis时,Spring就是通过FactoryBean机制来帮我们创建SqlSessionFactory对象的。

通过实现FactoryBean接口可以实现,返回装配的类的对象

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

创建一个UserFactory类实现FactoryBean接口,在配置UserFactory的Bean,在getBean()对象的时候返回FactoryBean接口中装配的Bean对象。

2.14 基于xml的自动装配

自动装配:

根据指定的策略,在IOC容器中匹配某一个bean,自动为指定的bean中所依赖的类类型或接口类型属性赋值,可以通过bean标签中的autowire属性自动装配的策略

在这里插入图片描述

配置bean

使用bean标签的autowire属性设置自动装配效果 ,
方法有两种byType和byName类型

方法一:自动装配方式:byType

byType:根据类型匹配IOC容器中的某个兼容类型的bean,为属性自动赋值

若在IOC中,没有任何一个兼容类型的bean能够为属性赋值,则该属性不装配, 即值为默认值null

若在IOC中,有多个兼容类型的bean能够为属性赋值,则抛出异常 NoUniqueBeanDefinitionException

在这里插入图片描述

在这里插入图片描述

方法二:自动装配方式:byName

byName:将自动装配的属性的属性名,作为bean的id在IOC容器中匹配相对应的bean进行赋值

在这里插入图片描述

在这里插入图片描述

三:基于注解管理bean

3.1 标记与扫描

①注解

和 XML 配置文件一样,注解本身并不能执行,注解本身仅仅只是做一个标记,具体的功能是框架检测到注解标记的位置,然后针对这个位置按照注解标记的功能来执行具体操作。

本质上:所有一切的操作都是Java代码来完成的,XML和注解只是告诉框架中的Java代码如何执行。

②扫描

Spring 为了知道程序员在哪些地方标记了什么注解,就需要通过扫描的方式,来进行检测。然后根据注解进行后续操作。

③标识组件的常用注解

@Component:将类标识为普通组件
@Controller:将类标识为控制层组件
@Service:将类标识为业务层组件
@Repository:将类标识为持久层组件

问:以上四个注解有什么关系和区别?

在这里插入图片描述

通过查看源码我们得知,@Controller、@Service、@Repository这三个注解只是在@Component注解的基础上起了三个新的名字。对于Spring使用IOC容器管理这些组件来说没有区别。所以@Controller、@Service、@Repository这三个注解只是给开发人员看的,让我们能够便于分辨组件的作用。

注意:虽然它们本质上一样,但是为了代码的可读性,为了程序结构严谨我们肯定不能随便胡乱标记。

④创建组件

在这里插入图片描述

⑤扫描组件

情况一:最基本的扫描方式

<context:component-scan base-package="com.atguigu"> </context:component-scan>

情况二:指定要排除的组件 exclude-filter

<context:component-scan base-package="com.atguigu"> 
	<!-- context:exclude-filter标签:指定排除规则 --> 
	<!--
	type:设置排除或包含的依据 
	type="annotation",根据注解排除,expression中设置要排除的注解的全类名,例如排除@Controller标识的所有类 	
	type="assignable",根据类型排除,expression中设置要排除的类型的全类名 
	--> 
	<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> 

	<!--<context:exclude-filter type="assignable" expression="com.atguigu.controller.UserController"/>--> 

</context:component-scan>

情况三:仅扫描指定组件

必须设置use-default-filters=“false”,因为默认规则即扫描指定包下所有类

<context:component-scan base-package="com.atguigu" use-default-filters="false">
    <!-- context:include-filter标签:指定在原有扫描规则的基础上追加的规则 -->
    <!-- use-default-filters属性:取值false表示关闭默认扫描规则 -->
    <!-- 此时必须设置use-default-filters="false",因为默认规则即扫描指定包下所有类 -->
    <!--
        type:设置排除或包含的依据 
        type="annotation",根据注解排除,expression中设置要排除的注解的全类名 
        type="assignable",根据类型排除,expression中设置要排除的类型的全类名 
     -->
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    <!--<context:include-filter type="assignable" expression="com.atguigu.controller.UserController"/>-->
</context:component-scan>

⑥组件所对应的bean的id

在我们使用XML方式管理bean的时候,每个bean都有一个唯一标识,便于在其他地方引用。现在使用注解后,每个组件仍然应该有一个唯一标识。

默认情况

类名首字母小写就是bean的id。例如:UserController类对应的bean的id就是userController。

自定义bean的id,可通过标识组件的注解的value属性设置自定义的bean的id

@Service("userService")
//默认为userServiceImpl
 public class UserServiceImpl implements  UserService {}

3.2 基于注解自动装配

①@Autowired注解

在成员变量上直接标记@Autowired注解即可完成自动装配,不需要提供setXxx()方法。以后我们在项目中的正式用法就是这样。

在这里插入图片描述

②@Autowired注解其他细节

@Autowired注解可以标记在构造器和set方法上

在这里插入图片描述

③@Autowired工作流程和原理

@Autowired注解的原理:

a.默认通过byType的方式,在IOC容器中通过类型匹配某个bean为属性赋值,
b.若有多个类型匹配的bean,此时会自动转换为byName方式实现自动配置的效果
c.若byType和byName的方式都无法实现自动装配,但IOC容器中有多个类型匹配名称不匹配的bean,可以通过添加注解@qualifier通过该注解的value属性值,指定某个bean的id,将这个bean为属性复制

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值