• 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
     */
    @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="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%conflogging.properties" goto noJuli
    set JAVA_OPTS=%JAVA_OPTS% -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djava.util.logging.config.file="%CATALINA_BASE%conflogging.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

  • 相关阅读:
    端口扫描器的几种代码实现方案
    滴滴推理引擎IFX:千万规模设备下AI部署实践
    报名啦!第四届滴滴-IEEE未来精英论坛今夏来袭
    torch单机多卡重点:
    常见异常总结,入职阿里巴巴大概率面试题!!!
    龙小树 | R语言学习参考用书推荐
    英文写作常用网址
    LaTeX常用链接与资料
    LaTeX:算法模板
    崩溃中!我终于看明白了,什么是财富自由的底层逻辑!思维导图+笔记精华
  • 原文地址:https://www.cnblogs.com/edison2012/p/6240251.html
Copyright © 2020-2023  润新知