摘要 出於興趣,想要搭建一個本身的小站點,目前正在積極的準備環境,利用Spring+SpringMVC+MyBatis+LogBack+C3P0+Maven+Git,這裏總結下最近遇到的一些問題及解決辦法,後續慢慢的繼續補~html
目錄[-]java
這裏主要介紹下如何使用MyEclipse建立一個Maven結構的Web項目mysql
1:下載並安裝好本身的maven,並在環境變量中配置對應MAVEN_HOME、PATH路徑web
檢測是否安裝完畢,能夠在cmd中輸入命令檢測:mvn --versionspring
2:在MyEclipse中關聯並使用Maven(這裏可使用MyEclipse自帶的Maven4MyEclipse,也能夠本身下載一個MyEclipse對應的Maven插件來關聯咱們的Maven3.1.1)sql
設置下本身的倉庫和配置文件路徑:數據庫
3:新建工程以下:express
至今生成一個web結構的maven項目(還缺乏部分maven目錄結構,下面補齊)apache
4:補充maven目錄結構:json
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>
在新建完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種數據類型:
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種緩存的數據結構之後,咱們得有個流程和方式把這些數據加載進來吧,這裏就先介紹下加載的大體流程。
第一步:註冊監聽,在容器啓動時就開始加載咱們的數據
在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自定義標籤形式實現配置項類型數據的緩存數據結構解析及加載:在上一篇幅中,咱們瞭解到能夠經過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下面:
擦,不報錯了。
注:代碼太多,貼上很亂,後面我整理工程上傳。
這裏列下標籤對應的文件在我工程裏的位置:
正常狀況下:咱們經過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:效果
個人代碼按照本身的習慣進行了模塊劃分,因此也但願輸出的日誌能各回各家各找各媽(按照目錄分別輸出)
我目前幾個核心的模塊劃分爲: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"
轉自 http://www.07net01.com/2015/08/889518.html