问题
在spring中如果在方法上添加了诸如@Transactional
、@Cacheable
或是切面之类的注解,那么这个类就将由spring生成其代理对象,对指定方法进行相关的包装。当该方法在其他对象中被调用时是可以正常触发代理方法的,然而在本类的方法中进行内部调用时却不会,最终调用的还是原始方法。
class Service {
public void methodA(){
methodB();
}
@Transactional
public void methodB(){
// do something
}
}
也就是说通过methodA
调用methodB
不会走代理方法,这是为什么呢?
原因分析
我们知道spring生成代理对象常见的有两种方式,一种是基于接口的JDK动态代理,一种是基于子类的CGLIB风格代理,可通过proxyTargetClass
属性来控制。JDK文档中的一段话:
Users can control the type of proxy that gets created for FooService using the proxyTargetClass() attribute. The following enables CGLIB-style ‘subclass’ proxies as opposed to the default interface-based JDK proxy approach.
当为其配置了false时强制使用JDK动态代理,代理对象必须实现接口;当配置为true时强制使用CGLIB风格代理,代理方法不可使用final修饰;当没有配置该项时spring将自动选择有效的代理方式来实现。
JDK动态代理情况
JDK动态代理大家应该都比较熟悉了,这儿列出大致实现步骤,进而说明为什么无法触发代理方法。
public interface Subject
{
public void methodA();
public void methodB();
}
public class RealSubject implements Subject
{
public void methodA()
{
// do something
}
public void methodB()
{
// do something
}
}
public class InvocationHandlerImpl implements InvocationHandler
{
/**
* real proxied object
*/
private Object subject;
public InvocationHandlerImpl(Object subject)
{
this.subject = subject;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
// do something before invocation
Object returnValue = method.invoke(subject, args);
// do something after invocation
return returnValue;
}
}
// generate proxy step
Subject realSubject = new RealSubject();
InvocationHandler handler = new InvocationHandlerImpl(realSubject);
ClassLoader loader = realSubject.getClass().getClassLoader();
Class[] interfaces = realSubject.getClass().getInterfaces();
Subject subject = (Subject) Proxy