Spring
1、概述:
Spring是最受欢迎的轻量级的企业级Java应用程序开发框架;Spring框架的 核心特性可以用于开发任何Java应用程序,但是在JavaEE平台上构建Web应 用程序是需要扩展的。Spring框架的目标是使J2EE开发变得更容易使用,通 过启用基于POJO编程模型来促进良好的编程实践
2、使用Spring框架的核心优势:
轻量级的IOC容器
一致的事物管理
面向切面编程(AOP)
3、核心概念:
依赖注入(DI):
当编写一个复杂的Java应用程序时,应用程序类应该尽可能的独立于其他的Java类来增加这些类可重用可能性,当进行单元测试时,可以使他们独立于其他类进行测试。依赖注入(或者有时被称为配线)有助于将这些类粘合在一起,并且在同一时间让他们保持独立。依赖注入实现的3种方式:构造函数注入,setting方法注入、注解注入
面向切面的程序设计(AOP):
一个程序中跨越多个点的功能被称为横切关注点,这些横切关注点在概念上独 立于应用程序的业务逻辑,有各种各样常见的很好的关于方面的例子,比如日志记录、声明性事物、安全性和缓存等等。在OOP中模块化的关键单元是类,而在AOP中模块化的关键单元是方面。AOP帮助你将横切关注点从他们所影响的对象中分离出来,然而依赖注入帮助你将你的应用程序对象从彼此中分离出来。Spring框架的AOP模块提供了面向对象的程序设计实现,允许你定义拦截器方法和切入点,可以实现将应该被分开的代码干净的分开功能
4、Spring的体系结构:
核心容器由核心,Bean上下文和表达式语言模块组成,
他们包括:
1、核心模块提供了框架的基本组成部分,包括IOC和依赖注入功能
2、Bean模块提供BeanFactory,他是一个工厂模式的复杂实现
3、上下文模块建立在由核心和Bean模块提供的坚实基础上,他是访问定义和配置的任何对象的媒介。ApplicationContext接口是上下文模块的重点
4、表达式语言模块在运行时提供了查询和操作一个对象图的强大的表达式语言
核心容器=核心+Bean+上下文+表达式语言
Container(实现)容器有两种方式:
BeanFactory(Bean工厂)、ApplicationContext(上下文应用)
ApplicationContext context=new ClassPathXmlApplicationContext(“路径”);
ApplicationContext context=new FileSystemXmlApplicationContext(“路径”);
XmlBeanFactory bean=new XmlBeanFactory(new ClassPathResource(“路径”));
定义Bean的属性以及作用域
1、定义Bean的属性:
属性 | 描述 |
---|---|
Class | 这个属性是强制性的,并且指定用来创建 bean 的 bean 类 |
Name | 这个属性指定唯一的 bean 标识符。在基于 XML 的配置元数据中,你可以使用 ID 和/或 name 属性来指定 bean 标识符。 |
Scope | 这个属性指定由特定的 bean 定义创建的对象的作用域 |
Constructor-arg | 它是用来注入依赖关系的 |
Properties | 它是用来注入依赖关系的 |
Autowiring mode | 它是用来注入依赖关系的 |
Lazy-initialization mode | 延迟初始化的 bean 告诉 IoC 容器在它第一次被请求时,而不是在启动时去创建一个 bean 实例。 |
Initialization方法 | 在 bean 的所有必需的属性被容器设置之后,调用回调方法。 |
destruction方法 | 当包含该 bean 的容器被销毁时,使用回调方法。 |
2、Bean的作用域:
当在Spring中定义一个bean时,你必须声明该bean作用域的选项,例如, 为了强制Spring在每次需要时都产生一个新的bean实例,你应该声明bean的作用域的属性为prototype,同理,如果你想让Spring在每次需要时都返回同一个bean实例,你应该声明bean的作用域属性为singleton
定义Bean的作用域:
作用域 | 描述 |
---|---|
Singleton | 该作用域将 bean 的定义的限制在每一个 Spring IoC 容器中的一个单一实例(默认)。 |
Prototype | 该作用域将单一 bean 的定义限制在任意数量的对象实例。 |
Request | 该作用域将 bean 的定义限制为 HTTP 请求。只在 web-aware Spring ApplicationContext 的上下文中有效。 |
Session | 该作用域将 bean 的定义限制为 HTTP 会话。 只在web-aware Spring ApplicationContext的上下文中有效。 |
Global-session | 该作用域将 bean 的定义限制为全局 HTTP 会话。只在 web-aware Spring ApplicationContext 的上下文中有效。 |
Spring依赖注入
类型:
Constructor-based dependency injection 当容器调用带有多个参数的构造函数类时,实现基于构造函数的DI,每个代表在其他类中的一个依赖关系
Setter-based dependency injection 基于setter方法的DI是通过在调用无 参数的构造函数,或无参数的静态工厂方法,实例化bean之后,容器调用beans的 setter方法
Bean的生命周期与继承
1.生命周期的概述:
当一个bean被实例化时,他可能需要执行一些初始化使他转换成可用状态,同样,当bean不在需要,并且从容器中移除时,可能需要做一些清除工作,这样状态转换的过程就是bean的生命周期,生命周期用代码体现的两种方式分别为代码方法实现和XML配置文件实现,由于配置文件实现方式更加灵活,所以我们更多采取配置文件
2.继承的概述:
Bean定义可以包含很多的配置信息,包括构造函数的参数,属性值,容器的具体信息例如初始化方法,静态工厂方法名,等等。子bean的定义继承父定义的配置数据。子bean定义可以根据需要重写一些值,或者添加其他值。Spring Bean定义的继承与Java类的继承无关,但是继承的概念是一样的,你可以定义一个父bean的定义作为模板和其他字bean就可以从父bean中继承所需的配置,但你使用基于XML的配置元数据时,通过使用父属性,指定父bean作为该属性的值来表明字bean的定义
3.补充说明:
定义一个bean时,如果用abstract=”true”属性的话则表明该bean自身不能 实例化,仅仅只是作为一个纯粹的模板bean定义来使用的,充当子定义的父定义使用
Spring容器
1、Spring容器的基本介绍:
Spring容器是Spring框架的核心,容器将创建对象,把他们连接在一起,配置他们,并管理他们的整个生命周期从创建到销毁。Spring容器使用依赖注入(DI)来管理组成一个应用程序的组件,通过阅读配置元数据提供的指令,容器知道对那些对象进行实例化,配置和组装。配置元数据可以通过XML,Java注释或Java代码来表示
2、Spring容器的类型:
Spring BeanFactory容器
Spring ApplicationContext容器
Spring自动装配
1、基本介绍:
前面我们已经学习了通过元素来声明bean,和XML配置文件中的和元素来注入bean,其实Spring容器可以在不使用注入和元素的情况下自动装配相互协作的bean之间的关系,这有助于减少编写一个大的基于Spring的应用程序的XML配置的数量
2、自动装配模式:
① 用于指示Spring容器为来使用自动装配进行依赖注入,使用元素的autowire属性为一个bean定义指定自动装配模式
② 模式表
模式名称 | 描述 |
---|---|
No | 默认的设置,他意味着没有自动装配 |
byName | 由属性名自动装配,Spring容器看到在XML配置文件中bean的自动装配的属性设置为byName,然后尝试匹配,并且将他的属性与在配置文件中被定义为相同名称的beans的属性进行连接 |
byType | 由属性数据类型自动装配。Spring容器看到在XML配置文件中bean的自动配置的属性设置为byType,然后如果他的类型匹配配置文件中的一个确切的bean名称,他将尝试匹配和连接属性的类型,如果存在不止一个这样的bean,则一个致命的异常将会被抛出 |
Constructor | 类似于byType,但该类型适用于构造函数参数类型,如果在容器中没有一个构造函数参数类型的bean,则一个致命的错误将会发生 |
autodetect | Spring首先尝试通过constructor使用自动装配来连接,如果他不执行,Spring尝试通过byType来自动装配 |
3、自动装配的局限性:
重写的可能性:可以使用总是重写自动装配的和设置来指定依赖关系
原始数据类型:不能自动装配所谓的简单类型包括基本类型
混乱的本质:自动装配不如显式装配精确,所以如果可能的话尽可能使用显式装配
注解注入
1、概述:
从Spring2.5开始就可以使用注解来配置依赖注入,而不是采用XML来描述一个bean连线,你可以使用相关类,方法或字段声明的注解,将bean配置移动到组件类本身,在XML注入之前进行注解注入,因此后者的配置将通过两种方式的属性连线被前者重写。注解连线在默认情况下载Spring容器中打不开,因此,在可以使用基于注解的连线之前,我们将需要在我们的Spring配置文件中启动它使用
<context:annotation-config/>来启动
2、几大重要注解:
名称 | 描述 |
---|---|
@Required | @Required注解应用于bean属性的setter方法 |
@Autowired | @Autowired注解可以应用到bean属性的setter方法,非setter方法,构造函数和属性 |
@Qualifier | 通过指定确切的将被连线的bean,@Autowired和@Qualifier注解可以用来删除混乱 |
JSR-250 Annotations | Spring支持JSR-250的基础的注解,其中包括了@Resource,@PostConstruct和@PreDestroy注解 |
① @Required注解:
@Required注解应用于bean属性的setter方法,它表明受影响的bean属性 在配置时必须放在XML配置文件中,否则容器就会抛出一个 BeanlnitializationException异常(比如有两个值,你只给他一个值那么他 就会出现异常),@Required注解在bean里面要放到set方法上面,而不是放 到get方法上面
② @Autowired注释:
@Autowired注释可以在setter方法中被用于自动连接bean,就像 @Autowired注释,容器,一个属性或者任意命名的可能带有多个参数的方法。 当Spring遇到一个setter方法中使用的@Autowired注释,他会在方法中试 图执行 byType自动连接
③ @Qualifier注释:
可能会有这样一种情况,当你创建多个具有相同类型的bean时,并且想要用 一个属性只为他们其中的一个进行装配,在这种情况下,使用@Qualifier注 释和@Autowired注释通过指定哪一个真正的bean将会被装配来消除混乱
④ @Resource注释:
在字段中或者setter方法中使用@Resource注释,@Resource注释使用一个
’name’属性,该属性以一个bean名称的形式被注入
⑤ @Configuration注解和@Bean注解:
带有@Configuration的注解类表示这个类可以使用Spring loC容器作为bean 定义的来源。@Bean注解告诉Spring,一个带有@Bean的注解方法将返回一 个对象,该对象应该被注册为在Spring应用程序上下文中的bean
将不使用XML配置文件,而是直接采用注解
Spring事件处理
1、概述:
在我们之前的例子中,我们都是使用ApplicationContext,该容器负责管理bean 的生命周期,其实,当容器加载管理的bean时会伴随着一系列的事件发生和处理, 例如启动、停止、监听等
专业词汇早识:
ContextStartedEvent:容器启动
ContextStoppedEvent:容器停止
ApplicationEvent:容器事件
ApplicationListener:容器事件监听
2、Spring内置标准事件:
名字 | 描述 |
---|---|
ContextRefreshedEvent | ApplicationContext被初始化或刷新时,该事件被发布,这也可以在ConfigurableApplicationContext接口中使用,refresh()方法来发生 |
ContextStartedEvent | 当使用ConfigurableApplicationContext接口中的start()方法启动ApplicationContext时,该事件发布,你可以调查你的数据库,或者你可以在接受到这个事件后重启任何停止的应用程序 |
ContextStoppedEvent | 当使用ConfigurableApplicationContext接口中的stop()方法停止ApplicationContext时,发布这个事件,你可以在接受到这个事件后做必要的清理的工作 |
ContextClosedEvent | 当使用ConfigurableApplicationContext接口中的close()方法关闭ApplicationContext时,该事件被发布,一个已关闭的上下文到达生命周期末端,它不能被刷新或重启 |
RequestHandledEvent | 这是一个Web-specific事件,告诉所有bean HTTP 请求已经被服务 |
3、特别注意:
Spring的事件处理是单线程的,所以如果一个事件被发布,直至并且除非所有的接收者得到的该消息,该进程被阻塞并且流程将不会继续
Spring AOP
1、概述:
Spring框架的一个关键组件是面向切面的编程(AOP)框架,面向切面的编程需要把程序分解成不同的部分称为所谓的关注点。跨一个应用程序的多个点的功能被称为横切关注点,这些横切关注点在概念上独立于应用程序的业务逻辑,有各种各样的常见的很好的切面的例子,如日志记录、审计、声明式事务、安全性和缓存等。在OOP中,关键单元模块是类,而在AOP中关键单元模块是切面。依赖注入帮助你对应用程序对象相互解耦和AOP可以帮助你从它们所影响的对象中对横切关注点解耦
2、AOP相关专业词汇:
Aspect:一个模块具有一组提供横切需求的APIs。应用程序可以拥有任意数量的切面,这取决于需求
Join point:在应用程序中它代表一个切入点
Advice:这是实际行动之前或之后执行的方法
Pointcut:这是一组一个或多个连接点,通知应该被执行,你可以使用表 达式或模式指定切入点
Introduction:引用允许你添加新方法或属性到现有的类中
Target Object:被一个或者多个方面所通知的对象,这个对象永远是一个被代理对象,也称为被通知对象
Weaving:把方面连接到其它的应用程序类型或者对象上,并创建一个被通知的对象
3、Spring切面可使用的5种通知类型:
前置通知:在一个方法执行之前,执行通知
后置通知:在一个方法执行之后,不考虑其结果,执行通知
返回后通知:在一个方法执行之后,只有在一个方法成功完成时,才能执行通知
抛出异常通知:在一个方法执行之后,只有在方法退出抛出异常时,才能执行通知
环绕通知:在建议方法调用之前和之后,执行通知
Spring支持自定义切面的两种方式:
@AspectJ annotation style的方法(注解方式)
基于模式的方法(XML配置方式)
Spring事务管理
1、概述:
事务(Transaction)是访问并可能更新数据库中各种数据项的一个程序执行 单元(unit).这些操作应该要么完整的执行,要么完全不执行。一确保数据 完整性和一致性
2、事务的4个关键特点:
原子性(atom):事务应该当作一个单独单元的操作,这意味着整个序列操作要么是成功,要么是失败的
一致性(consistency):这表示数据库的引用完整性的一致性,表中唯一的主键等
隔离性(isolation):可能同时处理很多有相同的数据集的事务,每个事务应该与隔离其他事务,以防止数据损坏
持久性(durable):一个事务一旦完成全部操作后,这个事务的结果必须是永久性的,不能因系统故障而从数据库中删除
总结:ACID
3、使用SQL发布事务到数据库系统的过程:
① 使用begin transaction命令开始事务
② 使用SQL查询语句执行各种删除、更新或插入的操作
③ 如果所有的操作都成功,则执行提交操作,否则回滚所有操作
4、Spring事务管理类型:
按作用范围分为局部事务和全局事务:
局部事务:局部事务是特定于一个单一的事务资源,如一个JDBC连接, 局部事务管理在一个集中的计算环境中是有用的,该计算环境中 应用程序组件和资源位于一个单位点,而事务管理只涉及到运行 在一个单一机器中的本地数据管理器,局部事务更容易实现
全局事务:全局事务可以跨多个事务资源,如在一个分布式系统中的事务。 全局事务管理需要在分布式计算环境中,所有的资源都分布在多 个系统中。在这种情况下事务管理需要同时在局部和全局范围内 进行。分布式或全局事务跨多个系统执行,它的执行需要全局事 务管理系统和所有相关系统的局部数据管理人员之间的协调
按实现方式分为声明式事务和编程式事务:
声明式事务:从业务代码中分离出来,仅关注XML配置来管理事务
编程式事务:灵活但难以维护
补充:声明式事务管理比编程式事务管理更可取,尽管它不如编程式事务 管理灵活,但它允许你通过代码控制事务,但作为一种横切关注点, 声明式事务管理可以使用AOP方法进行模块化。Spring支持使用 Spring AOP框架的声明式事务管理
5、事务操作的重要方法:
名称 | 说明 |
---|---|
TransactionStatus getTransaction(TransactionDefinition definition) | 根据指定的传播行为,该方法返回当前活动事务或创建一个新的事务。 |
void commit(TransactionStatus status) | 该方法提交给定的事务和关于它的状态。 |
void rollback(TransactionStatus status) | 该方法执行一个给定事务的回滚。 |
int getPropagationBehavior() | 该方法返回传播行为。Spring 提供了与 EJB CMT 类似的所有的事务传播选项。 |
int getIsolationLevel() | 该方法返回该事务独立于其他事务的工作的程度。 |
String getName() | 该方法返回该事务的名称。 |
int getTimeout() | 该方法返回以秒为单位的时间间隔,事务必须在该时间间隔内完成。 |
boolean isReadOnly() | 该方法返回该事务是否是只读的。 |
boolean hasSavepoint() | 该方法返回该事务内部是否有一个保存点,也就是说,基于一个保存点已经创建了嵌套事务。 |
boolean isCompleted() | 该方法返回该事务是否完成,也就是说,它是否已经提交或回滚。 |
boolean isNewTransaction() | 在当前事务时新的情况下,该方法返回 true。 |
boolean isRollbackOnly() | 该方法返回该事务是否已标记为 rollback-only。 |
void setRollbackOnly() | 该方法设置该事务为 rollback-only 标记。 |
6、并发下事务可能会产生的问题:(重要)
① 脏读:所谓脏读,就是指事务A读到了事务B还没有提交的数据,比如银行取钱,事务A开启事务,此时切换到事务B,事务B开启事务–>取走 100元,此时切换回事务A,事务A读取的肯定是数据库里面的原始数据,因为事务B取走了100块钱;并没有提交,数据库里面的账务余额肯定还是原始余额,这就是脏读
② 不可重复读:是指在一个事务里面读取了两次某个数据,读出来的数据不一致。还是以银行取钱为例,事务A开启事务–>查出银行卡余额为1000 元,此时切换到事务B,事务B开启事务–>事务B取走了100元–>提交,数据库里面余额变为900元,此时切换回事务A,事务A再查一次账户余额为900元,这样对事务A而言,在同一个事务内两次读取账户余额数据不一致,这就是不可重复读
③ 幻读:是指在一个事务里面的操作中发现了未被操作的数据。比如学生信息, 事务A开启了事务–>修改所有学生当天签到状况为false,此时切换 到事务B,事务B开启了事务–>事务B插入了一条学生数据,此时切换回事务A,事务A提交的时候发现了一条自己没有修改过的数据,这就是幻读,就好像发生了幻觉一样,幻读出现的前提是并发的事务中有事务发生了插入、删除操作
7、事务的隔离级别:
事务隔离级别,就是为了解决并发情况下产生的脏读、幻读、不可重复读的问题而诞生的,为什么要有事务隔离级别,因为事务隔离级别越高,在并发下会产生的问题就越少,但同时付出的性能消耗也将越大,因此很多时候必须在并发性和性能之间做一个权衡。所以设立了几种事务隔离级别,以便让不同的项目可以根据自己项目的并发情况选择合适的事务隔离级别,对于在事务隔离级别之外会产生的并发问题,在代码中做补偿
事务的5级隔离级别:
Transaction Definition.ISOLATION_DEFAULT:
这是默认的隔离级别
Transaction Definition.ISOLATION_READ_COMMITTED:
表明能够阻止误读;可以发生不可重复读和虚读
Transaction Definition.ISOLATION_READ_UNCOMMITTED:
表明可以发生误读;不可重复读和虚读
Transaction Definition.ISOLATION_REPEATABLE_READ:
表明能够阻止误读和不可重复读;可以发生虚读
Transaction Definition.ISOLATION_SERIALIZABLE:
表明能够阻止误读;不可重复读和虚读
补充:不是事务隔离级别设置得越高越好,事务隔离级别设置得越高,意味着势必要花手段去加锁用以保证事务的正确性,那么效率就要降低,因此实际开发中往往要在效率和并发正确性之间做一个取舍,一般情况下会设置为 READ_COMMITTED,此时避免了脏读,并发性也还不错
8、事务的传播行为:
事务的传播为规定了事务方法和事务嵌套调用时事务如何进行传播
8种类型:
名称 | 说明 |
---|---|
Transaction Definition.PROPAGATION_MANDATORY | 支持当前事务;如果不存在当前事务,则抛出一个异常 |
Transaction Definition.PROPAGATION_NESTED | 如果存在当前事务,则在一个嵌套的事务中执行 |
Transaction Definition.PROPAGTION_NEVER | 不支持当前事务;如果存在当前事务,则抛出一个异常 |
Transaction Definition.PROPAGTION_NOT_SUPPORTED | 不支持当前事务;而总是执行非事务性 |
Transaction Definition.PROPAGTION_REQUIRED | 支持当前事务;如果不存在事务,则创建一个新的事务 |
Transaction Definition.PROPAGTION_REQUIRES_NEW | 创建一个新事务;如果存在一个新事务,则把当前事务挂起 |
Transaction Definition.PROPAGTION_SUPPORTS | 支持当前事务;如果不存在,则执行非事务性 |
Transaction Definition.TIMEOUT_DEFAULT | 使用默认超时的底层事务系统,或者如果不支持超时则没有 |
Spring代理
1、概述:
代理(Proxy)是一种设计模式。提供了对目标对象另外的访问方式;即通过代理访问目标对象。这样好处是可以在目标对象实现的基础上,增强额外的功能操作。 (扩展目标对象的功能)
代理模式的关键点:代理对象和目标对象
2、代理的类型:
静态代理:静态代理,是指程序运行前就已经存在了代理类的字节码文件,代理类和被代理类的关系在运行前就已经确定
特点:
① 可以在不修改目标对象的功能前提下,对目标对象功能扩展
② 因为代理对象,需要与目标对象实现一样的接口,所以会有很多代理类(类 太多)
③ 一旦接口增加方法,目标对象和代理对象就要维护(维护麻烦)
④ 一个静态代理类只代理一个具体类。如果需要对实现了同一接口的不同具 体类作代理,静态代理需要为每一个具体类创建相应的代理类
动态代理:动态代理类的字节码是在程序运行期间的动态生成,所以不存在代 理类的字节码文件,代理类和被代理类的关系是在程序运行时确定的
JDK从1.3开始引入动态代理,可以通过java.lang.refiect.Proxy类的静态方法Proxy.newProxyInstance动态创建代理类和实例,并且由它动态创建出来的代理类都是Proxy类的子类
3、代理行为:
代理类往往会在代理对象业务逻辑前后增加一些功能性的行为,如使用事务或 者打印日志。把这些行为称之为代理行为
4、Spring AOP底层实现原理—代理模式:
Spring的AOP有JDK动态代理和cglib两种实现方式,JDK动态代理要求被 代理对象实现接口;
Cglib通过动态继承实现,因此不能代理被final修饰的类;JDK动态代理生成 代理对象速度比cglib快;
Cglib生成的代理对象比JDK动态代理生成的代理对象执行效率高
5、JDK代理和cglib代理对比:
字节码创建方式:JDK动态代理通过JVM实现代理类字节码的创建,cglib 通过ASM创建字节码
对被代理对象的要求:JDK动态代理要求被代理对象实现接口,cglib要求被代理对象未被final修饰
代理对象创建速度:JDK动态代理创建代理对象速度比cglib快
代理对象执行速度:cglib代理对象执行速度比JDK动态代理快