Spring+SpringMVC+MyBatis+LogBack+C3P0+Maven+Git小结

一:建立一个Maven结构的Web工程

    这里主要介绍下如何使用MyEclipse创建一个Maven结构的Web项目

    1:下载并安装好自己的maven,并在环境变量中配置对应MAVEN_HOME、PATH路径

        检测是否安装完毕,可以在cmd中输入命令检测:mvn --version

     2:在MyEclipse中关联并使用Maven(这里可以使用MyEclipse自带的Maven4MyEclipse,也可以自己下载一个MyEclipse对应的Maven插件来关联我们的Maven3.1.1)

        设置下自己的仓库和配置文件路径:

    3:新建工程如下:

        

        至此生成一个web结构的maven项目(还缺少部分maven目录结构,下面补齐)

    4:补充maven目录结构:

    5:结构弄完了,自己往pom.xml里面灌点jar配置就好了,我的配置见最下面的下载链接。

二:Spring、SpringMVC重复加载bean对象问题。

在新建完Web项目并通过Pom.xml引入需要的jar后,我就开始配置Spring、SpringMVC的相关配置文件(在配置文件中启用注解扫描Bean组件的方式),这中间遇到一个问题,在web.xml中配置的:Spring对应的ContextLoaderListener加载beans.xml并扫描注册了一次@Component对象,而SpringMVC的DispatcherServlet加载spring-mvc.xml又扫描注册了一次注解的Bean对象,造成一个对象被实例化两次。

    针对摘要中的问题,我们可以这样理解并分配:将与SpringMVC关联的Controller层注解对象都归属于spring-mvc.xml对应的上下文管理,而剩下来的组件都交由Spring的beans.xml对应上下文管理。2个配置文件的内容分别如下:

    spring-mvc.xml配置内容如下:只扫描注解类型为@Controller的组件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:mvc="http://www.springframework.org/schema/mvc"  
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jdbc="http://www.springframework.org/schema/jdbc"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="
     http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
     http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
     http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd
     http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
     http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
     http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">


	<!-- 使用 annotation 自动注册Controller bean,并检查@Controller注解已被注入 --> 
	<context:annotation-config /> 
	<context:component-scan base-package="org.wjlmgqs.mtag" use-default-filters="false"  > 
		<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>  
	</context:component-scan> 
	
    <!-- 完成请求和注解POJO的映射 -->
    <mvc:annotation-driven /> 
    
	<!-- 静态资源处理    -->
	<mvc:resources mapping="/resource/**" location="/resource/"/>
	<mvc:default-servlet-handler />  

	<!-- 视图对应的文件映射 -->
	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">  
		<property name="prefix" value="/WEB-INF/views/" />  
		<property name="suffix" value=".jsp" />  
	</bean>  
	
	<aop:aspectj-autoproxy proxy-target-class="true" /> 
</beans>

    beans.xml配置内容如下:只扫描排除@Controller后组件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jdbc="http://www.springframework.org/schema/jdbc"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="
     http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
     http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
     http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd
     http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
     http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

	<!-- 注解扫描的目录:使用 annotation 排除@Controller注解 --> 
	<context:annotation-config /> 
	<context:component-scan base-package="org.wjlmgqs.mtag" >
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
     </context:component-scan>
	
	<!-- mybatis相关配置 -->
	<import resource="beans-mybatis.xml"/>
	
	<!-- 配置项类型配置 -->
	<import resource="beans-config-type.xml"/>
	
	<!-- 事务配置 -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource" />
	</bean>
	
	<!-- @Transactional -->
	<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>
	
	
</beans>

三:实现自个的数据缓存机制

    自个目前只实现了2种数据的缓存:

        一:数据字典加载到缓存中 ;

        二:配置项类型数据加载到缓存中

  • 2种缓存数据简介及其数据结构分析

        这里先简单介绍下2种数据类型:

        1:数据字典,表结构如下:

            

            数据例如:

            

            加载到我们缓存中的数据结构大致如下:Map<ID,Map<DICT_KEY,Map<String,String>>>   

        2:配置项类型,打个比方,我们站点有如下几种需要:

            支付方式包括:支付宝、微信支付、银联在线支付等  (PayType);

            物流配送方式:顺丰、中通、EMS等(DeliveryType);

            社会化登录方式:QQ、Baidu、Sina等(SocialType);

            而且上述几种类型和方式都是可插拔、可扩展的,这个时候我们可以尝试定义一个接口:IConfigPart,定义这些配置项的功能属性,例如ID、Name等

/**
 * IConfigPart.java<br>
 * <b>功能:配置项类型接口:所有实现该接口的类型都将注册入缓存CacheClient中</b><br>
 * @author wjl Email:wjlmgqs@sina.com
 * Time:2015-7-24 上午11:11:56
 */
public interface IConfigPart {

	/**
	 * <b>简要说明:配置项类型ID</b><br> 
	 * Create on 2015-7-23 下午2:27:31 <br>
	 */
	public String getId();
	
	/**
	 * <b>简要说明:实现类Code</b><br> 
	 * Create on 2015-7-23 下午2:27:31 <br>
	 */
	public String getCode();
	
	/**
	 * <b>简要说明:实现类名称</b><br> 
	 * Create on 2015-7-23 下午2:27:31 <br>
	 */
	public String getName();	
	
	/**
	 * <b>简要说明:顺序号</b><br> 
	 * Create on 2015-7-23 下午2:33:54 <br>
	 */
	public int getSeq();
	
	/**
	 * <b>简要说明:是否启用</b><br> 
	 * Create on 2015-7-23 下午2:33:54 <br>
	 */
	public boolean isMrb();
}

           然后,我们又为了能够区分这些类型中哪些是支付类型、哪些是配送类型、哪些是社会化登录类型,就需要给各个类型定义一个自己的接口,并继承接口:IConfigPart,例如社会化类型:ISocailLoginConfigPart

/**
 * ISocailLoginConfigPart.java<br>
 * <b>功能:社会化登录-配置类型默认实现接口</b><br>
 * @author wjl Email:wjlmgqs@sina.com
 * Time:2015-7-27 下午12:10:19
 */
public interface ISocailLoginConfigPart extends IConfigPart{

}

            再然后我们就可以定义我们具体的登录类型有哪些了:

            例如:QQConfig.java

public class QQConfig implements ISocailLoginConfigPart {
	
	public static String ID = "QQConfig";
	
	@Override
	public String getId() {
		return ID;
	}

	@Override
	public String getCode() {
		return "QQ";
	}

	@Override
	public String getName() {
		return "腾讯QQ";
	}

	@Override
	public boolean isMrb() {
		return true;
	}

	@Override
	public int getSeq() {
		return 1;
	}
}

            定义上面这样的结构以后,我们就可以想象这样一个数据结构:Map<ISocailLoginConfigPart,Map<QQConfig,IConfigPart>>

                //ISocailLoginConfigPart:是哪种类型,如社会化登录类型的ID

                //QQConfig:是社会化登录类型下的具体哪种类型方式:QQ登录方式的ID

                //IConfigPart:代表QQ登录类型这个对象,包含ID、Name、Seq等数据的具体对象

            额,定义了上面2种缓存数据结构以后,分别给这2个数据结构定义一个常量ID,并扔进一个叫做CacheClient对象的Map里面,实现大致如下:

/**
 * CacheClient.java<br>
 * <b>功能:缓存数据操作集</b><br>
 * @author wjl Email:wjlmgqs@sina.com
 * Time:2015-7-23 下午4:19:11
 */
@Component
public class CacheClient implements ApplicationContextAware{

	private static String threadClassName = Thread.currentThread().getStackTrace()[1].getClassName();
	protected static Logger log = LoggerFactory.getLogger(threadClassName);
	/**
	 * <b>描述:缓存服务</b>
	 */
	private static ICacheService cacheService = null;
	
	/**
	 * <b>描述:配置项类型</b>
	 */
	private static ConfigType configType = null;
	
	/**
	 * <b>简要说明:构造参数私有化</b><br> 
	 * Create on 2015-7-23 下午3:30:13 <br>
	 * @author 翁加林
	 */
	private CacheClient(){
		log.debug("...CacheClient create cache client ");
	}
	
	/**
	 * <b>描述:数据缓存结构对象</b>
	 */
	private static Map<String,Map<String,Object>> cacheMap = new HashMap<String,Map<String,Object>>();
	
	/**
	 * <b>简要说明:初始化缓存数据结构</b><br> 
	 * Create on 2015-7-23 下午3:50:10 <br>
	 * @author 翁加林
	 */
	public static void initCache(){
		Map<String, Object> loadDictCache = loadDictCache();//字典数据
		Map<String, Object> loadConfigTypeCache = loadConfigTypeCache();//配置项
		
		cacheMap.put(PubConstants.DICT_CLIENT_CACHE, loadDictCache);
		cacheMap.put(PubConstants.CONFIG_TYPE_CACHE, loadConfigTypeCache);
		
		log.debug("...CacheClient initCache data : "+cacheMap);
	}
	.....
}

  • 2种缓存数据加载实现流程介绍

        定义好这2种缓存的数据结构以后,我们得有个流程和方式把这些数据加载进来吧,这里就先介绍下加载的大致流程。

        第一步:注册监听,在容器启动时就开始加载我们的数据

            在web.xml中注册:

  <listener>
    <listener-class>org.wjlmgqs.mtag.core.listener.InitListenter</listener-class>
  </listener>

            对应的java代码如下:

/**
 * InitListenter.java<br>
 * <b>功能:服务启动时加载系统需要的Cache内容:
 * 			java类型:ConfigPart 配置项		
 * 			数据库:数据字典 
 * </b><br>
 * @author 翁加林 Email:wjlmgqs@sina.com
 * Time:2015-7-23 下午2:32:49
 */
public class InitListenter implements ServletContextListener{

	private static String threadClassName = Thread.currentThread().getStackTrace()[1].getClassName();
	protected static Logger log = LoggerFactory.getLogger(threadClassName);
	
	@Override
	public void contextDestroyed(ServletContextEvent arg0) {
		
	}

	@Override
	public void contextInitialized(ServletContextEvent sce) {
		 log.debug("...InitListenter contextInitialized start ...");
		 CacheClient.initCache();//初始化缓存操作
		 log.debug("...InitListenter contextInitialized end ...");
	}

}

            可以看出,我们在容器启动时,调用

CacheClient.initCache();//初始化缓存操作

            完成了缓存数据的加载。

        第二步:在initCache中分别加载2种缓存数据

/**
	 * <b>简要说明:初始化缓存数据结构</b><br> 
	 * Create on 2015-7-23 下午3:50:10 <br>
	 * @author 翁加林
	 */
	public static void initCache(){
		Map<String, Object> loadDictCache = loadDictCache();//字典数据
		Map<String, Object> loadConfigTypeCache = loadConfigTypeCache();//配置项
		
		cacheMap.put(PubConstants.DICT_CLIENT_CACHE, loadDictCache);
		cacheMap.put(PubConstants.CONFIG_TYPE_CACHE, loadConfigTypeCache);
		
		log.debug("...CacheClient initCache data : "+cacheMap);
	}

            这里分别加载了2种数据,我们分开来讲。

        第三步:加载字典数据:loadDictCache()

/**
	 * <b>简要说明:从数据库加载枚举数据到缓存结构中</b><br> 
	 * Create on 2015-7-23 下午3:01:47 <br>
	 * @author 翁加林
	 */
	private static Map<String, Object> loadDictCache(){
		log.debug("...CacheClient loadDictCache start....");
		//从数据库中获取所有字典类型
		List<String> dictTypes = cacheService.getDictTypes();
		log.debug("...CacheClient loadDictCache 共加载"+dictTypes.size()+"种类型枚举数据");
		Map<String, Object> dictCaches = getDictCaches();
		//获取所有类型对应的字典数据,并保存至缓存结构
		for(String dictType : dictTypes){
			Map<String,Object> dict =  cacheService.getDictByType(dictType);
			dictCaches.put(dictType, dict);
		}
		log.debug("...CacheClient loadDictCache end....");
		return dictCaches;
	}

           从代码中可以看出,这里我们调用cacheService来进行数据查询,在其底层就是通过dao层调用mybatis方式查询数据库并获取数据。

           不过这里有个问题:我们的cacheService对象咋来滴?

           答:我们搭建的环境中所有service都是交由spring上下文管理的,所以从里面抓就行了。

           问:咋抓?

           答: 可以通过注解方式实现:

/**
	 * <b>描述:缓存服务</b>
	 */
	@Autowired
	private static ICacheService cacheService = null;

            不过洒家不是这么干的,吾让CacheClient实现了ApplicationContextAware接口,通过:

/**
	 * 重载函数:注入上下文,从中获取ICacheService操作缓存数据及配置项对象
	 * (non-Javadoc)
	 * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
	 */
	@Override
	public void setApplicationContext(ApplicationContext context)
			throws BeansException {
		cacheService = (ICacheService) context.getBean("cacheService");
		log.debug("...CacheClient setApplicationContext and get cacheService : "+cacheService);
		configType = (ConfigType) context.getBean("configType");
		log.debug("...CacheClient setApplicationContext and get ConfigType : "+configType);
	}

            获得cacheService实例,所以在loadDictCache()中才能正常调用

            当然,实现这个接口并要让Spring上下文把cacheService对象注入到CacheClient,CacheClient也必须添加注解才行啊。

@Component
public class CacheClient implements ApplicationContextAware{
...
}

            dao层查数不是我本文关注的重点,无视......

        第四步:加载配置项数据:loadConfigTypeCache

            这货有些麻烦,简单来说就是这样一个流程:

                1:通过xml定义一个我们配置项内容的结构

<mtag:configType id="configType">
		<mtag:configItem classKey="org.wjlmgqs.mtag.sso.config.ISocailLoginConfig" >
			<mtag:configPart classKey="org.wjlmgqs.mtag.sso.config.BaiduConfig"></mtag:configPart>
			<mtag:configPart classKey="org.wjlmgqs.mtag.sso.config.QQConfig"></mtag:configPart>
		</mtag:configItem>
		......
	</mtag:configType>

                2:通过java代码读取并解析xml,并转换为我们之前分析的Map数据结构(实际上这里我是通过Spring自定义标签的方式实现的,我们后面详解)

        第五步:拿到2个Map数据结构后,就像第二步代码中看到的一样,根据常量ID扔进CacheClient的cacheMap中就ok啦

  • 通过Spring自定义标签形式实现配置项类型数据的缓存数据结构解析及加载

        通过Spring自定义标签形式实现配置项类型数据的缓存数据结构解析及加载:在上一篇幅中,我们了解到可以通过xml形式定义我们的配置项类型数据及其结构,然后由Spring来解析这些标签并转换为我们需要的数据结构。那么让Spring来识别我们自定义标签我们需要干些啥呢?

        我们先要了解下,自定义Spring标签都需要些啥东西:

        1:自己的xml文件和里面自定义的标签呗

        2:你要知道每一个规范的xml文件都有对应的一个xsd文件来规范xml中出现的每一个标签的格式:这个<mtag:configType>标签最多能出现几次,它有哪些字节点,它有啥属性等

        3:你定义的xml文件谁来解析啊?你得指定个和Spring打交道的类来帮你解析吧?那就来个实现了NamespaceHandlerSupport接口的类:ConfigTypeHandler

        4:这实现了NamespaceHandlerSupport接口后,并重写了默认的init方法后,发现handler需要调用一些继承了AbstractSimpleBeanDefinitionParser的parser来解析你自己写的每一个标签:<mtag:configType>、<mtag:configItem>、<mtag:configPart>,然后在handler中注册一下:

	@Override
	public void init() {
		log.debug("...ConfigTypeHandler start...");
		// TODO Auto-generated method stub
		registerBeanDefinitionParser("configType",new ConfigTypeParser());
		registerBeanDefinitionParser("configItem",new ConfigItemParser());
		registerBeanDefinitionParser("configPart",new ConfigPartParser());
		log.debug("...ConfigTypeHandler end...");
	}

        5:大家也都知道Spring在xml中检测到bean标签后会根据指定的class来实例化一个javabean对象,其实也是通过Parser来帮我们转换成对应的javabean对象的,那么我们自定义标签也一样,需要给每个标签都定义一个javabean对象,它在解析标签后就直接给我们返回我们需要的javabean对象了。

            这些javabean对象子元素和属性都要和配置标签对应属性保持一致(感觉好麻烦的说),注意我们是三级嵌套的标签哈~

        6:我们定义的标签都是mtag开头的,那么怎么把这类<mtag>开头的标签和我们的handler映射起来呢?

            答:在META-INF下建立spring.handlers,并配置:

http\://www.wjlmgqs.org/schema/mtag=org.wjlmgqs.mtag.core.config.ConfigTypeHandler

        7:看上面的图,我们知道我们很装逼的指定了我们xsd文件的位置在:http://www.wjlmgqs.org/schema/mtag/config-type.xsd,其实大爷现在连个域名和云服务器都买不起,又咋给你放着破配置文件哈,所以我们要很装逼的映射下这个路径到我们本地路径(项目路径)下面

            META-INF下建立spring.schemas,并配置:

http\://www.wjlmgqs.org/schema/mtag/config-type.xsd=org/wjlmgqs/mtag/core/config/config-type.xsd

        综上,就是我们自定Spring标签需要的东西,在搞这些东西的时候还碰到一些问题,如下:

            一:在xml中写出标签后,第一行总有个红叉,提示:

Referenced file contains errors (http://www.wjlmgqs.org/schema/mtag/config-type.xsd). For more information, right click on the message in the Problems View and 

 select "Show Details..."

                然后我还真看了:   

                说是xsd定义有问题,我反复检查,反复检查,没看出个球。

                同样xsd文件也一直报个错:

                同样检查出个球。

          二:默认META-INF目录如下图:

            

                不在classpath路径下面,这样运行工程后会出现如下异常:org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Failed to import bean definitions from relative location [beans-config-type.xml]

                于是乎,我看了看spring是怎么搞的:

               

                很直接,是classpath下面的,于是我把META-INF文件都放到了src/main/resources下面:

                  

                擦,不报错了。

        注:代码太多,贴上很乱,后面我整理工程上传。

            这里列下标签对应的文件在我工程里的位置:

                    

四:对method做统一的起始&结束的日志处理(切面方式)

正常情况下:我们通过url访问某个ui请求对应method,里面都有很多service以及适当的日志输出,当日志内容较多的时候,我们可能就分不清楚哪些日志是哪个方法里执行出来的日志,或者说是为了更好、更快的定位到我们需要的日志。所以,这里我们简单的通过AOP方式为method包裹一层日志处理。


    先简单描述下,我们要实现的方式:我们自定义一个注解(LogSign),在我们需要日志处理的method方法体上添加该注解,当我们的请求方法遇到LogSign注解时,会自动调用我们自定义的一个切面类(LogSignService),并执行里面的@Around标注的log()方法,在这个方法中输出起始日志,执行method方法,输出结束日志等。具体步奏:

    1:自定义注解LogSign

/**
 * LogSign.java<br>
 * <b>功能:定义日志注解,标有该注解的service进行日志切面拦截</b><br>
 * @author 翁加林 Email:wjlmgqs@sina.com
 * Time:2015-7-22 下午4:28:13
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface LogSign {
	String beforeMsg() default "";
	String endMsg() default "";
}

    2:自定义切面LogSignService

/**
 * LogSignService.java<br>
 * <b>功能:定义日志服务切面,被拦截的方法前后插入日志信息(execution中限定:只platform路径下有效)
 * 	   在beanx.xml中配置了代理实现方式为代理类,所以所在类必须有默认构造函数
 * </b><br>
 * @author 翁加林 Email:wjlmgqs@sina.com
 * Time:2015-7-22 下午4:25:41
 */
@Aspect
@Component
public class LogSignService extends BaseService implements ApplicationContextAware{
	
	private static Logger log = LoggerFactory.getLogger("org.wjlmgqs.mtag.platform");

	//环绕通知方法
    @Around("execution(* org.wjlmgqs.mtag.platform..*.*(..))")
	public Object log(ProceedingJoinPoint point) throws Throwable{
    	//获取被拦截的对象及其执行方法
        Object target = point.getTarget();  // 拦截的实体类
        String className = target.getClass().getName();
        String methodName = point.getSignature().getName(); // 拦截的方法名称
        Class<?>[] parameterTypes = ((MethodSignature) point.getSignature()).getMethod().getParameterTypes();  // 拦截的放参数类型
        Method method = target.getClass().getMethod(methodName,parameterTypes);
        Object object = null;
        if(method != null && method.isAnnotationPresent(LogSign.class)) {
        	LogSign annotation = method.getAnnotation(LogSign.class);
        	String beforeMsg = "--->>>执行"+className+"类的"+methodName+"方法--开始";
        	String endMsg = "---->>>执行"+className+"类的"+methodName+"方法--结束";
        	if(!StringUtils.isEmpty(annotation.beforeMsg())){
        		beforeMsg = annotation.beforeMsg();
        	}
        	if(!StringUtils.isEmpty(annotation.endMsg())){
        		endMsg = annotation.endMsg();
        	}
        	log.debug(beforeMsg);
            object = point.proceed();// 执行该方法
            log.debug(endMsg);
        } else { // 没有包含该注解则不进行其他处理
            object = point.proceed();// 执行该方法
        }
        return object;
	}

	@Override
	public void setApplicationContext(ApplicationContext arg0)
			throws BeansException {
		// TODO Auto-generated method stub
	}
	
}

    3:建一个测试类Test.java

@Controller
@RequestMapping(value="/test/json")  
public class Test extends BasePlatformUI{
    
	private static Logger log = LoggerFactory.getLogger(Test.class);
	
    /**
     * <b>简要说明:测试输出json格式数据</b><br> 
     * Create on 2015-7-30 下午5:06:04 <br>
     * @author 翁加林
     */
    @RequestMapping(value="/write")  
    @ResponseBody   
    @LogSign
    public String write(HttpServletRequest request,HttpSession session) throws Exception{  
        ModelAndView mav = new ModelAndView();  
        ModelMap modelMap = new ModelMap();  
        modelMap.put("mapKey", "mapValue");  
        modelMap.addAttribute("attributeKey", "attributeValue");  
        mav.addObject("model", modelMap);  
        mav.addObject("modelMap", modelMap);  
        String writeValueAsString = super.writeJson(mav);
        log.debug("...测试数据:"+writeValueAsString);
        return writeValueAsString;  
    }  
}

        这里将map对象转换为json字符串是通过jackjson实现的,代码如下:

/**
	 * <b>简要说明:将对象转换为Json字符串</b><br> 
	 * Create on 2015-7-31 上午10:45:22 <br>
	 * @author 翁加林
	 */
	public String writeJson(Object obj){
		ObjectMapper mapper = new ObjectMapper();
		String msg = null;
        try {
        	msg = mapper.writeValueAsString(obj);
		}catch (Exception e) {
			log.error("...BasePlatformUI writeJson parse error : "+e.getMessage());
		}
        return msg ;
	}

    4:效果

五:LogBack日志文件配置&中文乱码&MyBatis日志不输出SQL

我的代码按照自己的习惯进行了模块划分,所以也希望输出的日志能各回各家各找各妈(按照目录分别输出)。我目前几个核心的模块划分为:core、platform、sso,所以,我希望这几个目录的代码日志能够分别输出到core.log、mybatis.log、platform.log;另外,我希望mybatis启动、读取文件等操作日志输出到mybatis.log,查询数据库的sql语句输出到sql.log、所有的错误消息统一输出到error.log(指定error级别)。


    实现上述的功能,只需要2种配置方式就成:

       第一:错误消息统一输出到error.log,使用root配置

    <!-- 走默认方式输出ERROR到单独文件 -->
    <root level="DEBUG">   
        <appender-ref ref="error" />   
    </root>

       第二种:按照目录输出到对应的日志文件,类似下面贴出代码,只要修改各自模块路径就成

<!-- 按照包路径输出DEBUG级日志 -->
  	<logger name="org.wjlmgqs.mtag.platform" level="DEBUG">
  		<appender-ref ref="platform" />   
  	</logger>

           不过这里需要提出和注意的2个地方:

                1:mybatis等操作日志的包路径指定为:org.mybatis,就成。

                2:mybatis执行sql语句默认输出是按照mapper文件(xml)中的namespace路径,所以我这里将namespace路径指定为mapper文件的包路径

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.wjlmgqs.mtag.mapper.Dict">

    <select id="getDictTypes" resultType="string" >
       	SELECT DISTINCT ID FROM MT_DATA_DICT 
    </select>
    
    <select id="getDictByType" parameterType="string" resultType="Dict" >
       	SELECT ID,DICT_KEY AS dictKey,DICT_VALUE AS dictValue,SUBJ FROM MT_DATA_DICT WHERE ID = #{value} 
			<!-- ORDER BY SEQ  -->
    </select>

</mapper>

            这样子,几种输出类型我们都定好了,我们剩下来需要做的就是为每个logger和root中的ref指定appender对象,基本配置如下:

    <appender name="core" class="ch.qos.logback.core.rolling.RollingFileAppender">   
        <Encoding>UTF-8</Encoding>   
        <File>${LOG_HOME}/core.log</File>
        <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">   
            <FileNamePattern>${LOG_HOME}/core%i.log</FileNamePattern>   
            <MinIndex>1</MinIndex>
            <MaxIndex>5</MaxIndex>
        </rollingPolicy>   
        <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
        	<MaxFileSize>5MB</MaxFileSize>
        </triggeringPolicy>
        <encoder>   
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
            <charset>UTF-8</charset>
        </encoder>   
    </appender>

            另外,因为error只输出error级别日志,所以需要在它的<appender>下添加一个过滤器:

        <filter class="ch.qos.logback.classic.filter.LevelFilter">
          <level>ERROR</level>
          <onMatch>ACCEPT</onMatch>
          <onMismatch>DENY</onMismatch>
        </filter>

        酱紫:我们总体配置就完了,这里面已经处理了sql日志输出的问题,但是还隐藏了另一个问题:部分jar中会输出一些中文乱码(GBK编码,我们总体环境都是UTF-8),虽然我们的appender中指定了编码格式、tomcat也指定了(server.xml):

   <Connector port="80" protocol="HTTP/1.1" 
               connectionTimeout="20000" 
               redirectPort="8443" URIEncoding="UTF-8" />

        但还是乱码,经过搜索尝试,发现需要设置我们tomcat容器启动编码(catalina.bat):

if not exist "%CATALINA_BASE%\conf\logging.properties" goto noJuli
set JAVA_OPTS=%JAVA_OPTS% -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djava.util.logging.config.file="%CATALINA_BASE%\conf\logging.properties" -Dfile.encoding="UTF-8" -Dsun.jnu.encoding="UTF-8"
:noJuli

        其中,下面这部分是我追加上去的:

-Dfile.encoding="UTF-8" -Dsun.jnu.encoding="UTF-8"


注:剩下的一些配置和细节等不是我发博客的意图,所以就不多笔画了,点击此处下载源码


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WJL_MGQS

本不富裕的收入,还得买服务器

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值