首页 > 代码库 > Spring+SpringMVC+MyBatis+LogBack+C3P0+Maven+Git小结(转)

Spring+SpringMVC+MyBatis+LogBack+C3P0+Maven+Git小结(转)

摘要 出于兴趣,想要搭建一个自己的小站点,目前正在积极的准备环境,利用Spring+SpringMVC+MyBatis+LogBack+C3P0+Maven+Git,这里总结下最近遇到的一些问题及解决办法,后续慢慢的继续补~

目录[-]

  • 一:建立一个Maven结构的Web工程
  • 二:Spring、SpringMVC重复加载bean对象问题。
  • 三:实现自个的数据缓存机制
  • 2种缓存数据简介及其数据结构分析
  • 2中缓存数据加载实现流程介绍
  • 三:通过Spring自定义标签形式实现配置项类型数据的缓存数据结构解析及加载
  • 四:对method做统一的起始&结束的日志处理(切面方式)
  • 五:LogBack日志文件配置&中文乱码&MyBatis日志不输出SQL。

一:建立一个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配置就好了,我的如下:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">	<modelVersion>4.0.0</modelVersion>	<groupId>org.wjlmgqs</groupId>	<artifactId>mtag</artifactId>	<packaging>war</packaging>	<version>0.0.1-SNAPSHOT</version>	<name>mtag Webapp</name>	<url>http://maven.apache.org</url>	<dependencies>		<dependency>			<groupId>org.apache.maven.plugins</groupId>			<artifactId>maven-resources-plugin</artifactId>			<version>2.4.3</version>		</dependency>		<dependency>			<groupId>c3p0</groupId>			<artifactId>c3p0</artifactId>			<version>0.9.1.2</version>		</dependency>		<!--springframework 3.2.0.RELEASE -->		<dependency>			<groupId>org.springframework</groupId>			<artifactId>spring-aop</artifactId>			<version>3.2.12.RELEASE</version>		</dependency>		<dependency>			<groupId>org.springframework</groupId>			<artifactId>spring-aspects</artifactId>			<version>3.2.12.RELEASE</version>		</dependency>		<dependency>			<groupId>org.springframework</groupId>			<artifactId>spring-beans</artifactId>			<version>3.2.12.RELEASE</version>		</dependency>		<dependency>			<groupId>org.springframework</groupId>			<artifactId>spring-context</artifactId>			<version>3.2.12.RELEASE</version>			<exclusions>				<exclusion>					<groupId>commons-logging</groupId>					<artifactId> commons-logging</artifactId>				</exclusion>			</exclusions>		</dependency>		<dependency>			<groupId>org.springframework</groupId>			<artifactId>spring-context-support</artifactId>			<version>3.2.12.RELEASE</version>		</dependency>		<dependency>			<groupId>org.springframework</groupId>			<artifactId>spring-core</artifactId>			<version>3.2.12.RELEASE</version>		</dependency>		<dependency>			<groupId>org.springframework</groupId>			<artifactId>spring-expression</artifactId>			<version>3.2.12.RELEASE</version>		</dependency>		<dependency>			<groupId>org.springframework</groupId>			<artifactId>spring-jdbc</artifactId>			<version>3.2.12.RELEASE</version>		</dependency>		<dependency>			<groupId>org.springframework</groupId>			<artifactId>spring-orm</artifactId>			<version>3.2.12.RELEASE</version>		</dependency>		<dependency>			<groupId>org.springframework</groupId>			<artifactId>spring-test</artifactId>			<version>3.2.12.RELEASE</version>		</dependency>		<dependency>			<groupId>org.springframework</groupId>			<artifactId>spring-tx</artifactId>			<version>3.2.12.RELEASE</version>		</dependency>		<dependency>			<groupId>org.springframework</groupId>			<artifactId>spring-web</artifactId>			<version>3.2.12.RELEASE</version>		</dependency>		<dependency>			<groupId>org.springframework</groupId>			<artifactId>spring-webmvc</artifactId>			<version>3.2.12.RELEASE</version>		</dependency>		<!--aspectj -->		<dependency>			<groupId>org.aspectj</groupId>			<artifactId>aspectjrt</artifactId>			<version>1.8.5</version>		</dependency>		<dependency>			<groupId>org.aspectj</groupId>			<artifactId>aspectjweaver</artifactId>			<version>1.8.5</version>		</dependency>		<dependency>			<groupId>aopalliance</groupId>			<artifactId>aopalliance</artifactId>			<version>1.0</version>		</dependency>		<dependency>			<groupId>cglib</groupId>			<artifactId>cglib</artifactId>			<version>2.2.2</version>		</dependency>		<!--mybatis 3.3.1 -->		<dependency>			<groupId>org.mybatis</groupId>			<artifactId>mybatis</artifactId>			<version>3.1.1</version>		</dependency>		<dependency>			<groupId>org.mybatis</groupId>			<artifactId>mybatis-spring</artifactId>			<version>1.2.3</version>		</dependency>		<dependency>			<groupId>mysql</groupId>			<artifactId>mysql-connector-Java</artifactId>			<version>5.1.19</version>		</dependency>		<!--log :slf4j+classes+core+logback.xml -->		<dependency>			<groupId>org.slf4j</groupId>			<artifactId>slf4j-api</artifactId>			<version>1.7.7</version>		</dependency>		<dependency>			<groupId>ch.qos.logback</groupId>			<artifactId>logback-core</artifactId>			<version>1.1.2</version>		</dependency>		<dependency>			<groupId>ch.qos.logback</groupId>			<artifactId>logback-classic</artifactId>			<version>1.1.2</version>		</dependency>		<dependency>			<groupId>org.logback-extensions</groupId>			<artifactId>logback-ext-spring</artifactId>			<version>0.1.1</version>		</dependency>		<!--commons utils -->		<dependency>			<groupId>commons-beanutils</groupId>			<artifactId>commons-beanutils</artifactId>			<version>1.9.2</version>		</dependency>		<dependency>			<groupId>commons-chain</groupId>			<artifactId>commons-chain</artifactId>			<version>1.2</version>		</dependency>		<dependency>			<groupId>commons-codec</groupId>			<artifactId>commons-codec</artifactId>			<version>1.6</version>		</dependency>		<dependency>			<groupId>commons-collections</groupId>			<artifactId>commons-collections</artifactId>			<version>3.2.1</version>		</dependency>		<dependency>			<groupId>commons-configuration</groupId>			<artifactId>commons-configuration</artifactId>			<version>1.7</version>		</dependency>		<dependency>			<groupId>commons-digester</groupId>			<artifactId>commons-digester</artifactId>			<version>2.0</version>		</dependency>		<dependency>			<groupId>commons-fileupload</groupId>			<artifactId>commons-fileupload</artifactId>			<version>1.2.2</version>		</dependency>		<dependency>			<groupId>commons-io</groupId>			<artifactId>commons-io</artifactId>			<version>2.1</version>		</dependency>		<dependency>			<groupId>commons-lang</groupId>			<artifactId>commons-lang</artifactId>			<version>2.6</version>		</dependency>		<dependency>			<groupId>commons-logging</groupId>			<artifactId>commons-logging</artifactId>			<version>1.1.3</version>		</dependency>		<dependency>			<groupId>commons-net</groupId>			<artifactId>commons-net</artifactId>			<version>3.0.1</version>		</dependency>		<dependency>			<groupId>commons-pool</groupId>			<artifactId>commons-pool</artifactId>			<version>1.6</version>		</dependency>		<dependency>			<groupId>commons-validator</groupId>			<artifactId>commons-validator</artifactId>			<version>1.3.1</version>		</dependency>		<dependency>			<groupId>org.apache.commons</groupId>			<artifactId>commons-compress</artifactId>			<version>1.3</version>		</dependency>		<!--测试 -->		<dependency>			<groupId>junit</groupId>			<artifactId>junit</artifactId>			<version>4.10</version>		</dependency>		<!--j2ee web spec -->		<dependency>			<groupId>javax.servlet</groupId>			<artifactId>servlet-api</artifactId>			<version>2.5</version>			<scope>provided</scope>		</dependency>		<dependency>			<groupId>javax.servlet</groupId>			<artifactId>jstl</artifactId>			<version>1.2</version>		</dependency>		<dependency>			<groupId>taglibs</groupId>			<artifactId>standard</artifactId>			<version>1.1.2</version>		</dependency>		<!--json -->		<dependency>			<groupId>org.codehaus.jackson</groupId>			<artifactId>jackson-mapper-asl</artifactId>			<version>1.9.13</version>		</dependency>	</dependencies>	<build>		<finalName>mtag</finalName>	</build></project>

 

二: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="http://my.oschina.net/WEB-INF/views/" />  		<property name="suffix" value="http://my.oschina.net/u/1163141/blog/.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 */@Componentpublic 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也必须添加注解才行啊。

@Componentpublic 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@Inheritedpublic @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@Componentpublic 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="http://my.oschina.net/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="http://my.oschina.net/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 noJuliset 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" 

 

转自 http://www.07net01.com/2015/08/889518.html

Spring+SpringMVC+MyBatis+LogBack+C3P0+Maven+Git小结(转)