2019独角兽企业重金招聘Python工程师标准>>>
在使用Spring Aop的时候遇到一个问题,假如我们有如下的几个类(简单起见用的spring的注解配置,不过换成xml配置问题也是一样的):
1. 一个基类,是否是abstract无所谓,关键点是它具有一个default权限的成员函数,并且该成员函数访问了基类的一个成员属性,如代码中的getTime()方法:
package org.fox;
public abstract class TimeService {
private long time = 0L;
public void setTime() {
time = System.currentTimeMillis(); }
long getTime() {
return time;
}
}
2. 子类,子类如何实现不是重点,他可以单纯的继承基类,重点是
它和基类不在同一个包中。
package org.fox.impl;
import org.fox.TimeService;
import org.springframework.stereotype.Component;
@Component
public class SystemTimeService extends TimeService {
}
3.一个Aspect,为TimeService的bean织入一个过程,这里简单的就system.out
package org.fox;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class Log {
@Before("execution(* TimeService.*(..))")
public void log() {
System.out.println("log");
}
}
Spring的bean配置文件为:
<context:annotation-config />
<context:component-scan base-package="org.fox" />
<aop:aspectj-autoproxy proxy-target-class="true" />
现在的问题是,如下代码getTime()的返回值是什么。
TimeService service = context.getBean(TimeService.class);
service.setTime();
System.out.println(service.getTime());
这个问题来自项目里面自己实现的DataCache模块,按照常理讲,setTime()和getTime()都是基类TimeService定义的,子类没有做任何的改变,getTime()应该返回的是当前时间毫秒值才对。但是,实际上因为cglib的原因,结果是0。
对代码打上断点后才发现问题的所在,为达到Aspect的目的,cglib产生了原类(SystemTimeService)的一个子类,这个子类和SystemTimeService在同一个包下,对于一般的被代理的方法,通过super便可以简单调用,但是问题是这个getTime()方法,因为他是基类定义的default方法,作为不在同一个包下的子孙类,是无法调用它的。从结果上看,cglib放弃了用反射绕过权限控制的做法,而是将原类中的成员属性以及这个getTime()方法复制了一份定义到代理类中。
这样实际上TimeService bean实例中是存在有两个不同的time成员属性,public的setTime()方法由于代理类可以通过super直接调用,所以他操作的是原本TimeService定义的time,而getTime()则因为在代理类中重新定义,所以他返回的是代理类自己定义的time属性,这就是上面问题为0的原因了。解决方法按照上面的思路也很容易想到,只需要给子类有调用getTime()的权限,把访问权限改成protected即可。
实例如下图,注意this和target下的lastUpdate的值