Spring
2023-11-06 16:13:09 0 举报
AI智能生成
Spring详解
作者其他创作
大纲/内容
体系结构
<br>
Core Container(核心容器)
Beans<br>
管理 Beans<br>
Core
Spring 的核心,提供<font color="#e74f4c">IOC容器的对象的创建</font>并<font color="#e74f4c">处理依赖对象关系</font><br>
ExpressionLanguage
Context
配置文件 就是<font color="#e74f4c">Bean的关系的集合</font>,也叫做<font color="#e74f4c">IOC容器</font>,调用了大部分spring-core中的方法<br>
ExpressionLanguage
SpEL 表达式<br>
AOP
切面编程 使 Spring框架管理对象支持AOP,同时这个模块也提供了<font color="#e74f4c">事务管理</font><br>
ORM
提供了对<font color="#e74f4c">现有的ORM框架的支持</font>,例如Hibernate,JDO,Mybatis等
DAO
提供了<font color="#e74f4c">对数据访问对象(Data Access Object,DAO)模式</font>和<font color="#e74f4c">JDBC的支持</font>,把实现<font color="#e74f4c">业务逻辑</font>和<font color="#e74f4c">数据库访问的代码</font><font color="#4669ea">实现分离</font>等。
Data Access(数据库整合)
JDBC<br>
ORM
OXM<br>
JMS
Transaction
springWeb(MVC Web 开发)
springWeb 和 spring 无需通过中间整合层进行整合, 是一个基于 mvc 的 web 框架,方便前后端数据的传输, <br> 拥有控制器,接收外部求,解析参数传给服务层<br>
bean的管理
基于<font color="#e74f4c"> xml 配置</font>方式依赖注入<br>
get, set 方式注入
构造方法注入
<br>
<font color="#e74f4c">注解方式</font>实现依赖注入
注解需要的 jar 包, 注解功能封装在 AOP 包中,需要导入<font color="#e74f4c">spring-aspects包</font><br>
在spring中<font color="#e74f4c">开启注解扫描</font><br>
<context:component-scan base-package="包名"> </<font color="#e74f4c">context:component-scan</font>><br>
创建对象注解方式
@Component(value=“admin”)<br>
相当于 <bean id=“admin” class=“”></bean><br>
@Service
@Repository<br>
注意:只是为了<font color="#e74f4c">后续扩展功能</font>,在不同的层使用不同的注解标记<br>@Scope(value=“<font color="#e74f4c">prototype</font>”) 原型 @Scope(value=“<font color="#e74f4c"> singleton </font>”) 单例<br>
注解方式<font color="#e74f4c">注入属性</font>
@Autowired<br>
byType 自动注入
该注解默认使用<font color="#e74f4c">按类型自动装配</font> Bean 的方式
byName 自动注入<br>
结合<font color="#e74f4c">@Qualifier</font> 注解一起使用,按照名称(byName)来装配<br>@Qualifier 的value 属性用于<font color="#e74f4c">指定要匹配的 Bean 的 id 值</font><br>
@Resource
Spring 提供了对<font color="#e74f4c"> jdk 中@Resource </font>注解的支持<br>
既可以按名称匹配 Bean,也可以按类型匹配 Bean。<font color="#e74f4c">默认</font>按照 ByType 自动注入
注解与 XML 的对比
注解<br>
<font color="#e74f4c">优点:</font>方便,直观,高效
<font color="#e74f4c">缺点:</font>以硬编码的方式写入到 Java 代码中,修改是需要重新编译代码的
XML
<font color="#e74f4c">优点:</font>配置和代码是<font color="#e74f4c">分离</font>的,在 xml 中做修改,无需编译代码,只需重启服务器即可将新的配置加载
<font color="#e74f4c">缺点:</font>编写麻烦,效率低,大型项目过于复杂
Bean 的作用域
singleton<br>
在Spring容器仅存在一个Bean实例,Bean以单实例的方式存在,是<font color="#e74f4c">Bean默认的作用域</font>
prototype<br>
每次从容器重调用Bean时,都会返回一个新的实例<br>
request<br>
每一次HTTP请求都会产生一个新的Bean,该Bean仅在当前HTTP Request内有效
session<br>
同一个HTTP Session共享一个Bean,不同的HTTP Session使用不同的Bean<br>
globalSession
同一个全局Session共享一个Bean,只用于基于Protlet的Web应用,<font color="#e74f4c">Spring5中已经不存在了。</font><br>
事务管理
事物可以看做是由对<font color="#e74f4c">数据库若干操作</font>组成的一个单元。<br>事务的作用就是为了保证用户的<font color="#e74f4c">每一个操作都是可靠的</font>,事务中的每一步操作都必须成功执行,<br>只要有发生异常就回退到事务开始未进行操作的状态,这些操作要么都完成,要么都取消,从而<font color="#e74f4c">保证数据满足一致性</font>的要求<br>
事务形式<br>
编程式事务<br>
需 要 注 入 一 个 事 务 管 理 <font color="#e74f4c"> TransactionTemplate</font> ,然后在我们<font color="#e74f4c">代码中</font>需要提交事务或回滚事务时<font color="#e74f4c">自己写代码</font>实现,<font color="#e74f4c">少用</font>
声明式事务<br>
管理建立在 AOP 基础上,本质是对方法前后进行拦截,所以<font color="#e74f4c">声明式事务是方法级别</font>的
管理方式
基于 xml 配置<br>
基于注解实现
配置事物管理器
<br>
开启注解事务管理<br>
< tx :annotation-driven transaction-manager ="transactionManager" />
@Transactional
用法<br>
一般把 @Transactional标签添加在service中
@Transactional可以添加在service层的类上,类中所有的方法都会<font color="#e74f4c">添加事务管理功能</font>
@Transactional如果只添加在某个方法上,那么表示此方法在事务管理中进行<br>
<b><font color="#e74f4c">失效情况</font></b>
修饰一个<font color="#e74f4c">非public的方法</font>,底层权限只针对public修饰的方法<br>
方法中的异常<font color="#e74f4c">被catch捕获处理</font>了
默认情况下出现<font color="#e74f4c">编译期异常</font>,事务不生效
@Transactional<font color="#e74f4c">事务传播行为设置错误</font>
数据库引擎不支持事务, <font color="#e74f4c">数据库引擎</font>是mysql底层具体的一种数据处理实现的机制,<br>innodb(支持事务功能),myisam(不支持事务)<br>
在一个<font color="#e74f4c">非事务方法中</font>使用this(原始的对象==自己new出来的对象)
java创建对象的方式
构造方法:new student()<br>
反射
序列化
动态代理
容器:tomcat容器、ioc容器
相关面试问题
说说Spring AOP 和 AspectJ AOP 区别
Spring AOP<br>
Spring AOP 属于<font color="#e74f4c">运行时增强</font><br>
特点
基于动态代理来实现,默认如果使用接口的,用 JDK 提供的动态代理实现,如果是方法则使用 CGLIB 实现
Spring AOP 需要依赖 IOC 容器来管理,并且只能作用于 Spring 容器,使用纯 Java 代码实现
在性能上,由于 Spring AOP 是基于动态代理来实现的,在容器启动时需要生成代理实例,<br>在方法调用上也会增加栈的深度,使得 Spring AOP 的性能不如 AspectJ 的那么好<br>
AspectJ AOP<br>
AspectJ 是一个易用的功能强大的 AOP 框架,属于<font color="#e74f4c">编译时增强</font>, 可以单独使用,也可以整合到其它框架中,<br>是 AOP 编程的完全解决方案。AspectJ 需要用到<font color="#e74f4c">单独的编译器 ajc</font><br>
AspectJ 属于<font color="#e74f4c">静态织入</font>,通过修改代码来实现,在实际运行之前就完成了织入,所以说它生成的类是<font color="#e74f4c">没有额外运行时开销</font>的
织入的时机
编译期织入(Compile-time weaving)<br>
如类 A 使用 AspectJ 添加了一个属性,类 B 引用了它,这个场景就需要编译期的时候就进行织入,否则没法编译类 B。<br>
编译后织入(Post-compile weaving)
也就是已经生成了 .class 文件,或已经打成 jar 包了,这种情况我们需要增强处理的话,就要用到编译后织入。<br>
类加载后织入(Load-time weaving)
指的是在加载类的时候进行织入,要实现这个时期的织入<br>
设计模式
<b><font color="#e74f4c">工厂设计模式</font></b>
Spring使用工厂模式通过 BeanFactory、ApplicationContext 创建 bean 对象
<b><font color="#e74f4c">代理设计模式</font></b>
Spring AOP 功能的实现。<br>
<b><font color="#e74f4c">单例设计模式</font></b>
Spring 中的 Bean 默认都是单例的。<br>
<b><font color="#e74f4c">模板方法模式</font></b>
Spring 中jdbcTemplate、hibernateemplate 等以Template 结尾的对数据库操作的类,它们就使用到了模板模式<br>
<b><font color="#e74f4c">包装器设计模</font></b>
我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库,<br>这种模式让我们可以根据客户的需求能够动态切换不同的数据源<br>
<b><font color="#e74f4c">观察者模式</font></b>
Spring 事件驱动模型就是观察者模式很经典的一个应用。<br>
<b><font color="#e74f4c">适配器模式</font></b>
Spring AOP 的增强或通知(Advice)使用到了适配器模式、spring MVC 中也是用到了适配器模式适配Controller<br>
1、bean的注入方式
1、@Configuration注解去标记了该类,这样标明该类是一个Spring的一个配置类,在加载配置的时候会去加载它,<br> @Bean的注解,标明这是一个注入Bean的方法,会将下面的返回的Bean注入IOC<br>
2、通过构造方法注入Bean
<b><font color="#e74f4c">IOC(控制反转)</font></b><br>
它是一种 设计思想 ,就是 将原本在程序中<font color="#e74f4c">手动创建对象</font>的控制权,交由 <font color="#e74f4c">Spring 框架来管理</font>
IOC 容器是具有<font color="#e74f4c">依赖注入</font>功能的容器,负责<font color="#e74f4c">对象的实例化、对象的初始化,对象和对象之间依赖关系配置、<br>对象的销毁、对外提供对象的查找</font>等操作,对象的<font color="#e74f4c">整个生命周期</font>都是由容器来控制<br>
正控
若要使用某个对象,需要 自己去负责对象的创建<br>
反控
若要使用某个对象,只需要 从<font color="#e74f4c"> Spring 容器中获取需要使用的对象</font>,不关心对象的创建过程,也就是把 创建对象的控制权反转给了 Spring 框架.
底层实现方式
<font color="#e74f4c">解析 xml/扫描注解标签 </font>+ <font color="#e74f4c">工厂模式</font> + <font color="#e74f4c">反射机制</font><br>
Spring 通过 IoC 容器来<font color="#e74f4c">管理对象</font>的实例化和初始化,以及对象从创建到销毁的整个生命周期
两种类型容器
BeanFactory<br>
Spring里面最低层的接口,提供了最简单的容器的功能,只提供了<font color="#e74f4c">实例化对象</font>和<font color="#e74f4c">拿对象</font>的功能;<br>BeanFactory在<font color="#e74f4c">启动的时候不会去实例化Bean</font>,当有从容器中<font color="#e74f4c">拿Bean的时候才会去实例化</font>;<br>
ApplicationContext
<font color="#e74f4c">应用上下文</font>,继承BeanFactory接口,它是Spring的一个更高级的容器,提供了更多的有用的功能<br>ApplicationContext在启动的时候就把所有的Bean全部实例化了。<br>它还可以为Bean配置l<font color="#e74f4c">azy-init=true</font>来让Bean延迟实例化;<br>
实现类<br>
ClassPathXmlApplicationContext<br>
该类从类路径 ClassPath 中寻找指定的 XML 配置文件,并完成 ApplicationContext 的实例化工作<br>
FileSystemXmlApplicationContext<br>
该类从指定的文件系统路径中寻找指定的 XML 配置文件,并完成 ApplicationContext 的实例化工作<br>
<b><font color="#e74f4c">AOP(面向切面编程)</font></b><br>
可以对<font color="#e74f4c">业务逻辑和非业务逻辑进行隔离</font>,从而使得各部分之间的耦合度降低,提高<font color="#e74f4c">程序的可重用性</font>,同时提高了<font color="#e74f4c">开发的效率</font>
应用场景
验证权限
打印日志
提交事务
统一异常处理
核心原理
使用<font color="#e74f4c">动态代理的方式</font>在执行方法前后或者出现异常的时候做加入相关的逻辑.<br>
基本概念
连接点(Joinpoint)<br>
类中可以<font color="#e74f4c">被增强的方法</font>,这个方法就被称为连接点
切入点(pointcut)
类中有很多方法可以被增强,但实际中只有 add 和 update被增强了,那么 add 和 update 方法就被称为切入点
通知(Advice)
通知是指一个切面在<font color="#e74f4c">特定的连接点</font>要做的事情(增强的功能)。<br>通知分为方法<font color="#e74f4c">执行前通知</font>,<font color="#e74f4c">方法执行后通知</font>,<font color="#e74f4c">环绕通知</font>等.<br>
切面(Aspect)
把<font color="#e74f4c">通知添加到切入点</font>的整个过程称为切面<br>
目标(Target)<br>
代理的目标对象(连接点,切入点所在类)
代理(Proxy)<br>
向目标对象应用通知时创建的代理对象<br>
AspectJ
基于 aspectj 的 xml 配置方式实现
<br>
基于注解方式的实现
启动 AspectJ 支持:<font color="#e74f4c"><aop:aspectj-autoproxy /></font> <!--自动代理-->
通知类型<br>
前置通知
@Before
后置通知
@After<br>
环绕通知
@Around<br>
多个切面的情况下,可以通过<font color="#e74f4c"> @Order 指定先后顺序,数字越小,优先级越高</font>
异常通知
@AfterThrowing<br>
返回通知<br>
@AfterReturning<br>
动态代理
jdk动态代理
基于接口,Java的Proxy类和InvocationHandler接口是JDK动态代理的核心,代理对象实现了目标接口,<br>通过InvocationHandler来拦截对目标方法的调用。<br>
Interface<br>
对于 JDK 动态代理,目标类需要至少需要实现一个Interface(接口)<br>
InvocationHandler
InvocationHandler是一个接口,可以通过实现这个接口,定义横切逻辑,再通过<font color="#e74f4c">反射机制(invoke)</font>调用目标类的代码,<br>在次过程,可能包装逻辑,对目标方法进行前置后置处理<br>
Proxy
Proxy利用InvocationHandler动态创建一个符合目标类实现的接口的实例,生成目标类的代理对象<br>
Proxy.newProxyInstance
优缺点
优点
无需引用第三方库,在JRE运行环境中就可以运行,生成代理对象更加简单、快捷
缺点
仅支持基于接口进行代理,无法对类进行代理,所以它的作用有限<br>
cglib动态代理<br>
基于继承,CgLib 动态代理是使用<font color="#e74f4c">字节码处理框架 ASM</font>,其原理是通过<font color="#e74f4c">字节码技术为一个类创建子类</font>,<br>并在子类中采用<font color="#e74f4c">方法拦截的技术</font>拦<font color="#e74f4c">截所有父类方法的调用,顺势织入横切逻辑</font>。<br>
实现MethodInterceptor接口重写intercept<br>利用第三方Enhancer工具类创建目标子类代理对象<br>
CGLib 由于是采用<font color="#e74f4c">动态创建子类的方法</font>,对于 <font color="#e74f4c">final 方法</font>,无法进行代理。
优缺点
优点
Cglib支持对类进行代理,即使没有接口,也可通过设置回调接口间接地实现。<br>性能比JDK动态代理更高,能够代理那些没有实现任何接口的目标对象<br>
缺点
对比
实现方式
JDK 动态代理是通过<font color="#e74f4c">反射</font>实现的,而CGLIB动态代理是通过<font color="#e74f4c">继承目标类</font>来实现的
目标类限制
JDK 动态代理要求目标类<font color="#e74f4c">必须要实现接口</font>,而CGLIB动态代理则没有这个限制<br>
性能
JDK 动态代理相对于 CGLIB 动态代理来说,因为实现方式不同,生成的代理类的效率会低一些<br>
对象类型
JDK 动态代理只能代理实现了接口的类,CGLIB 通过继承实现,不能代理 final 类<br>
依赖库
JDK 动态代理是 Java 自带的库,不需要额外的依赖,而 CGLIB 动态代理需要依赖 cglib 库<br>
<b><font color="#e74f4c">事务传播行为</font></b>
事务传播行为(propagation behavior)指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行。<br>事务传播行为是 Spring 框架<font color="#e74f4c">独有的事务增强特性</font>,他不属于的事务实际提供方数据库行为<br>
七种传播行为
PROPAGATION_REQUIRED<br>
默认的,如果当前没有事务,就<font color="#e74f4c">新建一个事务</font>,如果已经存在一个事务中,<font color="#e74f4c"> 加入到这个事务</font>中<br>
PROPAGATION_SUPPORTS
支持当前事务,如果当前没有事务,就<font color="#e74f4c">以非事务方式执行</font><br>
PROPAGATION_MANDATORY<br>
使用当前的事务,如果当前没有事务,<font color="#e74f4c">就抛出异常</font>
PROPAGATION_REQUIRES_NEW
<font color="#e74f4c">新建事务</font>,如果当前存在事务,把<font color="#e74f4c">当前事务挂起</font><br>
PROPAGATION_NOT_SUPPORTED
以<font color="#e74f4c">非事务方式</font>执行操作,如果当前存在事务,就把<font color="#e74f4c">当前事务挂起</font><br>
PROPAGATION_NEVER
以<font color="#e74f4c">非事务方式</font>执行,如果当前存在事务,则<font color="#e74f4c">抛出异常</font><br>
PROPAGATION_NESTED
如果当前存在事务,则在<font color="#e74f4c">嵌套事务内</font>执行。<br>如果当前没有事务, 则执行<font color="#e74f4c">与 PROPAGATION_REQUIRED 类似</font>的操作。<br>
Spring Bean生命周期<br>
实例化(Instantiation)
通过<font color="#e74f4c">反射调用</font>构造方法实例化对象<br>
属性赋值(Populate)
为 Bean 设置相关属性和依赖<br>
初始化(Initialization)
初始化前回调<br>
初始化后回调<br>
Bean可用
销毁(Destruction)
销毁前回调
生命周期图
主要步骤
实例化 Bean<br>
通过反射调用构造方法实例化对象
依赖注入<br>
装配 Bean 的属性<br>
实现了<font color="#e74f4c"> Aware接口</font>的 Bean,执行接口方法
如<font color="#e74f4c">顺序</font>执行 <font color="#e74f4c">BeanNameAware、BeanClassLoaderAware</font>、<font color="#e74f4c">BeanFactoryAware、ApplicationContextAware</font>的接口方法。<br>
Bean 对象初始化前
<font color="#e74f4c">循环调用</font>实现了<font color="#e74f4c"> BeanPostProcessor 接口</font>的预初始化方法(<font color="#e74f4c">postProcessBeforeInitialization</font>)
Bean 对象初始化
<font color="#e74f4c">顺序</font>执行<font color="#e74f4c"> @PostConstruct</font> 注解方法、<font color="#e74f4c">InitializingBean 接口</font>方法、<font color="#e74f4c">init-method 方法</font>
Bean 对象初始化后
循环调用实现了 BeanPostProcessor 接口的后初始化方法<font color="#e74f4c">(postProcessAfterInitialization)</font>
容器关闭时
执行 Bean 对象的<font color="#e74f4c">销毁方法</font>,<font color="#e74f4c">顺序</font>是:<font color="#e74f4c">@PreDestroy 注解</font>方法、<font color="#e74f4c">DisposableBean 接口</font>方法、<font color="#e74f4c">destroy-method</font>
自动装配类型
byName<br>
根据名称进行自动匹配,假设Boss又一个名为car的属性,如果容器中刚好有一个名为car的bean,Spring就会自动将其装配给Boss的car属性<br>
byType
根据类型进行自动匹配,假设Boss有一个Car类型的属性,如果容器中刚好有一个Car类型的Bean,Spring就会自动将其装配给Boss这个属性<br>
constructor
与 byType类似, 只不过它是针对构造函数注入而言的。如果Boss有一个构造函数,构造函数包含一个Car类型的入参,<br>如果容器中有一个Car类型的Bean,则Spring将自动把这个Bean作为Boss构造函数的入参;<br>如果容器中没有找到和构造函数入参匹配类型的Bean,则Spring将抛出异常。<br>
autodetect
根据Bean的自省机制决定采用byType还是constructor进行自动装配,如果Bean提供了默认的构造函数,则采用byType,否则采用constructor。<br>
0 条评论
下一页