利用Spring的CGLIB给运行时新建的JdbcTemplate&NamedParameterJdbcTemplate织入实现打印带参数的SQL功能

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/rocklee/article/details/80346091

    上一篇博文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

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页