梧州网站建设制作,wordpress文章函数,wordpress 移动 主题,宁波小型建网站公司上次我们在druid-spring-boot-starter里面看到有一个DruidSpringAopConfiguration的配置类#xff0c;然后引入了DruidStatInterceptor这样一个切面逻辑。今天我们就来看一下这个类的实现。
DruidStatInterceptor
这个类的包路径下入com.alibaba.druid.support.spring.stat。…上次我们在druid-spring-boot-starter里面看到有一个DruidSpringAopConfiguration的配置类然后引入了DruidStatInterceptor这样一个切面逻辑。今天我们就来看一下这个类的实现。
DruidStatInterceptor
这个类的包路径下入com.alibaba.druid.support.spring.stat。它定义了一个切面所有符合这个切面的切点表达式都会被拦截执行增强逻辑这个切点定义可以在配置文件里面设定通过spring.datasource.druid.aop-patterns配置即可。
它定义了一个advice其核心逻辑就是下面的invoke方法
Override
public Object invoke(MethodInvocation invocation) throws Throwable {SpringMethodStat lastMethodStat SpringMethodStat.current();SpringMethodInfo methodInfo getMethodInfo(invocation);SpringMethodStat methodStat springStat.getMethodStat(methodInfo, true);if (methodStat ! null) {methodStat.beforeInvoke();}long startNanos System.nanoTime();Throwable error null;try {return invocation.proceed();} catch (Throwable e) {error e;throw e;} finally {long endNanos System.nanoTime();long nanos endNanos - startNanos;if (methodStat ! null) {methodStat.afterInvoke(error, nanos);}SpringMethodStat.setCurrent(lastMethodStat);}
}这个方法里面有几个类是druid自己定义的简单解释下。
SpringMethodInfo
spring方法的抽象记录了spring bean的方法信息包括签名信息目标类以及方法的Method对象。
SpringMethodStat
spring方法的状态抽象记录了很多spring方法监控的指标比如方法正在执行的次数方法最大并发执行数方法执行次数方法执行时间jdbc执行次数更新条数等。下面是部分参数截图实际还有很多其他的参数有兴趣的可以自己研究下。 注意这里面的监控其实分两块常规的监控其实只监视数据库侧即这里jdbc相关的信息方法执行其实是不会监控的而定义了spring.datasource.druid.aop-patterns还会监控方法的执行相关信息。然后因为web请求可能多个线程调用一个方法所以用的一个ThreadLocal记录避免每个线程记录的值互不影响。
invoke方法
我们来具体看下invoke方法首先拿到当前线程的最后一次方法状态信息然后拿到对应的方法信息注意这里的方法默认最多只支持10层代理如果超过10层代理也只能拿到第10层的。然后你可以看到这个方法里面其实有两个SpringMethodStat一个是lastMethodStat这个好像没什么实际用处也可能我没太看明白有清楚的朋友可以评论区说明下还有一个是methodStat每次修改值其实是在这个里面。这个methodStat其实是从SpringStat这个静态类的一个concurrentMap里面根据SpringMethodInfo拿到对应的SpringMethodStat如果你的SpringMethodInfo是同一个那么修改的状态也就是一样的。
如果methodStat有值就执行前置逻辑 public void beforeInvoke() {currentLocal.set(this);int running runningCount.incrementAndGet();for (; ; ) {int max concurrentMax.get();if (running max) {if (concurrentMax.compareAndSet(max, running)) {break;}} else {break;}}executeCount.incrementAndGet();Profiler.enter(methodInfo.getSignature(), Profiler.PROFILE_TYPE_SPRING);}前置逻辑很简单对runningCountconcurrentMax,executeCount这三个值进行更新将其暂时存到Profiler的ThreadLocal里面。然后执行切点方法逻辑最后执行后置逻辑 public void afterInvoke(Throwable error, long nanos) {runningCount.decrementAndGet();executeTimeNano.addAndGet(nanos);histogramRecord(nanos);if (error ! null) {executeErrorCount.incrementAndGet();lastError error;lastErrorTimeMillis System.currentTimeMillis();}Profiler.release(nanos);}后置方法会更新runningCountexecuteTimeNanohistogramRecordlastErrorTimeMillis等值最后将结果存到Profiler里面的statsMapLocal里面最后显示的数据会从Profiler里面拿。
这里代码有一个写的比较优雅的地方是在执行当前逻辑的时候用的return invocation.proceed();后置逻辑是在finally块里面写的。充分利用了finally里面的代码一定会执行的特性将代码写的很简洁这块值得借鉴。
有的朋友可能会一问关于jdbc的执行次数修改在哪实现的呢这里完全没有看到呢也是在这个类里面它里面有个内部类SpringMethodContextListener就是实现的这块逻辑拿到SpringMethodStat,然后执行对应的修改动作即可。
它在bean初始化的时候会被注册到StatFilterContext里面然后在执行sql的时候会利用观察者模式调用所有监听器直接各自逻辑。 class SpringMethodContextListener extends StatFilterContextListenerAdapter {Overridepublic void addUpdateCount(int updateCount) {SpringMethodStat springMethodStat SpringMethodStat.current();if (springMethodStat ! null) {springMethodStat.addJdbcUpdateCount(updateCount);}}Overridepublic void addFetchRowCount(int fetchRowCount) {SpringMethodStat springMethodStat SpringMethodStat.current();if (springMethodStat ! null) {springMethodStat.addJdbcFetchRowCount(fetchRowCount);}}Overridepublic void executeBefore(String sql, boolean inTransaction) {SpringMethodStat springMethodStat SpringMethodStat.current();if (springMethodStat ! null) {springMethodStat.incrementJdbcExecuteCount();}}Overridepublic void executeAfter(String sql, long nanos, Throwable error) {SpringMethodStat springMethodStat SpringMethodStat.current();if (springMethodStat ! null) {springMethodStat.addJdbcExecuteTimeNano(nanos);if (error ! null) {springMethodStat.incrementJdbcExecuteErrorCount();}}}------------------------省略-------------------------}总结
总得说起来这块实现还是挺清晰的设定切点然后在切点前后记录相关数据其中在看源码的时候可能有的细节地方觉得不太清晰不懂它干嘛的可以写个单元测试或者demodebug调试下大概就能知道到底干嘛了。
记住看完源码能让你更好的使用框架这只是第一层更多的是学习他们写代码的方式学习优秀开源项目的设计思维然后你就知道了怎么去写优秀的代码看的多了你代码实力自然就上去了想写出“低质量“的代码也难。