好记性不如烂笔头98-spring3学习(19)-实施spring AOP事务的方法的漏网之鱼
来源:互联网 发布:淘宝发布宝贝模版 编辑:程序博客网 时间:2024/06/05 08:41
Spring的事务增强管理,主要是基于各种动态代理或者动态字节码技术;
对于基于接口动态代理的AOP事务来说,因为接口都是要求public的,因此这个问题并不突出;
对于基于CGLIb动态字节码技术的方案来说,因为使用final,static,private修饰的方法不能够被子类覆盖,这些方法都无法进行AOP增强。
因此,在需要事务的场景,一定要注意是否有漏网之鱼。这种隐藏的错误是非常难被发现的。
1) 前提和准备
我们需要利用log4j的debug模式来观察,因此我们需要系统能支持log4j运行;
我们需要观察事务,因此我们要访问一个数据库。
create table FFM_USER( USERNAME VARCHAR2(64), PASSWORD VARCHAR2(64), LAST_LOGON_TIME VARCHAR2(64), CHARGE INTEGER)
里面有一条数据: 在username字段对应“ffm”
2) 业务场景
UserService里面有很多的方法,有的方法能够被AOP增强,有的不行。
能够被增强的会启动一个事务,如果没有启动事务,说明无法被AOP增强
3) 实现简单的查看漏网之鱼的源代码
基础父类
package com.spring.special;import org.slf4j.Logger;import org.slf4j.LoggerFactory;/** * 模拟spring事务嵌套调用的父类 * @author 范芳铭 */ public class BaseService { protected static final Logger log = LoggerFactory.getLogger(BaseService.class);}
模拟被spring的AOP事务增强的漏网之鱼
package com.spring.special;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;import org.springframework.jdbc.core.JdbcTemplate;import org.springframework.stereotype.Service;/** * 模拟被spring的AOP事务增强的漏网之鱼 * private, final,static都不能被AOP事务增强,public和protected可以 * @author 范芳铭 */ @Service("userService")public class UserService extends BaseService{ @Autowired private JdbcTemplate jdbcTemplate; private void logon_private(String userName){ updateLastLogonTime(userName); String name = Thread.currentThread().getStackTrace()[1].getMethodName(); System.out.println("当前运行的方法:" + name); } public final void logon_final(String userName){ updateLastLogonTime(userName); String name = Thread.currentThread().getStackTrace()[1].getMethodName(); System.out.println("当前运行的方法:" + name); } public static void logon_static(UserService user){ user.updateLastLogonTime("ffm"); String name = Thread.currentThread().getStackTrace()[1].getMethodName(); System.out.println("当前运行的方法:" + name); } public void logon_public(String userName){ updateLastLogonTime(userName); String name = Thread.currentThread().getStackTrace()[1].getMethodName(); System.out.println("当前运行的方法:" + name); } protected void logon_protected(String userName){ updateLastLogonTime(userName); String name = Thread.currentThread().getStackTrace()[1].getMethodName(); System.out.println("当前运行的方法:" + name); } public void updateLastLogonTime(String userName){ String sql = "update ffm_user u set u.last_logon_time = ? where username =? "; jdbcTemplate.update(sql, System.currentTimeMillis(),userName); } //模拟容器运行 public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("b_special.xml"); UserService user = (UserService)ctx.getBean("userService"); user.logon_private("ffm"); user.logon_final("ffm"); //静态方法比较特殊,特殊处理下 logon_static(user); user.logon_public("ffm"); user.logon_protected("ffm"); }}
4)配置文件b_special.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:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:task="http://www.springframework.org/schema/task" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd"> <context:component-scan base-package="com.spring.special"/> <context:property-placeholder location="classpath:dbconfig.properties" /> <bean id = "dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method = "close" p:driverClassName="${jdbc.o2o.driverClassName}" p:url="${jdbc.o2o.url}" p:username="${jdbc.o2o.username}" p:password="${jdbc.o2o.password}" /> <bean id="jdbcTemplate" class ="org.springframework.jdbc.core.JdbcTemplate" p:dataSource-ref="dataSource" /> <bean id="jdbcManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" p:dataSource-ref="dataSource" /> <tx:advice id="jdbcAdvice" transaction-manager="jdbcManager"> <tx:attributes> <tx:method name = "*" /> </tx:attributes> </tx:advice> <aop:config proxy-target-class="true"> <aop:pointcut id="serviceJdbcMethod" expression="within(com.spring.special.BaseService+)" /> <aop:advisor pointcut-ref="serviceJdbcMethod" advice-ref="jdbcAdvice" order="0" /> </aop:config></beans>
5)观察和结论
开启DEBUG模式,启动“模拟容器运行”应用。查看日志。
利用“Creating new transaction”在一大堆日志中查找,我们能看到
Creating new transaction with name [com.spring.special.UserService.logon_public]:
Creating new transaction with name [com.spring.special.UserService.logon_protected]:
就只有这两个启动了新的事务。
其他的,就是日志操作方法本身的事务的信息。
Creating new transaction with name [com.spring.special.UserService.updateLastLogonTime]
结论
A被private, final,static修饰的方法都不能被AOP事务增强;
B 被public和protected修饰的方法可以被AOP事务增强。
如果在一个方法中,包括了A,也包括了B,那么最终以A为准。
- 好记性不如烂笔头98-spring3学习(19)-实施spring AOP事务的方法的漏网之鱼
- 好记性不如烂笔头93-spring3学习(14)-spring事务传播机制的7剑客
- 好记性不如烂笔头97-spring3学习(18)-多线程模式下的spring事务
- 好记性不如烂笔头82-spring3学习(3)-spring的Resource使用和传统方法的对比
- 好记性不如烂笔头83-spring3学习(4)-spring的BeanFactory(IoC容器)
- 好记性不如烂笔头84-spring3学习(5)-spring的ApplicationContext(应用上下文)
- 好记性不如烂笔头94-spring3学习(15)-用spring的jdbcTemplate简单访问数据库
- 好记性不如烂笔头92-spring3学习(13)-spring和hibernate访问数据库的小差异
- 好记性不如烂笔头88-spring3学习(9)-schema的配置的解读和说明
- 好记性不如烂笔头85-spring3学习(6)-BeanFactory 中bean的生命周期
- 好记性不如烂笔头86-spring3学习(7)-ApplicationContext中bean的生命周期
- 好记性不如烂笔头87-spring3学习(8)-IoC的理解和比喻
- 好记性不如烂笔头91-spring3学习(12)-bean的PostConstruct和PreDestroy
- 好记性不如烂笔头96-spring3学习(17)-利用debug观察事务传播
- 好记性不如烂笔头95-spring3学习(16)-spring配置log4j
- 好记性不如烂笔头100-spring3(21)-高并发下的数据库自增主键
- 好记性不如烂笔头101-spring3(21)-jdbcTemplate和NamedParameterJdbcTemplate的小差异
- 好记性不如烂笔头102-spring3(22)-高并发下的延迟加载陷阱
- 基于visual c++之windows核心编程代码分析(17)通过pipe进程间通信
- Ceph 用户管理
- POJ 2533-Longest Ordered Subsequence
- Java多线程-工具篇-BlockingQueue
- 网站缓存技术总结( ehcache memcache redis)
- 好记性不如烂笔头98-spring3学习(19)-实施spring AOP事务的方法的漏网之鱼
- linux内核函数fls()
- android(java) socket判断网络连接状态
- 备战网络工程师认证考试:历年真题合集
- mybatis和spring整合
- Bootloader流程-Linux
- VC CString,int,string,char*之间的转换
- dnsmasq TFTP directory /tftpd inaccessible: Permission denied
- hdu 4027 线段树