1 aop:config标签:主要是用来配置自动代理
1,加入schame文件
2,加入相应jar包
3,xml文件需要加入几句话...具体看文档
使用aop的专用标签来完成相关的配置.
需要引入一些相应的schame文件spring-aop-3.2.xsd
<aop:config> : 配置代理对象
筛选出切入点
把相应的切面带到切入点
1,<aop:pointcut>+<aop:advisor>
2,<aop:pointcut>+<aop:aspect>+<aop:after method=""/>
其中主要表现是使用Aspect的expression的操作:
execution(表达式):主要用来筛选连接点-->切入点
方法
权限修饰符 返回值 包 方法名 参数列表
public * com.briup.service.*.*(*)
表达式:
1,返回类型模式
* 任意的返回类型 返回类型模式决定了方法的返回类型必须依次匹配一个连接点。
2,名字模式
* 所有或者部分命名模式 名字模式匹配的是方法名,
3,参数模式
() 没有参数的方法
(..) 参数任意的方法
(*) 一个参数的方法
(*,String) 匹配两个参数,第一个参数任意,第二个必须为String
注意在使用之前需要在xml文件的beans标签中加入新的schame文件:并在Eclipse中进行关联配置
<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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">
下面给出一些常见切入点表达式的例子。
1)任意包下的任意类中的公共方法的执行:
execution(public * *(..)) 公有的 返回值不限 方法名不限 参数任意
2)任何一个以“set”开始的方法的执行:
execution(* set*(..)) 所有的以set开头 参数任意的方法
3)AccountService 接口的任意方法的执行:
execution(* com.briup.service.AccountService.*(..)) 所有 这个类中方法参数不限
4)定义在service包里的任意方法的执行:
execution(* com.briup.service.*.find*(..)) 所有 这个包下所有类里面的所有方法
5)定义在service包或者子包里的任意方法的执行:
execution(* com.briup.service..*.*(..)) 所有 包下以及service的子包下所有类下所有方法 参数不限
eg:expression="execution(* com.briup.service.*.*(..)) or execution(* com.shiyue.service.*.*(..))"
筛选连接点-->切入点
注意: 1.从spring容器中拿代理对象的时候也是要用目标对象的名字来拿。
2.没有实现任何接口的目标对象也能产生代理对象。
<!-- 配置aop的代理 -->
<aop:config>
<!-- 定义一个切入点 并给切入点起名为myPointCut -->
<!-- 切入点是一组连接点的集合 -->
<aop:pointcut expression="execution(public * com.briup.aop.service.*.*(..))" id="myPointCut"/>
<!-- 定义哪一个advice在哪一个切入点上面起作用 -->
<aop:advisor advice-ref="beforeAdvice" pointcut-ref="myPointCut" />
</aop:config>
织入: 切面 --> 切入点
<!--
expression="execution(public * com.briup.aop.service.*.*(..))"
这个引号""里面就是用表达式的方式来定义切入点,只要是符合我们这个表达式要求的
方法就是我们的连接点,连接点的集合就是我们要定义的切入点。
表达式中从左到右的*号:
第一个* 表示方法的返回类型不限。
第二个* 表示包中的任意一个类
第三个* 表示类中的任意一个方法
同时方法的参数也没有限制.
-->
2 在一个切面类中定个多个方法,根据xml文件的配置每个方法都可以织入到切入点的不同位置,并且advice是在aop的标签中进行配置,不需要再写对应的advice类了
例如:
//这个类相当于我们之前的切面类
//只不过这个切面类中有很多方法都可以织入到切入点上面
//我们可以控制把这里的任何一个方法织入到任何一个切入点上面
public class XmlHandler {
public void beforeTest(JoinPoint p){
System.out.println(p.getSignature().getName()+" before...");
}
public void afterTest(JoinPoint p){
System.out.println(p.getSignature().getName()+" after...");
}
public void afterReturningTest(JoinPoint p){
System.out.println(p.getSignature().getName()+" afterReturning");
}
//在和aroundAdvice结合的时候,这个方法一定要加上这个ProceedingJoinPoint类型的参数
public Object aroundTest(ProceedingJoinPoint pjp)throws Throwable{
//JoinPoint对象不能调用连接点所表示的方法
//ProceedingJoinPoint能调用连接点所表示的方法 pjp.proceed()
System.out.println(pjp.getSignature().getName()+" is start..");
//调用到连接点方法
Object obj = pjp.proceed();
System.out.println(pjp.getSignature().getName()+" is end..");
return obj;
}
public void throwingTest(JoinPoint p,Exception ex){
System.out.println(p.getSignature().getName()+" is throwing..."+ex.getMessage());
}
}
xml文件配置:
<!-- 配置dao层对象 -->
<bean id="dao"
class="com.briup.aop.dao.AccountDaoImpl"/>
<!-- 配置目标对象 -->
<bean name="target"
class="com.briup.aop.service.AccountServiceImpl">
<property name="accountDao" ref="dao"></property>
</bean>
<!-- 配置切面类 -->
<bean name="handler" class="com.briup.aop.xml.XmlHandler"></bean>
<!-- 配置aop的代理 -->
<aop:config>
<!-- 定义切入点名为myPointCut -->
<aop:pointcut expression="execution(public * com.briup.aop.service.*.*(..))" id="myPointCut"/>
<!-- 定义切面类 以及需要使用的advice -->
<aop:aspect id="aspect" ref="handler">
<!-- 表示beforeAdvice会把切面类handler中的beforeTest方法织入到名字叫myPointCut的切入点上面 -->
<aop:before method="beforeTest" pointcut-ref="myPointCut"/>
<!-- after表示不管方法是否正常结束都会起作用 -->
<aop:after method="afterTest" pointcut-ref="myPointCut"/>
<!-- after-returning表示方法正常结束才会起作用(抛异常时候不起作用) -->
<aop:after-returning method="afterReturningTest" pointcut-ref="myPointCut"/>
<aop:around method="aroundTest" pointcut-ref="myPointCut"/>
<!-- throwing="ex"表示throwingTest方法中接收异常对象的名字一定要是ex -->
<aop:after-throwing method="throwingTest" pointcut-ref="myPointCut" throwing="ex"/>
</aop:aspect>
</aop:config>
3 使用注解配置AOP:其实就是在上面的类XmlHandler中加入上注解然后去掉xml中的aop标签配置,这里把类改名为AnnotationHandler,
切面类:@Aspect
写在类上
切入点: @Pointcut 空方法是@载体
写一个空方法:因为@Pointcut-->id-->空方法名()
通知: @Before,@After,@AfterReturning,@Around
切面的方法
<aop:aspectj-autoproxy/>:如果使用了AOP有关注解 需要这个标签标示
例子:
@Component
@Aspect//标示这个类是切面类 写在类上
public class AnnotationHandler {
/*
* 在一个方法上面加上注解来定义切入点
* 这个切入点的名字就是这个方法的名字
* 这个方法本身不需要有什么作用
* 这个方法的意义就是:给这个 @Pointcut注解一个可以书写的地方
* 因为注解只能写在方法、属性、类的上面,并且方法名作为切入点的名字
* @Pointcut 注解 写在方法上,""中写切入点表达式
* */
@Pointcut("execution(public * com.briup.aop.service..*.*(..))")
public void myPointCut(){}
//注:这里面的所有方法的JoinPoint类型对象都可以去掉不写
@Before("myPointCut()")// 前置通知 携带下面方法 织入到切入点
public void beforeTest(JoinPoint p){
System.out.println(p.getSignature().getName()+" before...");
}
/*
* @After和@AfterReturning
*
* @After标注的方法会在切入点上的方法结束后被调用(不管是不是正常的结束).
* @AfterReturning标注的方法只会在切入点上的方法正常结束后才被调用.
* */
@After("myPointCut()")
public void afterTest(JoinPoint p){
System.out.println(p.getSignature().getName()+" after...");
}
@AfterReturning("myPointCut()")
public void afterReturningTest(JoinPoint p){
System.out.println(p.getSignature().getName()+" afterReturning");
}
@Around("myPointCut()")
public Object aroundTest(ProceedingJoinPoint pjp)throws Throwable{
System.out.println(pjp.getSignature().getName()+" is start..");
//调用连接点的方法去执行
Object obj = pjp.proceed();
System.out.println(pjp.getSignature().getName()+" is end..");
return obj;
}
//在切入点中的方法执行期间抛出异常的时候,会调用这个 @AfterThrowing注解所标注的方法
@AfterThrowing(value="myPointCut()",throwing="ex")
public void throwingTest(JoinPoint p,Exception ex){
System.out.println(p.getSignature().getName()+" is throwing..."+ex.getMessage());
}
}
xml配置:注意给例子中使用的其他的类上面也使用注解
<aop:aspectj-autoproxy/>
<context:component-scan base-package="com.briup.aop"/>
4 Spring与jdbc结合 Connection
jdbc编程不变,主要是Connection对象的维护,即配置并使用数据源
- <!-- 基于jdk的规范数据源 -->
<bean name="dataSource1"
class="oracle.jdbc.pool.OracleConnectionPoolDataSource">
<property name="networkProtocol">
<value>tcp</value>
</property>
<property name="databaseName">
<value>XE</value>
</property>
<property name="driverType">
<value>thin</value>
</property>
<property name="portNumber">
<value>1521</value>
</property>
<property name="user">
<value>briup</value>
</property>
<property name="serverName">
<value>127.0.0.1</value>
</property>
<property name="password">
<value>briup</value>
</property>
</bean>
注意:别忘了读取配置文件
<!-- 读取这个资源文件 读完之后下面就可以用${key}来去文件中的value值了 -->
<!-- 这种方式是我们第一节学习的那种配置方式方式的简写 -->
<context:property-placeholder location="com/briup/db/jdbc/jdbc.properties"/>
- <!-- dbcp数据源 -->
<bean id="dataSource2"
class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName">
<value>${jdbc.driverClassName}</value>
</property>
<property name="url">
<value>${jdbc.url}</value>
</property>
<property name="username">
<value>${jdbc.username}</value>
</property>
<property name="password">
<value>${jdbc.password}</value>
</property>
<!-- 最大连接数 -->
<property name="maxActive">
<value>80</value>
</property>
<!-- 最大空闲连接数 -->
<property name="maxIdle">
<value>20</value>
</property>
<!-- 最大等待时间:当没有可用连接时,连接池等待连接被归还的最大时间 单位:毫秒 -->
<!-- 超过时间则抛出异常,如果设置为-1表示无限等待 -->
<property name="maxWait">
<value>3000</value>
</property>
</bean>
- <!-- spring提供的一种数据源 -->
<bean id="dataSource3" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>${jdbc.driverClassName}</value>
</property>
<property name="url">
<value>${jdbc.url}</value>
</property>
<property name="username">
<value>${jdbc.username}</value>
</property>
<property name="password">
<value>${jdbc.password}</value>
</property>
</bean>
Connection-->面向DataSource接口编程:
重点注意:
重点注意:
重点注意:
java代码:
//基础获取数据连接对象的方法:spring 不会管理
DataSource dbcp_data = (DataSource)container.getBean("dataSource");
Connection conn = dbcp_data.getConnection();
//获取连接对象的第二种方式:这样获取的连接对象 spring会帮我们管理事务
Connection conn_spring = DataSourceUtils.getConnection(dbcp_data);
spring在jdbc的使用中还给我们提供了一个模板类:JdbcTemplate
用以简化我们的jdbc操作
例如:
//使用xml进行配置时候的java类中的写法:
public class JdbcTemplateDao implements AccountDao{
private JdbcTemplate jdbcTemplate;
//只要类中有set方法 在xml文件就可以进行配置和注入
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
...
}
//使用注解进行配置时候的java类中的写法:
@Repository
public class JdbcTemplateDao implements AccountDao {
private JdbcTemplate jdbcTemplate;
@Autowired
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
....
...
}
注意:JdbcTemplate模板类如何使用:在htmlsingle中搜索即可,其中包含有大量的使用实例
5 Spring与Mybatis结合
使用Spring整合Mybatis时我们可以不需要mybatis_config.xml文件。
1,能结合
2,记住去哪里找文档
3,看懂文档里的东西
4,自己玩
需要先配置好数据源:
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>${jdbc.driverClassName}</value>
</property>
<property name="url">
<value>${jdbc.url}</value>
</property>
<property name="username">
<value>${jdbc.username}</value>
</property>
<property name="password">
<value>${jdbc.password}</value>
</property>
</bean>
重点在于配置sessionFactory
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<!-- 自动扫描mapping.xml文件 -->
<property name="mapperLocations" value="classpath:com/briup/a/dao/*.xml"></property>
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.briup.db" />
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean>
<!-- MyBatis 配置spring的事务管理器 相当于aop配置中的切面类-->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 配置事务拦截器 相当于aop配置中advice-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- 配置在些方法上面加入哪些事务属性 以及抛出什么异常的时候回滚-->
<tx:method name="with*" propagation="REQUIRED" rollback-for="Throwable"/>
<tx:method name="depo*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!-- 上面配置了切面类以及advice -->
<!-- 这里就要做aop的配置了 -->
<aop:config>
<!-- 配置切入点 -->
<aop:pointcut id="myPointCut"
expression="execution(* com.briup.db.Mybatis..*.*(..))"/>
<!-- 配置事务拦截器在哪一个切入点上起作用 -->
<aop:advisor pointcut-ref="myPointCut" advice-ref="txAdvice"/>
</aop:config>
声明式事务
1,事务管理器
<!-- 配置Mybatis的事务管理器 (切面类)-->
<bean id="MytxTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
2,事务拦截器
<!-- 配置事务拦截器 -->
<tx:advice id="transactionInterceptor" transaction-manager="transactionManager">
<tx:attributes>
<!-- *代表所有的方法 -->
<tx:method name="*" propagation="REQUIRED" rollback-for="Throwable"/>
</tx:attributes>
</tx:advice>
3,aop标签 配置自动代理
<!-- 配置aop -->
<aop:config>
<!-- 配置切入点 -->
<aop:pointcut expression="execution(public * com.briup.tran.service.*.*(..))" id="myPointCut"/>
<!-- 配置事务拦截器在哪一个切入点上起作用 -->
<aop:advisor advice-ref="transactionInterceptor" pointcut-ref="myPointCut"/>
</aop:config>
<tx:advice/> 专业用于配置事务有关的设置
事务传播设置 是 REQUIRED
隔离级别是DEFAULT
事务是 读/写
事务超时默认是依赖于事务系统的,或者事务超时没有被支持。
任何 RuntimeException 将触发事务回滚,但是任何 checked Exception 将不触发事务回滚
这些默认的设置当然也是可以被改变的。
<tx:advice/> 和 <tx:attributes/> 标签里的 <tx:method/> 各种属性设置总结如下:
属性 是否需要? 默认值 描述
name 是 无 与事务属性关联的方法名。通配符(*)可以用来指定一批关联到相同的事务属性的方法。 如:'get*'、'handle*'、'on*Event'等等。
propagation 不 REQUIRED 事务传播行为
isolation 不 DEFAULT 事务隔离级别
timeout 不 -1 事务超时的时间(以秒为单位)
read-only 不 false 事务是否只读
rollback-for 不 无 将被触发进行回滚的 Exception(s);以逗号分开。 如:'com.foo.MyBusinessException,ServletException'
no-rollback-for 不 不 发生异常任然提交 Exception(s);以逗号分开。 如:'com.foo.MyBusinessException,ServletException'
事务传播行为:
注意:
事务属性包括:
PROPAGATION_REQUIRED --支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
PROPAGATION_SUPPORTS --支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY--支持当前事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW--新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED--以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER--以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED--如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。
----------------------------------------------
了解内容:
<prop key="myMethod">PROPAGATION_REQUIRED,readOnly,-Exception</prop>
这样的配置,其中:
-Exception表示有Exception抛出时,事务回滚. -代表回滚+就代表提交
readonly 就是read only, 设置操作权限为只读,一般用于查询的方法,优化作用.
注意:指定事务属性的取值有较复杂的规则。具体的书写规则如下:
: 传播行为 [,隔离级别] [,只读属性] [,超时属性] [不影响提交的异常] [,导致回滚的异常]
1)事务传播行为
所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。在TransactionDefinition定义中包括了如下几个表示传播行为的常量:
TransactionDefinition.PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
TransactionDefinition.PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。
TransactionDefinition.PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
TransactionDefinition.PROPAGATION_NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。
这里需要指出的是,前面的六种事务传播行为是 Spring 从 EJB 中引入的,他们共享相同的概念。而 PROPAGATION_NESTED是 Spring 所特有的。以 PROPAGATION_NESTED 启动的事务内嵌于外部事务中(如果存在外部事务的话),此时,内嵌事务并不是一个独立的事务,它依赖于外部事务的存在,只有通过外部的事务提交,才能引起内部事务的提交,嵌套的子事务不能单独提交。如果熟悉 JDBC 中的保存点(SavePoint)的概念,那嵌套事务就很容易理解了,其实嵌套的子事务就是保存点的一个应用,一个事务中可以包括多个保存点,每一个嵌套子事务。另外,外部事务的回滚也会导致嵌套子事务的回滚。
2)事务隔离级别
隔离级别是指若干个并发的事务之间的隔离程度。TransactionDefinition 接口中定义了五个表示隔离级别的常量:
TransactionDefinition.ISOLATION_DEFAULT:这是默认值,表示使用底层数据库的默认隔离级别。对大部分数据库而言,通常这值就是TransactionDefinition.ISOLATION_READ_COMMITTED。
TransactionDefinition.ISOLATION_READ_UNCOMMITTED:该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。该级别不能防止脏读和不可重复读,因此很少使用该隔离级别。
TransactionDefinition.ISOLATION_READ_COMMITTED:该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,这也是大多数情况下的推荐值。
TransactionDefinition.ISOLATION_REPEATABLE_READ:该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。即使在多次查询之间有新增的数据满足该查询,这些新增的记录也会被忽略。该级别可以防止脏读和不可重复读。
TransactionDefinition.ISOLATION_SERIALIZABLE:所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
3)事务的只读属性
事务的只读属性是指,对事务性资源进行只读操作或者是读写操作。所谓事务性资源就是指那些被事务管理的资源,比如数据源、 JMS 资源,以及自定义的事务性资源等等。如果确定只对事务性资源进行只读操作,那么我们可以将事务标志为只读的,以提高事务处理的性能。在 TransactionDefinition 中以 boolean 类型来表示该事务是否只读
4)事务超时
所谓事务超时,就是指一个事务所允许执行的最长时间,如果超过该时间限制但事务还没有完成,则自动回滚事务。在 TransactionDefinition 中以 int 的值来表示超时时间,其单位是秒。
5)事务的回滚规则
通常情况下,如果在事务中抛出了未检查异常(继承自 RuntimeException 的异常),则默认将回滚事务。如果没有抛出任何异常,或者抛出了已检查异常,则仍然提交事务。这通常也是大多数开发者希望的处理方式,也是 EJB 中的默认处理方式。但是,我们可以根据需要人为控制事务在抛出某些未检查异常时任然提交事务,或者在抛出某些已检查异常时回滚事务。
-Exception表示有Exception抛出时,事务回滚. -代表回滚+就代表提交
事务以及事务所引发的问题
1.脏读 主要针对update操作。 一个事务A读到另一个事务B中修改过但是还没有提交的数据
2.不可重复读 主要针对update操作。 一个事务A在第一次读数据和第二次读数据之间,有另一个事务B把这个数据更改并提交了,所以就出现了事务A里面读一个数据俩次,但是读到的结果是不同的。
3.幻读 主要针对的是insert/delete操作。事务A第一次用where条件筛选出了10条数据,事务A第二次用通样的where条件筛选出的却是11条数据,因为事务B在事务A的第一次和第二次查询之间进行了插入操作,并且插入的这个数据满足事务A的where筛选条件.
isolation 事务隔离级别
read-uncommitted 不提交也能读(三个问题都存在)
read-committed 提交之后才能读 解决了脏读
repeatable-read 解决了脏读和不可重复读
serializable 三个问题都解决了
级别越高解决的问题越多但是效率越低。
注意:并不是所有数据库都支持这四种事务隔离级别,比如oracle就只支持第二种和第四种这俩种,比如mysql就四种全支持.
oracle里面默认的事务隔离级别是第二种:read-committed