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

<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="/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"
时间: 2024-10-11 20:55:43

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自定义标签形式实现配置项类型数据的缓存数据结构

Maven配置Spring+SpringMVC+MyBatis(3.2.2)Pom 以及 IntelliJ IDEA 怎样打开依赖视图

Maven配置Spring+SpringMVC+MyBatis(3.2.2)Pom 配置原则: 利用依赖,将所需的jar包加载到project中. 先依赖主要jar包 Spring + Spring MVC + MyBatis + database 再依赖次要jar包 C3p0连接池.log4j,Junit 外围jar包 POI jFreeCharts jxl 样例: 通常仅仅使用Spring时,将spring-context依赖到pom中. <dependency> <groupId&

Spring+SpringMVC+MyBatis+Maven框架整合

本文记录了Spring+SpringMVC+MyBatis+Maven框架整合的记录,主要记录以下几点 一.Maven需要引入的jar包 二.Spring与SpringMVC的配置分离 三.Spring与MyBatis的整合 一.Maven需要引入的jar包 本文默认读者已经掌握Maven的使用,Maven配置片段如下 Xml代码   <!-- 引入spring-webmvc与spring-jdbc --> <dependency> <groupId>org.sprin

Maven配置Spring+SpringMVC+MyBatis(3.2.2)Pom 以及 IntelliJ IDEA 如何打开依赖视图

Maven配置Spring+SpringMVC+MyBatis(3.2.2)Pom 配置原则: 利用依赖,将所需的jar包载入到工程中. 先依赖主要jar包 Spring + Spring MVC + MyBatis + database 再依赖次要jar包 C3p0连接池,log4j,Junit 外围jar包 POI jFreeCharts jxl 例子: 通常只使用Spring时,将spring-context依赖到pom中. <dependency> <groupId>org

[JSP]Maven+SSM框架(Spring+SpringMVC+MyBatis)

来源:http://blog.csdn.net/zhshulin/article/details/37956105?utm_source=tuicool&utm_medium=referral(以下博文对原博文有改动和补充) 开发环境: Eclipse Java EE IDE for Web Developers. Version: Mars.2 Release (4.5.2) apache-tomcat-8.0.33 jdk1.8.0_77 MySQL 5.0.11-dev(官网下载需要账号登

IDEA中maven搭建Spring+SpringMVC+mybatis项目

一.介绍 使用IDEA搭建maven web项目,整合框架Spring+SpringMVC+mybatis 项目结构图: 二.搭建 1.新建maven项目 2.创建项目结构(如上图) 3.配置pom.xml 1 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 2 xsi:schemaLocation=

SSM框架整合(IntelliJ IDEA + maven + Spring + SpringMVC + MyBatis)

原文链接: http://blog.csdn.net/gallenzhang/article/details/51932152 本篇文章主要内容是介绍如何使用IntelliJ IDEA创建spring+ SpringMVC + MyBatis项目,下面会给出项目搭建的详细步骤以及相关的配置文件. =======================================前言 p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px "Helve

Maven+SSM框架(Spring+SpringMVC+MyBatis) - Hello World(转发)

[JSP]Maven+SSM框架(Spring+SpringMVC+MyBatis) - Hello World 来源:http://blog.csdn.net/zhshulin/article/details/37956105?utm_source=tuicool&utm_medium=referral(以下博文对原博文有改动和补充) 博文里红底白字的,为注意修改的地方,在这里先声明 源码:Maven&SSM框架 - Hello World 开发环境: Eclipse Java EE I

使用maven整合spring+springmvc+mybatis

使用maven整合spring+springmvc+mybatis 开发环境: 1. jdk1.8 2. eclipse4.7.0 (Oxygen) 3. mysql 5.7 在pom.xml文件中,引入我们需要的jar包:(按需引入jar包) <properties> <!-- 自定义的spring的版本号 --> <spring.version>4.3.18.RELEASE</spring.version> <!-- 自定义的mybaits的版本号