2016/1/20 14:47:28
原創,轉載請註明出處
曾經看過《那些年我們一起追過的女孩》,片中有個比較經典的畫面,至今記憶猶新,柯景騰多年後,做了一名作家,每天面對電腦碼字,背後是一張張一閃而過的字幅“人生就是不停的戰鬥”
作爲程序員,我想一生就是不停的和BUG做戰鬥了,新來的這家公司,用的很老的Spring+Struts2.0+JDBC的框架,非常的不好用,其中的問題可以陳述10條以上,但是限於當時的技術水平所限,不能達到也很正常,所以我萌生了一個推翻重寫後臺的想法(從這到正文部分純屬裝逼的,大神繞過,新人還是需要看一哈,熟悉一下爲什麼是MAVEN)
JAVA這門語言,之前看一個漫畫是這麼形容的,C++是加農炮,威力強大,什麼都能炸;JAVA是一個手機,它能打電話叫來各種炮,炸的你爹媽不認得,但是它的JAR存在着依賴關係,比如Spring中的對AspectJ和JSR的支持是基於AspectJ和其他的JAR包的,你用它之前必須要先有依賴的jar包;項目中有兩個典型的jar就是如此
- aopalliance-1.0.jar 它是SpringAOP的基礎依賴包
- hamcrest-core-1.1.jar 他是Junit依賴的jar包
那麼問題來了,我要是打電話,請人幫我“炸樓”,就好比JAVA請JAR包,我是不是連它爸媽一起請?我是不是要配置所有的依賴包,答案NO,因爲我可以藉助MAVEN這個神器啊,它會自動解析包的依賴關係去自動下載
在這個項目的搭建和第一次用maven沒有人幫助我的情況下,能做出來還是有些小激動,也學習到了很多新的知識,重新把Spring學習了一遍,進一步理解了XML配置文件,SpringIOC和SpringAOP,驚歎於Spring框架的神奇之處,自己也掌握了許多Spring相關的技術,例如:Spring是支持JSON的,我們可以直接在控制層方法加@ResponseBody,可以自動返回JSON對象,So Easy
本文作者所用:所有東西的版本
- MyEClipse GA 2014
- Spring 4.2.4(最新)
- Mybatis 3.3.0(最新)
參考博客
http://blog.csdn.net/zhshulin/article/details/37956105
http://blog.csdn.net/l349440843/article/details/45788017
http://blog.csdn.net/zhshulin/article/details/37937365
本文主要介紹的技術:(大神繞過)
- MAVEN搭建JAVA-WEB項目
- Spring的配置文件
- Junit單元測試
- Spring整合JSON輸出
- Spring整合Mybatis不需要寫DAO層代碼
- Log4j控制輸出,主要介紹擴展方式寫日誌進數據庫的方式
- 代碼註釋的規範,和一些MyEclipse的規範操作(個人覺得規範的)
作爲程序界的老鳥,看到這篇文章就自動過濾之前這一段,不過本作也真誠的希望你們能爲我提出建議,謝謝,言歸正傳,開始操練
1.MAVEN
- Maven是基於項目對象模型(POM),可以通過一小段描述信息來管理項目的構建,報告和文檔的軟件項目管理工具。
- Maven 除了以程序構建能力爲特色之外,還提供高級項目管理工具。由於 Maven 的缺省構建規則有較高的可重用性,所以常常用兩三行 Maven 構建腳本就可以構建簡單的項目。由於 Maven 的面向項目的方法,許多 Apache Jakarta 項目發文時使用 Maven,而且公司項目採用 Maven 的比例在持續增長。
- 用MAVEN來管理jar包,不用一個一個去添加和下載jar包了,直接在maven配置文件中配置就可以了,maven可以幫助我們自動下載
1.1首先搭建一個maven項目
第一次新建MAVEN項目的筒子,你們在這裏要稍微等一哈,MAVEN要去下載依賴
WEB項目點中上面的maven-archetype-webapp點擊下一步
Group Id一般填公司的包名吧,第二個填公司名,其實我也不是很懂這個= =好像沒有多大用
然後MAVEN就開始在US鏡像倉庫下載包和一些準備工作了你的MyEclipse的右下角,就會出現
至此,你的MAVEN項目就初步搭建好了
1.2那麼接下來怎麼搞
搭建好的MAVEN項目如下圖
問題有兩個,一個我,作爲一個高逼格的程序員,一定要用最新版本的JRE環境,所以把項目構建改到最高版本,另外,報了一個 “javax.servlet.http.HttpServlet” was not found on the Java Build Path index.jsp錯誤,這是由於在index.jsp頁面上缺少對JavaEE的支持,因此需要添加JavaEE的類庫
- 右鍵工程–>properties–>JAVA Build Path–>Edit
- 在pox.xml中添加對JAVA EE的依賴
1.3最後是pom.xml的配置
如果遇到環境配置的問題,可以參考博文:MyEclipse+Tomcat+MAVEN+SVN項目完整環境搭建
點開pom.xml配置你的項目maven依賴,坐等幾分鐘,maven會下載JAR,如果不下載,或者停止,就點開MyEclipse 找到 MAVEN4MyEclipse 把下載按鈕打開,或者還原爲默認的
<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>com.zjx</groupId>
<artifactId>zjx</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>zjx Maven Webapp</name>
<url>http://maven.apache.org</url>
<properties>
<!-- spring版本號 -->
<spring.version>4.2.4.RELEASE</spring.version>
<!-- mybatis版本號 -->
<mybatis.version>3.3.0</mybatis.version>
<!-- log4j日誌文件管理包版本 -->
<slf4j.version>1.7.7</slf4j.version>
<log4j.version>1.2.16</log4j.version>
</properties>
<dependencies>
<!-- 單元測試的依賴 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
<!-- spring核心包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- spring對web的支持 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- spring對ORM映射的支持-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-oxm</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- spring事務管理支持 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- spring對JDBC的支持 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- spring對MVC框架的支持 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- spring切面支持 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- spring上下文支持 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- spring測試支持 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- AspectJ支持 -->
<dependency>
<groupId>aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.5.4</version>
</dependency>
<!-- 織入 -->
<dependency>
<groupId>aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.5.4</version>
</dependency>
<!-- mybatis核心包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency>
<!-- mybatis對spring的支持 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.2.3</version>
</dependency>
<!-- 導入Mysql數據庫鏈接jar包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.30</version>
</dependency>
<!-- 導入dbcp的jar包,用來在applicationContext.xml中配置數據庫 -->
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
<!-- JSTL標籤類 -->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- 日誌文件管理包 -->
<!-- log start -->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<!-- 格式化對象,方便輸出日誌 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.1.41</version>
</dependency>
<!-- 導入java ee jar 包 -->
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
</dependency>
<!-- log end -->
<!-- 映入JSON支持 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.5.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.5.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.5.1</version>
</dependency>
<!-- 文件上傳 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.9</version>
</dependency>
</dependencies>
<build>
<finalName>zjx</finalName>
</build>
</project>
所有的依賴包,和作用都註釋清楚,至此,MAVEN的工作就完了,剩下的事情就是項目
2. Spring配置文件
首先,先幾個題外話,所有人都知道程序分離是爲了簡化,人們習慣上把複雜的事情分解爲若干個簡單的事情處理,這就體現的是分層思想,所以程序上纔有了MVC,纔有了三層架構
當人們認知事物的高度達到一定程度之後,就會抓住事物的核心問題,也就是程序設計上常說的問題域,那麼請看下面兩個故事:
讓時間回到2001年,地點是澳大利亞悉尼的Clarence Street有一家叫做Cirrus Technologies的公司,這是一家做J2EE企業級應用開發和諮詢的公司,在會議桌上一個小夥子和老闆正在進行着激烈的討論。
小夥子:”老闆,我總覺得開發的效率太低了,我用了EJB的Entity bean 1.1時,我總覺得我浪費了好多時間在處理Entity Bean的體系架構上,卻沒有花時間在覈心業務邏輯的開發上,而且CMP給我們的限制太多了”。
老闆:”Gavin,別傻了,EJB是業界的標準,也是最流行的技術,而且我們公司是IBM的合作伙伴。如果有問題,問題就是我們還沒有適應這樣的開發模式”。
小夥子:”不,我覺得肯定有更好的解決的方案。我們可以設計出比Entity Bean更好的方案”。
老闆:”哦,Gavin,我知道你很聰明,開發水平也不錯。但是開發這樣的系統太難了,而且你根本就沒有用SQL開發過任何數據庫系統。不要想這樣一個不現實的目標啦!”
小夥子皺了皺眉,說道:”不,我相信我有能力開發出這個系統。我的想法絕對是可行的。”
(注:以上場景純屬虛構,但至少以下內容完全屬實:Gavin King開發hibernate的動機有兩個:發現CMP太爛;贏得對老闆的爭執。Gavin King當時沒有任何用SQL開發數據庫的經驗,Gavin King開發hibernate的第一件事是去街上買了本SQL基礎的書)
也許Cirrus Technologies的老闆做夢也想不到兩年以後,這個小夥子開發出的那個產品會成爲全世界最流行的O/R Mapping工具,而那個對SQL和數據庫一竅不通的小夥子居然會成爲全世界J2EE數據庫解決方案的領導者。
這就是Gavin King,一個充滿激情、脾氣很倔、永不言敗的人。他的成就也許全世界搞Java的人都知道:他是hibernate的創始人;他是EJB 3.0的Entity bean specification的實際領導人(sun任命的領導人應該是Linda DeMichiel);他也是那本經典的書hibernate in action的作者;他也參加了XDoclet和Middlegen的開發;他在全世界各種著名的會議(TheServerSide Symposium等)進行演講和講座。
2010年這個項目由apache software foundation 遷移到了google code,並且改名爲MyBatis 。2013年11月遷移到Github。它的廣告語是“我們已經學習了SQL,爲什麼我們還要學習HQL”;
故事給我的感觸是,必須要學習新的技術,永遠做第一個吃螃蟹的人,因爲只有你知道了什麼框架,有什麼不足,你纔會去改進,纔會去發明新的框架,這也就是許多大牛,喜歡鑽研技術,自己寫框架,而不用別人現成的框架,小弟只是個渣渣,各位路過的大神勿噴,能指點我一二最好
不扯閒話:上配置(配置也體現了控制層和業務層分離的思想),因爲項目開發的主要問題域在於業務邏輯的處理上,而不在DAO代碼上,因此沒有寫DAO層,而交給Mybatis去自動控制,也體現了故事一種的思想,配置文檔截圖如下
- applicationContext-action.xml 是專門配置持久層的
- applicationContext-base.xml 是專門配置控制層的
- jdbc.properties 是專門配置數據庫連接的,這樣配置提現分離和修改數據庫的作用
- log4j.properties 是專門配置日誌輸出和日誌控制的
- mybatis.cfg.xml 是爲了提供ORM映射的配置,其中只需要配置一個類的別名,mapper已經移交Spring管理,但是個人嫌寫類名麻煩,喜歡用別名
- mybatis.cfg.xml 這個配置可以不要,看到有大神才用註解的方式,希望有大神路過不吝賜教
applicationContext-action.xml
<?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:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-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/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
<!-- 自動掃描該包,使SpringMVC認爲包下用了@controller註解的類是控制器,控制器全部放在這個包下 -->
<context:component-scan base-package="com.zjx.action">
<!-- 如果使用包含採用該配置 <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> -->
<!-- 如果使用過濾採用該配置 <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/> -->
</context:component-scan>
<!--避免IE執行AJAX時,返回JSON出現下載文件 -->
<bean id="mappingJacksonHttpMessageConverter"
class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<!-- 設置返回的編碼和解析方式 -->
<value>text/html;charset=UTF-8</value>
</list>
</property>
</bean>
<!-- 啓動SpringMVC的註解功能,完成請求和註解POJO的映射 -->
<bean
class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="mappingJacksonHttpMessageConverter" /> <!-- JSON轉換器 -->
</list>
</property>
</bean>
<!-- 定義跳轉的文件的前後綴 ,視圖模式配置-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 這裏的配置我的理解是自動給後面action的方法return的字符串加上前綴和後綴,變成一個 可用的url地址 -->
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
<!-- 配置文件上傳,如果沒有使用文件上傳可以不用配置,當然如果不配,那麼配置文件中也不必引入上傳組件包 -->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 默認編碼 -->
<property name="defaultEncoding" value="utf-8" />
<!-- 文件大小最大值 -->
<property name="maxUploadSize" value="10485760000" />
<!-- 內存中的最大值 -->
<property name="maxInMemorySize" value="40960" />
</bean>
</beans>
applicationContext-base.xml
<?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:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-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/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<!-- 自動掃描 -->
<context:component-scan base-package="com.zjx" />
<!-- 引入配置文件 -->
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:jdbc.properties" />
</bean>
<!-- 配置數據源 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<!-- 初始化連接大小 -->
<property name="initialSize" value="${jdbc.initialSize}"></property>
<!-- 連接池最大數量 -->
<property name="maxActive" value="${jdbc.maxActive}"></property>
<!-- 連接池最大空閒 -->
<property name="maxIdle" value="${jdbc.maxIdle}"></property>
<!-- 連接池最小空閒 -->
<property name="minIdle" value="${jdbc.minIdle}"></property>
<!-- 獲取連接最大等待時間 -->
<property name="maxWait" value="${jdbc.maxWait}"></property>
</bean>
<!-- spring和MyBatis整合-->
<bean id="sessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<!-- 指明mybatis主配置文件路徑 -->
<property name="configLocation" value="classpath:mybatis.cfg.xml"></property>
<!-- 指明mybatis的映射位置 -->
<property name="mapperLocations">
<list>
<value>classpath:orm/*.xml</value>
</list>
</property>
</bean>
<!-- 配置SqlSession模版類,SqlSessionTemplate是線程安全類 -->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate" scope="prototype">
<constructor-arg ref="sessionFactory"></constructor-arg>
</bean>
<!-- 事務管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 允許使用註解配置事務 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- 掃描spring註解類 -->
<context:component-scan base-package="com.zjx">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
</beans>
jdbc.properties
jdbc.driver=org.gjt.mm.mysql.Driver
#定義連接的URL地址,設置編碼集,允許多條SQL語句操作
jdbc.url=jdbc:mysql://localhost:3306/test?characterEncoding=utf-8&allowMultiQueries=true
jdbc.username=root
jdbc.password=82651205
#定義初始連接數
jdbc.initialSize=10
#定義最大連接數
jdbc.maxActive=20
#定義最大空閒
jdbc.maxIdle=20
#定義最小空閒
jdbc.minIdle=10
#定義最長等待時間
jdbc.maxWait=60000
log4j.properties
### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### direct messages to file hibernate.log ###
#log4j.appender.file=org.apache.log4j.FileAppender
#log4j.appender.file.File=hibernate.log
#log4j.appender.file.layout=org.apache.log4j.PatternLayout
#log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### set log levels - for more verbose logging change 'info' to 'debug' ###
log4j.rootLogger=warn, stdout
#log4j.logger.org.hibernate=info
#log4j.logger.org.hibernate=debug
### log HQL query parser activity
#log4j.logger.org.hibernate.hql.ast.AST=debug
### log just the SQL
#log4j.logger.org.hibernate.SQL=debug
### log JDBC bind parameters ###
#log4j.logger.org.hibernate.type=info
#log4j.logger.org.hibernate.type=debug
### log schema export/update ###
#log4j.logger.org.hibernate.tool.hbm2ddl=debug
### log HQL parse trees
#log4j.logger.org.hibernate.hql=debug
### log cache activity ###
#log4j.logger.org.hibernate.cache=debug
### log transaction activity
#log4j.logger.org.hibernate.transaction=debug
### log JDBC resource acquisition
#log4j.logger.org.hibernate.jdbc=debug
### enable the following line if you want to track down connection ###
### leakages when using DriverManagerConnectionProvider ###
#log4j.logger.org.hibernate.connection.DriverManagerConnectionProvider=trace
################################################################################
########文件記錄日誌的方式
##定義LOG輸出級別
#log4j.rootLogger=INFO,Console,File
##定義日誌輸出目的地爲控制檯
#log4j.appender.Console=org.apache.log4j.ConsoleAppender
#log4j.appender.Console.Target=System.out
##可以靈活地指定日誌輸出格式,下面一行是指定具體的格式
#log4j.appender.Console.layout = org.apache.log4j.PatternLayout
#log4j.appender.Console.layout.ConversionPattern=[%c] - %m%n
#
##文件大小到達指定尺寸的時候產生一個新的文件
#log4j.appender.File = org.apache.log4j.RollingFileAppender
##指定輸出目錄
#log4j.appender.File.File = logs/ssm.log
##定義文件最大大小
#log4j.appender.File.MaxFileSize = 10MB
## 輸出所以日誌,如果換成DEBUG表示輸出DEBUG以上級別日誌
#log4j.appender.File.Threshold = ALL
#log4j.appender.File.layout = org.apache.log4j.PatternLayout
#log4j.appender.File.layout.ConversionPattern =[%p] [%d{yyyy-MM-dd HH:mm:ss}][%c]%m%n
################################################################################
########數據庫記錄日誌的方式
log4j.logger.com.zjx.util=error,database
log4j.appender.database=org.apache.log4j.jdbc.JDBCAppender
log4j.appender.database.URL=jdbc:mysql://localhost:3306/test?characterEncoding=utf-8
log4j.appender.database.driver=org.gjt.mm.mysql.Driver
log4j.appender.database.user=root
log4j.appender.database.password=82651205
log4j.appender.database.sql=INSERT INTO t_log(content,logDate) VALUES ('%m','%d{yyyy-MM-dd}')
log4j.appender.database.layout=org.apache.log4j.PatternLayout
log4j.appender.database.layout.ConversionPattern=%m
mybatis.cfg.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<!-- 如果還有其餘的映射類,則繼續添加 -->
<typeAlias type="com.zjx.bean.UserBean" alias="user"/>
</typeAliases>
</configuration>
UserBean.xml
<?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="user">
<!-- 結果集映射 -->
<resultMap type="user" id="userMap">
<result property="passWord" column="pwd"/>
</resultMap>
<select id="findById" parameterType="int" resultMap="userMap">
select * from t_user where id = #{id};
</select>
<!-- 字段模糊的動態語句 -->
<sql id="paramSql">
<if test="userName !=null and userName != ''">
and userName like '%${userName}%'
</if>
</sql>
<select id="findPage" resultMap="userMap">
select * from t_user
where 1=1 <include refid="paramSql"></include>
limit #{start},#{pageSize}
</select>
<select id="findCount" resultType="int">
select count(*) from t_user where 1=1 <include refid="paramSql"></include>
</select>
</mapper>
3.Junit測試
表自己建好,我也懶
那麼首先書寫實體:
package com.zjx.bean;
import java.sql.Date;
/**
* @Description 用戶實體類
* @author tony_kanper
* @date 2016年1月20日 上午11:30:32
* @version V1.0
*
*/
public class UserBean {
/**
* id
*/
private int id;
/**
* 用戶名
*/
private String userName;
/**
* 生日
*/
private Date birthday;
/**
* 密碼
*/
private String passWord;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getPassWord() {
return passWord;
}
public void setPassWord(String passWord) {
this.passWord = passWord;
}
@Override
public String toString() {
return "UserBean [id=" + id + ", userName=" + userName + ", birthday="
+ birthday + ", passWord=" + passWord + "]";
}
public UserBean(String userName, Date birthday, String passWord) {
super();
this.userName = userName;
this.birthday = birthday;
this.passWord = passWord;
}
public UserBean() {
super();
}
}
業務接口Service
package com.zjx.service;
import com.zjx.bean.CutPageBean;
import com.zjx.bean.UserBean;
/**
* @Description: 用戶業務接口
* @author tony_kanper
* @date 2016年1月20日 上午11:30:50
* @version V1.0
*
*/
public interface IUserService {
/**
* 每頁顯示記錄數
*/
public final int PAGESIZE = 10;
/**
* @Desciption 按照用戶id查找該用戶
* @param id 用戶id
* @return 用戶實體
*/
public UserBean findById(int id);
/**
* @Description 按照頁碼和用戶姓名模糊字查找用戶分頁對象
* @param pageNO 頁碼
* @param userName 用戶姓名模糊字
* @return 分頁對象
*/
public CutPageBean<UserBean> findByItem(int pageNO,String userName);
}
業務接口實現類ServiceImpl超類和具體實現類
package com.zjx.service.impl;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.stereotype.Service;
import com.zjx.bean.CutPageBean;
/**
* @Description 所有業務實現類的超類
* @author tony_kanper
* @date 2016年1月20日 上午11:31:07
* @version V1.0
*
* @param <T> 通用泛型
*/
@Service
public class BaseService<T> {
/**
* SqlSesion模板類
*/
@Resource
protected SqlSessionTemplate session;
/**
* @Description 所有的查詢分頁對象的超類方法
* @param pageNO 頁碼
* @param pageSize 每頁顯示記錄數
* @param paramMap 需要查詢的字段集合
* @param listSql 查詢某頁記錄的SQL語句
* @param countSql 查詢總記錄數的SQL語句
* @return 分頁對象
*/
public CutPageBean<T> cutpage(int pageNO, int pageSize,
Map<String, Object> paramMap, String listSql, String countSql) {
CutPageBean<T> cutBean = new CutPageBean<T>();
if (paramMap == null) {
paramMap = new HashMap<String, Object>();
}
// 設置查詢的起始頁
paramMap.put("start", (pageNO - 1) * pageSize);
// 設置查詢頁開始向後的記錄數
paramMap.put("pageSize", pageSize);
// 查詢Mybatis配置文件下,命名空間內的查詢結果
List<T> list = this.session.selectList(listSql, paramMap);
cutBean.setList(list);
// 設置總記錄數
int count = this.session.selectOne(countSql, paramMap);
cutBean.setCount(count);
// 設置總頁數
cutBean.setTotalPage(count / pageSize);
if (count % pageSize != 0) {
cutBean.setTotalPage(count / pageSize + 1);
}
return cutBean;
}
}
具體實現類如下
package com.zjx.service.impl;
import java.util.HashMap;
import java.util.Map;
import org.springframework.stereotype.Service;
import com.zjx.bean.CutPageBean;
import com.zjx.bean.UserBean;
import com.zjx.service.IUserService;
/**
* @Description 用戶業務實現類
* @author tony_kanper
* @date 2016年1月20日 上午11:50:57
* @version V1.0
*
*/
@Service
public class UserServiceImpl extends BaseService<UserBean> implements IUserService {
@Override
public UserBean findById(int id) {
// 測試切面類是否完成錯誤記錄進入數據庫時,解除註釋
// throw new NullPointerException();
return session.selectOne("user.findById", id);
}
@Override
public CutPageBean<UserBean> findByItem(int pageNO, String userName) {
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("userName", userName);
return cutpage(pageNO, PAGESIZE, paramMap, "user.findPage", "user.findCount");
}
}
測試類:
測試類超類:
package com.zjx.test;
import org.junit.After;
import org.junit.Before;
import org.springframework.beans.BeansException;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.util.StringUtils;
public class UnitTestBase {
/**
* 相對路徑應用上下文
*/
private ClassPathXmlApplicationContext context;
/**
* XML文件的存放路徑
*/
private String springXmlPath;
public UnitTestBase() {
}
public UnitTestBase(String springXmlPath){
this.springXmlPath = springXmlPath;
}
/**
* 加載XML文件
*/
@Before
public void before(){
if (StringUtils.isEmpty(springXmlPath)) {
springXmlPath = "classpath*:spring-*.xml";
}
try {
// 多個xml文件用逗號或者空格符隔開,均可加載
context = new ClassPathXmlApplicationContext(springXmlPath.split("[,\\s]+"));
context.start();
} catch (BeansException e) {
e.printStackTrace();
}
}
/**
* 銷燬
*/
@After
public void after(){
context.destroy();
}
/**
* @param beanId SpringXML文件中的bean的id
* @return 該bean的id對應下的實例,默認單例
*/
@SuppressWarnings("unchecked")
protected <T extends Object> T getBean(String beanId){
try {
return(T)context.getBean(beanId);
} catch (BeansException e) {
e.printStackTrace();
return null;
}
}
/**
* @param clazz 通過類模板獲取該類
* @return 該類的實例,默認單例
*/
protected <T extends Object> T getBean(Class<T> clazz){
try {
return context.getBean(clazz);
} catch (BeansException e) {
e.printStackTrace();
return null;
}
}
}
測試類
package com.zjx.test;
import org.junit.runner.RunWith;
import org.junit.runners.BlockJUnit4ClassRunner;
import com.zjx.service.IUserService;
@RunWith(BlockJUnit4ClassRunner.class)
public class Test extends UnitTestBase{
public Test() {
super("classpath:applicationContext-base.xml");
}
@org.junit.Test
public void testUnit(){
IUserService service = super.getBean("userServiceImpl");
System.out.println(service.findByItem(1, "張"));
}
}
這樣測試,免去了註釋實現類中的測試類的麻煩,並且在pom.xml中設置了scope它僅在測試時引入這個包,實際使用時,並不會用到
<!-- 單元測試的依賴 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
4.Spring整合JSON輸出
也許小夥伴會問,不是說好了,要來玩JSON嗎?
<!--避免IE執行AJAX時,返回JSON出現下載文件 -->
<bean id="mappingJacksonHttpMessageConverter"
class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<!-- 設置返回的編碼和解析方式 -->
<value>text/html;charset=UTF-8</value>
</list>
</property>
</bean>
<!-- 啓動SpringMVC的註解功能,完成請求和註解POJO的映射 -->
<bean
class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="mappingJacksonHttpMessageConverter" /> <!-- JSON轉換器 -->
</list>
</property>
</bean>
Spring對JSON的支持也挺噁心的,如果Spring的版本足夠高,他對JSON的支持會用到配置文件中的MappingJackson2HttpMessageConverter,注意這個Jackson2,如果你按着ctrl沒鏈接,那麼把2刪除,說明你用的Spring版本是之前的,你用的json版本1也都足夠了,如果你只能用2,那麼,你的pom.xml中必須加入2.0版本以上的依賴,對此Spring上的說明也是這樣的,樓主看了很久的源碼= =,快給我點贊
<!-- 映入JSON2.5支持 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.5.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.5.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.5.1</version>
</dependency>
由圖可知,在類的註釋上最後一行,說明MappingJackson2HttpMessageConverter兼容Jackson 2.1和更高版本,之所以貼出來說,是想說明看源碼很重要,也想說明我不是亂說的
接下來寫一個JSP頁面發送AJAX請求試試
<%@ page language="java" pageEncoding="UTF-8"%>
<html>
<body>
<h2>Hello World!</h2>
<hr/>
<a href="/zjx/user/nextPage.do">去看看用戶頁</a>
<script type="text/javascript" src="/zjx/jquery-2.1.4.min.js"></script>
<script type="text/javascript">
$(function(){
$.post("/zjx/user/findById.do","id=1",function(data){
alert(data);
});
});
</script>
</body>
</html>
action控制層:
package com.zjx.action;
import javax.annotation.Resource;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.zjx.bean.CutPageBean;
import com.zjx.bean.UserBean;
import com.zjx.service.IUserService;
/**
* @Description 用戶控制器
* @author tony_kanper
* @date 2016年1月20日 上午11:33:46
* @version V1.0
*
*/
@Controller
@RequestMapping("/user")
public class UserAction {
/**
* 注入的業務接口
*/
@Resource
private IUserService service;
/**
* @Description 根據視圖層傳入的id調用業務方法查找到用戶後採用轉換爲JSON對象返回
* @param id 用戶id
* @return 用戶實體的JSON對象
*/
@RequestMapping("/findById")
@ResponseBody//Spring集成的JSON,可以將Model轉換爲JSON對象存放在HttpServletResponse中
private UserBean findById(int id){
return service.findById(id);
}
/**
* @Description 按照頁碼和姓名模糊字查找某頁全部用戶對象
* @param pageNO 頁碼
* @param userName 用戶姓名模糊字
* @return 某頁全部用戶對象(JSON)
*/
@RequestMapping("/findByPageAndName")
@ResponseBody
private CutPageBean<UserBean> findByPageAndName(int pageNO,String userName){
return service.findByItem(pageNO, userName);
}
/**
* @Description 測試是否跳轉去WEB-INF目錄下的jsp頁面
* @return 返回視圖的名稱
*/
@RequestMapping("/nextPage")
private String nextPage(){
return "allUserPage";
}
}
嘿,通過測試,還真的在頁面拿到了JSON數據呢,好神奇啊
5.Spring整合Mybatis不需要寫DAO層代碼
話說,倪們看到我寫了DAO層代碼了嗎?因爲DAO已經寫到UserBean.xml中和配置裏了,要是不懂這個XML裏面怎麼寫SQL語句,就網上或者買書看看,不多說;
這裏值得一提的是,我用了SqlSessionTemplate這個類,這個類有點搞笑,他是自動管理的,也就是說,他的創建銷燬是不必依賴Spring的,也就是說,當SQL模版類查詢結束之後,他自己就知道關閉了,而懂點SpringIOC的筒子都知道,bean默認是單例的,那麼要是他自己關了,或者別人又用了這個類,併發的訪問扎個辦?答案是:你知道Mybatis的工程師有多騷不?
這個SqlSessionTemplate的註釋是這個意思,他是線程安全的,是受Spring管理的,和Spring事務管理器一起工作保證只用在一個當前的線程,它也是Spring管理下的事務內;此外,它管理會話的生命週期,包括關閉,提交或回滾基於Spring事務配置必要的
看到這裏,也就是說,Mybatis的工程師們,把這個棘手的問題甩給Spring去管,他們就當甩手掌櫃了啊,反正有mybatis-spring.jar這個提供對Spring支持的神器在
<!-- mybatis對spring的支持 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.2.3</version>
</dependency>
也正是如此,我們在配置base的時候,會將模版類設置爲多例,我問騷年你爲何如此勇敢,因爲它是線程安全的,也受Spring的管理
<!-- 配置SqlSession模版類,SqlSessionTemplate是線程安全類 -->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate" scope="prototype">
<constructor-arg ref="sessionFactory"></constructor-arg>
</bean>
另外,就算你不寫這個多例,也不影響,最多報個警告
WARN DisposableBeanAdapter:364 - Invocation of destroy method ‘close’ failed on bean with name ‘sqlSession’: java.lang.UnsupportedOperationException: Manual close is not allowed over a Spring managed SqlSession
這個警告是說,在Spring管理下的sqlSession是不允許自己關閉的
PS:如果有大神找到其中更深的緣由,請不吝賜教
6. Log4j控制輸出
關於Log4j的東西,網上太多了,各位慢慢看,個人覺得這篇博文蠻不錯
關於擴展輸出的,我用的是AOP方式
工具類如下:
package com.zjx.util;
import java.sql.Date;
import java.text.SimpleDateFormat;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Service;
/**
* @Description 日誌處理類
* @author tony_kanper
* @date 2016年1月20日 上午11:39:48
* @version V1.0
*
*/
@Service
@Aspect
public class LogRecord {
/**
* 日期格式化輸出對象
*/
private SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss");
/**
* 日誌對象
*/
private Logger log = Logger.getLogger(LogRecord.class);
/**
* @Description 在方法拋出異常之後,記錄異常信息,並將異常信息以error等級封裝到log對象中
* @param point 切入點
* @param e 方法拋出的異常
*/
@AfterThrowing(value = "execution(* com.zjx.service.impl.*.*(..))", throwing = "e")
public void writeLog(JoinPoint point, Exception e) {
// 得到目標對象的類名
String className = point.getTarget().getClass().getName();
// 得到目標對象的方法名
String methodName = point.getSignature().getName();
// 當前日期
String timeStr = df.format(new Date(System.currentTimeMillis()));
// 得到異常類信息
String exceptionStr = e.getClass().getName();
String info = timeStr+" 在"+className+"的 "+methodName+"()方法,拋出"+exceptionStr+"異常";
//System.out.println(info);
log.error(info);
}
}
一旦在com.zjx.service.impl包下的所有類的所有方法拋出異常之後,都會向log日誌對象寫入錯誤信息,並且在log4j配置文檔中寫入數據庫,經測試無誤
PS:對於環繞通知,和其它通知方式,關於訂單類的記錄,有大神會的,希望給我一個DEMO謝謝
附上郵箱:[email protected]
7.代碼註釋的規範
之前的項目也不是無可挽救,但是代碼實在是太糟糕了,沒用註釋,或者關鍵性的註釋用單行註釋而不用文檔註釋,這對於代碼閱讀是相當困難的
對於文檔註釋,可以生成Javadoc,同時,當鼠標浮動到具有文檔註釋的類/方法/變量時,能提供說明,簡單粗暴
通常我們可以在Myeclipse中配置你的註釋樣本
如圖,我的文件註釋上,擁有標題,包,文件描述,作者創建日期等信息
當然,你也可以通過Edit編輯你的輸出樣式
然後你可以按住Alt+Shirft+J快速在相應位置生成註釋
最後你只需要填就是了
程序員一定要養成寫註釋的習慣,不關多大的公司,就說國內的支付寶,別個寫對外接口不也一樣寫好的文檔註釋和代碼的單行註釋,我等菜鳥,更應該如此,這更多的是爲了今後的程序員便於維護,而不是爲了你看懂(當然你肯定希望別人寫好註釋)
8.最後
花了很多時間學習這些,最後的收穫也蠻大,希望能遇到更多志同道合的朋友
項目中容易遇到的問題主要有這些,有的問題之前我已經提到了
- MAVEN環境變量的配置,在path中加入%MAVEN_HOME%\bin;如果還需要配置jar包的倉庫可以參看博文MyEclipse+Tomcat+MAVEN+SVN項目完整環境搭建
- 對於SqlSession報錯的問題,要加多例
- 對於Spring配置文件讀取jdbc.properties文件時,報錯,有可能是mySQL的權限驗證問題,它會默認使用系統的userName,解決方案有兩種,一種參看博文mysql啓動問題access denied for user ‘root’@’localhost’(using password:YES) 另一種是找到mysql安裝目錄,在my.ini中,以記事本打開的文末添加–skip-grant-tables
對於Spring版本較低的,Json,可以用如下依賴
org.codehaus.jackson
jackson-mapper-asl
1.9.13
MAVEN倉庫傳送門MAVEN倉庫,你只需要像百度一樣在搜索欄中輸入jar,它們自動會爲你找到最新版本jar包依賴
- Myeclipse GA 2014的下載門,做爲程序員肯定要用最新版的哇,遇到問題去解決就是了,不要怕遇到問題而用舊版,很多過時方法和不支持的東西,新的東西,永遠更多插件支持,永遠兼容舊版http://www.my-eclipse.cn/
- 如果有興趣,可以關注我的CSDN http://blog.csdn.net/kang82651204/