百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

spring框架 - 带你一文读懂spring

ccwgpt 2024-10-16 08:10 33 浏览 0 评论

2.1 IOC和DI

2.1.1 IOC(Inversion of Control):反转控制

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

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

传统方式: 我想吃饭 我需要买菜做饭

反转控制: 我想吃饭 饭来张口


2.1.2 DI(Dependency Injection):依赖注入

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

总结: IOC 就是一种反转控制的思想, 而DI是对IOC的一种具体实现。

2.1.3 IOC容器在Spring中的实现

前提: Spring中有IOC思想, IOC思想必须基于 IOC容器来完成, 而IOC容器在最底层实质上就是一个对象工厂.

1)在通过IOC容器读取Bean的实例之前,需要先将IOC容器本身实例化。

2)Spring提供了IOC容器的两种实现方式

① BeanFactory:IOC容器的基本实现,是Spring内部的基础设施,是面向Spring本身的,不是提供给开发人员使用的。

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

2.1.4 ApplicationContext的主要实现类

1) ClassPathXmlApplicationContext:对应类路径下的XML格式的配置文件

2) FileSystemXmlApplicationContext:对应文件系统中的XML格式的配置文件

3) 在初始化时就创建单例的bean,也可以通过配置的方式指定创建的Bean是多实例的。

2.1.5 ConfigurableApplicationContext

1) 是ApplicationContext的子接口,包含一些扩展方法

2) refresh()和close()让ApplicationContext具有启动、关闭和刷新上下文的能力。

2.1.6 WebApplicationContext

1) 专门为WEB应用而准备的,它允许从相对于WEB根目录的路径中完成初始化工作

2.2 通过类型获取bean

1) 从IOC容器中获取bean时,除了通过id值获取,还可以通过bean的类型获取。但如果同一个类型的bean在XML文件中配置了多个,则获取时会抛出异常,所以同一个类型的bean在容器中必须是唯一的。

HelloWorld helloWorld = cxt.getBean(HelloWorld. class);


2) 或者可以使用另外一个重载的方法,同时指定bean的id值和类型

HelloWorld helloWorld = cxt.getBean(“helloWorld”,HelloWorld. class);


2.3 给bean的属性赋值

2.3.1 依赖注入的方式

1. 通过bean的setXxx()方法赋值

Hello World中使用的就是这种方式

2. 通过bean的构造器赋值

1) Spring自动匹配合适的构造器

<bean id="book" class="com.learn.java.spring.bean.Book" >

<constructor-arg value= "10010"/>

<constructor-arg value= "Book01"/>

<constructor-arg value= "Author01"/>

<constructor-arg value= "20.2"/>

</bean >

2) 通过索引值指定参数位置

<bean id="book" class="com.learn.java.spring.bean.Book" >

<constructor-arg value= "10010" index ="0"/>

<constructor-arg value= "Book01" index ="1"/>

<constructor-arg value= "Author01" index ="2"/>

<constructor-arg value= "20.2" index ="3"/>

</bean >

3) 通过类型区分重载的构造器

<bean id="book" class="com.learn.java.spring.bean.Book" >

<constructor-arg value= "10010" index ="0" type="java.lang.Integer" />

<constructor-arg value= "Book01" index ="1" type="java.lang.String" />

<constructor-arg value= "Author01" index ="2" type="java.lang.String" />

<constructor-arg value= "20.2" index ="3" type="java.lang.Double" />

</bean >

2.3.2 p名称空间

为了简化XML文件的配置,越来越多的XML文件采用属性而非子元素配置信息。Spring 从2.5版本开始引入了一个新的p命名空间,可以通过<bean>元素属性的方式配置Bean 的属性。

使用p命名空间后,基于XML的配置方式将进一步简化。

需加入:xmlns:p="http://www.springframework.org/schema/p"

<bean id="studentSuper"

class="com.java.learn.helloworld.bean.Student"

p:studentId="2002" p:stuName="Jerry2016" p:age="18" />

2.3.3 可以使用的值

1. 字面量

1) 可以使用字符串表示的值,可以通过value属性或value子节点的方式指定

2) 基本数据类型及其封装类、String等类型都可以采取字面值注入的方式

3) 若字面值中包含特殊字符,可以使用<![CDATA[]]>把字面值包裹起来

2. null值

<bean class="com.learn.java.spring.bean.Book" id="bookNull" >

<property name= "bookId" value ="2000"/>

<property name= "bookName">

<null/>

</property>

<property name= "author" value ="nullAuthor"/>

<property name= "price" value ="50"/>

</bean >

3. 给bean的级联属性赋值*

<bean id="action" class="com.learn.java.spring.ref.Action">

<property name="service" ref="service"/>

<!-- 设置级联属性(了解) -->

<property name="service.dao.dataSource" value="DBCP"/>

</bean>

4. 外部已声明的bean、引用其他的bean

<bean id="shop" class="com.learn.java.spring.bean.Shop" >

<property name= "book" ref ="book"/>

</bean >

5. 内部bean

当bean实例仅仅给一个特定的属性使用时,可以将其声明为内部bean。内部bean声明直接包含在<property>或<constructor-arg>元素里,不需要设置任何id或name属性

内部bean不能使用在任何其他地方

<bean id="shop2" class="com.learn.java.spring.bean.Shop" >

<property name= "book">

<bean class= "com.learn.java.spring.bean.Book" >

<property name= "bookId" value ="1000"/>

<property name= "bookName" value="innerBook" />

<property name= "author" value="innerAuthor" />

<property name= "price" value ="50"/>

</bean>

</property>

</bean >

2.4 集合属性

在Spring中可以通过一组内置的XML标签来配置集合属性,例如:<list>,<set>或<map>。

2.4.1 数组和List

配置java.util.List类型的属性,需要指定<list>标签,在标签里包含一些元素。这些标签 可以通过<value>指定简单的常量值,通过<ref>指定对其他Bean的引用。通过<bean>指定内置bean定义。通过<null/>指定空元素。甚至可以内嵌其他集合。

数组的定义和List一样,都使用<list>元素。

配置java.util.Set需要使用<set>标签,定义的方法与List一样。

<bean id="shop" class="com.learn.java.spring.bean.Shop" >

<property name= "categoryList">

<!-- 以字面量为值的List集合 -->

<list>

<value> 历史</value >

<value> 军事</value >

</list>

</property>

<property name= "bookList">

<!-- 以bean的引用为值的List集合 -->

<list>

<ref bean= "book01"/>

<ref bean= "book02"/>

</list>

</property>

</bean >

2.4.2 Map

Java.util.Map通过<map>标签定义,<map>标签里可以使用多个<entry>作为子标签。每个条目包含一个键和一个值。

必须在<key>标签里定义键。

因为键和值的类型没有限制,所以可以自由地为它们指定<value>、<ref>、<bean>或<null/>元素。

可以将Map的键和值作为<entry>的属性定义:简单常量使用key和value来定义;bean引用通过key-ref和value-ref属性定义。

<bean id="cup" class="com.learn.java.spring.bean.Cup">

<property name="bookMap">

<map>

<entry>

<key>

<value>bookKey01</value>

</key>

<ref bean="book01"/>

</entry>

<entry>

<key>

<value>bookKey02</value>

</key>

<ref bean="book02"/>

</entry>

</map>

</property>

</bean>

2.4.3 集合类型的bean

如果只能将集合对象配置在某个bean内部,则这个集合的配置将不能重用。我们需要 将集合bean的配置拿到外面,供其他bean引用。

配置集合类型的bean需要引入util名称空间

<util:list id="bookList">

<ref bean="book01"/>

<ref bean="book02"/>

<ref bean="book03"/>

<ref bean="book04"/>

<ref bean="book05"/>

</util:list>


<util:list id="categoryList">

<value>编程</value>

<value>极客</value>

<value>相声</value>

<value>评书</value>

</util:list>


2.5 FactoryBean


2.5.1 FactoryBean

Spring中有两种类型的bean,一种是普通bean,另一种是工厂bean,即FactoryBean。

工厂bean跟普通bean不同,其返回的对象不是指定类的一个实例,其返回的是该工厂bean的getObject方法所返回的对象。

2.6 bean的作用域

在Spring中,可以在<bean>元素的scope属性里设置bean的作用域,以决定这个bean是单实例的还是多实例的。

默认情况下,Spring只为每个在IOC容器里声明的bean创建唯一一个实例,整个IOC容器范围内都能共享该实例:所有后续的getBean()调用和bean引用都将返回这个唯一的bean实例。该作用域被称为singleton,它是所有bean的默认作用域。

当bean的作用域为单例时,Spring会在IOC容器对象创建时就创建bean的对象实例。而当bean的作用域为prototype时,IOC容器在获取bean的实例时创建bean的实例对象。


2.7 bean的生命周期

1) Spring IOC容器可以管理bean的生命周期,Spring允许在bean生命周期内特定的时间点执行指定的任务。

2) Spring IOC容器对bean的生命周期进行管理的过程:

① 通过构造器或工厂方法创建bean实例

② 为bean的属性设置值和对其他bean的引用

③ 调用bean的初始化方法

④ bean可以使用了

⑤ 当容器关闭时,调用bean的销毁方法

3) 在配置bean时,通过init-method和destroy-method 属性为bean指定初始化和销毁方法

4) bean的后置处理器

① bean后置处理器允许在调用初始化方法前后对bean进行额外的处理

② bean后置处理器对IOC容器里的所有bean实例逐一处理,而非单一实例。

其典型应用是:检查bean属性的正确性或根据特定的标准更改bean的属性。

③ bean后置处理器需要实现接口:

org.springframework.beans.factory.config.BeanPostProcessor。在初始化方法被调用前后,Spring将把每个bean实例分别传递给上述接口的以下两个方法:

●postProcessBeforeInitialization(Object, String)

●postProcessAfterInitialization(Object, String)

5) 添加bean后置处理器后bean的生命周期

①通过构造器或工厂方法创建bean实例

②为bean的属性设置值和对其他bean的引用

③将bean实例传递给bean后置处理器的postProcessBeforeInitialization()方法

④调用bean的初始化方法

⑤将bean实例传递给bean后置处理器的postProcessAfterInitialization()方法

⑥bean可以使用了

⑦当容器关闭时调用bean的销毁方法

2.8 引用外部属性文件

当bean的配置信息逐渐增多时,查找和修改一些bean的配置信息就变得愈加困难。这时可以将一部分信息提取到bean配置文件的外部,以properties格式的属性文件保存起来,同时在bean的配置文件中引用properties属性文件中的内容,从而实现一部分属性值在发生变化时仅修改properties属性文件即可。这种技术多用于连接数据库的基本信息的配置。

2.8.1 直接配置

<!-- 直接配置 -->

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">

<property name="username" value="root"></property>

<property name="password" value="root"></property>

<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>

<property name="url" value="jdbc:mysql://localhost:3306/mysql"></property>

</bean>


2.8.2 使用外部的属性文件

1. 创建properties属性文件

prop.userName=root

prop.password=root

prop.url=jdbc:mysql:///test

prop.driverClass=com.mysql.jdbc.Driver


3. 引入context名称空间

4. <?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/context http://www.springframework.org/schema/context/spring-context.xsd
"
>

3.指定properties属性文件的位置

<!-- 指定properties属性文件的位置 -->

<!-- classpath:xxx 表示属性文件位于类路径下 -->

<context:property-placeholder location="classpath:jdbc.properties"/>



4.从properties属性文件中引入属性值

<!-- 从properties属性文件中引入属性值 -->

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">

<property name="username" value="${prop.username}"></property>

<property name="password" value="${prop.password}"></property>

<property name="driverClassName" value="${prop.driver}"></property>

<property name="url" value="${prop.url}"></property>

</bean>


2.9自动装配

2.9.1 自动装配的概念

1) 手动装配:以value或ref的方式明确指定属性值都是手动装配。

2) 自动装配:根据指定的装配规则,不需要明确指定,Spring自动将匹配的属性值注入bean中。

3) 注意:自动装配属性的数据类型,只能是[非字面量]值。[字面量]值不能自动装配。

2.9.2 装配模式

1) 根据类型自动装配:将类型匹配的bean作为属性注入到另一个bean中。若IOC容器中有多个与目标bean类型一致的bean,Spring将无法判定哪个bean最合适该属性,所以不能执行自动装配

2) 根据名称自动装配:必须将目标bean的名称和属性名设置的完全相同

3) 通过构造器自动装配:当bean中存在多个构造器时,此种自动装配方式将会很复杂。不推荐使用。

2.9.3 选用建议及使用

相对于使用注解的方式实现的自动装配,在XML文档中进行的自动装配略显笨拙,在项目中更多的使用注解的方式实现。

在bean中添加autowire属性实现自动装配,可选值:byName|byType

2.10 通过注解配置bean

2.10.1 概述

相对于XML方式而言,通过注解的方式配置bean更加简洁和优雅,而且和MVC组件化开发的理念十分契合,是开发中常用的使用方式。

现阶段Spring基于注解开发,仍然需要在xml中配置“组件扫描”,才能识别Spring框架的注解,使用Spring注解开发,需要做如下两个准备。

1. <context:component-scan base-package="被扫描的包,及其子包"/>

2. 必须在原有JAR包组合的基础上再导入一个:spring-aop-5.2.5.RELEASE.jar

2.10.2 使用注解标识组件

1) 普通组件:@Component

标识一个受Spring IOC容器管理的组件

2) 持久化层组件:@Repository

标识一个受Spring IOC容器管理的持久化层组件

3) 业务逻辑层组件:@Service

标识一个受Spring IOC容器管理的业务逻辑层组件

4) 表述层控制器组件:@Controller

标识一个受Spring IOC容器管理的表述层控制器组件

5) 组件命名规则

①默认情况:使用组件的简单类名首字母小写后得到的字符串作为bean的id

②使用组件注解的value属性指定bean的id

注意:事实上Spring并没有能力识别一个组件到底是不是它所标记的类型,即使将 @Respository注解用在一个表述层控制器组件上面也不会产生任何错误,所以 @Respository、@Service、@Controller这几个注解仅仅是为了让开发人员自己 明确当前的组件扮演的角色。

2.10.3 扫描组件

组件被上述注解标识后还需要通过Spring进行扫描才能够侦测到。

1) 指定被扫描的package

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

2) 详细说明

base-package属性指定一个需要扫描的基类包,Spring容器将会扫描这个基类包及其子包中的所有类。

②当需要扫描多个包时可以使用逗号分隔。

③如果仅希望扫描特定的类而非基包下的所有类,可使用resource-pattern属性过滤特定的类,示例:

<context:component-scan

base-package="com.learn.java.component"

resource-pattern="autowire/*.class"/>

④包含与排除

●<context:include-filter>子节点表示要包含的目标类

注意:通常需要与use-default-filters属性配合使用才能够达到“仅包含某些 组件”这样的效果。即:通过将use-default-filters属性设置为false, 禁用默认过滤器,然后扫描的就只是include-filter中的规则指定的 组件了。

●<context:exclude-filter>子节点表示要排除在外的目标类

●component-scan下可以拥有若干个include-filter和exclude-filter子节点

●过滤表达式

类别

示例

说明

annotation

com.learn.java.XxxAnnotation

过滤所有标注了XxxAnnotation的类。这个规则根据目标组件是否标注了指定类型的注解进行过滤。

assignable

com.learn.java.BaseXxx

过滤所有BaseXxx类的子类。这个规则根据目标组件是否是指定类型的子类的方式进行过滤。

aspectj

com.learn.java.*Service+

所有类名是以Service结束的,或这样的类的子类。这个规则根据AspectJ表达式进行过滤。

regex

com\.learn.java\.anno\.*

所有com.learn.java.anno包下的类。这个规则根据正则表达式匹配到的类名进行过滤。

custom

com.learn.java.XxxTypeFilter

使用XxxTypeFilter类通过编码的方式自定义过滤规则。该类必须实现org.springframework.core.type.filter.TypeFilter接口


3) JAR包

必须在原有JAR包组合的基础上再导入一个:spring-aop-5.2.5.RELEASE.jar

2.10.4 组件装配

1) 需求

Controller组件中往往需要用到Service组件的实例,Service组件中往往需要用到 Repository组件的实例。Spring可以通过注解的方式帮我们实现属性的装配。

2) 实现依据

在指定要扫描的包时,<context:component-scan> 元素会自动注册一个bean的后置处 理器:AutowiredAnnotationBeanPostProcessor的实例。该后置处理器可以自动装配标记 了@Autowired、@Resource或@Inject注解的属性。

3) @Autowired注解

①根据类型实现自动装配。

②构造器、普通字段(即使是非public)、一切具有参数的方法都可以应用@Autowired 注解

③默认情况下,所有使用@Autowired注解的属性都需要被设置。当Spring找不到匹 配的bean装配属性时,会抛出异常。

④若某一属性允许不被设置,可以设置@Autowired注解的required属性为 false

⑤默认情况下,当IOC容器里存在多个类型兼容的bean时,Spring会尝试匹配bean 的id值是否与变量名相同,如果相同则进行装配。如果bean的id值不相同,通过类型的自动装配将无法工作。此时可以在@Qualifier注解里提供bean的名称。Spring 甚至允许在方法的形参上标注@Qualifiter注解以指定注入bean的名称。

⑥@Autowired注解也可以应用在数组类型的属性上,此时Spring将会把所有匹配的bean进行自动装配。

⑦@Autowired注解也可以应用在集合属性上,此时Spring读取该集合的类型信息,然后自动装配所有与之兼容的bean。

⑧@Autowired注解用在java.util.Map上时,若该Map的键值为String,那么 Spring将自动装配与值类型兼容的bean作为值,并以bean的id值作为键。

4) @Resource

@Resource注解要求提供一个bean名称的属性,若该属性为空,则自动采用标注处的变量或方法名作为bean的名称。

5) @Inject

@Inject和@Autowired注解一样也是按类型注入匹配的bean,但没有reqired属性。

相关推荐

用Steam启动Epic游戏会更快吗?(epic怎么用steam启动)

Epic商店很香,但也有不少抱怨,其中一条是启动游戏太慢。那么,如果让Steam启动Epic游戏,会不会速度更快?众所周知,Steam可以启动非Steam游戏,方法是在客户端左下方点击“添加游戏”,然...

Docker看这一篇入门就够了(dockerl)

安装DockerLinux:$curl-fsSLhttps://get.docker.com-oget-docker.sh$sudoshget-docker.sh注意:如果安装了旧版...

AYUI 炫丽PC开发UI框架2016年6月15日对外免费开发使用 [1]

2016年6月15日,我AY对外发布AYUI(WPF4.0开发)的UI框架,开发时候,你可以无任何影响的去开发PC电脑上的软件exe程序。AYUI兼容XP操作系统,在Win7/8/8.1/10上都顺利...

别再说C#/C++套壳方案多了!Tauri这“借壳生蛋”你可能没看懂!

浏览器套壳方案,C#和C++有更多,你说的没错,从数量和历史积淀来看,C#和C++确实有不少方式来套壳浏览器,让Web内容在桌面应用里跑起来。但咱们得把这套壳二字掰扯清楚,因为这里面学问可大了!不同的...

OneCode 核心概念解析——Page(页面)

在接触到OneCode最先接触到的就是,Page页面,在低代码引擎中,页面(Page)设计的灵活性是平衡“快速开发”与“复杂需求适配”的关键。以下从架构设计、组件系统、配置能力等维度,解析确...

React是最后的前端框架吗,为什么这么说的?

油管上有一位叫Theo的博主说,React是终极前端框架,为什么这么说呢?让我们来看看其逻辑:这个标题看起来像假的,对吧?React之后明明有无数新框架诞生,凭什么说它是最后一个?我说的“最后一个”不...

面试辅导(二):2025前端面试密码:用3个底层逻辑征服技术官

面试官放下简历,手指在桌上敲了三下:"你上次解决的技术难题,现在回头看有什么不足?"眼前的候选人瞬间僵住——这是上周真实发生在蚂蚁金服终面的场景。2025年的前端战场早已不是框架熟练...

前端新星崛起!Astro框架能否终结React的霸主地位?

引言:当"背着背包的全能选手"遇上"轻装上阵的短跑冠军"如果你是一名前端开发者,2024年的框架之争绝对让你眼花缭乱——一边是React这位"背着全家桶的全能选...

基于函数计算的 BFF 架构(基于函数计算的 bff 架构是什么)

什么是BFFBFF全称是BackendsForFrontends(服务于前端的后端),起源于2015年SamNewman一篇博客文章《Pattern:BackendsFor...

谷歌 Prompt Engineering 白皮书:2025年 AI 提示词工程的 10 个技巧

在AI技术飞速发展的当下,如何更高效地与大语言模型(LLM)沟通,以获取更准确、更有价值的输出,成为了一个备受关注的问题。谷歌最新发布的《PromptEngineering》白皮书,为这一问题提供了...

光的艺术:灯具创意设计(灯光艺术作品展示)

本文转自|艺术与设计微信号|artdesign_org_cn“光”是文明的起源,是思维的开端,同样也是人类睁眼的开始。每个人在出生一刻,便接受了光的照耀和洗礼。远古时候,人们将光奉为神明,用火来...

MoE模型已成新风口,AI基础设施竞速升级

机器之心报道编辑:Panda因为基准测试成绩与实际表现相差较大,近期开源的Llama4系列模型正陷入争议的漩涡之中,但有一点却毫无疑问:MoE(混合专家)定然是未来AI大模型的主流范式之一。...

Meta Spatial SDK重大改进:重塑Horizon OS应用开发格局

由文心大模型生成的文章摘要Meta持续深耕SpatialSDK技术生态,提供开自去年9月正式推出以来,Meta持续深耕其SpatialSDK技术生态,通过一系列重大迭代与功能增强,不断革新H...

&quot;上云&quot;到底是个啥?用&quot;租房&quot;给你讲明白IaaS/PaaS/SaaS的区别

半夜三点被机房报警电话惊醒,顶着黑眼圈排查服务器故障——这是十年前互联网公司运维的日常。而现在,程序员小王正敷着面膜刷剧,因为公司的系统全"搬"到了云上。"部署到云上"...

php宝塔搭建部署thinkphp机械设备响应式企业网站php源码

大家好啊,欢迎来到web测评。本期给大家带来一套php开发的机械设备响应式企业网站php源码,上次是谁要的系统项目啊,帮你找到了,还说不会搭建,让我帮忙录制一期教程,趁着今天有空,简单的录制测试了一下...

取消回复欢迎 发表评论: