Spring AOP为什么不能拦截从java对象调用方法内部调用的方法

spring boot(12)
& & & &spring对应java web开发的同学来说,都不陌生,其中事务@Transactional在service层更是常常使用。
1.aop类内部调用不拦截原因
细心的同学也许早就发现当service中的某个没标注@Transactional的方法调用另一个标注了@Transactional的方法时,居然没开启事务。例如
public class UserService {
@Autowired
private UserMapper userM
@Transactional(propagation=Propagation.REQUIRED,isolation=Isolation.DEFAULT,readOnly=false)
public void insert01(User u){
this.userMapper.insert(u);
throw new RuntimeException(&测试插入事务&);
public void insert02(User u){
this.insert01(u);
throw new RuntimeException(&测试插入事务&);
}& &当controller或其他service直接调用insert01(User u)时,事务是正常的,数据库里面的确没数据;但是如果调用的是insert02(User u)方法,异常抛出了,但是数据库里面居然有数据,说明事务不正常了。
& & 我们知道@Transactional其实就是一个aop代理,是一个cglib动态代理(常用的动态代理有,有)。
& & 直接调用insert01(User u)时,其实流程是这样的:
也就是说controller或其他service首先调用的是AOP代理对象而不是目标对象,首先执行事务切面,事务切面内部通过TransactionInterceptor环绕增强进行事务的增强,即进入目标方法之前开启事务,退出目标方法时提交/回滚事务。
& controller或其他service调用insert02(User u)时,流程是这样的:
此处的this指向目标对象,因此调用this.b()将不会执行b事务切面,即不会执行事务增强。
我们通过debugger方式看看controller中的userService和insert02(xxx)中的this,会发现controller中的userService是一个代理对象
看到userService=UserService$$EnhanceBySpringCGLIB$$c2174c0b
这this明显和controller中的userSerivce不是同一个了。
& & &如果同学了解cglib(执行期进行代码植入)或aspectJ(编译期就进行代码植入,反编译后看更清晰了解,)。为了让同学对上面说的代理对象有个更直观的印象,我参考aspectJ那样编译后的代码植入方式,写个UserServiceProxy.java.(spring
对aop的真实处理不是这样的,我这只是举个例子直观说明,有些同学对invoke()方法不直观)
public class UserServiceProxy extends UserService{
//模拟目标对象实例化
private UserService targetService = new UserService();
public void insert01(User u) {
System.out.println(&开启事务.....transational starting....&);
targetService.insert01(u);
System.out.println(&结束事务.....transational end......&);
public void insert02(User u) {
targetService.insert01(u);
& &也就是说controller中的userSerivce实际是UserServiceProxy,那么UserServiceProxy.insert02()的时候里面调用的是目标对象UserService.insert01()了,并不是代理对象UserServiceProxy.insert01(),所以事务没有开启。
2.aop类内部调用拦截生效的解决方案
& &2.1 方案一--从beanFactory中获取对象
& &刚刚上面说到controller中的UserService是代理对象,它是从beanFactory中得来的,那么service类内调用其他方法时,也先从beanFacotry中拿出来就OK了。
public void insert02(User u){
getService().insert01(u);
private UserService getService(){
return SpringContextUtil.getBean(this.getClass());
& 2.2 方案二--获取代理对象
private UserService getService(){
// 采取这种方式的话,
//@EnableAspectJAutoProxy(exposeProxy=true,proxyTargetClass=true)
//必须设置为true
return AopContext.currentProxy() != null ? (UserService)AopContext.currentProxy() :
}& 如果aop是使用注解的话,那需要@EnableAspectJAutoProxy(exposeProxy=true,proxyTargetClass=true),如果是xml配置的,把expose-proxy设置为true,如
&aop:config expose-proxy=&true&&
& & & &&aop:aspect ref=&XXX&&
& & & & & &!-- 省略---&
& & & &&/aop:aspect&
&/aop:config&
& 2.3方案三--将项目转为aspectJ项目
& &将项目转为aspectJ项目,aop转为aspect 类。具体就不写了
& 2.4 方案四--BeanPostProcessor
& &通过BeanPostProcessor 在目标对象中注入代理对象,定义InjectBeanSelfProcessor类,实现BeanPostProcessor。也不具体写了
& &也许还有其他方案。
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:163387次
积分:2919
积分:2919
排名:第11687名
原创:118篇
转载:87篇
评论:27条
(3)(16)(5)(2)(1)(4)(3)(3)(9)(4)(9)(16)(2)(1)(2)(1)(1)(4)(14)(19)(1)(9)(18)(9)(1)(2)(17)(6)(9)(14)Spring AOP无法拦截内部方法调用 - 简书
Spring AOP无法拦截内部方法调用
假设一个接口里面有两个方法:
package demo.
public interface CustomerService {
public void doSomething1();
public void doSomething2();
接口实现类如下:
package demo.long.
import demo.long.CustomerS
public class CustomerServiceImpl implements CustomerService {
public void doSomething1() {
System.out.println("CustomerServiceImpl.doSomething1()");
doSomething2();
public void doSomething2() {
System.out.println("CustomerServiceImpl.doSomething2()");
现在我需要在CustomerService接口的每个方法被调用时都在方法前执行一些逻辑,所以需要配置一个拦截器:
package demo.
import org.aspectj.lang.annotation.A
import org.aspectj.lang.annotation.B
public class CustomerServiceInterceptor {
@Before("execution(* demo.long..*.*(..))")
public void doBefore() {
System.out.println("do some important things before...");
把Bean加到Spring配置中
&aop:aspectj-autoproxy /&
&bean id="customerService" class="demo.long.impl.CustomerServiceImpl" /&
&bean id="customerServiceInterceptor" class="demo.long.CustomerServiceInterceptor" /&
如果现在外部对象调用CustomerService的doSomething1()方法的时候,会发现只有doSomething1()方法执行前打印了“do some important things before...”,而doSomething1()内部调用doSomething2()时并没有打印上述内容;外部对象单独调用doSomething2()时会打印上述内容。
public class CustomerServiceTest {
@Autowired
ICustomerService customerS
public void testAOP() {
customerService.doSomething1();
拦截器的实现原理就是动态代理,实现AOP机制。Spring 的代理实现有两种:一是基于 JDK Dynamic Proxy 技术而实现的;二是基于 CGLIB 技术而实现的。如果目标对象实现了接口,在默认情况下Spring会采用JDK的动态代理实现AOP,CustomerServerImpl正是这种情况。
JDK动态代理生成的CustomerServiceImpl的代理类大致如下:
public class CustomerServiceProxy implements CustomerService {
private CustomerService customerS
public void setCustomerService(CustomerService customerService) {
this.customerService = customerS
public void doSomething1() {
doBefore();
customerService.doSomething1();
public void doSomething2() {
doBefore();
customerService.doSomething2();
private void doBefore() {
// 例如,可以在此处开启事务或记录日志
System.out.println("do some important things before...");
客户端程序使用代理类对象去调用业务逻辑:
public class TestProxy {
public static void main(String[] args) {
// 创建代理目标对象
// 对于Spring来说,这一工作是由Spring容器完成的。
CustomerService serviceProxyTarget = new CustomerServiceImpl();
// 创建代理对象
// 对于Spring来说,这一工作也是由Spring容器完成的。
CustomerServiceProxy serviceProxy = new CustomerServiceProxy();
serviceProxy.setCustomerService(serviceProxyTarget);
CustomerService serviceBean = (CustomerService) serviceP
// 调用业务逻辑操作
serviceBean.doSomething1();
执行main方法,发现doSomething1()中调用doSomething2()方法的时候并未去执行CustomerServiceProxy类的doBefore()方法。其实doSomething2()等同于this.doSomething2(),在CustomerServiceImpl类中this关键字表示的是当前这个CustomerServiceImpl类的实例,所以程序会去执行CustomerServiceImpl对象中的doSomething2()方法,而不会去执行CustomerServiceProxy类对象中的 doSomething2()方法。
在使用Spring AOP的时候,我们从IOC容器中获取的Bean对象其实都是代理对象,而不是那些Bean对象本身,由于this关键字引用的并不是该Service Bean对象的代理对象,而是其本身,因此Spring AOP是不能拦截到这些被嵌套调用的方法的。
修改类,不要出现“自调用”的情况:这是Spring文档中推荐的“最佳”方案;
若一定要使用“自调用”,那么this.doSomething2()替换为:((CustomerService) AopContext.currentProxy()).doSomething2();此时需要修改spring的aop配置:&aop:aspectj-autoproxy expose-proxy="true" /&Spring aop @aspect无法执行切入步骤 - J2EE当前位置:& &&&Spring aop @aspect无法执行切入步骤Spring aop @aspect无法执行切入步骤&&网友分享于:&&浏览:0次Spring aop @aspect无法执行切入方法,代码如下:
package&com.mon.
import&java.util.D
import&javax.annotation.R
import&org.aspectj.lang.ProceedingJoinP
import&org.aspectj.lang.annotation.A
import&org.aspectj.lang.annotation.AfterR
import&org.aspectj.lang.annotation.AfterT
import&org.aspectj.lang.annotation.A
import&org.aspectj.lang.annotation.A
import&org.aspectj.lang.annotation.B
import&org.aspectj.lang.annotation.P
import&org.
import&com.hexin.core.annotation.AnnotationU
import&com.hexin.core.annotation.RedisR
import&com.hexin.core.util.JSONU
import&com.hexin.core.util.cache.ShardedRedisU
@Component
public&class&AspectJRedisRead&{
@Pointcut("execution(*&com.hexin.pettyLoan.*.service.impl.*.*(..))")
private&void&pointCut(){}
public&AspectJRedisRead(){
System.out.println("aaaaaa");
// @Resource(name="shardedRedisUtil")
// ShardedRedisUtil&redisU
@Before("pointCut()")&&&&//spring中Before通知&&
&&&&public&void&readBefore()&{&&
&&&&&&&&System.out.println("readBefore:现在时间是:"+new&Date());&&
&&&&@After("pointCut()")&&&&//spring中After通知&&
&&&&public&void&readAfter()&{&&
&&&&&&&&System.out.println("readAfter:现在时间是:"+new&Date());&&
&&&&@AfterReturning("pointCut()")
&&&&public&void&readAfterReturning(){
&&&& System.out.println("readAfterReturning:现在时间是:"+new&Date());&&
&&&&@AfterThrowing("pointCut()")
&&&&public&void&readAfterThrowing(){
&&&& System.out.println("readAfterThrowing:现在时间是:"+new&Date());
&&&&@Around("pointCut()")&&&//spring中Around通知&&
&&&&public&Object&readAround(ProceedingJoinPoint&joinPoint)&{&&
&&&& Object&result&=&
&&&& try&{
System.out.println("readAround开始:现在时间是:"+new&Date());&&
result&=&joinPoint.proceed(joinPoint.getArgs());&
System.out.println("readAround结束:现在时间是:"+new&Date());
}&catch&(Throwable&e)&{
e.printStackTrace();
&&&& return&
配置文件已加入aspectj-autoproxy&如下:
&context:annotation-config&&/context:annotation-config&
&!--&启用aop&--&
&aop:aspectj-autoproxy&proxy-target-class="true"&/&
service实现类
@Service("flexkeyService")
public&class&FlexkeyServiceImpl&implements&FlexkeyService&{
&&&&&&&&@Override
public&String&redisTest(Integer&id){
return&"this&is&from&function";
controller调用
@RequestMapping("/redistest.do")
public&@ResponseBody&String&redistest(String&callback){
JsonResult&result&=&new&JsonResult();
result&=&new&JsonResult(1,&null,&flexkeyService.redisTest(1));
catch(ErrorCodeException&ex){
result&=&new&JsonResult(-1,&ex.toMessage(),&null);
logger.error(ex.toMessage(),&ex);
String&json&=JSONUtil.toJsonpString(result,&callback);
但是,在执行了redisTest方法的时候,,@Around等对应的方法
请大神帮忙查查,是什么原因?------解决思路----------------------1.&component-scan&定义了没
2.&试试&@Before("execution(*&com.hexin.pettyLoan.*.service.impl.*.*(..))")------解决思路----------------------引用:问题找到了
result&=&new&JsonResult(1,&null,&flexkeyService.redisTest(1));
这个方法里的flexkeyService对象如果使用autowared注入,无法启动aspect,
flexkeyService&=&ctx.getBean("xxxxx")获取,是可以启用aspect的
这是什么原因?
new&JsonResult:&直接&new&的对象不行,这个时候&flexkeyService&并不是注入进来的,即使有&@Autowired&注解,这时的注解没有任何作用。
只有&Spring&生成的对象才有&AOP&功能,因为&Spring&生成的代理对象才有&AOP&功能。------解决思路----------------------引用:Quote: 引用:
问题找到了
result&=&new&JsonResult(1,&null,&flexkeyService.redisTest(1));
这个方法里的flexkeyService对象如果使用autowared注入,无法启动aspect,
flexkeyService&=&ctx.getBean("xxxxx")获取,是可以启用aspect的
这是什么原因?
new&JsonResult:&直接&new&的对象不行,这个时候&flexkeyService&并不是注入进来的,即使有&@Autowired&注解,这时的注解没有任何作用。
只有&Spring&生成的对象才有&AOP&功能,因为&Spring&生成的代理对象才有&AOP&功能。
请问在什么情况下,会切入不进去。我也遇到了这样的问题,困扰了好久了。
12345678910
12345678910
12345678910 上一篇:下一篇:文章评论相关解决方案 1234567891011 Copyright & &&版权所有本帖子已过去太久远了,不再提供回复功能。本帖子已过去太久远了,不再提供回复功能。

我要回帖

更多关于 js获取调用方法的对象 的文章

 

随机推荐