Spring--开源的轻量级的Java开发框架

tech2022-09-28  78

目录:

一、Spring 简介1. 什么是Spring2. Spring 框架的优点3. Spring 体系结构 二、Spring 容器1. 什么是Spring容器2. Spring 容器的实例化3. Spring 容器的使用 三、Spring 容器对Bean的管理1. Bean的实例化2. Bean的作用域3. Bean 的生命周期(and 回调)4. Bean 延迟实例化 四、Spring IoC1. IoC(控制反转)概念2. DI(依赖注入)3. Spring Bean的装配 五、Spring AOP1. AOP 简介2. AOP与OOP之间的联系和区别3. Spring通知类型和创建AOP代理4. 基于XML和注解开发AOP 六、Spring 拦截器七、Spring对JDBC Template的支持八、Spring 事务1. 事务的特性2. Spring事务的配置方式3.事务的传播机制4. 事务的隔离级别

一、Spring 简介

Spring 是另一个主流的 Java Web 开发框架,该框架是一个轻量级的应用框架,具有很高的凝聚力和吸引力。Spring 框架因其强大的功能以及卓越的性能而受到众多开发人员的喜爱。

1. 什么是Spring

Spring就像是整个项目中装配bean的大工厂,在配置文件中可以指定使用特定的参数去调用实体类的构造方法来实例化对象。

在实际开发中,通常服务器端采用三层体系架构,分别为表现层(web)、业务逻辑层(service)、持久层(dao)。 Spring对每一层都提供了技术支持,在表现层提供了与Struts2框架的整合,在业务逻辑层可以管理事务和记录日志等,在持久层可以整合Hibernate和Mybaties、JdbcTemplate等技术。

总结: 1)Spring的核心是一个轻量级(Lightwegit)的容器(Container)。 2)Spring是实现IoC(Inversion of Control 控制反转)容器和非入侵性(No intrusive)的框架。 3)Spring提供AOP(Aspect-oriented programming 面向切面编程)概念的实现方式。 4)Spring提供对持久层(Persistence)、事务(Transcation)的支持。 5)Spring提供MVC Web框架的实现,并对一些常用的企业服务API(Application Interface)提供一致的模型封装。 6)Spring提供了对现存的各种框架(Structs、JSF、Hibernate、Mybatis、Webwork等)相整合的方案。

2. Spring 框架的优点

1)方便解耦,简化开发 Spring 就是一个大工厂,可以将所有对象的创建和依赖关系的维护交给Spring管理。 2)方便集成各种优秀框架 Spring 不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如 Struts2、Hibernate、MyBatis 等)的直接支持。 3)降低 Java EE API 的使用难度 Spring 对 Java EE 开发中非常难用的一些API(JDBC、JavaMail、远程调用等)都提供了封装,使这些API应用难度大大降低。 4)方便程序的测试 Spring 支持 JUnit4,可以通过注解方便的测试Spring程序。 5)AOP编程的支持 Spring提供面向切面编程,可以方便地实现对程序进行权限拦截和运行监控等功能。 6)声明式事务的支持 只需要通过配置就可以完成对事务的管理,而无须手动编程。

3. Spring 体系结构

Spring 框架采用分层架构,根据不同的功能呗划分为了多个模块。 1) Data Access/Integration(数据访问/集成) 数据访问/集成层包括JDBC、ORM、OXM、JMS和Transaction模块,具体介绍如下。

JDBC 模块:提供了一个JDBC的抽象层,大幅度减少了再开发过程中对数据库操作的代码。ORM 模块:对流行的对象关系映射API,包括JPA、JDO、Hibernate和Mybaties提供了的集成层。OXM 模块,指Java消息服务,包含的功能为生成和消费的信息。Transactions 事务模块:支持编程和声明式事务管理实现特殊接口类,并为所有的POJO。

2) Web 模块 Spring 的Web层包括Web、Servlet、Struts和Portlet组件,具体介绍如下。

Web 模块:提供了基本的Web开发集成特性,例如多文件上传功能、使用的Servlet监听器的IoC容器初始化以及Web应用上下文。Servlet 模块:包括Spring模型、视图、控制器(MVC)实现Web应用程序。Struts 模块:包含支持类内的Spring应用程序,集成了经典的Struts Web层。Portlet 模块:提供了在Portlet环境中使用MVC实现,类似Web-Servlet模块的功能。

3) Core Container(核心容器) Spring的核心容器是其他模块建立的基础,由Beans模块、Core核心模块、Context上下文模块和Expression Language表达式语言模块组成,具体介绍如下。

Beans 模块:提供了BeanFactory,是工厂模式的经典实现,Spring 将管理对象成为Bean。Core 核心模块:提供了Spring 框架的基本组成部分,包括IoC和DI功能。Context 上下文对象:建立在核心和Beans 模块的基础之上,它是访问定义和配置任何对象的媒介。ApplicationContext接口是上下文对象的焦点。Expression Language 模块:是运行时查询和操作对象图的强大的表达式语言。

4) 其他模块 Spring的其他模块还有AOP、Aspects、Instrumentation以及Test模块,具体介绍如下。

AOP 模块:提供了面向切面编程实现,允许定义方法拦截器和切入点,将代码按照功能进行分离,以降低耦合性。Aspects 模块:提供与AspectJ的集成,是一个功能强大且成熟的面向切面编程(AOP)框架。Instrumentation 模块:提供了类工具的支持和类加载器的实现,可以在特定的应用服务器中使用。Test 模块:支持Spring组件,使用JUnit或TestNG框架的测试。

二、Spring 容器

1. 什么是Spring容器

Spring容器是Spring的核心,一切Spring bean都存储在Spring容器内,并由其通过IoC技术管理。Spring容器也就是一个bean工厂(BeanFactory)。应用中bean的实例化,获取,销毁等都是由这个bean工厂管理的。

Spring 容器实现了IoC和AOP机制,这些可以简化Bean对象的创建和Bean对象之间的解耦。 Spring 提供了两种IoC容器,分别为BeanFactory和ApplicationContext两种类型。

2. Spring 容器的实例化

1)BeanFactory BeanFactory是基础类型的IoC容器,它由org.springframework.beans.facytory.BeanFactory 接口定义,并提供了完整的 IoC 服务支持。简单来说,BeanFactory就是一个管理Bean的工厂,它主要负责初始化各种Bean,并调用它们的生命周期方法。

BeanFactory 接口有多个实现类,最常见的是 org.springframework.beans.factory.xml.XmlBeanFactory,它是根据 XML 配置文件中的定义装配 Bean 的。

创建BeanFactory实例时,需要提供Spring所管理容器的详细配置信息,这些信息通常采用XML文件形式管理。其加载配置信息的代码如下所示:

BeanFactory beanFactory = new XmlBeanFactory(new FileSystemResource("D://applicationContext.xml"));

2)ApplicationContext ApplicationContext是BeanFactory的子接口,也被称为应用上下文。该接口的全路径为org.springframework.context.ApplicationContext,它不仅提供了BeanFactory的所有功能,还添加了对i18n(国际化)、资源访问、事件传播等方面的良好支持。 ApplicationContext接口有两个常用的实现类,具体如下。 ①ClassPathXmlApplicationContext 该类从 类路径ClassPath 中寻找指定的XML配置文件,找到并装载完成ApplicationContext的实例化工作,具体如下所示。

ApplicationContext applicationContext = new ClassPathXmlApplicationContext(String configLocation); //configLocation 参数用于指定 Spring 配置文件的名称和位置,如 applicationContext.xml。

②FileSystemXmlApplicationContext 该类从 指定的文件系统路径 中寻找指定的XML配置文件,找到并装载完成ApplicationContext的实例化工作,具体如下所示。

ApplicationContext applicationContext = new FileSystemXmlApplicationContext(String configLocation); /** *它与 ClassPathXmlApplicationContext 的区别是:在读取 Spring 的配置文件时,FileSystemXmlApplicationContext不再从类路径中读取配置文件, *而是通过参数指定配置文件的位置,它可以获取类路径之外的资源,如“F:/workspaces/applicationContext.xml”。 */

通常在 Java 项目中,会采用通过 ClassPathXmlApplicationContext 类实例化 ApplicationContext 容器的方式,而在 Web 项目中,ApplicationContext 容器的实例化工作会交由 Web 服务器完成。Web 服务器实例化 ApplicationContext 容器通常使用基于 ContextLoaderListener 实现的方式,它只需要在 web.xml 中添加如下代码:

<!--指定Spring配置文件的位置,有多个配置文件时,以逗号分隔--> <context-param> <param-name>contextConfigLocation</param-name> <!--spring将加载spring目录下的applicationContext.xml文件--> <param-value> classpath:spring/applicationContext.xml </param-value> </context-param> <!--指定以ContextLoaderListener方式启动Spring容器--> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener>

3. Spring 容器的使用

从本质上讲,BeanFactory 和 ApplicationContext 仅仅是一个维护 Bean 的定义以及相互 依赖关系的高级工厂接口。我们可以通过 BeanFactory 和 ApplicationContext 访问 Bean 的定义。 1)首先要在容器配置文件中 applicationContext.xml 添加 Bean 定义。

<bean id="标识符" class="Bean 类型"> <property name="属性名" value="属性值"/> </bean>

2)在创建 BeanFactory 和 ApplicationContext 容器对象之后,调用 getBean()方法获取 Bean 的实例

ApplicationContext容器对象.getBean("标识符")

三、Spring 容器对Bean的管理

Spring 配置文件支持两种不同的格式,分别是 XML 文件格式和 Properties 文件格式。

通常情况下,Spring 会以 XML 文件格式作为 Spring 的配置文件,这种配置方式通过 XML 文件注册并管理 Bean 之间的依赖关系。

XML 格式配置文件的根元素是 ,该元素包含了多个 子元素,每一个 子元素定义了一个 Bean,并描述了该 Bean 如何被装配到 Spring 容器中。

1. Bean的实例化

在 Spring 中,实例化 Bean 有三种方式,分别是构造器实例化、静态工厂方式实例化和实例工厂方式实例化。 1)构造器实例化

<bean id="calendar01" class="java.util.GregorianCalendar"/> <bean name="calendar02" class="java.util.GregorianCalendar"/> id 或 name 属性用于指定 Bean 的名称,用于从 Spring 容器中查找这个 Bean 对象。class 属性用于指定 Bean 的类型,会自动调用无参构造器创建对象。

2)静态工厂方式实例化

<bean id="calendar03" class="java.util.Calendar" factory-method="getInstance"/> id 属性用于指定 Bean 的名称。class 属性用于指定工厂类型。factory-method 属性用于指定工厂中创建 Bean 对象的方法,必须用 static 修饰的方法。

3)实例化工厂方式实例化

<bean id="calendar03" class="java.util.GregorianCalendar"/> <bean id="dateObj" factory-bean="calendar03" factory-method="getTime"/> //将对象的创建规则告诉 Spring,Spring 会帮你创建对象,基于配置和默认规则,减少代码的书写。 id 属性用于指定 Bean 的名称factory-bean 属性用于指定工厂 Bean 对象。factory-method 属性用于指定工厂中创建 Bean 对象的方法。

2. Bean的作用域

Spring 容器在初始化一个Bean的实例时,同时会指定该实例的作用域。作用域如下所示: 1)singleton 单例模式,使用 singleton 定义的 Bean 在 Spring 容器中只有一个实例,这也是 Bean 默认的作用域。 2)prototype 原型模式,每次通过 Spring 容器获取 prototype 定义的 Bean 时,容器都将创建一个新的 Bean 实例。 3)request 在一次 HTTP 请求中,容器会返回该 Bean 的同一个实例。而对不同的 HTTP 请求,会返回不同的实例,该作用域仅在当前 HTTP Request 内有效。 4)session 在一次 HTTP Session 中,容器会返回该 Bean 的同一个实例。而对不同的 HTTP 请求,会返回不同的实例,该作用域仅在当前 HTTP Session 内有效。 5)global Session 在一个全局的 HTTP Session 中,容器会返回该 Bean 的同一个实例。该作用域仅在使用 portlet context 时有效。

<bean id="person" class="com.mengma.scope.Person" scope="singleton"/> //通过<bean>的 scope 属性来指定 Bean 作用域。

3. Bean 的生命周期(and 回调)

实例化 》 属性赋值 》 初始化 》 销毁

1)根据配置情况调用 Bean 构造方法或工厂方法实例化 Bean。

2)利用依赖注入完成 Bean 中所有属性值的配置注入。

3)如果 Bean 实现了 BeanNameAware 接口,则 Spring 调用 Bean 的 setBeanName() 方法传入当前Bean 的 id 值。

4)如果 Bean 实现了 BeanFactoryAware 接口,则 Spring 调用 setBeanFactory()方法传入当前工厂实例的引用。

5)如果 Bean 实现了 ApplicationContextAware 接口,则 Spring 调用setApplicationContext() 方法传入当前 ApplicationContext 实例的引用。

6)如果 BeanPostProcessor 和 Bean 关联,则 Spring 将调用该接口的预初始化方法postProcessBeforeInitialzation() 对 Bean 进行加工操作,此处非常重要,Spring 的 AOP就是利用它实现的。

7)如果 Bean 实现了 InitializingBean 接口,则 Spring 将调用 afterPropertiesSet()方法。

8)如果在配置文件中通过 init-method 属性指定了初始化方法,则调用该初始化方法。

9)如果 BeanPostProcessor 和 Bean 关联,则 Spring 将调用该接口的初始化方法postProcessAfterInitialization()。此时,Bean 已经可以被应用系统使用了。

10)如果在 中指定了该 Bean 的作用范围为 scope=“singleton”,则将该 Bean 放入 SpringIoC 的缓存池中,将触发 Spring 对该 Bean 的生命周期管理;如果在 中指定了该 Bean 的作用范围为scope=“prototype”,则将该 Bean 交给调用者,调用者管理该 Bean 的生命周期,Spring 不再管理该 Bean。

11)如果 Bean 实现了 DisposableBean 接口,则 Spring 会调用 destory() 方法将 Spring 中的Bean 销毁;如果在配置文件中通过 destory-method 属性指定了 Bean 的销毁方法,则 Spring 将调用该方法对Bean 进行销毁。

指定初始化回调方法

<bean id="exampleBean" class="com.entity.User" init-method="init"/>

指定销毁回调方法 适用于 singleton 模式 Bean。

<bean id="exampleBean" class="com.entity.User" destroy-method="destroy"/>

为容器中所有 Bean 指定初始化回调方法 在顶级的元素中的 default-init-method 属性设置。

<beans default-init-method="init"> <bean id="exampleBean" class="com.entity.User"/> ... </beans>

为容器中所有的 Bean 指定销毁回调方法 在顶级的元素中的 default-destroy-method 属性设置。

<beans default-destroy-method="destroy"> <bean id="exampleBean" class="com.entity.User"/> ... </beans>

4. Bean 延迟实例化

Bean 延迟实例化适用于频率很低的单例对象。在 ApplicationContext 实现的默认行为就是在启动时将所有 singleton Bean 提前进行实例化。 如果不想让一个 singleton Bean 在 ApplicationContext 初始化时被提前实例化,可以使用<bean/>元素的 lazy-init="true"属性改变。 <bean id="exampleBean" class="com.entity.User" lazy-init="true"/>

为容器中所有的 Bean 指定延迟实例化特性。 在顶级的元素中设置 default-lazy-init 属性。

<beans default-lazy-init="true"> <bean id="exampleBean" class="com.entity.User"/> ... </beans>

四、Spring IoC

1. IoC(控制反转)概念

IOC:Inversion of Control,控制反转。 IOC 是指程序中对象的获取方式发生反转,由最初的 new方式创建转变为由第三方框架创建、注入(DI),降低了对象之间耦合度。 Spring 容器是采用 DI 方式实现 IOC 控制,IOC 是Spring 框架的基础和核心。

2. DI(依赖注入)

DI:Dependency Injection,依赖注入。 DI的基本原理就是将一起工作的具有关系的对象,通过构造方法或方法参数传入建立关联,因此容器的工作就是创建 Bean 时注入依赖关系。 IOC是一种思想,而 DI 是实现 IOC 的主要技术途径。 DI 主要有 2 种注入方式,即 Setter 注入和构造器注入。

1)Setter注入

指 IoC 容器使用 setter 方法注入被依赖的实例。通过调用无参构造器或无参 static 工厂方法实例化 bean 后,调用该 bean 的 setter 方法,即可实现基于 setter 的 DI。

//applicationContext.xml 中添加配置信息 <bean id="personService" class="com.mengma.ioc.PersonServiceImpl"> <!-- 将personDao实例注入personService实例中 --> <property name="personDao" ref="personDao"/> </bean>

2)构造器注入 指 IoC 容器使用构造方法注入被依赖的实例。基于构造器的 DI 通过调用带参数的构造方法实现,容器在实例化 Bean 时,根据参数类型执行相应的构造器。构造器注入可以强制给 Bean 注入某些参数,比 Setter 注入更严格。

3. Spring Bean的装配

1)基于XML的装配

<?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:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd"> <!-- 使用设值注入方式装配Person实例 --> <bean id="person1" class="com.mengma.assembly.Person"> <property name="name" value="zhangsan" /> <property name="age" value="20" /> </bean> <!-- 使用构造方法装配Person实例 --> <bean id="person2" class="com.mengma.assembly.Person"> <constructor-arg index="0" value="lisi" /> <constructor-arg index="1" value="21" /> </bean> </beans>

参数值注入: ① 注入基本值

<bean id="msg" class="com.entity.MessageBean"> <property name="name" value="李四"/> </bean>

②注入Bean对象

<bean id="computer" class="com.entity.Computer"> <property name="mainBoard" value="华硕"/> <property name="ram" value="金士顿"/> </bean> <bean id="msg" class="com.entity.MessageBean"> <property name="computer" ref="computer"/> </bean>

③List集合注入&引用List集合

<bean id="msg" class="com.entity.MessageBean"> <property name="langs"> <list>//<set/>、<map/>和<props/> <value>Java</value> <value>SQL</value> ... </list> </property> </bean> <util:list id="langList"> <value>Java</value> <value>SQL</value> <value>Python</value> </util:list> <bean id="msg2" class="com.entity.MessageBean"> <property name="langs" ref="langList"/> </bean> 同理:Set、Map、Properties 都可以采用引用方式注入。 使用<util:set/><util:map/><util:properties/>声明 Set、Map 和 Properties 集合。

④注入 Spring 表达式

<util:properties id="const" location="classpath:const.properties"> <bean id="demo" class="com.entity.DemoBean"> <property name="name" value="#{msg.name}"/> <property name="lang" value="#{msg.langs[0]}"/> <property name="score" value="#{msg.score.c1}"/> <property name="pageSize" value="#{const.PAGE_SIZE}" </bean>

⑤注入 null 或者空字符串

<bean id="msg" class="com.entity.MessageBean"> <property name="name> <null/> </property> </bean> <bean id="msg" class="com.entity.MessageBean"> <property name="name" value=""/> </bean>

2)基于注解的装配

开启组件扫描

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

① @Component 可以使用此注解描述 Spring 中的 Bean,但它是一个泛化的概念,仅仅表示一个组件(Bean),并且可以作用在任何层次。使用时只需将该注解标注在相应类上即可。 ② @Repository 用于将数据访问层(DAO层)的类标识为 Spring 中的 Bean,其功能与 @Component 相同。 ③ @Service 通常作用在业务层(Service 层),用于将业务层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。 ④ @Controller 通常作用在控制层(如 Struts2 的 Action),用于将控制层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。 ⑤ @Autowired 用于对 Bean 的属性变量、属性的 Set 方法及构造函数进行标注,配合对应的注解处理器完成 Bean 的自动配置工作。默认按照 Bean 的类型进行装配。 ⑥ @Resource 其作用与 Autowired 一样。其区别在于 @Autowired 默认按照 Bean 类型装配,而 @Resource 默认按照 Bean 实例名称进行装配。 @Resource 中有两个重要属性:name 和 type。

Spring 将 name 属性解析为 Bean 实例名称,type 属性解析为 Bean 实例类型。如果指定 name 属性,则按实例名称进行装配;如果指定 type 属性,则按 Bean 类型进行装配。

如果都不指定,则先按 Bean 实例名称装配,如果不能匹配,则再按照 Bean 类型进行装配;如果都无法匹配,则抛出 NoSuchBeanDefinitionException 异常。 ⑦ @Qualifier 与 @Autowired 注解配合使用,会将默认的按 Bean 类型装配修改为按 Bean 的实例名称装配,Bean 的实例名称由 @Qualifier 注解的参数指定。 ⑧**@PostConstruct** 和@PreDestroy 注解标记分别用于指定初始化和销毁回调方法。

3)自动装配 自动装配就是指 Spring 容器可以自动装配(autowire)相互协作的 Bean 之间的关联关系,将一个 Bean 注入其他 Bean 的 Property 中。

要使用自动装配,就需要配置 元素的 autowire 属性。

名称说明byName根据 Property 的 name 自动装配,如果一个 Bean 的 name 和另一个 Bean 中的 Property 的 name 相同,则自动装配这个 Bean 到 Property 中。byType根据 Property 的数据类型(Type)自动装配,如果一个 Bean 的数据类型兼容另一个 Bean 中 Property 的数据类型,则自动装配。constructor根据构造方法的参数的数据类型,进行 byType 模式的自动装配。autodetect根据构造方法的参数的数据类型,进行 byType 模式的自动装配。no根据构造方法的参数的数据类型,进行 byType 模式的自动装配。

五、Spring AOP

1. AOP 简介

面向切面编程(AOP)和面向对象编程(OOP)类似,也是一种编程模式。Spring AOP 是基于 AOP编程模式的一个框架,它的使用有效减少了系统间的重复代码,达到了模块间的松耦合目的。

AOP 的全称是“Aspect Oriented Programming”,即面向切面编程,它将业务逻辑的各个部分进行隔离,使开发人员在编写业务逻辑时可以专心于核心业务,从而提高了开发效率。

AOP 采取横向抽取机制,取代了传统纵向继承体系的重复性代码,其应用主要体现在事务处理、日志管理、权限控制、异常处理等方面。

AOP的优点: 使用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高开发效率。

相关术语:

名称说明Joinpoint(连接点)指那些被拦截到的点,在Spring中,可以被动态代理拦截目标类的方法Pointcut(切入点)指要对哪些Joinpoint进行拦截,即被拦截的连接点Advice(通知)指拦截到Joinpoint之后要做的事情,即对切入点增强的内容Target(目标)指代理的目标对象Weaving(植入)指把增强代码应用到目标上,生成代理对象的过程Proxy(代理)指生成的代理对象Aspect(切面)切入点和通知的结合

2. AOP与OOP之间的联系和区别

区别: 1)面向目标不同:简单来说OOP是面向名词领域,AOP面向动词领域。 2)思想结构不同:OOP是纵向结构,AOP是横向结构。 3)注重方面不同:OOP注重业务逻辑单元的划分,AOP偏重业务处理过程的某个步骤或阶段。

OOP和AOP的联系: AOP需要以OOP为前提和基础,是对OOP的补充,两者之间是一个相互补充和完善的关系。

3. Spring通知类型和创建AOP代理

1)Spring通知类型

名称说明org.springframework.aop.MethodBeforeAdvice(前置通知)在方法之前自动执行的通知称为前置通知,可以应用于权限管理等功能。org.springframework.aop.AfterReturningAdvice(后置通知)在方法之后自动执行的通知称为后置通知,可以应用于关闭流、上传文件、删除临时文件等功能。org.aopalliance.intercept.MethodInterceptor(环绕通知)在方法前后自动执行的通知称为环绕通知,可以应用于日志、事务管理等功能。org.springframework.aop.ThrowsAdvice(异常通知)在方法抛出异常时自动执行的通知称为异常通知,可以应用于处理异常记录日志等功能。org.springframework.aop.IntroductionInterceptor(引介通知)在目标类中添加一些新的方法和属性,可以应用于修改旧版本程序(增强类)。

2)声明式Spring AOP Spring 创建一个 AOP 代理的基本方法是使用org.springframework.aop.framework.ProxyFactoryBean,这个类对应的切入点和通知提供了完整的控制能力,并可以生成指定的内容。

属性名称描述target代理的目标对象proxyInterfaces代理要实现的接口,如果有多个接口,则可以使用以下格式赋值:<list> <value></value> </list>proxyTargetClass是否对类代理而不是接口,设置为 true 时,使用 CGLIB 代理interceptorNames需要植入目标的 Advicesingleton返回的代理是否为单例,默认为 true(返回单实例)optimize当设置为 true 时,强制使用 CGLIB <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http:/www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--目标类 --> <bean id="customerDao" class="com.mengma.dao.CustomerDaoImpl" /> <!-- 通知 advice --> <bean id="myAspect" class="com.mengma.factorybean.MyAspect" /> <!--生成代理对象 --> <bean id="customerDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <!--代理实现的接口 --> <property name="proxyInterfaces" value="com.mengma.dao.CustomerDao" /> <!--代理的目标对象 --> <property name="target" ref="customerDao" /> <!--用通知增强目标 --> <property name="interceptorNames" value="myAspect" /> <!-- 如何生成代理,true:使用cglib; false :使用jdk动态代理 --> <property name="proxyTargetClass" value="true" /> </bean> </beans>

4. 基于XML和注解开发AOP

使用 AspectJ 开发 AOP 通常有两种方式: 基于 XML 的声明式。 基于 Annotation 的声明式。 1)基于XML的声明式 基于 XML 的声明式是指通过 Spring 配置文件的方式定义切面、切入点及声明通知,而所有的切面和通知都必须定义在 aop:config 元素中。

<?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:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"> <!--目标类 --> <bean id="customerDao" class="com.mengma.dao.CustomerDaoImpl" /> <!--切面类 --> <bean id="myAspect" class="com.mengma.aspectj.xml.MyAspect"></bean> <!--AOP 编程 --> <aop:config> <aop:aspect ref="myAspect"> <!-- 配置切入点,通知最后增强哪些方法 --> <aop:pointcut expression="execution ( * com.mengma.dao.*.* (..))" id="myPointCut" /> <!--前置通知,关联通知 Advice和切入点PointCut --> <aop:before method="myBefore" pointeut-ref="myPointCut" /> <!--后置通知,在方法返回之后执行,就可以获得返回值returning 属性 --> <aop:after-returning method="myAfterReturning" pointcut-ref="myPointCut" returning="returnVal" /> <!--环绕通知 --> <aop:around method="myAround" pointcut-ref="myPointCut" /> <!--抛出通知:用于处理程序发生异常,可以接收当前方法产生的异常 --> <!-- *注意:如果程序没有异常,则不会执行增强 --> <!-- * throwing属性:用于设置通知第二个参数的名称,类型Throwable --> <aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointCut" throwing="e" /> <!--最终通知:无论程序发生任何事情,都将执行 --> <aop:after method="myAfter" pointcut-ref="myPointCut" /> </aop:aspect> </aop:config> </beans>

2)基于注解的声明式 在 Spring 中,尽管使用 XML 配置文件可以实现 AOP 开发,但是如果所有的相关的配置都集中在配置文件中,势必会导致 XML 配置文件过于臃肿,从而给维护和升级带来一定的困难。

为此,AspectJ 框架为 AOP 开发提供了另一种开发方式——基于 Annotation 的声明式。AspectJ 允许使用注解定义切面、切入点和增强处理,而 Spring 框架则可以识别并根据这些注解生成 AOP 代理。 在 applicationContext.xml 中开启 AOP 注解扫描。 <aop:aspectj-autoproxy proxy-target-class="true"/>

<!-- 开启注解扫描 --> <context:component-scan base-package="com. . ."/>

import org.springframework.stereotype.Component; @Component("admin") public class BraveAdmin { public void saying(){ System.out.println("我是(切点方法)"); } }

切点类:

import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; /** * 注解方式声明aop * 1.用@Aspect注解将类声明为切面 * (如果用@Component("")注解注释为一个bean对象,那么就要在spring配置文件中开启注解扫描,<context:component-scan base-package="com.."/> * 否则要在spring配置文件中声明一个bean对象) * 2.在切面需要实现相应方法的前面加上相应的注释,也就是通知类型。 * 3.此处有环绕通知,环绕通知方法一定要有ProceedingJoinPoint类型的参数传入,然后执行对应的proceed()方法,环绕才能实现。 */ @Component("annotationTest") @Aspect public class AnnotationTest { //定义切点 @Pointcut("execution(* *.saying(..))") public void sayings(){} @Before("sayings()") public void sayHello(){ System.out.println("注解类型前置通知"); } //后置通知 @After("sayings()") public void sayGoodbey(){ System.out.println("注解类型后置通知"); } //环绕通知。注意要有ProceedingJoinPoint参数传入。 @Around("sayings()") public void sayAround(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("注解类型环绕通知..环绕前"); pjp.proceed();//执行方法 System.out.println("注解类型环绕通知..环绕后"); } }

测试类:

import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Test { public static void main(String[] args) { ApplicationContext ac = new ClassPathXmlApplicationContext("com/xx/xx/application.xml"); BraveAdmin br = (BraveAdmin) ac.getBean("knight"); br.saying(); } } @Component注解标注当前类是一个组件,在扫描时会被扫描进来 @Pointcut 注解为了避免相同的匹配规则被定义多处,专门定义该方法设置执行的匹配规则,各个自行调用即可 名称说明@Aspect用于定义一个切面。@Before用于定义前置通知,相当于 BeforeAdvice。@AfterReturning用于定义后置通知,相当于 AfterReturningAdvice。@Around用于定义环绕通知,相当于MethodInterceptor。@AfterThrowing用于定义抛出通知,相当于ThrowAdvice。@After用于定义最终final通知,不管是否异常,该通知都会执行。@DeclareParents用于定义引介通知,相当于IntroductionInterceptor(不要求掌握)。

六、Spring 拦截器

Spring 的 HandlerMapping 处理器支持拦截器应用。当需要为某些请求提供特殊功能时,例如对用户的身份进行验证,就非常适用。

拦截器的接口 拦截器必须实现 HandlerInterceptor 接口,这个接口中含有 3 个方法: preHandle() 处理器执行前被调用。方法返回 true 表示会继续调用其它拦截器和处理器,返回 false 表示中断流程,不会执行后续拦截器和处理器。 postHandle() 处理器执行后,视图处理前调用。此时可以通过 ModelAndView 对象对模型数据进行处理或对视图进行处理。 afterCompletion() 整个请求处理完毕后调用。如性能监控中我们可以在此记录结束时间并输出消耗时间,还可以进行一些资源清理。只有 preHandle()返回 true 时才会执行afterCompletion()方法。

拦截器的使用 自定义拦截器示例:

public class SomeInterceptor implements HandlerInterceptor { public boolean preHandle(HttpServletRequest req,HttpServletResponse res, Object handler) throws Exception { //处理器执行前调用 return true; } public void postHandle(HttpServletRequest req,HttpServletResponse res, Object handler,ModelAndView mav) throws Exception { //处理器执行后调用 } public void afterCompletion(HttpServletRequest req,HttpServletResponse res, Object handler,Exception e) throws Exception { //请求完成处理后调用 } }

自定义拦截器时,实现 HandlerInterceptor 接口需要实现接口中定义的所有方法,但是如果只需要某一个方法,可以继承 HandlerInterceptorAdapter。

自定义拦截器需要在 Spring 的 xml 配置文件配置:

<mvc:interceptors> <mvc:interceptor> <!--拦截所有的请求,必须要写在前面,也就是写在"不拦截"的请求上面--> <mvc:mapping path="/**"/> <!--排除"不拦截"的请求--> <mvc:exclude-mapping path="/login/*"/> <mvc:exclude-mapping path="/regist/*"/> <bean class="com.web.interceptor.SomeInterceptor"/> <!-- 注意:如果有多个拦截器拦截处理器的请求,则依据配置的先后顺序来执行。 --> </mvc:interceptor> </mvc:interceptors>

七、Spring对JDBC Template的支持

Spring 框架针对数据库开发中的应用提供了 JDBCTemplate 类,该类是 Spring 对 JDBC 支持的核心,它提供了所有对数据库操作功能的支持。

Spring 框架提供的JDBC支持主要由四个包组成,分别是 core(核心包)、object(对象包)、dataSource(数据源包)和 support(支持包),org.springframework.jdbc.core.JdbcTemplate 类就包含在核心包中。 1)DataSource 其主要功能是获取数据库连接,具体实现时还可以引入对数据库连接的缓冲池和分布式事务的支持,它可以作为访问数据库资源的标准接口。 2)SQLExceptionTranslator org.springframework.jdbc.support.SQLExceptionTranslator 接口负责对 SQLException 进行转译工作。通过必要的设置或者获取 SQLExceptionTranslator 中的方法,可以使 JdbcTemplate 在需要处理 SQLException 时,委托 SQLExceptionTranslator 的实现类完成相关的转译工作。

JdbcOperations 接口定义了在 JdbcTemplate 类中可以使用的操作集合,包括添加、修改、查询和删除等操作。

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http:/www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 配置数据源 --> <bean id="dataSource" class="org.springframework.jdbc.dataSource.DriverManagerDataSource"> <!--数据库驱动--> <property name="driverClassName" value="com.mysql.jdbc.Driver" /> <!--连接数据库的url--> <property name= "url" value="jdbc:mysql://localhost/spring" /> <!--连接数据库的用户名--> <property name="username" value="root" /> <!--连接数据库的密码--> <property name="password" value="root" /> </bean> <!--配置JDBC模板--> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.jdbcTemplate"> <!--默认必须使用数据源--> <property name="dataSource" ref="dataSource"/> </bean> <!--配置注入类--> <bean id="xxx" class="xxx"> <property name="jdbcTemplate" ref="jdbcTemplate"/> </bean> ... </beans>

八、Spring 事务

1. 事务的特性

1、脏读(Dirty read) 脏读发生在一个事务读取了被另一个事务改写但尚未提交的数据时。如果这些改变在稍后被回滚了,那么第一个事务读取的数据就会是无效的。 2、不可重复读(Nonrepeatable read)【不可重复读重点在修改。】 不可重复读发生在一个事务执行相同的查询两次或两次以上,但每次查询结果都不相同时。这通常是由于另一个并发事务在两次查询之间更新了数据。 3、幻读(Phantom reads)【幻读重点在新增或删除。】 幻读和不可重复读相似。当一个事务(T1)读取几行记录后,另一个并发事务(T2)插入了一些记录时,幻读就发生了。在后来的查询中,第一个事务(T1)就会发现一些原来没有的额外记录。

1)原子性(Atomicity) 事务是一个原子操作,由一系列动作组成。事务的原子性确保动作要么全部完成,要么完全不起作用。 2)一致性(Consistency) 一旦事务完成(不管成功还是失败),系统必须确保它所建模的业务处于一致的状态,而不会是部分完成部分失败。在现实中的数据不应该被破坏。 3)隔离性(Isolation) 可能有许多事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏。 4)持久性(Durability) 一旦事务完成,无论发生什么系统错误,它的结果都不应该受到影响,这样就能从任何系统崩溃中恢复过来。通常情况下,事务的结果被写到持久化存储器中。

2. Spring事务的配置方式

1)编程式事务

编程式事务管理是侵入性事务管理,使用TransactionTemplate或者直接使用PlatformTransactionManager,对于编程式事务管理,Spring推荐使用TransactionTemplate。

TransactionTemplate 应用示例:

public class SimpleService implements Service { private final TransactionTemplate transactionTemplate; public SimpleService(PlatformTransactionManager tm) { this.transactionTemplate = new TransactionTemplate(tm); } public Object someServiceMethod() { return transactionTemplate.execute(new TransactionCallback(){ public Object doInTransaction(TransactionStatus status){ updateOperation1(); return resultOfUpdateOperation2(); } }); } }

如果不需要返回值,那么可以创建一个 TransactionCallbackWithoutResult 的匿名类:

transactionTemplate.execute( new TransactionCallbackWithoutResult() { protected void doInTransactionWithoutResult(TransactionStatus status) { try{ updateOperation1(); updateOperation2(); }catch(SomeBusinessException e){ status.setRollbackOnly(); } } } )

2)声明式事务

声明式事务管理建立在AOP之上,其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,执行完目标方法之后根据执行的情况提交或者回滚。

Spring 的声明式事务管理是通过 Spring AOP 实现的。使用时不需要修改原有的业务代码,只需要通过简单的配置就可以追加事务控制功能。 大多数 Spring 用户选择声明式事务管理。这是对程序代码影响最小的选择,因此也最符合非侵入式的理念。

注解实现声明式事务:

<!--声明事务管理组件--> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="datasSource" ref="ds"/> </bean> <!--开启事务注解扫描--> <tx:annotation-driven transation-manager="txManager" proxy-target-class="true"/>

使用@Transactional 注解声明事务

事务的传播性: @Transactional(propagation=Propagation.REQUIRED) 事务的隔离级别: @Transactional(isolation = Isolation.READ_UNCOMMITTED) 只读: @Transactional(readOnly=true) 该属性用于设置当前事务是否为只读事务,设置为true表示只读,false则表示可读写,默认值为false。 事务的超时性: @Transactional(timeout=30) 回滚: 指定单一异常类:@Transactional(rollbackFor=RuntimeException.class) 指定多个异常类:@Transactional(rollbackFor={RuntimeException.class, Exception.class})

@Transactional 注解属性默认设置: @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, readOnly = false)

属性说明propagation设置事务传播isolation设置事务隔离级别readOnly设置为只读还是可读写rollbackFor设置遇到哪些异常必须回滚norollbackFor设置遇到哪些异常不回滚

XML实现声明式事务:

<!--声明事务管理组件--> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="datasSource" ref="ds"/> </bean> <!--配置事务作用的范围及类型--> <tx:advice id="txAdvice" transaction-manager="txManager"> <tx:attributes> <tx:method name="find*" read-only="true"/> <tx:method name="add*" propagation="REQUIRED"/> <tx:method name="update*" propagation="REQUIRED"/> <tx:method name="delete*" propagation="REQUIRED"/> </tx:attributes> </tx:advice> <aop:config proxy-target-class="true"> <aop:advisor advice-ref="txAdvice" pointcut="within(com.web.controller..*)"/> </aop:config>

3.事务的传播机制

@Transactional(propagation = Propagation.REQUIRED)

属性说明REQUIREDSpring默认的传播机制,能满足绝大部分业务需求,如果外层有事务,则当前事务加入到外层事务,一块提交,一块回滚。如果外层没有事务,新建一个事务执行SUPPORT如果外层有事务,则加入外层事务,如果外层没有事务,则直接使用非事务方式执行。完全依赖外层的事务REQUES_NEW该事务传播机制是每次都会新开启一个事务,同时把外层事务挂起,当当前事务执行完毕,恢复上层事务的执行。如果外层没有事务,执行当前新开启的事务即可NOT_SUPPORT该传播机制不支持事务,如果外层存在事务则挂起,执行完当前代码,则恢复外层事务,无论是否异常都不会回滚当前的代码NEVER该传播机制不支持外层事务,即如果外层有事务就抛出异常MANDATORY与NEVER相反,如果外层没有事务,则抛出异常NESTED该传播机制的特点是可以保存状态保存点,当前事务回滚到某一个点,从而避免所有的嵌套事务都回滚,即各自回滚各自的,如果子事务没有把异常吃掉,基本还是会引起全部回滚的。

4. 事务的隔离级别

@Transactional(isolation = Isolation.DEFAULT)

隔离级别含义DEFAULT使用后端数据库默认的隔离级别UNCOMMITTED允许读取尚未提交的更改。可能导致脏读、幻读或不可重复读。READ_COMMITTED(Oracle 默认级别)允许从已经提交的并发事务读取。可防止脏读,但幻读和不可重复读仍可能会发生。REPEATABLE_READ(MYSQL默认级别)对相同字段的多次读取的结果是一致的,除非数据被当前事务本身改变。可防止脏读和不可重复读,但幻读仍可能发生。SERIALIZABLE完全服从ACID的隔离级别,确保不发生脏读、不可重复读和幻影读。这在所有隔离级别中也是最慢的,因为它通常是通过完全锁定当前事务所涉及的数据表来完成的。
最新回复(0)