Spring 框架详细总结及实例演示
Spring
1.概述
1.1 简介
开源免费的、轻量级的、非侵入式的容器(一个服务器软件,一个框架),类与类之间的管理,帮助开发人员创建对象,管理对象等的一款 Java 开发框架。
针对接口编程,解耦合。
1.2 核心思想
- IOC(控制反转)
- AOP(面向切面编程)
思想是上层建筑,不是具体的实现。好比:JVM 中方法区和元空间之间的关系。
1.3 优缺点
1.3.1 优点
-
轻量
Spring 核心功能所需的jar总共3M左右
-
针对接口编程,解耦合
通过 Spring 提供的IOC容器,可以将对象间的依赖关系交由 Spring 控制,避免在代码中通过代码相互使用。
用户也不必为单例模式类、属性文件解析等底层需求编写代码,可以更专注于上层的应用。
-
AOP 编程的支持
通过 Spring 的AOP功能,方便进行面向切面编程,许多不容易用传统 OOP (面向对象编程)实现的功能可以通过AOP轻松实现。
-
方便集成各种优秀框架
1.3.2 缺点
- 框架封装内部复杂
- 不易精通
2.IOC
2.1 简介
IOC(Inversion of Controller)(控制反转),分为控制和反转两部分。
把对象的创建、赋值、管理工作都交给代码之外的容器实现,也就是对象的创建是其它外部资源完成。
2.1.1 控制
对象的创建、管理、赋值、对象的声明周期和对象之间的关系等权限。
2.1.2 反转
2.1.2.1 正转
开发人员在代码中,使用 new 构造器创建对象,开发人员掌握了对象的创建、属性赋值、对象从开始到销毁的全部过程,开发人员对 对象有全部的控制。
2.1.2.2 反转
开发人员将管理对象的权限转移给代码之外的容器实现。由容器完成对象的管理,只需定义对象。
2.2 Spring 中的 IOC
在 Spring 中 IOC 的思想是通过 DI(依赖注入)实现,DI 底层通过反射机制实现(序列化也可以实现相关功能)。
开发人员只要知道需要使用对象的名称就行,其他的一切交给容器,其他容器内部的实现细节,无需关注。
通过 IOC 减少对代码的改动,也能实现不同的功能,实现业务对象之间的解耦合。例如:能实现service和dao对象之间的解耦合。
2.2.1 依赖
Class A 使用了 Class B 的属性或方法,就称为 A 依赖了 B。
2.2.2 注入
每一个 Bean(类的实例,在 Spring 中称为Bean)的属性都通过容器注入。注入的方式分为构造注入(构造器,显示的在类中进行声明包括无参构造器)、Set 注入(Spring 中通过 Set 进行 Bean 的属性注入)和自动注入
2.2.2.1 自动注入
构造器注入和 Set 注入在 Java 中比较常见,在 Spring 中还有自动注入,可以根据 类的类型或实例的名字进行注入赋值。
-
byName:按名称注入,Java 类中引用类型的属性名和 Spring 容器中 Bean 的【id】一样,
数据类型一样,将容器中的这个 Bean 赋值给引用类型 -
byType:按类型注入,Java 类中引用类型的是【数据类型】和 Spring 容器中的【class】是同源关系的,
这样的 Bean 就能赋值给引用类型注意:如果出现多个同源,则会注入失败
2.2.2.2 实例演示
通过 Spring 中的配置文件的方式演示。
构造注入
<bean id="xxx" class="xxx"><constructor-arg name="xxx" value="xxx"/><constructor-arg index="0/1/2/..." value="xx"/>
bean>
第一行:
id:指定实例的名称,也就是在容器中存在的名字,其他 Bean 通过这个id进行调用,id区分大小写
class:实例类的静态类型的全路径
Set 注入
<property name="属性名" value="属性值"/>
<property name="属性名" ref="属性值"/>
注:value 注入一般都是 Java 中的简单类型:基本数据类型 + String
通过这种方式,需要在具体 Bean 类中有具体属性的 Set 方法
ref 注入引用类型,指定在容器存在的id即可。
Spring执行set注入时,只会调用set方法,不会关注set中的代码,如果通过set注入,只要有相关的set方法就行(需要传递参数),无需在方法体中进行指定处理过程。
但在注入的过程中会执行方法体。
public void setName(String name){//没有执行过程//正常的是有:this.name = name;System.out.println(email + "该方法执行了"); }像上面这种,只有方法名,但是没有实现过程的,Spring 也可以通过这个 set 方法进行 name 的赋值并且会运行方法体中的代码。
2.2.3 对象的创建方式(Java)
- 构造方法,new Student()
- 反射
- 序列化(对象的数据传输)
- 克隆
- 动态代理
- IOC:容器创建对象
2.3 IOC 的体现 - TomCat
在我们使用 TomCat 服务器时,我们在 JavaWeb 中编写了 Servlet 以后,并没有进行 Servlet 类的实例化,只需要在 web.xml 文件中编写具体的映射和 myservlet 即可,这是因为 TomCat 帮我们进行了对象实例化的管理。
Tomcat作为容器:里面存放的有Servlet对象,Listener,Filter对象
这也是一种 IOC,在很多类似的框架或服务器中都频繁运用了 IOC 的思想,帮助程序员减少代码的编写和对象的管理,将更多的精力多放在业务代码的编写上。但并不是所有的类都交给容器管理。
2.4 总结
- IOC将对象放入容器中,实现对 对象的管理,Spring中通过依赖注入(DI)实现IOC
- 通过IOC来管理对象,来实现解耦合
- spring作为容器适合管理的对象:
- service对象
- dao对象
- 工具类对象
- spring作为容器不适合管理的对象:
- 实体类(容易变,每一次都不一样,如果交给容器管理,改变困难)
- servlet
- listener
- filter
- …等web中的对象
- 这些web对象应该交给服务器来管理(tomCat)
3.Spring 创建 Bean
3.0 Bean 范围
Spring容器中的bean可以分为5个范围:
- 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作用域效果相同。
3.1 配置文件
- 读取配置文件
- 扫描用户自定义配置文件,将Bean创建进容器中(需要指定扫描的文件)
- 当用户需要使用时,直接从容器中获取
2.扫描用户配置文件,将Bean创建进容器中
-
将 Bean 按功能或模块进行划分,每一个模块建立一个xml配置文件
-
创建一个 applicationContext 总的配置文件进行管理其他配置文件
-
各个配置文件中的 Bean都加入到了总容器中,可以相互引用使用
<import resource="classpath:/xx/xx-*.xml" />
扫描指定路径下的配置文件并进行加载。
classpath:项目路径开始的相对路径
3.2 注解
注解赋值,需要在配置文件中添加开启注解支持
如:使用了 @Component 注解,就需要在配置文件中添加一个组件扫描器,不然虽然添加了 Bean 注解,但是无法将 Bean 扫描到容器中。
3.2.1 实例类的容器注入 @Component
1.添加注解
@Component(value=“id”)
- value:该组件的名称,可写可不写
- 在当前类的开头
- 可自定以赋值名称,也可以使用默认名称,默认名称为类名小字母
2.在配置文件中添加注解支持
<context:component-scan base-package="xxx" />
// base-package:扫描的包路径,扫描这个包和子包中所有的内容
通常对于一个类使用@@Component即可,为了方便,对于不同的层次有不同的注解:
- @Repository:放在dao层,表示可以为持久层,可以和数据库交互
- @Service:表示业务层对象
- @Controller:表示为Servlet层
3.2.2 类中基本属性赋值 @Value
@Value:给简单数据类型赋值
- 无需set方法在属性上面直接赋值
- 也可以在set上面进行赋值
@Value("pox")
private String name;
// 也可以是@Value(value="pox")@Value("pox1")
public void setName(String name){this.name = name;
}
除了指定值以外,还可以通过加载外部的配置文件,然后读取配置文件的值来进行赋值。
通过在总的 Spring 配置中,读取外部的 properties 文件。然后通过@Value("${key}")来读取值。
1.在Spring配置文件中读取配置文件
<context:property-placeholder location="classpath:xxx"/>
2.通过注解给具体属性进行赋值
@Value("${具体key}") // 加载的配置文件上的key
private String name;
3.2.3 类中引用属性赋值
@Autowire(required=“true(默认)”:使用自动注入
-
支持byName、byType
-
默认为byType
-
可以通过set方法赋值,也可以直接在属性上面赋值
-
使用byName,通过两个注解实现
- @Autowire
- @Qualifier(value=“xxx”)
- value为指定容器id的bean赋值给当前引用类型
-
如果没有指定required的值,默认为true,如果没有在容器中找到相应的bean就会爆出异常。如果将required改为false,如果没有找到相应的bean会继续往下执行,不会爆出异常
@Resource(name=“bean的id”),来自 JDK 自带,给引用类型赋值,可以byName和byType,默认为byName
- 先按照byName,如果没有找到,则会按照byType寻找赋值
- 在jdk1.8中自带,高于1.8则没有这个注解,需要添加相关依赖
- 当指定了具体的bean id,则只能通过byName注入
推荐使用 @Resource 注解实现引用类型的赋值注入。在 IDEA 中使用 @Autowire 会有提示,网上有很多解释,笔者采纳因为 @Autowire 是 Spring 的在移植性会有问题,脱离了 Spring 框架就不能使用了,但是 @Resource 是 JDK 自带的,不存在这个问题。
4.AOP
4.1 简介
AOP(Aspect Oriented Program)(面向切面编程)(OOP面向对象编程的升级),将具体的方法当成一个切面,可以在切面基础上在方法的执行的前、后、环绕等添加新的功能。底层动过动态代理实现。
AOP是一个动态思想,在程序运行期间,创建代理。这个代理对象是存在内存中。
抽取并封装为一个可重用的模块,这个模块被命名为“切面”(Aspect),减少系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。可用于权限认证、日志、事务处理。
AOP代理主要分为静态代理和动态代理。
静态代理的代表为AspectJ;
动态代理则以Spring AOP为代表(JDK动态代理和CGLIB动态代理)。
4.2 动态代理
4.2.1 JDK 动态代理
JDK 自带。使用 JDK 反射包中的类实现创建代理对象的功能,InvocationHandler接口———调用处理程序
Method Proxy类代理。
4.2.2 CGLIB(Code generation Library)代理
使用第三方的工具库,实现代理对象的创建,继承类的动态代理,应对有些类没有实现接口,采用继承,所以方法不能是final MethodInterceptor方法拦截器接口、Enhancer增强器。
4.2.3 总结
看做是动态代理的规范化,把实现动态代理的步骤进行了一个规范、定义。
万物皆切面,编写程序的时候,考虑有哪些切面组成程序。以切面为核心设计开发。
4.3 作用
- 在目标类源代码不改变的情况下,增加功能
- 减少代码的重复
- 专注业务逻辑代码
- 解耦合,给你的业务功能和日志,事务等非业务功能分离
在Spring中的作用:提供声明式事务,允许用户自定义切面。
在非业务代码中使用面向切面编程,不要在业务代码中使用,面向切面编程主要用于日志、事务、权限检查等这类操作代码无需根据业务进行变化的代码。
给一个系统中存在的类修改功能,但是原有类的功能不完善,但你还没有源代码,使用aop就增加功能。
4.4 AOP 中的概念名词
-
aspect:切面
即它是一个类。表示增强的功能,就是一堆代码,完成某个功能,非业务功能,常见的切面功能有日志、事务、统计信息、参数检查、权限验证
-
JoinPoint:连接点,连接切面的业务方法。在这个业务方法执行时,会同时执行切面的功能;切面功能执行的位置
-
Pointcut:切入点,是一个或多个连接点的集合。表示在执行这些方法时,都能增加切面的功能
-
target:目标,被通知对象
给哪个类的方法增加功能,这个类就是目标对象。
-
Advice:通知,切面必须要完成的工作。即它是类中的一个方法。通知表示切面功能执行的时间。
-
Proxy:代理,向目标对象应用通知之后创建的对象
4.5 AOP 技术实现
4.5.1 概述
通过框架实现
-
Spring:实现了 AOP 思想中的部分功能
实现的操作比较复杂、繁重
-
Aspectj:独立的框架,专门做AOP
推荐使用 AspectJ,后面的实例演示也是根据 AspectJ 编写代码
4.5.2 AspectJ
使用 xml 配置文件中的标签实现 AOP 功能。
4.5.2.1 注解
-
@Advice:通知注解
-
@Before:前置通知
- 方法是公共的
- 方法的返回值为void
-
@AfterReturnring:后置通知
-
@Around:环绕通知
-
@AfterThrowing:异常通知
-
@After:最终通知
Pointcut:切面执行的位置,使用AspectJ中的切入点表达式
语法:execution([方法执行体])
4.5.2.2 实例演示
execution()方法。
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nJWtbj0S-1644055644556)(F:\StudyNotepad\img\image-20220205173237766.png)]](https://img-blog.csdnimg.cn/4deb53bc2303432a85dffa76c6bc38ae.png)
- modifiers-pattern:访问权限类型
- ret-type-pattern:返回值类型
- declaring-type-pattern:包名,类名
- name-pattern(param-pattern):方法名(方法参数)
- throws-pattern:抛出异常类型
- ?:代表可选
实例
- 导入依赖: spring-aspects依赖
- 创建切面类
- 在类的上面加上@Aspect注解
- 在具体的方法上面加入表示执行的时间,比如:@Before(value=“execution([方法体])”)
- 声明目标对象
- 声明切面类对象
- 声明自动代理生成器
- 寻找spring容器中的所有aspect bean
- 给每一个目标方法生成一个代理类
- 在执行目标方法时,通过代理类执行
@Before(value="execution(public void [具体方法]([如果这个方法有参数就需要标注,如果没有就不需要,只需要参数类型,不需要说明参数名称]))")
public void myBefore(){...
}
-- 自动代理生成器
<aop:aspectj-autoproxy />
在切面中添加JoinPoint参数,可以获取代理方法的详细信息
@Before(value="execution(public void [具体方法]([如果这个方法有参数就需要标注,如果没有就不需要,只需要参数类型,不需要说明参数名称]))")
public void myBefore(JoinPoint jp){jp.getSignature();// 等等,获取目标方法中的各种详细信息
}
4.6 总结
- 主要的作用是业务功能和非业务功能进行解耦合
- 一些事务、日志等非业务功能实现复用
- 给一些方法增加一些新的功能,通过AOP生成动态代理对象,来增强目标方法
- 切面类,加上@Aspect注解,以便于让容器扫描到
5.Spring 中的事务
5.1 事务概述
事务保证一组操作中,要么都成功要么都失败。事务管理来确保数据的完整性和一致性。
事务中的 ADID:
- Atomicity:事务是个原子操作,由一系列动作完成,事物的原子性确保动作要么都完成,要么都失败。
- Consistency:一旦所有事务动作完成,事务就被提交,数据和资源处于一种满足业务规则的一致性状态中。
- Isolation:可能有许多事务会同时处理相同的数据,因此每个事务都应与其他事务隔离开来,防止数据损坏。
- Durability:一旦事务完成,无论发生什么系统错误,结果不受影响。通常情况下,事物的结果被写到持久化存储器中。
5.2 事务管理
通过 Spring 的事务管理器,管理不同数据库访问技术的事务处理方法。
5.2.1 事务管理分类
-
编程式事务
-
声明式事务:把事务相关的资源和内容都提供给 Spring,Spring 就能处理事务提交,回滚了。几乎不用代码。
声明式事务管理(提倡)(Spring Aop 支持声明式事务管理)
5.2.2 使用时机
- 操作涉及多表或多个sql语句的insert、update、delete,要么都成功要么都失败
- 在 Java 代码中事务应该放在 service 类的业务方法上,因为业务方法会调用多个 dao 方法,执行多个sql语句
5.3 缺点
- 不同的数据库访问技术,处理事务的对象,方法不同,需要了解不同数据库访问技术使用事务的原理
- 掌握多种数据库中事务的处理逻辑,何时提交或回滚事务
- 处理事务的多种方法
总结:多种数据库的访问技术,有不同的事务处理机制,对象,方法。
5.4 事务的隔离级别
-
read_uncommitted(读未提交):为解决任何并发问题
-
read_committed(读以提交):解决脏读
-
repeatable_read(MySQL默认,可重复读):解决脏读、不可重复读、存在幻读
在 MySQL 中使用这种隔离级别,通过 MVCC 方法其实是可以解决幻读的
-
serializable:串行化。不存在并发问题(安全性高,性能低)
事务的隔离级别越高,并发性就越低、安全性越高;隔离级别越低,反之。
5.4.1 事务的超时时间
表示一个方法最长的执行时间,如果方法执行时超过了时间,事务就回滚。单位秒,整数值,默认是-1。
5.5 事务的7个传播行为
- Propagation.REQUIRED(默认)
- Propagation.REQUIRES_NEW
- Propagation.SUPPORTS
- Propagation.MANDATORY
- Propagation.NOT_SUPPORTED
- Propagation.NEVER
- Propagation.NESTED
重点理解前三个。
5.5.1 Propagation.REQUIRED
Spring 默认的传播行为,方法在调用的时候如果有事务就用当前的事务,如果没有事务,就新建一个事务,方法在新建的事务中执行。
5.5.2 Propagation.REQUIRES_NEW
每一个方法都需要一个新的事务。如果调用方法时,存在一个事务,那么将该事务停止,开启一个新的事务执行调用方法,当新事务执行完毕以后,继续执行原有的事务。
如果本身没有事务,则直接开启一个新的事务。
5.5.3 Propagation.SUPPORTS
支持,方法有事务可以正常执行,没有事务也可以正常执行。适用于查询操作。
5.6 Spring 中的事务控制
通过设置隔离级别、传播行为、超时来进行事务控制。通过注解 @Transactional 来开启事务支持。
在类的上面和方法的方面开启事务支持。
5.6.1 注解中的属性
- propagation:事务的传播行为,通过propagation的枚举类,保存7种传播行为
- isolation:表示隔离级别,同样通过相关枚举类实现,默认为READ_COMMIT
- readOnly:boolean类型的值,表示数据库操作不是只读的,默认为false
- timeout:超时时间,默认为-1,单位是秒
- rollbackFor:表示回滚的异常类列表,是一个数组,数组中的每一个值是具体的class
- 框架先检查方法抛出的异常是不是在rollbacFor列表中,如果在会回滚;如果没有指定rollbackFor,框架则会直接检查这个异常是不是一个运行时异常,如果是,那么也会回滚
- 更多的作用是增加其他异常的回滚
- rollbackForClassName:表示回滚的异常类列表,它的值是异常类的名称,是String类型的值
- noRollbackFor:表示不需要回滚的异常类列表。是class类型
- noRollbackForClassName:不需要回滚的异常类列表名称。是String
通过对属性的设置来进行事务控制。
5.6.2 使用方式
- 在配置类中
- 在具体的类上
5.6.2 优势
- 适合中小型项目
- Spring 自己提供的事务控制
- 使用方便,效率高
6.Spring Bean 的生命周期
6.1 Bean 的一生
-
实例化Bean
对于 ApplicationContext 容器,当容器启动结束后,通过获取 BeanDefinition 对象中的信息,实例化所有的配置文件中的所有 Bean。
-
设置对象属性(依赖注入)
实例化后的对象被封装在 BeanWrapper 对象中,紧接着,Spring 根据 BeanDefinition 中的信息,以及通过 BeanWrapper 提供的设置属性的接口完成依赖注入。
-
处理Aware接口
Spring 会检测该对象是否实现了 xxxAware 接口,并将相关的 xxxAware 实例注入给Bean。
-
BeanPostProcessor
如果想对 Bean 进行一些自定义的处理,那么可以让 Bean 实现了 BeanPostProcessor 接口,
那将会调用 postProcessBeforeInitialization(Object obj, String s) 方法。如果在 中指定了该 Bean 的作用范围为 scope=“singleton”,则将该 Bean 放入 Spring IoC 的缓存池中,将触发 Spring 对该 Bean 的生命周期管理;如果在 中指定了该 Bean 的作用范围为 scope=“prototype”,则将该 Bean 交给调用者,调用者管理该 Bean 的生命周期,Spring 不再管理该 Bean。
-
InitializingBean 与 init-method
如果 Bean 在 Spring 配置文件中配置了 init-method 属性,则会自动调用其配置的初始化方法。
-
如果这个Bean实现了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Objectobj, String s)方法;由于这个方法是在Bean初始化结束时调用的,所以可以被应用于内存或缓存技术;
-
DisposableBean
当 Bean 不再需要时,会经过清理阶段,如果 Bean 实现了 DisposableBean 这个接口,会调用其实现的destroy()方法;
-
destroy-method
如果这个 Bean 的 Spring 配置中配置了 destroy-method 属性,会自动调用其配置的销毁方法。
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Xz8TagAr-1644055644557)(F:\StudyNotepad\img\5-1ZF1100325116.png)]](https://img-blog.csdnimg.cn/cfecd1039bf041fead7aa72d2df6a12d.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAcG94MjFz,size_20,color_FFFFFF,t_70,g_se,x_16)
底部
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
