【原】通过BeanNameAutoProxyCreator改变臃肿代码
前言:
最近接手了一个项目,大概过了下需求,然后打开项目准备开搞的时候发现一个问题,这个项目是提供rest服务的一个web项目,其中很多旧系统由于还没改成微服务,所以只能通过HttpClient发起调用。之前的开发人员为了监控每个方法的执行时间,在方法开始和结束写了很多logger.info("耗时:"+time)这种代码。很显然这是不规范的,当项目里到处有这样的代码存在降低了代码的可读性,于是改造后如下:
#创建一个方法拦截器:
/** * 用来监控方法的执行时间-- 对应配置文件是spring-servlet.xml * PS:必须放到springmvc的配置文件里,放在spring父容器里面由于先初始化的是spring父容器上下文,先实例化的是除@Controller外的bean,所以无法生成代理。 * * @author dada * @version $Id: MethodTimeAdvice.java, v 0.1 * @date 2017年11月15日 09:47:23 */ public class MethodTimeAdvice implements MethodInterceptor { private final static Logger logger = Logger.getLogger("DAL-MONITOR"); /** * @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation) */ public Object invoke(MethodInvocation invocation) throws Throwable { //用 commons-lang 提供的 StopWatch 计时,Spring 也提供了一个 StopWatch StopWatch clock = new StopWatch(); clock.start(); //计时开始 Object result = null; //监控的方法名 String methodName = getRequestMappingName(invocation); if(null == methodName){ methodName = invocation.getMethod().getDeclaringClass().getSimpleName(); } try { //这个是我们监控的bean的执行并返回结果 result = invocation.proceed(); } catch (Throwable e) { //监控的类名 String className = invocation.getMethod().getDeclaringClass().getSimpleName(); //监控的参数 Object[] objs = invocation.getArguments(); logger.error("控制层执行异常,方法名:" + className + "参数: " + getString(objs), e); throw e; } clock.stop(); //计时结束 if (logger.isInfoEnabled()) { logger.info("[ " + methodName + " ] 执行时间:" + clock.getTime() + " ms "); } return result; } /** * 这个类主要是用于输出方法的参数 * * @param objs * @return */ @SuppressWarnings("unchecked") public String getString(Object[] objs) { StringBuffer stringBuffer = new StringBuffer(); for (int i = 0, len = objs.length; i < len; i++) { if (objs[i] instanceof String) { stringBuffer.append("String类型:" + objs[i].toString()); } else if (objs[i] instanceof Map) { HashMap<String, Object> hashMap = (HashMap<String, Object>) objs[i]; HashMap<String, Object> map = hashMap; HashSet<String> set = (HashSet<String>) map.keySet(); stringBuffer.append("Map类型"); for (String str : set) { stringBuffer.append(str + "=" + map.get(str)); } } else if (objs[i] instanceof Integer) { stringBuffer.append("整数类型:"); stringBuffer.append(objs[i].toString()); } else { stringBuffer.append(objs[i].toString()); } } return stringBuffer.toString(); } public String getRequestMappingName(MethodInvocation invocation){ Method method = invocation.getMethod(); RequestMapping requestMapping = method.getAnnotation(RequestMapping.class); if(requestMapping!=null){ return requestMapping.name(); } return null; } }
#配置 Spring BeanNameAutoProxyCreator, 用来拦截上面的 MethodTimeAdvice,切记必须放到spring-mvc.xml配置里,因为已经测试过如果放到spring的xml里面会导致扫描不到从而无法切面。
<bean id="methodTimeAdvice" class="com.i2p.admin.interceptor.MethodTimeAdvice" /> <!-- 根据 Bean 的名字自动实现代理拦截 --> <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <property name="interceptorNames"> <list> <value>methodTimeAdvice</value> </list> </property> <property name="beanNames"> <list> <!-- 添加到其中的 Bean 自动就被代理拦截了(下面的是对应的bean的名称,那么当请求InnovatePartBorrowController任何一个方法都会执行MethodTimeAdvice里面的Invoke方法) --> <value>innovatePartBorrowController</value> </list> </property> </bean>
#其中上面的配置里innovatePartBorrowController就是需要被打印的类。
总结:
通过spring 代理,不仅减少了代码的copy工作,同时又方便管理;至于后续如果有新的类需要实现打印功能,我们只需要在spring-mvc.xml里配置好对应的bean就能实现日志的打印。