欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring3.x框架笔记

程序员文章站 2022-02-08 07:31:03
...

Spring框架笔记


标签: Spring 框架 MingFeng197


Spring框架的概述


什么是Spring

Spring是分层的JavaSE/EE full-stack(一站式) 轻量级开源框架

分层:
	SUN提供的EE的三层结构:web层、业务层、数据访问层(持久层,集成层)
	Struts2是web层基于MVC设计模式框架.
	Hibernate是持久的一个ORM的框架.

一站式:
	Spring框架有对三层的每层解决方案:
	web层:Spring MVC.
	持久层:JDBC Template (JDBC模板)
	业务层:Spring的Bean管理.

Spring的核心

IOC:(Inverse of Control 反转控制)
	控制反转:将对象的创建权交由Spring完成.

AOP:(Aspect Oriented Programming) 面向切面编程
	是面向对象的功能延伸.不是替换面向对象,是用来解决OO中一些问题

Spring的优点

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

Spring快速入门

1. 创建web工程引入相应jar包

spring-beans-3.2.0.RELEASE.jar
spring-context-3.2.0.RELEASE.jar
spring-core-3.2.0.RELEASE.jar
spring-expression-3.2.0.RELEASE.jar

开发的日志记录的包:
    com.springsource.org.apache.commons.logging-1.1.1.jar	--- 用于整合其他的日志的包(类似Hibernate中slf4j)
    com.springsource.org.apache.log4j-1.2.15.jar
提示:Spring3.0.x版本asm jar包已经被合并到Spring core包中

1.1 创建一个HelloServiceImpl类

2. 创建Spring配置文件

在src下创建一个applicationContext.xml
引入XML的约束: 找到xsd-config.html.引入beans约束:
<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">
</beans>

3. 在配置文件中配置类

<bean id="helloService" class="com.zy.HelloServiceImpl"></bean>

4. 创建测试类获取实例对象

@Test
public void demo1(){
	ApplicationContext act=new ClassPathXmlApplicationContext("applicationContext.xml");//参数为配置文件的名称
	HelloService hs=(HelloService)act.getBean("helloService");//这里的参数是配置文件中的bean标签的id
	hs.say();//调用它的方法
}

Ioc和DI的区别

Ioc:控制反转:将对象的创建权由Spring管理
DI:依赖注入
	需要先有Ioc
	在Spring创建对象的过程中,把对象依赖的属性注入到类中

面向对象中对象之间的管理
	依赖:在一个a对象中需要用到b对象 a依赖b
	继承:
	聚合:
		聚集:
		组合:

Spring加载配置文件

ApplicationContext 应用上下文,加载Spring 框架配置文件

加载classpath下的配置文件:

new ClassPathXmlApplicationContext("applicationContext.xml");

加载磁盘路径下的配置文件:

new FileSystemXmlApplicationContext("applicationContext.xml");

Spring的BeanFactory与ApplicationContext的区别

ApplicationContext类继承了BeanFactory.
BeanFactory采取了延迟加载 在使用到这个类的getBean()方法的时候才会加载这个类.
ApplicationContext类加载配置文件的时候,创建所有的类.
ApplicationContext对BeanFactory提供了扩展:
	* 国际化处理
	* 事件传递
	* Bean自动装配
	* 各种不同应用层的Context实现

Ioc装配Bean(XML方式)

Spring框架实例化Bean的方式

提供了三种实例化Bean的方式:
1. 构造方法实例化:(默认无参数)

<!-- 默认情况下使用的就是无参数的构造方法. -->
<bean id="bean1" class="cn.itcast.spring3.demo2.Bean1"></bean>

2. 静态工厂实例化:

<!-- 第二种使用静态工厂实例化 提供个静态工厂和方法 -->
<bean id="bean2" class="cn.itcast.spring3.demo2.Bean2Factory" factory-method="getBean2"></bean>

3. 实例工厂实例化:

<!-- 第三种使用实例工厂实例化 -->
<bean id="bean3" factory-bean="bean3Factory" factory-method="getBean3"></bean>
<bean id="bean3Factory" class="cn.itcast.spring3.demo2.Bean3Factory"/>

关于Bean标签的其它配置

id与name的区别

id遵守了xml约束的约束,id约束保证这个属性值是唯一的,而且必须以字母开头,可以使用字母,下划线,数字,连字符,句号,冒号
name没有这些要求
如果bean标签上没有配置id 那么name可以作为id
现在开发中一般都使用id即可

类的作用域

scope属性: 默认是singleton(单例)
	singleton: 在SpringIoc容器中仅存在一个Bean实例,Bean以单例方式存在
	prototype: 每次从容器中调用Bean时,都返回一个新实例
	request: 每次HTTP请求都会创建一个新的Bean,该作用域只适用于WebApplicationContext环境
	session: 同一个HTTPSession共享一个Bean,不同Session使用不同的Bean,仅适用于WebApplicationContext环境
	globaSession: 一般用于Porlet应用环境(Porlet指的是分布式开发),该作用域仅适用于WebApplicationContext环境

Bean的生命周期

<!-- init-method="init" 设置初始化方法 destroy-method="destroy" 销毁方法 -->
<bean id="test" class="cn.zy.Test" init-method="init" destroy-method="destroy"/>

销毁的方法只对scope="singleton"时有效,执行销毁时必须手动关闭工厂
act.close();//手动关闭工厂 需要使用子类(ClassPathXmlApplicationContext)对象

Bean的声明周期分为11个步骤:

  1. instantiate bean对象实例化

  2. populate properties 封装属性

  3. 如果Bean实现BeanNameAware 执行 setBeanName

  4. 如果Bean实现BeanFactoryAware 或者 ApplicationContextAware 设置工厂 setBeanFactory 或者上下文对象 setApplicationContext

  5. 如果存在类实现 BeanPostProcessor(后处理Bean) ,执行postProcessBeforeInitialization

  6. 如果Bean实现InitializingBean 执行 afterPropertiesSet 属性设置后执行

  7. 调用 指定初始化方法 init

  8. 如果存在类实现 BeanPostProcessor(处理Bean) ,执行postProcessAfterInitialization

  9. 执行自己的业务处理

  10. 如果Bean实现 DisposableBean 执行 destroy

  11. 调用 指定销毁方法 customerDestroy

    第五步第八步需要定义一个类实现BeanPostProcessor接口,并且这个类需要在配置文件中配置,不需要配置id 因为它是由Spring容器自动调用
    只要有一个类创建 第五步和第八步都会执行
    第五步和第八步可以用来权限校验,
    ##Bean中属性注入

Spring支持构造方法注入和setter方法注入

构造器注入

<bean id="test" class="com.zy.Test">
	<!-- 构造方法注入 通过name来注入属性 -->
	<constructor-arg name="属性名" value=""/>
	<!-- 通过下标index来注入属性 type为注入属性的类型 可省略 -->
	<constructor-arg index="0" type="java.lang.String" value=""/>
	<constructor-arg index="1" ref="xxx" />
</bean>

Setter方法注入

常用的是使用Setter方法注入
<bean id="test" class="com.zy.Test">
	<!-- 通过<property>标签注入属性 value与ref只需要写一个 -->
	<!-- 如果是用ref来指定 那么直接指定那个类配置的bean标签的ID即可 -->
	<property name="属性名" value="指普通值"/>
	<property name="属性名"  ref="指对象"/>
</bean>

名称空间p: 注入属性

为了简化xml文件配置,Spring从2.5开始引入了一个新的p名称空间
xmlns:p="http://www.springframework.org/schema/p"

p:属性名="xxx" 引入常量值
p:属性名-ref="xxx" 引用其它Bean对象
<bean id="test" class="com.zy.Test" p:属性名="" p:属性名-ref="bean标签的id"/>

SpEL 属性注入

SpEL(Spring Expression Language)
SpEL使用#{…}作为界定符,所有在大括号中的字符都被认为是SpEL

字符常量表示

<property name="count" value="#{'5'}"/>
<property name="count" value="#{5.5}"/>
<property name="count" value="#{1e4}"/>

<property name="count" value="#{'Hello'}"/>
<property name="count" value='#{"Hello"}'/>

<property name="count" value="#{false}"/>

引用其它对象

<!-- 引用其它对象 -->
<bean id="a" value="com.zy.A"/>
<bean ...>
	<property name="count" value="#{a}"/>
</bean>

<!-- 引用其它对象的属性 count是a的属性名 count必须提供get方法,否则获取不到 -->
<property name="count" value="#{a.count}"/>

<!-- 调用其它方法 并将返回值注入到count属性中 -->
<property name="count" value="#{a.getCount()}"/>

<!-- 也可以通过链式来调用方法 -->
<property name="name" value="#{a.getName().toUpperCase()}"/>

<!-- 使用?表达式 如果getName()返回为null或抛异常,后面的方法就不会执行了 -->
<property name="name" value="#{a.getName()?.toUpperCase()}"/>

<!-- 调用静态方法 使用T() 它将返回一个Class Object -->
<property name="count" value="T(java.lang.Math).PI"/>

运算符

<!-- SpEL支持运算符 +,-,*,/,%,^ -->
<property name="count" value="#{a.getCount() + 10}"/>

<!-- 加号还可以作为字符串连接 -->
<property name="name" value="#{a.fristName + ' : '+a.lastName}"/>

<!-- 比较运算符 ==,lt,gt,eq,le,ge -->
<property name="count" value="#{a.getCount() == 100}"/>

<!-- 不可以使用<和> 因为这两个符号在xml中有特殊含义,可以使用lt gt代替 -->
<property name="count" value="#{a.getCount() gt 100}"/>

<!-- 逻辑运算符 and or not ! -->
<!-- if-else运算符 条件表达式?值1:值2 -->
<!-- 变体的?: -->
<property name="count" value="#{a.count!=null?a.count:0}"/>
<!-- 上面的可转换为 -->
<property name="count" value="#{a.count!=null?:0}"/>

<!-- 正则表达式 matches  返回值为true或false -->
<property name="email" value="#{user.email matches '[a-zA-Z0-9][email protected][a-z]+\\.[a-z]{2,4}'}"/>

SpEL对集合的支持

<!-- SpEL支持java集合 -->
<!-- 获取List的值 下标也可以使用变量指定 -->
<property name="count" value="#{arr[0]}"/>
<!-- 获取Map的值 -->
<property name="count" value="#{map['key']}"/>
<!-- 通过下标获取String字符串中某个字符 -->
<property name="count" value="#{'Hello World'[4]}"/>
<!-- 筛选子集		.?[筛选条件] -->
<!-- 获取第一个		.^[筛选条件] -->
<!-- 获取最后一个		.$[筛选条件] -->
<!-- 集合的投影		.![筛选条件] -->

集合属性注入

<bean id="test" class="com.zy.Test">
	<!-- 注入List集合 数组的注入与list一样的 -->
	<property name="list">
		<list>
			<!-- 注入普通属性使用<value>标签 注入对象使用<ref>标签 -->
			<value>值1</value>
				...
			<value>值n</value>
		</list>
	</property>

	<!-- 注入Set集合 -->
	<property name="set">
		<set>
			<value>值1</value>
				...
			<value>值n</value>
		</set>
	</property>

	<!-- 注入Map集合 -->
	<property name="map">
		<map>
			<!-- 如果key是一个对象 使用key-ref="" 如果value是一个对象 使用value-ref="" 属性 -->
			<!-- 这是简化写法 一般使用这用写法 -->
			<entry key="键1" value="值1" />
			<!-- 这是复杂写法 -->
			<entry>
				<key>
					<value>键2</value>
				</key>
				<value>值2</value>
			</entry>
				...
			<entry key="键n" value="值n" />
		</map>
	</property>

	<!-- 注入Properties java.util.Properties继承自HashTable -->
	<property name="properties">
		<props>
			<prop key="键1">值1</prop>
				...
			<prop key="键n">值n</prop>
		</props>
	</property>
</bean>

配置文件分离与引入

//通过加载配置文件时传入多个参数来加载多个配置文件:
ApplicationContext act=new ClassPathXmlApplicationContext("bean1.xml","bean2.xml");
<!-- 在主配置文件中引入其它的配置文件 -->
<import resource="bean2.xml"/>

Ioc容器装配Bean(注解方式)

在Spring2.5引入使用注解去定义Bean
	@Component 描述Spring框架中的Bean

Spring的框架中提供了与@Component注解等效的三个注解:
	@Repository 用于对DAO实现类进行标注
	@Service 用于对Service实现类进行标注
	@Controller 用于对Controller(Servlet)实现类进行标注

代码示例:

import org.springframework.stereotype.Component;

@Component("test")//这里的内容是bean的Id
public class Test{
	public void test(){
		System.out.println("Hello Spring Annotation...");
	}
}
需要在核心配置文件中配置扫描哪些位置
基于注解的开发需要引入context名称空间
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:context="http://www.springframework.org/schema/context"
   xsi:schemaLocation="
   http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
   http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
	<!-- 当注解与xml配置混搭使用时需要配置下面这一句 -->
	<context:annotation-config></context:annotation-config>
	<!-- 下面配置的是Spring要自动扫描那些包下的类 -->
	<context:component-scan base-package="cn.itcast.spring3"/>
</beans>

使用注解对Bean属性注入

普通属性注入

@Value("值")//普通属性注入使用Value注解注入
private String info;

对象属性注入

@Autowired//自动装配,默认使用类型注入 就是要注入的是什么类型,它会自动找到对应类型进行注入
private UserDao ud;

@Autowired
@Qualifier("userDao")//按名称注入
private UserDao ud1;

@Autowired(required=false)
//required=false 该属性表示忽略异常

@Resource(name="userDao")//这一句相当于上面的两句(Autowired和Qualifier)合起来
private UserDao ud2;

Bean的初始化方法与销毁方法

@PostConstruct//配置初始化方法的注解
public void init(){}

@PerDestroy//销毁只对单例的有效
public void destroy(){}

配置Bean的作用范围

@Service(value="testService")
@Scope(value="prototype")//设置Bean的作用范围 默认是单例的
public class TestService(){}

Spring3.0提供使用Java类定义Bean信息的方法

@Configuration//用来定义类
public class BeanConfig{
	//当@Bean不指定name时,方法名就是它的name
	@Bean(name="userService")//用来定义bean
	public UserService test(){
		UserService us=new UserService();
		us.set(...);
		return us;
	}
}

Spring的XML和注解结合使用

一般使用XML注册Bean 使用注解进行属性的注入
当XML配置与注解混合使用时,需要写上下面这个标签 来打开一些注解
<context:annotation-config/>

Spring整合Web开发

需要导入Spring web开发jar包
spring-web-3.2.0.RELEASE.jar

ServletContext对象是全局对象,服务器启动时创建,在创建ServletContext时加载Spring环境

通过ServletContextListener来对Spring环境进行加载和销毁
spring-web-3.2.0.RELEASE.jar已经对这些进行了封装

将Spring容器初始化交由Web容器负责
配置核心监听器ContextLoaderListener
配置全局参数contextConfigLocation 用于指定Spring框架的配置文件位置
<context-param>
    <!-- 配置初始化参数来指定Spring框架的配置文件位置 -->
	<param-name>contextConfigLocation</param-name>
	<param-value>clsspath:applicationContext.xml</param-value>
</context-param>

<listener>
    <!-- 配置监听器 使Web项目在启动时创建加载Spring环境 -->
	<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

获取方式

因为Spring容器已经交由web容器初始化和管理
获得WebApplicationContext对象,需要依赖ServletContext对象
通常在Servlet中完成
//使用工具类获取
WebApplicationContext wact=WebApplicationContextUtils.getWebApplicationContext(getServletContext());
//直接获取
WebApplicationContext wact=(WebApplicationContext)getServletContext().getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);

Spring与JUnit整合

1. 程序中导入JUnit环境
2. 导入jar包
	spring-test-3.2.0.RELEASE.jar
3. 进行测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:applicationContext.xml")
public class SpringTest{
	@Autowired
	private UserService us;
	@Test
	public void demo(){
		us.login();
	}
}

AOP的概述

AOP Aspect Oriented Programing 面向切面编程
AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码(性能监视、事务管理、安全检查、缓存)
Spring AOP使用纯Java实现,不需要专门的编译过程和类加载器,在运行期通过代理方式向目标类织入增强代码
AspecJ是一个基于Java语言的AOP框架,Spring2.0开始,Spring AOP引入对Aspect的支持,AspectJ扩展了Java语言,提供了一个专门的编译器,在编译时提供横向代码的织入

Spring的AOP

AOP术语

Joinpoint(连接点): 所谓连接点是指那些可以被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点.
Pointcut(切入点): 所谓切入点是指我们要对哪些Joinpoint进行拦截的定义.
Advice(通知/增强): 所谓通知是指拦截到Joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)
Introduction(引介): 引介是一种特殊的通知在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field.
Target(目标对象): 代理的目标对象
Weaving(织入): 是指把增强应用到目标对象来创建新的代理对象的过程. spring采用动态代理织入,而AspectJ采用编译期织入和类装在期织入
Proxy(代理): 一个类被AOP织入增强后,就产生一个结果代理类
Aspect(切面): 是切入点和通知(引介)的结合 Aspect允许有多个切点和多个通知组合

JDK的动态代理

AOP的底层原理就是动态代理
JDK动态代理对实现了接口的类生成代理

JDK动态代理例子

代理类实现与被代理对象同一个接口,代理类持有被代理对象...

接口:

public interface UserDao{
	public void add();
	public void del();
}

实现类:

public class UserDaoImpl implements UserDao{
	public void add(){System.out.println("add...");}
	public void del(){System.out.println("del...");}
}

代理对象:

public class JDKProxy implements InvocationHandler {
	private UserDao userDao;
	public JDKProxy(UserDao ud){this.userDao=ud;}

	public UserDao createProxy(){
		UserDao proxy=(UserDao)Proxy.newProxyInstance(userDao.getClass().getClassLoader(),userDao.getClass().getInterfaces(),this);
		return proxy;
	}

	public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{
		if("add".equals(method.getName())){
			System.out.println("日志记录...");
			Object result=method.invoke(userDao,args);
			return result;
		}
		return method.invoke(userDao,args);
	}
}

CGLIB的动态代理

CGLIB(Code Generation Library)是一个开源项目!是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。 
Hibernate支持它来实现PO(Persistent Object 持久化对象)字节码的动态生成
Hibernate生成持久化类的javassist.
CGLIB生成代理机制:其实生成了一个真实对象的子类

下载cglib的jar包.
	现在做cglib的开发,可以不用直接引入cglib的包.已经在spring的核心中集成cglib.

cglib动态代理例子

public class CGLibProxy implements MethodInterceptor{
	private ProductDao productDao;//被代理对象

	public CGLibProxy(ProductDao productDao) {
		this.productDao = productDao;
	}

	public ProductDao createProxy(){
		// 使用CGLIB生成代理:
		// 1.创建核心类:
		Enhancer enhancer = new Enhancer();
		// 2.为其设置父类:
		enhancer.setSuperclass(productDao.getClass());
		// 3.设置回调:
		enhancer.setCallback(this);
		// 4.创建代理:
		return (ProductDao) enhancer.create();
	}

	public Object intercept(Object proxy, Method method, Object[] args,MethodProxy methodProxy) throws Throwable {
		if("add".equals(method.getName())){
			System.out.println("日志记录操作");
			Object obj = methodProxy.invokeSuper(proxy, args);
			return obj;
		}
		return methodProxy.invokeSuper(proxy, args);
	}
}

结论:Spring框架,如果类实现了接口,就使用JDK的动态代理生成代理对象,如果这个类没有实现任何接口,使用CGLIB生成代理对象

Spring的传统AOP(了解)

AOP:不是由Spring定义.AOP联盟的组织定义.

Spring中的通知:(增强代码)
前置通知 org.springframework.aop.MethodBeforeAdvice
	在目标方法执行前实施增强
后置通知 org.springframework.aop.AfterReturningAdvice
	在目标方法执行后实施增强
环绕通知 org.aopalliance.intercept.MethodInterceptor
	在目标方法执行前后实施增强
异常抛出通知 org.springframework.aop.ThrowsAdvice
	在方法抛出异常后实施增强
引介通知 org.springframework.aop.IntroductionInterceptor(.类级别增强)
	在目标类中添加一些新的方法和属性

Spring中的切面类型

Advisor : Spring中传统切面.
	Advisor:都是有一个切点和一个通知组合.
	Aspect:多个切点和多个通知组合.

Advisor : 代表一般切面,Advice本身就是一个切面,对目标类所有方法进行拦截(* 不带有切点的切面.针对所有方法进行拦截)
PointcutAdvisor : 代表具有切点的切面,可以指定拦截目标类哪些方法(带有切点的切面,针对某个方法进行拦截)
IntroductionAdvisor : 代表引介切面,针对引介通知而使用切面(不要求掌握)

针对所有方法的增强:(不带有切点的切面)

第一步:导入相应jar包.

spring-aop-3.2.0.RELEASE.jar
com.springsource.org.aopalliance-1.0.0.jar

第二步:编写被代理对象:

interface CustomerDao接口
class CustoemrDaoImpl实现类

第三步:编写增强的代码:

//参数method:执行的方法,args:参数,target:目标对象
public class MyBeforeAdvice implements MethodBeforeAdvice{
	public void before(Method method, Object[] args, Object target)
		throws Throwable {
		System.out.println("前置增强...");
	}
}

第四步:生成代理:(配置生成代理:)

Spring基于ProxyFactoryBean类.底层自动选择使用JDK的动态代理还是CGLIB的代理.

属性:
	target : 代理的目标对象
	proxyInterfaces : 代理要实现的接口
		如果多个接口可以使用以下格式赋值
			<list>
			    <value></value>
			    ....
			</list>

proxyTargetClass : 是否对类代理而不是接口,设置为true时,使用CGLib代理(与proxyInterfaces互斥)
interceptorNames : 需要织入目标的Advice
singleton : 返回代理是否为单实例,默认为单例
optimize : 当设置为true时,强制使用CGLib
<!-- 定义目标对象 -->
<bean id="customerDao" class="cn.itcast.spring3.demo3.CustomerDaoImpl"></bean>

<!-- 定义增强 -->
<bean id="beforeAdvice" class="cn.itcast.spring3.demo3.MyBeforeAdvice"></bean>

<!-- Spring支持配置生成代理: -->
<bean id="customerDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
	<!-- 设置目标对象 -->
	<property name="target" ref="customerDao"/>
	<!-- 设置实现的接口 ,value中写接口的全路径 -->
	<property name="proxyInterfaces" value="cn.itcast.spring3.demo3.CustomerDao"/>
	<!-- 需要使用value:要的名称,不是ref -->
	<property name="interceptorNames" value="beforeAdvice"/>
</bean>

注入的时候要注入代理对象:

@Autowired
// @Qualifier("customerDao")// 注入是真实的对象,必须注入代理对象.
@Qualifier("customerDaoProxy")
private CustomerDao customerDao;

带有切点的切面:(针对目标对象的某些方法进行增强)

PointcutAdvisor 接口:
	DefaultPointcutAdvisor 最常用的切面类型,它可以通过任意Pointcut和Advice 组合定义切面
	RegexpMethodPointcutAdvisor 构造正则表达式切点切面

第一步:创建被代理对象.
class OrderDao

第二步:编写增强的类:

	public class MyAroundAdvice implements MethodInterceptor{//环绕增强
		public Object invoke(MethodInvocation methodInvocation) throws Throwable {
			System.out.println("环绕前增强...");
			Object result = methodInvocation.proceed();// 执行目标对象的方法
			System.out.println("环绕后增强...");
			return result;
		}
	}

第三步:生成代理:

<!-- 带有切点的切面 -->
<!-- 定义目标对象 -->
<bean id="orderDao" class="cn.itcast.spring3.demo4.OrderDao"></bean>

<!-- 定义增强 -->
<bean id="aroundAdvice" class="cn.itcast.spring3.demo4.MyAroundAdvice"></bean>

<!-- 定义切点切面: -->
<bean id="myPointcutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
	<!-- 定义表达式,规定哪些方法执行拦截 -->
	<!-- . 任意字符  * 任意个 -->
	<!-- name="pattern" 一个  name="patterns" 多个 -->
	<!-- <property name="pattern" value=".*"/> 拦截所有 -->
	<!-- <property name="pattern" value="cn\.itcast\.spring3\.demo4\.OrderDao\.add.*"/> 针对OrderDao下的add开头的方法进行拦截 -->
	<!-- <property name="pattern" value=".*add.*"></property> 对所有以add开头的方法进行拦截(包含add的) -->
	<property name="patterns" value=".*add.*,.*find.*"></property>
	<!-- 应用增强 -->
	<property name="advice" ref="aroundAdvice"/>
</bean>

<!-- 定义生成代理对象 -->
<bean id="orderDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
	<!-- 配置目标 -->
	<property name="target" ref="orderDao"/>
	<!-- 针对类的代理 -->
	<property name="proxyTargetClass" value="true"/>
	<!-- 在目标上应用增强 -->
	<property name="interceptorNames" value="myPointcutAdvisor"/>
</bean>

自动代理

前面的案例中,每个代理都是通过ProxyFactoryBean织入切面代理,在实际开发中,非常多的Bean每个都配置ProxyFactoryBean开发维护量巨大

自动创建代理(基于后处理Bean.在Bean创建的过程中完成的增强.生成Bean就是代理对象.)
BeanNameAutoProxyCreator 根据Bean名称创建代理 
DefaultAdvisorAutoProxyCreator 根据Advisor本身包含信息创建代理
AnnotationAwareAspectJAutoProxyCreator 基于Bean中的AspectJ 注解进行自动代理

BeanNameAutoProxyCreator :按名称生成代理

<!-- 定义目标对象 -->
<bean id="customerDao" class="cn.itcast.spring3.demo3.CustomerDaoImpl"></bean>
<bean id="orderDao" class="cn.itcast.spring3.demo4.OrderDao"></bean>

<!-- 定义增强 -->
<bean id="beforeAdvice" class="cn.itcast.spring3.demo3.MyBeforeAdvice"></bean>
<bean id="aroundAdvice" class="cn.itcast.spring3.demo4.MyAroundAdvice"></bean>

<!-- 自动代理:按名称的代理 基于后处理Bean,后处理Bean不需要配置ID-->
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
	<!-- 对哪些类进行代理 -->
	<property name="beanNames" value="*Dao"/>
	<!-- 应用哪些增强 -->
	<property name="interceptorNames" value="beforeAdvice"/>
</bean>

DefaultAdvisorAutoProxyCreator :根据切面中定义的信息生成代理

<!-- 定义目标对象 -->
<bean id="customerDao" class="cn.itcast.spring3.demo3.CustomerDaoImpl"></bean>
<bean id="orderDao" class="cn.itcast.spring3.demo4.OrderDao"></bean>

<!-- 定义增强 -->
<bean id="beforeAdvice" class="cn.itcast.spring3.demo3.MyBeforeAdvice"></bean>
<bean id="aroundAdvice" class="cn.itcast.spring3.demo4.MyAroundAdvice"></bean>

<!-- 定义一个带有切点的切面 -->
<bean id="myPointcutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
	<!-- 对哪些切点进行拦截 -->
	<property name="pattern" value=".*add.*"/>
	<!-- 应用哪个增强 -->
	<property name="advice" ref="aroundAdvice"/>
</bean>

<!-- 自动生成代理 -->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"></bean>

基于ProxyFattoryBean的代理与自动代理的区别?

ProxyFactoryBean:先有被代理对象,将被代理对象传入到代理类中生成代理.
自动代理:基于后处理Bean.在Bean的生成过程中,就产生了代理对象,把代理对象返回.生成Bean已经是代理对象.

Spring的AspectJ的AOP开发(重点)

AspectJ是一个面向切面的框架,它扩展了Java语言。AspectJ定义了AOP语法所以它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件。
AspectJ是一个基于Java语言的AOP框架
Spring2.0以后新增了对AspectJ切点表达式支持
@AspectJ 是AspectJ1.5新增功能,通过JDK5注解技术,允许直接在Bean类中定义切面
新版本Spring框架,建议使用AspectJ方式来开发AOP

AspectJ表达式:

语法:execution(表达式)
execution(<访问修饰符>?<返回类型><方法名>(<参数>)<异常>)//?代表前面的可以省略

//这下面这个 *代表返回类型  cn.itcast.spring3.demo1.dao.* 这个包下的任意方法名 (..)括号内的点点代表任意个参数
execution("* cn.itcast.spring3.demo1.dao.*(..)")		---只检索当前包
execution("* cn.itcast.spring3.demo1.dao..*(..)")		---检索包及当前包的子包.
execution(* cn.itcast.dao.GenericDAO+.*(..))			---检索GenericDAO及子类 加号代表该类及其子类

AspectJ增强:

@Before 前置通知,相当于BeforeAdvice
@AfterReturning 后置通知,相当于AfterReturningAdvice
@Around 环绕通知,相当于MethodInterceptor
@AfterThrowing抛出通知,相当于ThrowAdvice
@After 最终final通知,不管是否异常,该通知都会执行
@DeclareParents 引介通知,相当于IntroductionInterceptor (不要求掌握)

基于注解:

第一步:引入相应jar包.

aspectj依赖aop环境.
	spring-aspects-3.2.0.RELEASE.jar
	com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar

第二步:编写被增强的类:

UserDao

第三步:使用AspectJ注解形式:

@Aspect//这个注解来定义切面
public class MyAspect {//定义一个切面类:就是切点与增强的结合

    //前置通知 不能阻止方法执行
	@Before("execution(* cn.itcast.spring3.demo1.UserDao.add(..))")
	public void before(JoinPoint joinPoint){//JoinPoint 这个方法可以有参数也可以没有参数
		System.out.println(joinPoint);
		//上面输出的内容是:execution(void cn.itcast.spring3.demo1.UserDao.add())
		System.out.println("前置增强....");
	}
	
	//下面这个 returning参数设置方法返回值的名称,可以在参数中接收获取 后置通知 不能阻止方法执行
	@AfterReturning(value="execution(* cn.itcast.spring3.demo1.UserDao.update(..))",returning="returnVal")
	public void afterReturning(Object returnVal){
		System.out.println("后置增强....方法返回值:"+returnVal);
	}
	
	//环绕增强 可以阻止方法的执行
	@Around("execution(* cn.itcast.spring3.demo1.UserDao.find(..))")
	public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
		System.out.println("环绕前增强....");
		Object obj=proceedingJoinPoint.proceed();//执行方法
		System.out.println("环绕后增强....");
		return obj;
	}
	
	//抛出异常增强 出现异常时执行
	@AfterThrowing(value="execution(* cn.itcast.spring3.demo1.UserDao.find(..))",throwing="e")
	public void afterThrowing(Throwable e){
		System.out.println("出现异常时执行..."+e.getMessage());
	}
	
	//最终通知 这里用到了下面定义的切点
	@After("MyAspect.myPointcut()")
	public void after(){
		System.out.println("最终通知...");
	}
}

第四步:创建applicationContext.xml

在applicationContext.xml中引入aop的约束
<!-- 自动生成代理:底层就是AnnotationAwareAspectJAutoProxyCreator -->
<aop:aspectj-autoproxy />
<bean id="userDao" class="cn.itcast.spring3.demo1.UserDao"></bean>
<bean id="myAspect" class="cn.itcast.spring3.demo1.MyAspect"></bean>

AspectJ的通知类型

@Before 前置通知,相当于BeforeAdvice
	就在方法之前执行.没有办法阻止目标方法执行的.
@AfterReturning 后置通知,相当于AfterReturningAdvice
	后置通知,获得方法返回值.
@Around 环绕通知,相当于MethodInterceptor
	在可以方法之前和之后来执行的,而且可以阻止目标方法的执行.
@AfterThrowing抛出通知,相当于ThrowAdvice
@After 最终final通知,不管是否异常,该通知都会执行
@DeclareParents 引介通知,相当于IntroductionInterceptor (不要求掌握)

切点的定义:

//作用:定义一个通用的表达式,使其它通知可以直接使用该切点
//使用方法:类名.方法名  可以查看上面的最终通知的使用
@Pointcut("execution(* cn.itcast.spring3.demo1.UserDao.find(..))")
private void myPointcut(){}

Advisor和Aspect的区别?

Advisor:Spring传统意义上的切面:支持一个切点和一个通知的组合.
Aspect:可以支持多个切点和多个通知的组合.

基于XML:

第一步:编写被增强的类:

ProductDao

第二步:定义切面

MyAspectXML

第三步:配置applicationContext.xml 引入AOP的名称空间

<!-- 配置被增强的类 -->
<bean id="productDao" class="cn.itcast.spring3.demo2.ProductDao"/>

<!-- 配置切面 -->
<bean id="myAspectXML" class="cn.itcast.spring3.demo2.MyAspectXML"/>

前置通知:

代码:

public void before(){
	System.out.println("前置通知...");
}

配置:

<aop:config>
	<!-- 定义切点: -->
	<aop:pointcut expression="execution(* cn.itcast.spring3.demo2.ProductDao.add(..))" id="mypointcut"/>
	<aop:aspect ref="myAspectXML">
		<!-- 前置通知 -->
		<aop:before method="before" pointcut-ref="mypointcut"/>
	</aop:aspect>
</aop:config>

后置通知:

代码:

public void afterReturing(Object returnVal){
	System.out.println("后置通知...返回值:"+returnVal);
}

配置:

<aop:config>
	<!-- 定义切点: -->
	<aop:pointcut expression="execution(* cn.itcast.spring3.demo2.ProductDao.add(..))" id="mypointcut"/>
	<aop:aspect ref="myAspectXML">
		<!-- 后置通知 -->
		<aop:after-returning method="afterReturing" pointcut-ref="mypointcut" returning="returnVal"/>
	</aop:aspect>
</aop:config>

环绕通知:

代码:

public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
	System.out.println("环绕前增强....");
	Object result = proceedingJoinPoint.proceed();
	System.out.println("环绕后增强....");
	return result;
}

配置:

<aop:config>
	<!-- 定义切点: -->
	<aop:pointcut expression="execution(* cn.itcast.spring3.demo2.ProductDao.add(..))" id="mypointcut"/>
	<aop:aspect ref="myAspectXML">
		<!-- 环绕通知 -->
		<aop:around method="around" pointcut-ref="mypointcut"/>
	</aop:aspect>
</aop:config>

异常通知:

代码:

public void afterThrowing(Throwable e){
	System.out.println("异常通知..."+e.getMessage());
}

配置:

<aop:config>
	<!-- 定义切点: -->
	<aop:pointcut expression="execution(* cn.itcast.spring3.demo2.ProductDao.add(..))" id="mypointcut"/>
	<aop:aspect ref="myAspectXML">
		<!-- 异常通知 -->
		<aop:after-throwing method="afterThrowing" pointcut-ref="mypointcut" throwing="e"/>
	</aop:aspect>
</aop:config>

最终通知:

代码:

public void after(){
	System.out.println("最终通知....");
}

配置:

<aop:config>
	<!-- 定义切点: -->
	<aop:pointcut expression="execution(* cn.itcast.spring3.demo2.ProductDao.add(..))" id="mypointcut"/>
	<aop:aspect ref="myAspectXML">
		<!-- 最终通知 -->
		<aop:after method="after" pointcut-ref="mypointcut"/>
	</aop:aspect>
</aop:config>

Spring的JDBC Template(JDBC模板)

JdbcTemplate模板与DbUtils工具类比较类似

1.6.1 Spring对持久层技术支持:

JDBC				:	org.springframework.jdbc.core.JdbcTemplate
Hibernate3.0		:	org.springframework.orm.hibernate3.HibernateTemplate
IBatis(MyBatis)		:	org.springframework.orm.ibatis.SqlMapClientTemplate
JPA					:	org.springframework.orm.jpa.JpaTemplate

开发JDBCTemplate入门

第一步:引入相应jar包:
	spring-tx-3.2.0.RELEASE.jar
	spring-jdbc-3.2.0.RELEASE.jar
	数据库驱动.

第二步:创建applicationContext.xml

第三步:编写一个测试类:

配置连接池

Spring默认的连接池

<!-- 配置Spring默认的连接池 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
	<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
	<property name="url" value="jdbc:mysql:///test"/>
	<property name="username" value="root"/>
	<property name="password" value="123"/>
</bean>

DBCP连接池:

导入jar包:
	com.springsource.org.apache.commons.dbcp-1.2.2.osgi.jar
	com.springsource.org.apache.commons.pool-1.5.3.jar
<!-- 配置DBCP连接池 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
	<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
	<property name="url" value="jdbc:mysql:///test"/>
	<property name="username" value="root"/>
	<property name="password" value="123"/>
</bean>

C3P0连接池:

导入jar包:
	com.springsource.com.mchange.v2.c3p0-0.9.1.2.jar
<!-- 配置c3p0连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
	<property name="driverClass" value="com.mysql.jdbc.Driver"/>
	<property name="jdbcUrl" value="jdbc:mysql:///spring3_day02"/>
	<property name="user" value="root"/>
	<property name="password" value="123"/>
</bean>

参数设置到属性文件中:

在src下创建jdbc.properties
jdbc.driver = com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql:///test
jdbc.user = root
jdbc.password = test
需要在applicationContext.xml 中使用属性文件配置的内容.
第一种写法:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
	<property name="location" value="classpath:jdbc.properties"></property>
</bean>
第二种写法:
<context:property-placeholder location="classpath:jdbc.properties"/>

JdbcTemplate的CRUD的操作:

Spring框架中提供了对持久层技术支持的类:
JDBC			:	org.springframework.jdbc.core.support.JdbcDaoSupport
Hibernate 3.0	:	org.springframework.orm.hibernate3.support.HibernateDaoSupport
iBatis			:	org.springframework.orm.ibatis.support.SqlMapClientDaoSupport

编写DAO的时候:
	Public class UserDao extends JdbcDaoSupport{
		...
	}

进行CRUD的操作;
	保存:update(String sql,Object... args)
	修改:update(String sql,Object... args)
	删除:update(String sql,Object... args)

查询:
	简单查询:
	select count(*) from user;			--- queryForInt(String sql);
	select name from user where id = ?;	--- queryForObject(String sql,Class clazz,Object... args);
	
	复杂查询:(返回对象,和对象集合)
	select * from user where id = ?		--- queryForObjectString sql,RowMapper<T> rowMapper,Object... args);
	select * from user;					--- query(String sql,RowMapper<T> rowMapper,Object... args);

Spring的事务管理

分层开发:事务处在Service层
Spring的事务管理分成两类:

  1. 编程式事务管理: 手动编写代码完成事务管理.
  2. 声明式事务管理: 不需要手动编写代码,配置

事务介绍

事务:是逻辑上一组操作,要么全都成功,要么全都失败.
事务特性:
	ACID:
	原子性:事务不可分割
	一致性:事务执行的前后,数据完整性保持一致.
	隔离性:一个事务执行的时候,不应该受到其他事务的打扰
	持久性:一旦结束,数据就永久的保存到数据库.

如果不考虑隔离性:
	脏读(Read Uncommitted):一个事务读到另一个事务未提交数据
	不可重复读(Read Committed):一个事务读到另一个事务已经提交数据(update)导致一个事务多次查询结果不一致
	可重复读()不会出现脏读,不可重复读,但会出现幻读
		事务在操作过程中进行两次查询,第二次查询的结果包含了第一次查询中未出现的数据或缺少了第一次查询中出现的数据
		(这里并不要求两次SQL语句相同),这是因为在两次查询过程中有另外一个事务插入数据造成的
	虚读:一个事务读到另一个事务已经提交数据(insert)导致一个事务多次查询结果不一致

事务的隔离级别:
	1. 未提交读:以上情况都有可能发生。
	2. 已提交读:避免脏读,但不可重复读,虚读是有可能发生。
	4. 可重复读:避免脏读,不可重复读,但是虚读有可能发生。
	8. 串行的(序列化):避免以上所有情况.

	mysql默认的格力级别是4
	oracle默认的隔离界别是2
	oracle只支持2和8隔离级别
	mysql全支持

Spring提供事务管理API:

PlatformTransactionManager:平台事务管理器.
commit(TransactionStatus status) 
getTransaction(TransactionDefinition definition) 
rollback(TransactionStatus status) 

TransactionDefinition:事务定义
	ISOLation_XXX:事务隔离级别.
	PROPAGATION_XXX:事务的传播行为.(不是JDBC中有的,为了解决实际开发问题.)
	过期时间:TIMEOUT_DEFAULT

TransactionStatus:事务状态
	是否有保存点
	是否一个新的事务
	事务是否已经提交

关系:PlatformTransactionManager通过TransactionDefinition设置事务相关信息管理事务,管理事务过程中,产生一些事务状态:状态由TransactionStatus记录.

API详解:

PlatformTransactionManager:接口.
	Spring为不同的持久化框架提供了不同PlatformTransactionManager接口实现

org.springframework.jdbc.datasource.DataSourceTransactionManager	:	使用Spring JDBC或iBatis 进行持久化数据时使用
org.springframework.orm.hibernate3.HibernateTransactionManager		: 	使用Hibernate3.0版本进行持久化数据时使用
org.springframework.orm.jpa.JpaTransactionManager	使用JPA进行持久化时使用
org.springframework.jdo.JdoTransactionManager	当持久化机制是Jdo时使用
org.springframework.transaction.jta.JtaTransactionManager	使用一个JTA实现来管理事务,在一个事务跨越多个资源时必须使用

TransactionDefinition:
	ISOLATION_DEFAULT:默认级别. Mysql  repeatable_read		oracle read_commited
	ISOLATION_READ_UNCOMMITTED
	ISOLATION_READ_COMMITTED
	ISOLATION_REPEATABLE_READ
	ISOLATION_SERIALIZABLE

事务的传播行为:(不是JDBC事务管理,用来解决实际开发的问题.)传播行为:解决业务层之间的调用的事务的关系.
PROPAGATION_REQUIRED		:支持当前事务,如果不存在 就新建一个
	A,B	如果A有事务,B使用A的事务,如果A没有事务,B就开启一个新的事务.(A,B是在一个事务中。)
PROPAGATION_SUPPORTS		:支持当前事务,如果不存在,就不使用事务
	A,B	如果A有事务,B使用A的事务,如果A没有事务,B就不使用事务.
PROPAGATION_MANDATORY	:支持当前事务,如果不存在,抛出异常
	A,B	如果A有事务,B使用A的事务,如果A没有事务,抛出异常.
PROPAGATION_REQUIRES_NEW	如果有事务存在,挂起当前事务,创建一个新的事务
	A,B	如果A有事务,B将A的事务挂起,重新创建一个新的事务.(A,B不在一个事务中.事务互不影响.)
PROPAGATION_NOT_SUPPORTED	以非事务方式运行,如果有事务存在,挂起当前事务
	A,B	非事务的方式运行,A有事务,就会挂起当前的事务.
PROPAGATION_NEVER 	以非事务方式运行,如果有事务存在,抛出异常
PROPAGATION_NESTED	如果当前事务存在,则嵌套事务执行
基于SavePoint技术.
	A,B	A有事务,A执行之后,将A事务执行之后的内容保存到SavePoint.B事务有异常的话,用户需要自己设置事务提交还是回滚.

常用:(重点)
	PROPAGATION_REQUIRED	
	PROPAGATION_REQUIRES_NEW
	PROPAGATION_NESTED

事务操作的环境搭建:

  1. 创建数据库和表…

  2. 创建一个Web项目:

     导入响应jar包
     引入配置文件:applicationContext.xml、log4j.properties、jdbc.properties
    
  3. 创建实体类:

     AccountService
     AccountDao
    
  4. 在Spring中注册:

<!-- 业务层类 -->
<bean id="accountService" class="cn.itcast.spring3.demo1.AccountServiceImpl">
	<!-- 在业务层注入Dao -->
	<property name="accountDao" ref="accountDao"/>
</bean>

<!-- 持久层类 -->
<bean id="accountDao" class="cn.itcast.spring3.demo1.AccountDaoImpl">
	<!-- 注入连接池的对象,通过连接池对象创建模板. -->
	<property name="dataSource" ref="dataSource"/>
</bean>

5.编写测试类

Spring的事务管理

手动编码的方式完成事务管理:

需要事务管理器:真正管理事务对象.
	Spring提供了事务管理的模板(工具类.)

第一步:注册事务管理器:

<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<!-- 需要注入连接池,通过连接池获得连接 -->
	<property name="dataSource" ref="dataSource"/>
</bean>

第二步:注册事务模板类:

<!-- 事务管理的模板 -->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
	<property name="transactionManager" ref="transactionManager"/>
</bean>

第三步:在业务层注入模板类:(模板类管理事务)

<!-- 业务层类 -->
<bean id="accountService" class="cn.itcast.spring3.demo1.AccountServiceImpl">
	<!-- 在业务层注入Dao -->
	<property name="accountDao" ref="accountDao"/>
	<!-- 在业务层注入事务的管理模板 -->
	<property name="transactionTemplate" ref="transactionTemplate"/>
</bean>

第四步:在业务层代码上使用模板:

public void transfer(final String from, final String to, final Double money) {
	transactionTemplate.execute(new TransactionCallbackWithoutResult() {
		@Override
		protected void doInTransactionWithoutResult(TransactionStatus status) {
			accountDao.out(from, money);
			int d = 1 / 0;
			accountDao.in(to, money);
		}
	});
}

手动编码方式缺点: 代码量增加,代码有侵入性.

声明事务管理:(原始方式)

基于TransactionProxyFactoryBean.
	导入:aop相应jar包.

第一步:注册平台事务管理器:

<!-- 事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<!-- 注入连接池 -->
	<property name="dataSource" ref="dataSource"/>
</bean>

第二步:创建业务层代理对象:

<!-- 配置生成代理对象 -->
<bean id="accountServiceProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
	<!-- 目标对象 -->
	<property name="target" ref="accountService"/>
	<!-- 注入事务管理器 -->
	<property name="transactionManager" ref="transactionManager"/>
	<!-- 事务的属性设置 -->
	<property name="transactionAttributes">
		<props>
			<prop key="transfer">PROPAGATION_REQUIRED</prop>
		</props>
	</property>
</bean>

第三步:编写测试类:

@Autowired
@Qualifier("accountServiceProxy")//千万注意:注入代理对象!
private AccountService accountService;
prop格式:PROPAGATION,ISOLATION,readOnly,-Exception,+Exception
顺序:传播行为、隔离级别、事务是否只读、发生哪些异常可以回滚事务(所有的异常都回滚)、发生了哪些异常不回滚.
缺点:就是需要为每一个管理事务的类生成代理.需要为每个类都需要进行配置.

###声明式事务管理:(自动代理,基于切面)###

第一步:导入相应jar包.

aspectj

第二步:引入相应约束: aop、tx约束.

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
	http://www.springframework.org/schema/beans/spring-beans.xsd
	http://www.springframework.org/schema/context
	http://www.springframework.org/schema/context/spring-context.xsd
	http://www.springframework.org/schema/aop
	http://www.springframework.org/schema/aop/spring-aop.xsd
	http://www.springframework.org/schema/tx 
	http://www.springframework.org/schema/tx/spring-tx.xsd">
</beans>

第三步:注册事务管理器;

<!-- 事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<property name="dataSource" ref="dataSource"/>
</bean>

第四步:定义增强(事务管理)

<!-- 定义一个增强 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
	<!-- 增强(事务)的属性的配置 -->
	<tx:attributes>
		<!-- 
			isolation:DEFAULT	:事务的隔离级别.
			propagation			:事务的传播行为.
			read-only			:false.不是只读
			timeout				:-1
			no-rollback-for		:发生哪些异常不回滚
			rollback-for		:发生哪些异常回滚事务
		 -->
		<tx:method name="transfer"/>
	</tx:attributes>
</tx:advice>

第五步:定义aop的配置(切点和通知的组合)

<!-- aop配置定义切面和切点的信息 -->
<!-- config 参数:proxy-target-class="false" false:默认基于JDK动态代理 true:基于cglib -->
<aop:config>
	<!-- 定义切点:哪些类的哪些方法应用增强 -->
	<aop:pointcut expression="execution(* cn.itcast.spring3.demo3.AccountService+.*(..))" id="mypointcut"/>
	<!-- 定义切面: -->
	<aop:advisor advice-ref="txAdvice" pointcut-ref="mypointcut"/>
</aop:config>

第六步:编写测试类: 注入Service对象,不需要注入代理对象(生成这个类的时候,已经是代理对象.)

基于注解的事务管理:

第一步:事务管理器:

<!-- 事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<property name="dataSource" ref="dataSource"/>
</bean>

第二步:注解事务:

<!-- 开启注解的事务管理 -->
<tx:annotation-driven transaction-manager="transactionManager"/>

第三步:在Service上使用注解

@Transactional//注解中有属性值: isolation,propagation,readOnly...
public class AccountService{
    @Autowired
    private AccountDao accountDao;
    //转账的业务操作
    public void transfer(String from,String to,double money){
        accountDao.outMoney(from,money);
        //int d=1/0;
        accountDao.inMoney(to,money);
    }
}

Spring笔记 2018年10月27日 MingFeng197


相关标签: Spring