- 浏览: 514454 次
- 性别:
- 来自: 北京
文章分类
- 全部博客 (563)
- 工作经验 (12)
- 数据库 (13)
- Servlet (10)
- Struts2 (1)
- Spring (25)
- Eclipse (5)
- Hibernate (5)
- Eclips (8)
- HTTP (7)
- J2EE (21)
- EHcache (1)
- HTML (11)
- 工具插件使用 (20)
- JPA (2)
- 杂谈 (17)
- 数据结构与算法 (3)
- Cloud Foundry (1)
- 安全 (10)
- J2SE (57)
- SQL (9)
- DB2 (6)
- 操作系统 (2)
- 设计模式 (1)
- 版本代码管理工具 (13)
- 面试 (10)
- 代码规范 (3)
- Tomcat (12)
- Ajax (5)
- 异常总结 (11)
- REST (2)
- 云 (2)
- RMI (3)
- SOA (1)
- Oracle (12)
- Javascript (20)
- jquery (7)
- JSP自定义标签 (2)
- 电脑知识 (5)
- 浏览器 (3)
- 正则表达式 (3)
- 建站解决问题 (38)
- 数据库设计 (3)
- git (16)
- log4j (1)
- 每天100行代码 (1)
- socket (0)
- java设计模式 耿祥义著 (0)
- Maven (14)
- ibatis (7)
- bug整理 (2)
- 邮件服务器 (8)
- Linux (32)
- TCP/IP协议 (5)
- java多线程并发 (7)
- IO (1)
- 网页小工具 (2)
- Flash (2)
- 爬虫 (1)
- CSS (6)
- JSON (1)
- 触发器 (1)
- java并发 (12)
- ajaxfileupload (1)
- js验证 (1)
- discuz (2)
- Mysql (14)
- jvm (2)
- MyBatis (10)
- POI (1)
- 金融 (1)
- VMWare (0)
- Redis (4)
- 性能测试 (2)
- PostgreSQL (1)
- 分布式 (2)
- Easy UI (1)
- C (1)
- 加密 (6)
- Node.js (1)
- 事务 (2)
- zookeeper (3)
- Spring MVC (2)
- 动态代理 (3)
- 日志 (2)
- 微信公众号 (2)
- IDEA (1)
- 保存他人遇到的问题 (1)
- webservice (11)
- memcached (3)
- nginx (6)
- 抓包 (1)
- java规范 (1)
- dubbo (3)
- xwiki (1)
- quartz (2)
- 数字证书 (1)
- spi (1)
- 学习编程 (6)
- dom4j (1)
- 计算机系统知识 (2)
- JAVA系统知识 (1)
- rpcf (1)
- 单元测试 (2)
- php (1)
- 内存泄漏cpu100%outofmemery (5)
- zero_copy (2)
- mac (3)
- hive (3)
- 分享资料整理 (0)
- 计算机网络 (1)
- 编写操作系统 (1)
- springboot (1)
最新评论
-
masuweng:
亦论一次OutOfMemoryError的定位与解错 -
变脸小伙:
引用[color=red][/color]百度推广中运用的技术 ...
Spring 3 mvc中返回pdf,json,xml等不同的view -
Vanillva:
不同之处是什么??
Mybatis中的like查询 -
thrillerzw:
转了。做个有理想的程序员
有理想的程序员必须知道的15件事 -
liujunhui1988:
觉得很有概括力
15 个必须知道的 Java 面试问题(2年工作经验)
源:https://segmentfault.com/a/1190000008379179
评:
起因
考虑如下一个例子:
@Target(value = {ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyMonitor {
}
@Component
@Aspect
public class MyAopAdviseDefine {
private Logger logger = LoggerFactory.getLogger(getClass());
@Pointcut("@annotation(com.xys.demo4.MyMonitor)")
public void pointcut() {
}
// 定义 advise
@Before("pointcut()")
public void logMethodInvokeParam(JoinPoint joinPoint) {
logger.info("---Before method {} invoke, param: {}---", joinPoint.getSignature().toShortString(), joinPoint.getArgs());
}
}
@Service
public class SomeService {
private Logger logger = LoggerFactory.getLogger(getClass());
public void hello(String someParam) {
logger.info("---SomeService: hello invoked, param: {}---", someParam);
test();
}
@MyMonitor
public void test() {
logger.info("---SomeService: test invoked---");
}
}
@EnableAspectJAutoProxy(proxyTargetClass = true)
@SpringBootAppliMyion
public class MyAopDemo {
@Autowired
SomeService someService;
public static void main(String[] args) {
SpringAppliMyion.run(MyAopDemo.class, args);
}
@PostConstruct
public void aopTest() {
someService.hello("abc");
}
}
在这个例子中, 我们定义了一个注解 MyMonitor, 这个是一个方法注解, 我们的期望是当有此注解的方法被调用时, 需要执行指定的切面逻辑, 即执行 MyAopAdviseDefine.logMethodInvokeParam 方法.
在 SomeService 类中, 方法 test() 被 MyMonitor 所注解, 因此调用 test() 方法时, 应该会触发 logMethodInvokeParam 方法的调用. 不过有一点我们需要注意到, 我们在 MyAopDemo 测试例子中, 并没有直接调用 SomeService.test() 方法, 而是调用了 SomeService.hello() 方法, 在 hello 方法中, 调用了同一个类内部的 SomeService.test() 方法. 按理说, test() 方法被调用时, 会触发 AOP 逻辑, 但是在这个例子中, 我们并没有如愿地看到 MyAopAdviseDefine.logMethodInvokeParam 方法的调用, 这是为什么呢?
这是由于 Spring AOP (包括动态代理和 CGLIB 的 AOP) 的限制导致的. Spring AOP 并不是扩展了一个类(目标对象), 而是使用了一个代理对象来包装目标对象, 并拦截目标对象的方法调用. 这样的实现带来的影响是: 在目标对象中调用自己类内部实现的方法时, 这些调用并不会转发到代理对象中, 甚至代理对象都不知道有此调用的存在.
即考虑到上面的代码中, 我们在 MyAopDemo.aopTest() 中, 调用了 someService.hello("abc"), 这里的 someService bean 其实是 Spring AOP 所自动实例化的一个代理对象, 当调用 hello() 方法时, 先进入到此代理对象的同名方法中, 然后在代理对象中执行 AOP 逻辑(因为 hello 方法并没有注入 AOP 横切逻辑, 因此调用它不会有额外的事情发生), 当代理对象中执行完毕横切逻辑后, 才将调用请求转发到目标对象的 hello() 方法上. 因此当代码执行到 hello() 方法内部时, 此时的 this 其实就不是代理对象了, 而是目标对象, 因此再调用 SomeService.test() 自然就没有 AOP 效果了.
简单来说, 在 MyAopDemo 中所看到的 someService 这个 bean 和在 SomeService.hello() 方法内部上下文中的 this 其实代表的不是同一个对象(可以通过分别打印两者的 hashCode 以验证), 前者是 Spring AOP 所生成的代理对象, 而后者才是真正的目标对象(SomeService 实例).
解决
弄懂了上面的分析, 那么解决这个问题就十分简单了. 既然 test() 方法调用没有触发 AOP 逻辑的原因是因为我们以目标对象的身份(target object) 来调用的, 那么解决的关键自然就是以代理对象(proxied object)的身份来调用 test() 方法.
因此针对于上面的例子, 我们进行如下修改即可:
@Service
public class SomeService {
private Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private SomeService self;
public void hello(String someParam) {
logger.info("---SomeService: hello invoked, param: {}---", someParam);
self.test();
}
@CatMonitor
public void test() {
logger.info("---SomeService: test invoked---");
}
}
上面展示的代码中, 我们使用了一种很 subtle 的方式, 即将 SomeService bean 注入到 self 字段中(这里再次强调的是, SomeService bean 实际上是一个代理对象, 它和 this 引用所指向的对象并不是同一个对象), 因此我们在 hello 方法调用中, 使用 self.test() 的方式来调用 test() 方法, 这样就会触发 AOP 逻辑了.
Spring AOP 导致的 @Transactional 不生效的问题
这个问题同样地会影响到 @Transactional 注解的使用, 因为 @Transactional 注解本质上也是由 AOP 所实现的.
例如我在 stackoverflow 上看到的一个类似的问题: Spring @Transaction method call by the method within the same class, does not work?
这里也记录下来以作参考.
那个哥们遇到的问题如下:
public class UserService {
@Transactional
public boolean addUser(String userName, String password) {
try {
// call DAO layer and adds to database.
} catch (Throwable e) {
TransactionAspectSupport.currentTransactionStatus()
.setRollbackOnly();
}
}
public boolean addUsers(List<User> users) {
for (User user : users) {
addUser(user.getUserName, user.getPassword);
}
}
}
他在 addUser 方法上使用 @Transactional 来使用事务功能, 然后他在外部服务中, 通过调用 addUsers 方法批量添加用户. 经过了上面的分析后, 现在我们就可知道其实这里添加注解是不会启动事务功能的, 因为 AOP 逻辑整个都没生效嘛.
解决这个问题的方法有两个, 一个是使用 AspectJ 模式的事务实现:
<tx:annotation-driven mode="aspectj"/>
另一个就是和我们刚才在上面的例子中的解决方式一样:
public class UserService {
private UserService self;
public void setSelf(UserService self) {
this.self = self;
}
@Transactional
public boolean addUser(String userName, String password) {
try {
// call DAO layer and adds to database.
} catch (Throwable e) {
TransactionAspectSupport.currentTransactionStatus()
.setRollbackOnly();
}
}
public boolean addUsers(List<User> users) {
for (User user : users) {
self.addUser(user.getUserName, user.getPassword);
}
}
}
评:
起因
考虑如下一个例子:
@Target(value = {ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyMonitor {
}
@Component
@Aspect
public class MyAopAdviseDefine {
private Logger logger = LoggerFactory.getLogger(getClass());
@Pointcut("@annotation(com.xys.demo4.MyMonitor)")
public void pointcut() {
}
// 定义 advise
@Before("pointcut()")
public void logMethodInvokeParam(JoinPoint joinPoint) {
logger.info("---Before method {} invoke, param: {}---", joinPoint.getSignature().toShortString(), joinPoint.getArgs());
}
}
@Service
public class SomeService {
private Logger logger = LoggerFactory.getLogger(getClass());
public void hello(String someParam) {
logger.info("---SomeService: hello invoked, param: {}---", someParam);
test();
}
@MyMonitor
public void test() {
logger.info("---SomeService: test invoked---");
}
}
@EnableAspectJAutoProxy(proxyTargetClass = true)
@SpringBootAppliMyion
public class MyAopDemo {
@Autowired
SomeService someService;
public static void main(String[] args) {
SpringAppliMyion.run(MyAopDemo.class, args);
}
@PostConstruct
public void aopTest() {
someService.hello("abc");
}
}
在这个例子中, 我们定义了一个注解 MyMonitor, 这个是一个方法注解, 我们的期望是当有此注解的方法被调用时, 需要执行指定的切面逻辑, 即执行 MyAopAdviseDefine.logMethodInvokeParam 方法.
在 SomeService 类中, 方法 test() 被 MyMonitor 所注解, 因此调用 test() 方法时, 应该会触发 logMethodInvokeParam 方法的调用. 不过有一点我们需要注意到, 我们在 MyAopDemo 测试例子中, 并没有直接调用 SomeService.test() 方法, 而是调用了 SomeService.hello() 方法, 在 hello 方法中, 调用了同一个类内部的 SomeService.test() 方法. 按理说, test() 方法被调用时, 会触发 AOP 逻辑, 但是在这个例子中, 我们并没有如愿地看到 MyAopAdviseDefine.logMethodInvokeParam 方法的调用, 这是为什么呢?
这是由于 Spring AOP (包括动态代理和 CGLIB 的 AOP) 的限制导致的. Spring AOP 并不是扩展了一个类(目标对象), 而是使用了一个代理对象来包装目标对象, 并拦截目标对象的方法调用. 这样的实现带来的影响是: 在目标对象中调用自己类内部实现的方法时, 这些调用并不会转发到代理对象中, 甚至代理对象都不知道有此调用的存在.
即考虑到上面的代码中, 我们在 MyAopDemo.aopTest() 中, 调用了 someService.hello("abc"), 这里的 someService bean 其实是 Spring AOP 所自动实例化的一个代理对象, 当调用 hello() 方法时, 先进入到此代理对象的同名方法中, 然后在代理对象中执行 AOP 逻辑(因为 hello 方法并没有注入 AOP 横切逻辑, 因此调用它不会有额外的事情发生), 当代理对象中执行完毕横切逻辑后, 才将调用请求转发到目标对象的 hello() 方法上. 因此当代码执行到 hello() 方法内部时, 此时的 this 其实就不是代理对象了, 而是目标对象, 因此再调用 SomeService.test() 自然就没有 AOP 效果了.
简单来说, 在 MyAopDemo 中所看到的 someService 这个 bean 和在 SomeService.hello() 方法内部上下文中的 this 其实代表的不是同一个对象(可以通过分别打印两者的 hashCode 以验证), 前者是 Spring AOP 所生成的代理对象, 而后者才是真正的目标对象(SomeService 实例).
解决
弄懂了上面的分析, 那么解决这个问题就十分简单了. 既然 test() 方法调用没有触发 AOP 逻辑的原因是因为我们以目标对象的身份(target object) 来调用的, 那么解决的关键自然就是以代理对象(proxied object)的身份来调用 test() 方法.
因此针对于上面的例子, 我们进行如下修改即可:
@Service
public class SomeService {
private Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private SomeService self;
public void hello(String someParam) {
logger.info("---SomeService: hello invoked, param: {}---", someParam);
self.test();
}
@CatMonitor
public void test() {
logger.info("---SomeService: test invoked---");
}
}
上面展示的代码中, 我们使用了一种很 subtle 的方式, 即将 SomeService bean 注入到 self 字段中(这里再次强调的是, SomeService bean 实际上是一个代理对象, 它和 this 引用所指向的对象并不是同一个对象), 因此我们在 hello 方法调用中, 使用 self.test() 的方式来调用 test() 方法, 这样就会触发 AOP 逻辑了.
Spring AOP 导致的 @Transactional 不生效的问题
这个问题同样地会影响到 @Transactional 注解的使用, 因为 @Transactional 注解本质上也是由 AOP 所实现的.
例如我在 stackoverflow 上看到的一个类似的问题: Spring @Transaction method call by the method within the same class, does not work?
这里也记录下来以作参考.
那个哥们遇到的问题如下:
public class UserService {
@Transactional
public boolean addUser(String userName, String password) {
try {
// call DAO layer and adds to database.
} catch (Throwable e) {
TransactionAspectSupport.currentTransactionStatus()
.setRollbackOnly();
}
}
public boolean addUsers(List<User> users) {
for (User user : users) {
addUser(user.getUserName, user.getPassword);
}
}
}
他在 addUser 方法上使用 @Transactional 来使用事务功能, 然后他在外部服务中, 通过调用 addUsers 方法批量添加用户. 经过了上面的分析后, 现在我们就可知道其实这里添加注解是不会启动事务功能的, 因为 AOP 逻辑整个都没生效嘛.
解决这个问题的方法有两个, 一个是使用 AspectJ 模式的事务实现:
<tx:annotation-driven mode="aspectj"/>
另一个就是和我们刚才在上面的例子中的解决方式一样:
public class UserService {
private UserService self;
public void setSelf(UserService self) {
this.self = self;
}
@Transactional
public boolean addUser(String userName, String password) {
try {
// call DAO layer and adds to database.
} catch (Throwable e) {
TransactionAspectSupport.currentTransactionStatus()
.setRollbackOnly();
}
}
public boolean addUsers(List<User> users) {
for (User user : users) {
self.addUser(user.getUserName, user.getPassword);
}
}
}
发表评论
-
使用Spring+Junit+Mockito做代码自测
2019-05-29 15:27 510源:https://blog.csdn.net/z19917 ... -
Spring Transaction属性之Propagation
2016-08-10 17:09 548源:http://blog.csdn.net/kiwi_cod ... -
循环依赖检测方法 spring源码方法
2016-07-06 18:58 1167场景:checkForAliasCircle(name, al ... -
Spring的Quartz定时器同一时刻重复执行二次的问题解决
2016-03-11 18:27 1004源:http://www.linuxidc.com/Linux ... -
spring factory-method
2016-03-01 11:22 487源:http://blog.sina.com.cn/s/blo ... -
spring中lazy-init详解
2016-02-29 17:01 779源:http://blog.csdn.net/fhx0 ... -
Spring Refresh Application Context
2015-12-13 14:48 861源:http://techdive.in/spring/spr ... -
spring context 扫描与 mvc扫描类 区分开包
2015-07-15 13:23 6111.applicationContext.xml <!- ... -
Filter中注入spring
2015-07-09 10:45 512源:http://zy116494718.iteye.com/ ... -
获取spring的ApplicationContext几种方式
2015-06-24 15:35 698源:http://blog.sina.com.cn/s/blo ... -
spring获取webapplicationcontext,applicationcontext几种方法详解
2015-04-02 16:38 466源:http://www.blogjava.net/Todd/ ... -
Spring+Mybatis整合事务不起作用之解决方案汇总
2014-12-29 21:36 1353源:http://blog.csdn.net/walkerjo ... -
context:component-scan扫描使用上的容易忽略的use-default-filters
2014-12-29 21:29 450源:http://jinnianshilongnian.ite ... -
Spring线程池开发实战
2014-12-12 10:44 499源:http://blog.csdn.net/chszs/ar ... -
AOP生成代码有三种可能方式
2014-05-11 21:24 449源:http://blog.csdn.net/zuyi532/ ... -
Spring 3 mvc中返回pdf,json,xml等不同的view
2014-05-09 12:11 1041源:http://jackyrong.iteye.com/bl ... -
使用Spring MVC统一异常处理实战
2014-05-04 00:00 525源:http://cgs1999.iteye.com/blog ... -
spring配置文件异常
2013-11-26 10:53 906源:http://wodar.iteye.com/blog/ ... -
自己对spring ioc的理解
2013-07-14 11:59 1075此时心情:有时候工作的很努力,很认真也会被辞退,被小外包公 ... -
spring 定时器配置
2013-03-20 15:09 1096源:http://blog.csdn.net/cl61917 ...
相关推荐
在Spring AOP的例子中,我们可能会创建一个`@RunWith(SpringJUnit4ClassRunner.class)`标记的测试类,以利用Spring的测试支持。在测试方法中,可以注入需要的bean,然后调用方法来触发AOP代理。这样,通知将在适当的...
这里定义了两个通知:`logBefore` 和 `logAfterReturning`,分别用于在方法调用前和返回后记录日志。`execution(* com.example.service.UserService.*(..))` 定义了一个切点,表示 `UserService` 类中的所有方法。 ...
本示例是关于如何在Spring Boot项目中实现AOP功能的一个简单演示。 首先,我们需要了解AOP的基本概念。AOP的核心是切面(Aspect),它封装了跨越多个对象的行为或关注点,如日志记录。切点(Pointcut)定义了在何处...
代理对象在调用方法时会触发InvocationHandler的invoke()方法,从而可以在这个方法中实现对方法调用的拦截。JDK动态代理要求被代理类必须实现一个接口,代理类会实现相同的接口。 Spring框架中的AOP模块使用了动态...
在这个"SpringAOP的例子"中,我们将深入探讨如何在Eclipse环境下利用Spring AOP和动态代理来实现这些功能。 首先,让我们理解什么是AOP。AOP是一种编程范式,旨在减少代码的重复性和增强可维护性。在传统的OOP中,...
这些通知可以在方法调用前、后或发生特定情况时执行。 3. **切点(Pointcut)**:切点是程序执行中的特定点,例如某个方法的调用。切点表达式用于定义这些点,它可以用Java或XPath语法编写,并与通知关联,决定何时...
假设我们需要在调用某个公共方法前记录日志,我们可以定义一个`BeforeAdvice`,并在目标方法上应用此通知。 ```java package com.example.aop; import org.aspectj.lang.JoinPoint; import org.aspectj.lang....
在运行时,JDK动态代理会创建一个新的类,该类实现目标对象的所有接口,并在方法调用时插入自定义的行为(通知)。Spring的`java.lang.reflect.Proxy`类和`java.lang.reflect.InvocationHandler`接口是实现JDK动态...
以注解为例,`@Aspect`注解用于定义一个切面类,`@Before`、`@After`、`@Around`、`@AfterReturning`和`@AfterThrowing`分别代表在方法执行前、后、前后以及异常发生后执行的通知类型。这些注解可以与`@Pointcut`...
在实际的Spring配置中,我们需要定义一个切面类,然后在类上使用`@Aspect`注解。在类中,我们创建带有通知注解的方法。此外,还需要在Spring配置文件中启用AOP代理,并将切面类注册到Spring容器中。对于基于注解的...
在"SpringAOP测试Demo"中,我们通常会涉及以下几个核心概念和操作: 1. **切面(Aspect)**:切面是关注点的一个模块化,它包括了连接点、通知、目标对象、织入和引入。在Spring AOP中,切面通常由一个或多个注解的...
"通知"是指在特定连接点执行的实际代码,它可以是前置通知(在方法调用前执行)、后置通知(在方法调用后执行,无论结果如何)、异常通知(在方法抛出异常后执行)、最终通知(无论方法是否正常结束都会执行)以及...
它通过反射机制在运行时生成一个实现了所有目标接口的代理类实例,代理类会调用InvocationHandler接口的invoke方法来转发方法调用。 3. **CGLIB代理**:如果目标对象没有实现任何接口,Spring将使用CGLIB(Code ...
在Spring中,可以使用`@Aspect`注解来定义一个切面类,然后在该类中定义通知方法。 ```java @Aspect public class LoggingAspect { @Before("execution(* com.example.service.*.*(..))") public void logBefore...
然后创建一个通知,在方法调用前后打印日志信息。通过注解将这个切面应用到目标类,观察日志输出,验证AOP是否正确工作。 在实践中,还可以尝试以下操作: - 使用`@Before`、`@After`、`@AfterReturning`、`@...
JDK动态代理适用于实现了接口的目标对象,通过反射机制创建一个代理类来拦截方法调用。而CGLIB是在运行时动态生成一个目标类的子类,从而实现对方法的拦截。如果目标对象没有实现接口,Spring将自动切换到CGLIB代理...
通过上述步骤,你可以创建一个简单的Spring AOP应用,实现对特定方法的调用进行日志记录。当然,AOP的潜力远不止于此,你可以根据实际场景扩展通知类型,或者创建更复杂的切入点表达式,以实现更细粒度的控制。 在...
例如,我们可以创建一个`LoggingAspect`类,其中包含一个前置通知,用来在方法调用前打印日志。 ```java public class LoggingAspect { @Before("execution(* com.example.service.*.*(..))") public void log...
Spring AOP通过构建一个拦截器链(interceptor chain)来实现方法的增强。每个拦截器都有机会在方法调用前后执行自己的逻辑。这种链式结构使得多个增强逻辑可以被灵活地组合在一起。 **5. **Spring AOP配置与使用**...
在Spring AOP中,切面可以由一个单独的类定义,这个类包含了通知和其他元数据。 - **通知(Advice)**:通知是在特定连接点上执行的代码,即切面的逻辑。Spring支持五种类型的通知:前置通知(Before)、后置通知...