Spring AOP cannot intercept inner method calls

Suppose an interface has two methods:

package demo.long;

public interface CustomerService {  
    public void doSomething1();  
    public void doSomething2();  
}

The interface implementation class is as follows:

package demo.long.impl;

import demo.long.CustomerService; 

public class CustomerServiceImpl implements CustomerService {  

    public void doSomething1() {  
        System.out.println("CustomerServiceImpl.doSomething1()");  
        doSomething2();  
    }  

    public void doSomething2() {  
        System.out.println("CustomerServiceImpl.doSomething2()");  
    }  

}

Now I need to execute some logic before each method of the CustomerService interface when it is called, so I need to configure an interceptor:

package demo.long;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class CustomerServiceInterceptor {

    @Before("execution(* demo.long..*.*(..))")
    public void doBefore() {
        System.out.println("do some important things before..."); 
    }
}

Add the Bean to the Spring configuration

<aop:aspectj-autoproxy />

<bean id="customerService" class="demo.long.impl.CustomerServiceImpl" />
<bean id="customerServiceInterceptor" class="demo.long.CustomerServiceInterceptor" />

If the external object calls the doSomething1() method of CustomerService now, you will find that only the doSomething1() method prints "do some important things before..." before the execution of the doSomething1() method, and the above is not printed when doSomething1() internally calls doSomething2(). content; the above is printed when the outer object calls doSomething2() alone.

public class CustomerServiceTest {

    @Autowired
    ICustomerService customerService;

    @Test
    public void testAOP() {
        customerService.doSomething1();
    }
}

Cause Analysis

The implementation principle of the interceptor is dynamic proxy, which implements the AOP mechanism. There are two kinds of proxy implementations in Spring: one is based on JDK Dynamic Proxy technology; the other is based on CGLIB technology. If the target object implements the interface, by default Spring will use the JDK's dynamic proxy to implement AOP, which is the case for CustomerServerImpl.

The proxy class of CustomerServiceImpl generated by JDK dynamic proxy is roughly as follows:

public class CustomerServiceProxy implements CustomerService {  

    private CustomerService customerService;  

    public void setCustomerService(CustomerService customerService) {  
        this.customerService = customerService;  
    }  

    public void doSomething1() {  
        doBefore();  
        customerService.doSomething1();  
    }  

    public void doSomething2() {  
        doBefore();  
        customerService.doSomething2();  
    }  

    private void doBefore() {  
        // 例如,可以在此处开启事务或记录日志
        System.out.println("do some important things before...");  
    }  

}

Client programs use proxy class objects to invoke business logic:

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) serviceProxy;  

        // 调用业务逻辑操作  
        serviceBean.doSomething1();  
    }  
}

Execute the main method and find that the doBefore() method of the CustomerServiceProxy class is not executed when the doSomething2() method is called in doSomething1(). In fact, doSomething2() is equivalent to this.doSomething2(). The this keyword in the CustomerServiceImpl class represents the current instance of the CustomerServiceImpl class, so the program will execute the doSomething2() method in the CustomerServiceImpl object, but not the CustomerServiceProxy class. The doSomething2() method in the object.

When using Spring AOP, the Bean objects we get from the IOC container are actually proxy objects, not the Bean objects themselves, because the this keyword does not refer to the proxy object of the Service Bean object, but itself, Therefore, Spring AOP cannot intercept these nested methods.

solution

  1. Modify the class so that there is no "self-invoking" situation: this is the "best" solution recommended in the Spring documentation;
  2. If you must use "self-invocation", then this.doSomething2() is replaced by: ((CustomerService) AopContext.currentProxy()).doSomething2(); at this time, you need to modify the spring's aop configuration:
    <aop:aspectj-autoproxy expose-proxy="true" />
http://www.jianshu.com/p/6534945eb3b5

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326680855&siteId=291194637