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:[email protected]
 * 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:[email protected]
 * 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:[email protected]
 * 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:[email protected]
 * 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:[email protected]
 * 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:[email protected]
 * 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"


注:剩下的一些配置和細節等不是我發博客的意圖,所以就不多筆畫了,點擊此處下載源碼


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章