问题
貌似不能拦截私有方法?
试了很多次,都失败了,是不是不行啊?
我想了一下,因为aop底层是代理,
jdk是代理接口,私有方法必然不会存在在接口里,所以就不会被拦截到;
cglib是子类,private的方法照样不会出现在子类里,也不能被拦截。
我不是类内部直接调用方法,而是通过维护一个自身实例的代理
execution(* test.aop.ServiceA.*(..))
public class ServiceA {
private ServiceA self;
public void setSelf(ServiceA self) {
this.self = self;
}
public String methodA(String str) {
System.out.println("methodA: args=" + str);
self.methodB("b");
return "12345" + str;
}
private String methodB(String str) {
System.out.println("methodB: args=" + str);
self.methodC("c");
return "12345" + str;
}
public String methodC(String str) {
System.out.println("methodC: args=" + str);
return "12345" + str;
}
}
如果外部调用methodA,那么methodA和methodC会被拦截到,methodB不行
是不是这么回事?
但是stackoverflow上,有人说 it works fine
http://stackoverflow.com/questions/4402009/aspectj-and-catching-private-or-inner-methods
execution(public * test.aop.ServiceA.*(..))
还有个奇怪的现象,execution里如果不写权限,那么public protected package的方法都能被拦截到
如果写了public,那就只拦截public方法这个没问题,
如果写了protected,他就什么事情都不做,连protected的方法也不拦截。
分析
private方法 在Spring使用纯Spring AOP(只能拦截public/protected/包)都是无法被拦截的 因为子类无法覆盖;包级别能被拦截的原因是,如果子类和父类在同一个包中是能覆盖的。
在cglib代理情况下, execution(* *(..)) 可以拦截 public/protected/包级别方法(即这些方法都是能代理的)。
private static boolean isOverridable(Method method, Class targetClass) {
if (Modifier.isPrivate(method.getModifiers())) {
return false;
}
if (Modifier.isPublic(method.getModifiers()) || Modifier.isProtected(method.getModifiers())) {
return true;
}
return getPackageName(method.getDeclaringClass()).equals(getPackageName(targetClass));
}
如果想要实现拦截private方法的 可以使用 原生 AspectJ 编译期/运行期织入。
引用
如果写了protected,他就什么事情都不做,连protected的方法也不拦截;这个应该不会
原因基本分析明白了:
是否能应用增强的判断代码如下(org.springframework.aop.support.AopUtils):
public static boolean canApply(Pointcut pc, Class targetClass, boolean hasIntroductions) {
if (!pc.getClassFilter().matches(targetClass)) {
return false;
}
MethodMatcher methodMatcher = pc.getMethodMatcher();
IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
}
Set classes = new HashSet(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
classes.add(targetClass);
for (Iterator it = classes.iterator(); it.hasNext();) {
Class clazz = (Class) it.next();
Method[] methods = clazz.getMethods();
for (int j = 0; j < methods.length; j++) {
if ((introductionAwareMethodMatcher != null &&
introductionAwareMethodMatcher.matches(methods[j], targetClass, hasIntroductions)) ||
methodMatcher.matches(methods[j], targetClass)) {
return true;
}
}
}
return false;
}
此处Method[] methods = clazz.getMethods();只能拿到public方法。。
场景1:execution(* *(..))
public class Impl2 {
protected/public String testAop2() {
System.out.println("234");
return "1233";
}
}
因为切入点没有访问修饰符,即可以是任意,因此canApply方法能拿到如wait这种public方法,即可以实施代理。
场景2:execution(public * *(..))
public class Impl2 {
public String testAop2() {
System.out.println("234");
return "1233";
}
}
因为拦截public的,因此canApply方法能拿到如wait这种public方法,即可以实施代理。
场景3:execution(protected * *(..)) 、
public class Impl2 {
protected String testAop2() {
System.out.println("234");
return "1233";
}
}
还记得之前说过,在canApply方法中 的 Method[] methods = clazz.getMethods();只能拿到public方法的,因此跟protected访问修饰符是无法匹配的,所以如果“execution(protected * *(..))” 是 无法代理的。
这就是为什么execution(protected * *(..))在纯Spring AOP环境下不行的原因。
注,@Transactional注解事务的特殊情况:
引用
方法的可见度和 @Transactional
在使用代理的时候,@Transactional 注解应该只被应用到 public 可见度的方法上。 如果你在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,系统也不会报错, 但是这个被注解的方法将不会执行已配置的事务设置。如果你非要注解非公共方法的话,请参考使用AspectJ