我一想,这不是个很好的应用AOP的场景吗?
正好刚重新温习了遍AOP,应用到实际场景的机会就来了。
撸起袖子,说干就干。
由于需求明确,实现起来很顺利,跑完UT后,结果一片飘绿,非常开心的推荐给相关的几个系统使用,我还得意得沉浸在学以致用的喜悦中没多久,A同学跑过来说,你这个AOP应用不到啊!
简直是当头一棒!
先讲一下我的实现方案:
@Target(value = {ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface MaskFields{ }
@Target(value = {ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface MaskMethod{ }
@Component @Aspect public class MaskAop { private Logger logger = LoggerFactory.getLogger(getClass()); @Pointcut("@annotation(MaskMethod)") public void pointcut() { } // 定义 advise @Before("pointcut()") public void maskFieldProcess(JoinPoint joinPoint) { logger.info("---Before method {} invoke, param: {}---", joinPoint.getSignature().toShortString(), joinPoint.getArgs()); } }
<aop:aspectj-autoproxy/> <aop:aspectj-autoproxy proxy-target-class="true"/>
然后我仔细检查了A同学的使用方式,
@Service public class SomeService { private Logger logger = LoggerFactory.getLogger(getClass()); public List<UserVo> getUserList(String someParam) { List<UserModel> userModelList= userDao.getUsers(); return modelToVo(userModelList) } @MaskMethod public void modelToVo() { } }
经过debug调试,发现这个方法根本没有被AOP拦截到!查阅资料,发现这算是spring AOP的一个限制. spring AOP 并不是扩展了一个类(目标对象), 而是使用了一个代理对象来包装目标对象, 并拦截目标对象的方法调用. 这样的实现带来的影响是: 在目标对象中调用自己类内部实现的方法时, 这些调用并不会转发到代理对象中, 甚至代理对象都不知道有此调用的存在!
知道问题的原因,解决方法就简单了,给自己定义个内部的引用,通过这个引用去调方法就能被AOP拦截到了。
@Service public class SomeService { private Logger logger = LoggerFactory.getLogger(getClass()); private SomeService self; public List<UserVo> getUserList(String someParam) { List<UserModel> userModelList= userDao.getUsers(); return self.modelToVo(userModelList) } @MaskMethod public void modelToVo() { } }
当然,更合理的是提取一个转换的servuce类,把这种转换方法提取到这个类里面,统一进行拦截。