Spring优势
- 轻量级、非侵入
- IOC容器管理
- DI机制将对象之间的依赖关系交由框架处理,降低组件的耦合性
- 提供了AOP技术,将一些通用任务进行集中管理,从而提供更好的复用。
- 方便集成
- 丰富的功能
Spring的七大模块
- Spring Core:框架中最基础的部分,提供Ioc容器,对Bean进行管理
- Spring Context:基于bean,提供上下文信息,扩展出其他功能
- Spring DAO:提供JDBC的抽象层,可以消除冗长的JDBC编码,以及提供了声明式事务管理
- Spring ORM:提供常用的“对象/关系”映射APIs的集成层。包括JPA、JDO、Hibernate、MyBatis
- Spring AOP:提供面向切面的编程实现
- Spring Web:提供了基础的Web开发的上下文信息,可与其他Web进行集成
- Spring Web MVC:提供了Web应用的MVC全功能实现
什么是Spring Framework
Spring是一个轻量级的控制反转(Ioc)和面向切面(AOP)的非侵入式框架
Spring配置文件
Spring配置文件是XMl文件。该文件主要包括类信息。描述了这些类是如何配置以及相互引用的。
Spring IOC容器
IOC就是控制反转,创建对象的控制权的转移,IOC让对象的创建不用去new了,而是可以由Spring自动产生,使用java的反射机制,根据配置文件在运行时动态的去创建和管理对象,并调用对象的方法
-
注入方式
-
- 构造器注入
- Setter方法注入
- 注解注入
-
优点
-
- 最小化应用程序中的代码量
- 使得应用程序易于测试
- 以最小的影响和最少的侵入机制促进松耦合
- 支持即时的实例化和延迟加载服务
依赖注入(DI)
在依赖注入中,不必创建对象,但必须描述如何创建它们。不用直接在代码中将组件和服务连接在一起,而是描述配置文件中哪些组件需要哪些服务。由 IoC 容器将它们装配在一起。
依赖注入的方式
- 构造函数注入
- Setter注入
- 接口注入
构造函数注入和setter注入
构造函数注入:
- 没有部分注入
- 不会覆盖setter属性
- 任何修改都会创建一个新实例
- 适用于设置很多属性
setter方法注入
- 可以部分注入
- 会覆盖setter属性
- 任意修改不会创建一个新实例
- 适用于设置少量属性
BeanFactory和ApplicationContext
-
BeanFactory;
-
- 使用懒加载
- 使用语法显示提供资源对象
- 不支持国际化
- 不支持基于依赖的注解
-
ApplicationContext
-
- 使用即时加载
- 自己创建和管理资源对象
- 支持国际化
- 支持基于依赖的注解
什么是Spring bean
- 是构成用户应用程序主干的对象
- Bean由Spring IOC容器管理
- 有Spring IoC容器实例化,配置,装配和管理
- Bean是基于用户提供给容器的配置元数据创建
Spring的配置方式
-
基于XML的配置Bean所需的依赖项和服务在XML格式的配置文件中指定
-
基于注解的配置,可以通过相关的类,方法或字段声明上使用注解,将bean配置为组件类本身,而不是使用XML来描述bean配置。
-
基于Java API配置
-
- 通过@Bean和@Configuration来实现
-
-
- @Bean注解与元素相同的角色
- @Configuration类允许通过简单地调用同一个类中的其他@Bean方法来定义bean之间的依赖关系
-
Spring Bean的作用域
- singleton(单例模式):仅存在一个Bean实例,默认实现
- prototypr(原型模式):每次调用Bean都会返回一个新的实例
- request(HTTP请求):每次HTTP请求会创建一个新的Bean
- session(会话请求):同一个HTTP Session贡献一个Bean
- globalSession(全局会话):一般用于Portlet环境。
Spring Bean的生命周期
总体来说就是实例化Bean、利用依赖注入设置对象属性、处理Aware接口、BeanPostProcessor前置方法,初始化方法、BeanPostProcessor的后置方法、DisposableBean、destroy
- 实例化Bean对象:Spring 容器根据配置中的 bean 定义中实例化 bean。
- 设置Bean属性:Spring 使用依赖注入填充所有属性,如 bean 中所定义的配置。
- 如果通过Aware接口声明了依赖关系,则会注入Bean对容器基础设施层面的依赖:BeanNameAware(Bean ID)、BeanFactoryAware(Bean Factory)和ApplicationContextAware(ApplicationContext)
- 调用BeanPostProcessors的前置初始化方法preProcessBeforeInitialization() 。
- 如果为 bean 指定了 init 方法( 的 init-method 属性),那么将调 用它。
- 最后,如果存在与 bean 关联的任何 BeanPostProcessors,则将调用 postProcessAfterInitialization() 方法。
- 如果 bean 实现DisposableBean 接口,当 spring 容器关闭时,会调用 destory()。
- 如果为bean 指定了 destroy 方法( 的 destroy-method 属性),那么将 调用它。
自动装配
-
自动装配方式
-
- no:默认设置,表明没有自动装配。使用显式bean引用进行装配
- byName:根据bean的名称注入对象依赖项
- byType:根据类型注入对象依赖项
- autodetect:首先容器尝试通过构造函数使用aytowire装配,如果不能,则尝试通过bytype自动装配
-
自动装配局限
-
- 重写:始终可以通过 和 设置指定依赖项,这将覆盖自动装配。
- 基本数据类型:简单属性(如原数据类型,字符串和类)无法自动装配。
- 模糊特性:自动装配不如显式装配精确,如果有可能,建议使用显式装配。
常见注解
@Component:
将这个java标记为bean。是任何Spring管理组件的通用构造型。spring的组件扫描机制可以将其拾取并将其拉入应用程序环境中
@Controller:web层
标有它的Bean 会自动导入到 IoC 容器中
@Service:service层
此注解是组件注解的特化
@Repository:dao层
此注解是组件注解的特化
@Service
@Required
应用于bean属性setter方法。此注解仅指示必须在配置时使用bean 定义中的显式属性值或使用自动装配填充受影响的 bean 属性。如果尚未填充受影响的 bean 属性,则容器将抛出 BeanInitializationException。
@Autowired
可以更准确地控制应该在何处以及如何进行自动装配
@Qualifier
当您创建多个相同类型的 bean 并希望仅使用属性装配其中一个 bean 时,您可以使用其消除歧义
@RequestMapping
用于将特定 HTTP 请求方法映射到将处理相应请求的控制器中的特定类/方法。此注释可应用于两个级别:类级别:映射请求的 URL 方法级别:映射 URL 以及 HTTP 请求方法
使用Spring访问Hibernate的方法
- 使用 Hibernate 模板和回调进行控制反转
- 扩展 HibernateDAOSupport 并应用 AOP 拦截器节点
Spring支持的事务管理类型
Spring事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,Spring是无法提供事务功能的。真正的数据库层的事务提交和回归是通过binlog或者redo log实现的。
- 程序化事务管理:在此过程中,在编程的帮助下管理事务。它为您提供极大的灵活性,但维护起来非常困难。
- 声明式事务管理:在此,事务管理与业务代码分离。仅使用注解或基于 XML的配置来管理事务。
什么是AOP
面向切面编程, 它与OOP( 面向对象编程) 相辅相成, 提供了与OOP 不同的抽象软件结构的视角。用于将那些与业务无关,但对多个对象产生影响的公共行为和逻辑,抽象并封装为一个可重用的模块。
Aspect(切面)
被抽象的公共模块。可能会横切多个对象。在Spring AOP中,切面可以使用通用类或者在普通类中以@AspectJ注解来实现
连接点(JoinPoint)
程序运行中的一些时间点,在Spring AOP中,一个连接点总是代表一个方法的执行
通知(Advice)
在切面的某个特定的连接点上执行动作,并且通常以拦截器作为通知模型,并维护一个以连接点为中心的拦截器链。
切入点(Pointcut)
切入点是指我们要对那些连接点进行拦截的定义。通过切入点表达式,指定拦截的方式
引入(Introduction)
声明额外的方法或者某个类型的字段。Spring允许引入新的接口道任何被代理的对象。
目标对象(Target Object)
被一个或者多个切面所通知的对象。
织入(Weaving)
指把增强应用到目标对象来创建新的代理对象的过程。Spring是在允许时完成织入的。
Spring AOP中有哪些类型的通知(Advice)
- 前置通知Before:这些类型的Advice在joinpoint方法之前执行
- 返回后通知After Returning:这些类型的Advice在连接点方法正常执行后执行
- 跑出异常后通知After Throwing:这个类型的Advice仅在joinpoint方法通过排除异常退出
- 后通知After(finally):这些类型的Advice在连接点方法以后执行,无论方法退出是正常还是异常返回。
- 环绕通知3
- Around:这些类型的Advice在连接点之前和之后执行。
Spring AOP中concern和cross-cuttingconcern不同之处
concern
我们想要在应用程序的特定模块中定义的行为。它可以定义为我们想要实现的功能
cross-cuttingconcern
是一个适用于整个应用的行为,这会影响整个应用程序。例如,日志记录,安全性和数据传输是应用程序几乎每个模块都需要关注的问题,因此它们是跨领域的问题。
Spring中用到哪些设计模式
- 工厂模式:BeanFactory就是简单工厂模式,用来创建对象的实例
- 单例模式:Bean默认为单例模式
- 代理模式:AOP功能中用到了JDK的动态代理
- 模板方式:主要是一些对数据库操作的类,
- 观察者模式:定义对象键一种一对多的依赖关系
Spring AOP和AspectJ AOP有什么区别
Spring AOP 是基于动态代理实现的,属于运行时增强,不需要单独编译,性能差一些
- 默认如果使用接口的话使用JDK提供的动态代理实现,如果是方法则使用CGLIB实现
AspectJ则属于编译时增强,主要有三种方法:
- 编译时织入:指的是增强的代码和源代码我们都有,直接使用AspectJ编译器编译即可,编译以后生成一个新的类,他会作为一个正常的Java类装载在JVM中
- 编译后织入:指的是代码已经被编译成Class文件或者打包成jar包,这个时候要增强的话,就是编译后织入,比如当你依赖第三方的类库时,又需要对其增强的话,就可以使用这种方式
- 加载时织入:指的是在JVM加载类的时候进行织入
JDK动态代理和CGLIB代理有什么区别
-
JKD动态代理、
-
- JDK动态代理实现了InvocationHandler接口,重写的invoke方法
- 其基础是反射机制
-
CGlib
-
- 使用字节码处理框架ASM,原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势注入横切逻辑。
CGLib性能较高,但是在创建代理对象时花费时间较多,所以在单例模式下更加有优势
CGLib采用的是动态创建子类的方法,所以对于final方法无法代理
JDK的动态代理只可以为接口去完成操作,而CGlib即可以为没有实现的接口的类做代理,也可以为实现接口的类去做代理
Spring是如何解决循环依赖
解决循环依赖有两个前提条件
- 不全是构造器方式的循环依赖
- 必须是单例模式
基于上面的问题,我们知道Bean的生命周期,本质上解决循环依赖的问题就是三级缓存,通过三级缓存提前拿到未初始化的对象
三级缓存
- 一级缓存:用来保存实例化、初始化都完成的对象
- 二级缓存:用来保存实例化完成,但未初始化完成的对象
- 三级缓存:用来保存一个对象工厂,提供一个匿名内部类,用于创建二级缓存中的对象
AB互相依赖问题
A对象的创建过程:
- 创建A对象,实例化的时候把A对象工厂放入三级缓存
- A注入属性时,发现依赖B,转而去实例化B
- 同样创建对象B,注入属性时发现依赖A,依次从一级到三级缓存中查询A,从三级缓存中通过对象工厂拿到A,把A放入到二级缓存中,同时删除三级缓存中的A,此时B已经实例化并且初始化完成,把B放入一级缓存。
- 接着继续创建A,顺利从一级缓存拿到实例化且初始化完成的B对象,A对象创建也完成,删除二级缓存中的A,同时把A放入一级缓存中
- 最后一级缓存中保存着实例化、初始化都完成的A、B对象
因此由于把实例化和初始化的流程分开了,所以就解决了构造器不好解决的循环依赖
为什么要三级缓存,二级缓存不行吗
不可以,三级缓存的目的就是要生成代理对象。
因为三级缓存中放的是生成具体对象的匿名内部类,他可以生成代理对象,也可以是普通的实例对象。使用三级缓存是为了保证不管什么时候使用的都是一个对象。
@Component和@Bean的区别
-
作用对象不同
-
- @Component作用于类
- @Bean作用于方法
-
作用
-
- @Component通常是通过类路径来自动侦测以及自动装配到Spring容器中
- @Bean注解通常是我们在标有该注解的方法中定义这个bean,@Bean告诉了Spring这是个类的实例。
-
另外@Bean注解自定义性更强,很多地方比如第三方库中的类需要装配到spring容器中的时候只能通过@Bean实现
Spring事务
Spring管理事务的方式有几种
-
编程式事务,在代码中硬编码(不推荐使用)
-
声明式事务,在配置文件中配置(推荐使用)
-
- 基于XML的声明式事务
- 基于注解的声明式事务
Spring事务中的隔离级别有哪几种
TransactionDefinition接口中定义了五个表示隔离级别的常量。
- ISOLATION_DEFAULT: 后端数据库默认的隔离级别,Mysql 默认采用的 REPEATABLE_READ隔离级别 Oracle 默认采用的 READ_COMMITTED隔离级别.
- ISOLATION_READ_UNCOMMITTED: 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
- ISOLATION_READ_COMMITTED: 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
- ISOLATION_REPEATABLE_READ: 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
- ISOLATION_SERIALIZABLE: 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
Spring事务中有几种事务传播行为
支持当前事务的情况:
- PROPAGATION_REQUIRED: 如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
- PROPAGATION_SUPPORTS: 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
- PROPAGATION_MANDATORY: 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。(mandatory:强制性)
不支持当前事务的情况:
- PROPAGATION_REQUIRES_NEW: 创建一个新的事务,如果当前存在事务,则把当前事务挂起。
- PROPAGATION_NOT_SUPPORTED: 以非事务方式运行,如果当前存在事务,则把当前事务挂起。
- PROPAGATION_NEVER: 以非事务方式运行,如果当前存在事务,则抛出异常。
其他情况:
- PROPAGATION_NESTED: 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。
@Transaction注解
Java中的异常Exception分为运行时异常和非运行时异常。事务的管理对于企业应用来说是十分重要的,即使出现异常情况,可可以保证数据的一致性。
当@Transaction注解作用于类上时,该类的所有public方法都具有该类型的事务属性。同时也可以在方法级别使用该标注来覆盖类级别的定义。
类或者方法加了这个注解,那么这个类里面的方法抛出异常,就会回滚,数据库里面的数据也会回滚。
rollbackFor
如果在@Transaction注解中不配置rollbackFor属性,那么事务只会在遇到RuntimeException的时候才会回滚,加上rollbackFor=Exception.class,可以让事务在遇到非运行时异常时也回滚
Spring中单例Bean的线程安全问题
- 在Bean对象中尽量避免定义可变的成员变量
- 在类中定义一个ThreadLocal成员变量,将需要的可变成员变量保存在ThreadLocal中(推荐使用)
SpringMVC工作原理
- 客户端(浏览器)发送请求,直接请求到 DispatcherServlet。
- DispatcherServlet 根据请求信息调用 HandlerMapping,解析请求对应的 Handler。
- 解析到对应的 Handler(也就是我们平常说的 Controller 控制器)后,开始由 HandlerAdapter 适配器处理。
- HandlerAdapter 会根据 Handler来调用真正的处理器开处理请求,并处理相应的业务逻辑。
- 处理器处理完业务后,会返回一个 ModelAndView 对象,Model 是返回的数据对象,View 是个逻辑上的 View。
- ViewResolver 会根据逻辑 View 查找实际的 View。
- DispaterServlet 把返回的 Model 传给 View(视图渲染)。
- 把 View 返回给请求者(浏览器)
Spring MVC 中一个URL请求的执行过程
其本质就是:request是浏览器传过来的数据;response里面添加相应到浏览器的数据
-
根据请求URL找到相应的Servlet
-
- 在SpringMVC中采用通配的方式,即所有的请求都是到DispatcherServlet中
-
在DispatcherServlet的doGet中根据对应的URl找到相应的Controller
-
根据Controller返回值,通过渲染句柄找到对应的jsp进行处理,最终以字符串的方式返回给浏览器
最后
- 如果觉得看完有收获,希望能关注一下,顺便给我点个赞,这将会是我更新的最大动力,感谢各位的支持
- 欢迎各位关注我的公众号【java冢狐】,专注于java和计算机基础知识,保证让你看完有所收获,不信你打我
- 求一键三连:点赞、转发、在看。
- 如果看完有不同的意见或者建议,欢迎多多评论一起交流。感谢各位的支持以及厚爱。
——我是冢狐,和你一样热爱编程。