上一篇博文https://blog.csdn.net/rocklee/article/details/80312844讲利用aspect注释让spring加载Sqllogger来实现对spring容器管理的jdbctemplate的execute&query方法的拦截, 但实际应用时即行不通, 因为spring容器里面的jdbctemplate对象是单例的,多用户使用或递归引用中切换datasource就会出错 。而当运行时动态new的对象,spring也不会对新对象重新织入。
下面利用CGLIB的代理技术写一个代理,并对运行时动态新建的JdbcTemplate进行动态织入:
package com.freestyle.common.spring; import java.lang.reflect.Method; import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; import org.springframework.jdbc.core.ArgumentPreparedStatementSetter; import org.springframework.jdbc.core.ColumnMapRowMapper; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import com.freestyle.common.hibernate.dao.support.SqlLogger; /**** * JdbcTemplate&NamedParameterJdbcTemplate的代理,增加了输出带参数值的SQL日志,方便调试 * @author dgmislrh * */ public class JdbcTemplateProxy implements MethodInterceptor { private Class<?> targetClass; protected final Log logger = LogFactory.getLog(TransJdbcTemplate.class.getClass()); // 相当于JDK动态代理中的绑定 public <T> T getInstance(Class<T> targetClass) { this.targetClass = targetClass; Enhancer enhancer = new Enhancer(); // 创建加强器,用来创建动态代理类 enhancer.setSuperclass(targetClass); // 为加强器指定要代理的业务类(即:为下面生成的代理类指定父类) // 设置回调:对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept()方法进行拦 enhancer.setCallback(this); // 创建动态代理类对象并返回 return (T) enhancer.create(); } // 相当于JDK动态代理中的绑定 public <T> T getInstance(Class<T> targetClass, Class[] pvClasses, Object[] pvArgs) { this.targetClass = targetClass; Enhancer enhancer = new Enhancer(); // 创建加强器,用来创建动态代理类 enhancer.setSuperclass(targetClass); // 为加强器指定要代理的业务类(即:为下面生成的代理类指定父类) // 设置回调:对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept()方法进行拦 enhancer.setCallback(this); // 创建动态代理类对象并返回 return (T) enhancer.create(pvClasses, pvArgs); } // 实现回调方法 public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { // System.out.println("预处理——————"); String lvsMethodName = method.getName(); if (logger.isInfoEnabled() && ((targetClass==JdbcTemplate.class &&(lvsMethodName.equals("query") || lvsMethodName.equals("execute")) && args.length > 0 && args[0] instanceof String)|| targetClass==NamedParameterJdbcTemplate.class&&(lvsMethodName.equals("queryForObject") || lvsMethodName.equals("execute") ||lvsMethodName.equals("queryForList")))) { Object[] methodArgs = args, sqlArgs = null; Map<String, Object> sqlArgsM = null; // get the SQL statement String statement = methodArgs[0].toString(); boolean lvbSkip=false; // find the SQL arguments (parameters) for (int i = 1, n = methodArgs.length; i < n; i++) { Object arg = methodArgs[i]; if (targetClass == JdbcTemplate.class) { if (arg instanceof Object[]) { sqlArgs = (Object[]) arg; break; } if ( arg instanceof ArgumentPreparedStatementSetter){ lvbSkip=true; break; } } else { if (arg instanceof Map) { sqlArgsM = (Map<String, Object>) arg; break; } if ( arg instanceof MapSqlParameterSource){ lvbSkip=true; break; } } } if (targetClass==JdbcTemplate.class && args.length>=2 && args[1] instanceof ColumnMapRowMapper){ lvbSkip=true; } if (!lvbSkip){ String completedStatement = targetClass == JdbcTemplate.class ?(sqlArgs == null ? statement : SqlLogger.fillParameters(statement, sqlArgs)) :(sqlArgsM==null?statement: SqlLogger.fillNamedParameters(statement, sqlArgsM)); // log it logger.info(targetClass.getSimpleName() + "." + lvsMethodName + "\n------FULL SQL------\n" + completedStatement + "\n---------------------"); } } // proxy.invokeSuper(obj, args); //调用业务类(父类中)的方法 // return method.invoke(target, args); return proxy.invokeSuper(obj, args); // 调用业务类(父类中)的方法 // System.out.println("调用后操作——————"); } }
第二步,修改TransJdbcTemplate.java:
package com.freestyle.common.spring; import javax.annotation.Resource; import javax.sql.DataSource; import org.springframework.context.annotation.DependsOn; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionTemplate; /**** * 带有事务管理的JdbcTemplate,并能将参数值打印到日志... * @author dgmislrh * */ @DependsOn({"dsTransactionManager"}) //@Repository("transJdbcTemplate") public class TransJdbcTemplate extends org.springframework.jdbc.core.JdbcTemplate{ @Resource(name="dsTransactionManager") private org.springframework.jdbc.datasource.DataSourceTransactionManager dsTm; public static interface TransCallback{ public void onExecute(JdbcTemplate pvJt,NamedParameterJdbcTemplate pvNamedJt) throws Exception; } public static interface NamedTransCallback{ public void onExecute(NamedParameterJdbcTemplate pvJt) throws Exception; } public TransJdbcTemplate(DataSource dataSource) { super (dataSource); } /**** * 若需要回滚事务,在onExecute里面throw runtimeexception即可 * @param lvCallback */ public Exception doNamedTransactionBatch(final NamedTransCallback lvCallback){ if (dsTm==null){ dsTm=(DataSourceTransactionManager) ContextHolder.getBean("dsTransactionManager"); } TransactionTemplate lvTt = new TransactionTemplate (dsTm); return lvTt.execute(new TransactionCallback<Exception>() { @Override public Exception doInTransaction(TransactionStatus arg0) { try { //NamedParameterJdbcTemplate lvNamedJt=new NamedParameterJdbcTemplate(getDataSource()); NamedParameterJdbcTemplate lvNamedJt=new JdbcTemplateProxy().getInstance(NamedParameterJdbcTemplate .class,new Class[]{DataSource.class},new Object[]{getDataSource()}); lvCallback.onExecute(lvNamedJt); return null; } catch (Exception e) { // 抛出异常时进行回滚 arg0.setRollbackOnly(); return e; } } }); } /**** * 若需要回滚事务,在onExecute里面throw runtimeexception即可 * @param lvCallback */ public Exception doTransactionBatch(final TransCallback pvCallback){ if (dsTm==null){ dsTm=(DataSourceTransactionManager) ContextHolder.getBean("dsTransactionManager"); } TransactionTemplate lvTt = new TransactionTemplate (dsTm); return lvTt.execute(new TransactionCallback<Exception>() { @Override public Exception doInTransaction(TransactionStatus arg0) { try { //JdbcTemplate lvJt=new JdbcTemplate(getDataSource()); JdbcTemplate lvJt=new JdbcTemplateProxy().getInstance(JdbcTemplate.class,new Class[]{DataSource.class},new Object[]{getDataSource()}); //NamedParameterJdbcTemplate lvNamedJt=new NamedParameterJdbcTemplate(getDataSource()); NamedParameterJdbcTemplate lvNamedJt=new JdbcTemplateProxy().getInstance(NamedParameterJdbcTemplate .class,new Class[]{DataSource.class},new Object[]{getDataSource()}); pvCallback.onExecute(lvJt,lvNamedJt); return null; } catch (Exception e) { // 抛出异常时进行回滚 arg0.setRollbackOnly(); return e; } } }); } }
为了减少log的输出,所以将log4j的输出级别配置为INFO,并在log4j.properties后面加入以下两行用于关闭原jdbctemplate的日志输出和打开TransJdbcTemplate的日志输出:
log4j.logger.org.springframework.jdbc.core.JdbcTemplate=WARN log4j.logger.com.freestyle.common.spring.TransJdbcTemplate=INFO
并将此行代替为log4j.rootLogger=INFO,stdout,RollingAppender
运行的日志输出非常清爽:
10:05:26,959 INFO Class:83 - JdbcTemplate.query
------FULL SQL------
select fp_model from tp_jobmstr where fp_jobno='JB0490012' and fp_plant='DG'
---------------------
10:05:31,226 INFO Class:83 - JdbcTemplate.execute
------FULL SQL------
drop table if exists tmp_701_v9 ;create temp table tmp_701_v9 on commit drop as select * from fun_fetchproddataV2 ('101D1111111111','_t96_pd_log') f( f96_line varchar, f96_timeslot varchar, f96_order varchar, f96_op varchar, f96_station varchar, f96_mgtbarcd varchar, f96_result_type varchar, f96_result varchar,f96_create_by varchar, f96_create_dt timestamp ,recseq varchar,f96_job_proc bigint ,f96_qty int);
---------------------
10:05:31,278 INFO Class:83 - JdbcTemplate.query
------FULL SQL------
select to_char(a.f96_create_dt,'DD/Mon/YYYY HH24:MI') as create_dt,a.f96_timeslot,a.f96_op,a.f96_station,a.f96_result_type,a.f96_result ,a.f96_qty,a.f96_line,a.f96_order,a.f96_create_by, ''::varchar as sflag
from tmp_701_v9 as a order by a.f96_create_dt desc
---------------------
10:05:31,302 INFO Class:83 - JdbcTemplate.query
------FULL SQL------
select 'NV04-TFT内有物/脏污
NV03-左右端口偏
NV02-漏螺丝/螺丝打花' as k, exists(select * from t96_pd_log as b where b.f96_mgtbarcd='NV04-TFT内有物/脏污
NV03-左右端口偏
NV02-漏螺丝/螺丝打花' and (b.f96_line not like 'L%' and b.f96_line not like 'B%')) as sflag
---------------------
10:05:31,381 INFO BMS719V1:3382 - Single Query Completed.
DEBUG: Refresh Message File