java笔记:自己动手写javaEE
寫這篇博客前,我有個技術難題想請教大家,不知道誰有很好的建議,做過互聯(lián)網的童鞋應該都知道,有點規(guī)模的大公司都會做用戶行為分析系統(tǒng),而且有些大公司還會提供專業(yè)的用戶行為分析解決方案例如:百度分析,google analysis。用戶行為分析就是當用戶訪問某個網站的頁面,會有專門系統(tǒng)記錄用戶的相關信息以及使用狀況,然后分析這些數據用來指導網站的運營,我們現在遇到一個問題:如果某的訪客訪問了www.a.com頁面,我們怎么知道這個用戶訪問過www.b.com頁面,a頁面和b頁面毫無關系,比如:某個未知訪客訪問QQ主頁,他只要打開了QQ頁面我就知道他是否訪問過sina的頁面,聽說有人把這個做出來了,但是我還沒想到,哪位高手能想到解決方案嗎?
說到用戶行為分析,這個對于互聯(lián)網公司來說相當的重要系統(tǒng),以后有時間我會開一個系列從技術到業(yè)務的角度講講用戶行為分析系統(tǒng)是啥樣子,大伙好好交流下。感興趣的童鞋多多關注哈。
轉入正題前還閑話幾句,本來開博客是想系統(tǒng)研究javascript技術,但是最近工作比較忙,而且大量工作都是java開發(fā)任務(我們公司前端工程師太不專業(yè)了,哎),因此javascript現在只得暫停下,畢竟晚上的時間還是太少。因此我想就我現在做的技術開一個新系列:自己動手寫javaEE框架,這個和我現在工作有關比較好收集資料,這個系列我的想法很大,我想寫如下內容:
1.???? struts2+ibatis+spring+js
2.???? springmvc+ibatis+spring+js
3.???? flex+ibatis+spring
如果以上寫完還有時間的話,我會把ibatis換成hibernate,也許還會自己封裝一個js框架或者更加深入講解flex技術。哎,東西挺多了,能寫完不???天知道了。
由于內容比較多,我的博客里只做簡單講解,不做深入分析。好下面開始了。
我第一個寫的是struts2+ibatis+spring+js??蚣芙Y構是頁面+action+service+dao+數據庫,數據庫為oracle(公司電腦裝的是oracle)或者mysql(家里電腦裝的是mysql)。
今天任務是搭建好spring框架,spring里面集成好ibatis框架,然后編寫dao層,最后一個重要任務是加入junit測試框架,方便以后開發(fā)的單元測試(junit測試框架是本博文的亮點,我想許多做java童鞋看完本篇博客還是很有啟發(fā)的,因為我發(fā)現很多朋友都不太會做跟spring相關的單元測試,這個會了能提升不少開發(fā)效率)。我是按下面步驟寫的:
1.???? 首先是工程結構如下圖:
?
2.使用到的jar包如下:
?
?
3.???? 我首先寫的是web.xml,代碼如下:
?
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
??? <display-name>ssiprj</display-name>
? <!-- spring配置文件位置 -->
? <context-param>
????? <param-name>contextConfigLocation</param-name>
????? <param-value>classpath:conf/applicationContext*.xml</param-value>
? </context-param>
? <!-- 將spring框架裝載進我們的工程里 -->
? <listener>
????? <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
? </listener>
? <welcome-file-list>
??? <welcome-file>index.jsp</welcome-file>
? </welcome-file-list>
</web-app>
這里面主要是向工程里裝載spring框架。
4.???? 下面我在conf包下面建立constants.properties文件,這個文件用來放置一些經常會變化的配置信息,例如:數據庫配置信息、文件的路徑等等,內容如下:
#db.driverClass = oracle.jdbc.driver.OracleDriver
#db.user??????? = sharpxiajun
#db.password??? = sharpxiajun
#db.jdbcUrl???? = jdbc:oracle:thin:@127.0.0.1:1521:orcl
db.driverClass = com.mysql.jdbc.Driver
db.user??????? = root
db.password??? = root
db.jdbcUrl???? = jdbc\:mysql\://localhost\:3306/sq_xidi?useUnicode\=true&characterEncoding\=utf-8
5.?????? 接下來寫的是applicationContext.xml文件,這里首先是掃描spring組件和導入constants.properties文件的內容,接下來配置數據源(oracle或mysql),然后在spring里裝載ibatis框架和定義ibatis操作模板類,最后定義事物管理,這些做javaee開發(fā)的工程師都很清楚,我就不具體講解,實際使用copy就行了。內容如下:
<?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:context="http://www.springframework.org/schema/context"
? xmlns:aop="http://www.springframework.org/schema/aop"
? xmlns:tx="http://www.springframework.org/schema/tx"
? xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
???????
??????? <!-- 掃描該路徑下的spring組件 -->
??????? <context:component-scan base-package="cn.com.sharpxiajun" />
???????
??????? <!-- 讀取資源文件 -->
??????? <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
??????????? <property name="locations">
??????????????? <list>
??????????????????? <value>classpath:conf/constants.properties</value>
??????????????? </list>
??????????? </property>
??????? </bean>
???????
??????? <!-- 配置數據源 -->
???????? <!--? <bean id="myDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
???????????? <property name="driverClass" value="${db.driverClass}"/>
???????????? <property name="jdbcUrl" value="${db.jdbcUrl}"/>
???????????? <property name="user" value="${db.user}"/>
???????????? <property name="password" value="${db.password}"/>
???????? </bean>-->
????????
???????? <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
???????????? <property name="driverClassName" value="${db.driverClass}"/>
???????????? <property name="url" value="${db.jdbcUrl}"/>
???????????? <property name="username" value="${db.user}"/>
???????????? <property name="password" value="${db.password}"/>
???????? </bean>
???????
??????? <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
??????????? <property name="configLocation">
??????????????? <value>classpath:conf/SqlMapConfig.xml</value>
??????????? </property>
??????????? <property name="dataSource" ref="myDataSource"/>
??????? </bean>
???????
??????? <bean id="sqlMapClientTemplate" class="org.springframework.orm.ibatis.SqlMapClientTemplate">
??????????? <property name="sqlMapClient">
??????????????? <ref local="sqlMapClient"/>
??????????? </property>
??????? </bean>
???????
??????? <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
??????????? <property name="dataSource">
??????????????? <ref local="myDataSource"/>
??????????? </property>
??????? </bean>
</beans>
6.???? 接著我在文件路徑cn/com/sharpxiajun/dao/sqlmap/下編寫了USERS.xml配置文件,內容如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN" "http://ibatis.apache.org/dtd/sql-map-2.dtd">
<sqlMap namespace="USERS">
??? <select id="queryUserList" parameterClass="java.util.Map" resultClass="java.util.HashMap">
??????? select t.username,t.password,t.enabled from users t
??? </select>
</sqlMap>
里面我只寫了一個查詢方法,parameterClass="java.util.Map"代表傳入參數是map,resultClass="java.util.HashMap"表示返回的結果是一個map,這里正好應用了我前一篇博文里面談到的各個邏輯層用map來做為傳輸的介質。
7.???? 然后我在路徑cn.com.sharpxiajun.dao下編寫接口UsersDao,內容如下:
package cn.com.sharpxiajun.dao;
import java.util.List;
import java.util.Map;
public interface UsersDao {
???
??? public static final String QUERY_USERS_SQL = "USERS.queryUserList";
???
??? public List<Map<String, Object>> queryUserList(Map<String, Object> map) throws Exception;
}
在路徑cn.com.sharpxiajun.dao.impl下實現了UsersDaoImpl接口,代碼如下:
package cn.com.sharpxiajun.dao.impl;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Scope;
import org.springframework.orm.ibatis.SqlMapClientTemplate;
import org.springframework.stereotype.Repository;
import cn.com.sharpxiajun.dao.UsersDao;
@SuppressWarnings("unchecked")
@Scope("prototype")
@Repository("usersDao")
public class UsersDaoImpl implements UsersDao {
??? @Autowired
??? @Qualifier("sqlMapClientTemplate")
??? private SqlMapClientTemplate sqlMapClientTemplate = null;
??? public List<Map<String, Object>> queryUserList(Map<String, Object> map)
??????????? throws Exception {
??????? return sqlMapClientTemplate.queryForList(QUERY_USERS_SQL, map);
??? }
}
@Scope("prototype"):多線程, 生成多個實例。
@Repository("usersDao")將UsersDaoImpl注冊為spring的bean對象,Repository標簽只用于dao層,因為Repository標簽里面還封裝了dao層拋出的異常類型。
8.???? 寫好了dao層我接下來編寫了SqlMapConfig.xml文件,內容如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMapConfig PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN" "http://ibatis.apache.org/dtd/sql-map-config-2.dtd">
<sqlMapConfig>
??? <settings cacheModelsEnabled="true" enhancementEnabled="true" lazyLoadingEnabled="true" errorTracingEnabled="true"
??????????????? maxRequests="64" maxSessions="20" maxTransactions="10"
??????????????? useStatementNamespaces="true"/>
??? <sqlMap resource="cn/com/sharpxiajun/dao/sqlmap/USERS.xml"/>
</sqlMapConfig>
9.???? 最后我們編寫該UserDao單元測試類,代碼如下:
package cn.com.sharpxiajun.junittest.dao;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import cn.com.sharpxiajun.dao.UsersDao;
import junit.framework.TestCase;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:conf/applicationContext.xml"})
@TransactionConfiguration(defaultRollback = false)
public class UsersDaoImplTest extends AbstractTransactionalJUnit4SpringContextTests{
???
??? @Autowired
??? private UsersDao usersDao;
???
??? public UsersDaoImplTest()
??? {
??????? System.out.println("初始化測試類....");
??? }
???
??? @Before
??? public void setUp() throws Exception
??? {
??????? System.out.println("測試開始....");
??? }
???
??? @After
??? public void tearDown() throws Exception
??? {
??????? System.out.println("測試結束!!");
??? }
???
??? @Test
??? public void testQueryUserList()
??? {
??????? Map<String, Object> map = new HashMap<String, Object>();
??????? try {
??????????? List<Map<String, Object>> list = usersDao.queryUserList(map);
??????????? System.out.println(list);
??????? } catch (Exception e) {
??????????? // TODO Auto-generated catch block
??????????? e.printStackTrace();
??????? }
???????
??? }
}
運行結果如下:
初始化測試類....
2011-10-9 23:22:22 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [conf/applicationContext.xml]
2011-10-9 23:22:23 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.GenericApplicationContext@290fbc: startup date [Sun Oct 09 23:22:23 CST 2011]; root of context hierarchy
2011-10-9 23:22:23 org.springframework.core.io.support.PropertiesLoaderSupport loadProperties
信息: Loading properties file from class path resource [conf/constants.properties]
2011-10-9 23:22:23 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
信息: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@d16fc1: defining beans [usersDao,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,propertyConfigurer,myDataSource,sqlMapClient,sqlMapClientTemplate,transactionManager]; root of factory hierarchy
2011-10-9 23:22:23 org.springframework.test.context.transaction.TransactionalTestExecutionListener startNewTransaction
信息: Began transaction (1): transaction manager [org.springframework.jdbc.datasource.DataSourceTransactionManager@1b7c76]; rollback [false]
測試開始....
[{enabled=false, username=admin, password=admin}, {enabled=false, username=test, password=test}]
測試結束!!
2011-10-9 23:22:23 org.springframework.test.context.transaction.TransactionalTestExecutionListener endTransaction
信息: Committed transaction after test execution for test context [[TestContext@12b19c5 testClass = UsersDaoImplTest, locations = array<String>['classpath:conf/applicationContext.xml'], testInstance = cn.com.sharpxiajun.junittest.dao.UsersDaoImplTest@a8e586, testMethod = testQueryUserList@UsersDaoImplTest, testException = [null]]]
寫完了,寫的累死人了,這里用junit做spring組件的單元測試非常有用,希望很多做java的童鞋可以試試,另外我寫的junit測試類是用注解的方式,這是junit4里才有的,有興趣的童鞋可以學習下哈。
前一篇博文里有三位童鞋留言了,第一位童鞋問道我提出的那個技術難題,我得到一個答案,但是我比較懷疑這個方法的技術實現,以后我會驗證下,還有位童鞋問道源碼,我現在還沒有寫完,寫完后我會把源碼發(fā)到博客里的,最后一位童鞋的問題我要著重講講。其實開起這個系列時我是想過用什么題目,例如用ssh或者 ssi等等,但是這種命名就局限了,因為這里面每一個單詞都是指一個技術框架,而我想用到的框架比較多,這樣的標題不能代表我寫的所有內容。用 javaEE是有理由的,javaEE是j2ee的新名稱,(注意:為了嚴謹我下面的理解是我自己經驗得來的理解,寫下面內容時候我沒有查閱相關資料,假如不正確,大家可以直接指出),sun公司出品的java語言,當然現在是甲骨文的產品了,一共包括三大部分,j2se,j2ee和j2me,而j2ee 是什么了?是sun運用java語言為企業(yè)級開發(fā)提供的一套解決方案,j2ee也可以說是一套框架,這個框架里面主要是定義了java為企業(yè)級開發(fā)提供了那些技術,但是這些技術只是提供了接口和規(guī)范,而非是具體實現,最開始sun出品了ejb,但是ejb太繁瑣,最后就有類似ssh框架的實現,但是 j2ee的范疇很大,我們平時開發(fā)時候只是實現了其中的部分功能,例如一個頁面發(fā)出請求到后臺程序運算最后查詢數據庫,返回數據到頁面展示,這是j2ee 的一個子集實現,j2ee還有很多,比如jms,webservice,xml等等技術,我這里想寫的框架絕不是簡單的針對頁面請求響應,我還想引入很多功能到這些框架,比如webservice,mq,velocity,spring的調度,ant,緩存等等,要不我寫一個大眾化的框架放到博客里意義不大,就是重復。而且后面那些技術并不是能常用到,但很有可能會成為你面試的軟肋,因為有些現在我用過忘了,有些你沒用過別人會給你印象減分,這是功力的說法了,但是接觸的技術越到你做項目的余地越大,所以我用javaEE左標題,我想做一個盡量完善的j2ee規(guī)范實現的子集。
轉入正題還是要閑話下,最近在為公司的平臺做權限開發(fā)設計,由于人力和時間的原因這個功能復雜度的要求被降低了,就算降低了,這次權限設計還是和我以前的經驗比較起來有很大不同,我們做的是到程序方法級別的控制和數據級別的權限控制,數據級別的權限控制我以后再說了,這個主要是從系統(tǒng)設計塊實現,技術沒有特別之處。前者會用到spring的aop,這個和我現在寫的這個系列有關,所以我這里要提到這個問題。
今天我將寫service層,然后寫service的測試類,然后我會加入一個攔截器:攔截service的請求,這個攔截器是針對方法的攔截,這個攔截器里面我們可以知道調用到了那個service類,那個method,可以截獲到傳入的參數,也能截獲到返回值。我在公司的項目里的aop就會攔截到 service的方法,大家也許會很奇怪,為什么不做到action而是service,哎,這個沒法子,我們前臺用的是flex,而flex調用 java跟rpc很像,就是flex直接調用service的方法,因此控制層在前臺,和前臺的耦合度太高,只得做service方法級別的攔截了。
我是按下面順序開發(fā)的:
1.先看看我新的目錄結構
還要加入三個jar包,都是AspectJ相關的,如下圖:
2.在cn.com.sharpxiajun.service包下新建接口UsersService,代碼如下:
3.實現UsersService接口,在cn.com.sharpxiajun.service.impl包下面新建類UsersServiceImpl,代碼如下:
package cn.com.sharpxiajun.service.impl;import java.util.List; import java.util.Map;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Service;import cn.com.sharpxiajun.dao.UsersDao; import cn.com.sharpxiajun.service.UsersService;@SuppressWarnings("unchecked") @Scope("prototype") @Service("userService") public class UsersServiceImpl implements UsersService {@Autowired@Qualifier("usersDao")private UsersDao usersDao = null;@Overridepublic List<Map<String, Object>> queryUsersList(Map<String, Object> map)throws Exception {return usersDao.queryUserList(map);}}大家可以看到service注解是@service,其他和dao差不多(我感覺要寫篇文章好好介紹下spring相關注解,只有這樣才能對框架有深刻理解)
4.接下來編寫方法攔截器,這個類放在cn.com.sharpxiajun.common.aop包下,類名是:MethodServiceAdvisor,代碼如下:
package cn.com.sharpxiajun.common.aop;import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation;import cn.com.sharpxiajun.service.UsersService;public class MethodServiceAdvisor implements MethodInterceptor {@Overridepublic Object invoke(MethodInvocation invocation) throws Throwable {Object obj = null;System.out.println("進入到了方法攔截器。。。。");System.out.println("調用的service:");System.out.println(invocation.getThis());System.out.println("調用的方法:");System.out.println(invocation.getMethod());System.out.println("參數是:");for (int i = 0;i < invocation.getArguments().length;i++){Object[] objs = invocation.getArguments();System.out.println(objs[i]);}obj = invocation.proceed();System.out.println("返回結果是:");System.out.println(obj);System.out.println("攔截器執(zhí)行結束!!");return obj;}}攔截器的注解我今天不寫,太晚了,而且記得不清,下一篇里我會補上這些內容。
5.下面是修改后的applicationContext.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:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd"><!-- 掃描該路徑下的spring組件 --><context:component-scan base-package="cn.com.sharpxiajun" /><!-- 讀取資源文件 --><bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"><property name="locations"><list><value>classpath:conf/constants.properties</value></list></property></bean><!-- 配置數據源 --><!-- <bean id="myDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"><property name="driverClass" value="${db.driverClass}"/><property name="jdbcUrl" value="${db.jdbcUrl}"/><property name="user" value="${db.user}"/><property name="password" value="${db.password}"/></bean>--><bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"><property name="driverClassName" value="${db.driverClass}"/><property name="url" value="${db.jdbcUrl}"/><property name="username" value="${db.user}"/><property name="password" value="${db.password}"/></bean><bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean"><property name="configLocation"><value>classpath:conf/SqlMapConfig.xml</value></property><property name="dataSource" ref="myDataSource"/></bean><bean id="sqlMapClientTemplate" class="org.springframework.orm.ibatis.SqlMapClientTemplate"><property name="sqlMapClient"><ref local="sqlMapClient"/></property></bean><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource"><ref local="myDataSource"/></property></bean><!-- 將我自己定義的攔截器生成bean --><bean id="methodServiceAdvisor" class="cn.com.sharpxiajun.common.aop.MethodServiceAdvisor"/><aop:config><!--配置規(guī)則,滿足以下規(guī)則的將攔截,第一個*表示所有返回類型,第二個表示service包下的所有class,第三個表示所有方法--><aop:pointcut id="baseServiceMethods" expression="execution(* cn.com.sharpxiajun.service.*.*(..))"/><!-- 符合上面規(guī)則的攔截器都會調用到methodServiceAdvisor --><aop:advisor advice-ref="methodServiceAdvisor" pointcut-ref="baseServiceMethods"/></aop:config></beans>?
這里用到了aop:config這是spring為了迎合AspectJ,對于AspectJ這個以后有機會我也想研究下,寫篇文章。
6.最后編寫針對UsersService的測試類UsersServiceImplTest,所在包是:cn.com.sharpxiajun.junittest.service,代碼如下:
package cn.com.sharpxiajun.junittest.service;import java.util.HashMap; import java.util.List; import java.util.Map;import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.transaction.TransactionConfiguration;import cn.com.sharpxiajun.service.UsersService;@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations={"classpath:conf/applicationContext.xml"}) @TransactionConfiguration(defaultRollback = false) public class UsersServiceImplTest extendsAbstractTransactionalJUnit4SpringContextTests {@Autowiredprivate UsersService usersService = null;public UsersServiceImplTest(){System.out.println("初始化測試類....");}@Beforepublic void setUp() throws Exception{System.out.println("測試開始....");}@Afterpublic void tearDown() throws Exception{System.out.println("測試結束!!");}@Testpublic void testQueryUserList(){Map<String, Object> map = new HashMap<String, Object>();map.put("username", "sharpxiajun");try {List<Map<String, Object>> list = usersService.queryUsersList(map);System.out.println(list);} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}}}運行結果如下:
初始化測試類.... 2011-10-11 23:44:45 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions 信息: Loading XML bean definitions from class path resource [conf/applicationContext.xml] 2011-10-11 23:44:45 org.springframework.context.support.AbstractApplicationContext prepareRefresh 信息: Refreshing org.springframework.context.support.GenericApplicationContext@290fbc: startup date [Tue Oct 11 23:44:45 CST 2011]; root of context hierarchy 2011-10-11 23:44:46 org.springframework.core.io.support.PropertiesLoaderSupport loadProperties 信息: Loading properties file from class path resource [conf/constants.properties] 2011-10-11 23:44:46 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons 信息: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@922804: defining beans [usersDao,userService,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,propertyConfigurer,myDataSource,sqlMapClient,sqlMapClientTemplate,transactionManager,methodServiceAdvisor,org.springframework.aop.config.internalAutoProxyCreator,baseServiceMethods,org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor#0]; root of factory hierarchy 2011-10-11 23:44:46 org.springframework.test.context.transaction.TransactionalTestExecutionListener startNewTransaction 信息: Began transaction (1): transaction manager [org.springframework.jdbc.datasource.DataSourceTransactionManager@8de972]; rollback [false] 測試開始.... 進入到了方法攔截器。。。。 調用的service: cn.com.sharpxiajun.service.impl.UsersServiceImpl@15fc672 調用的方法: public abstract java.util.List cn.com.sharpxiajun.service.UsersService.queryUsersList(java.util.Map) throws java.lang.Exception 參數是: {username=sharpxiajun} 返回結果是: [{enabled=false, username=admin, password=admin}, {enabled=false, username=test, password=test}] 攔截器執(zhí)行結束!! [{enabled=false, username=admin, password=admin}, {enabled=false, username=test, password=test}] 測試結束!! 2011-10-11 23:44:46 org.springframework.test.context.transaction.TransactionalTestExecutionListener endTransaction 信息: Committed transaction after test execution for test context [[TestContext@58e2a1 testClass = UsersServiceImplTest, locations = array<String>['classpath:conf/applicationContext.xml'], testInstance = cn.com.sharpxiajun.junittest.service.UsersServiceImplTest@186f3b3, testMethod = testQueryUserList@UsersServiceImplTest, testException = [null]]]看來達到我們預期的結果了!!!
總結下了:這樣寫程序蠻有成就感,而且一步步來感覺很清晰。工作永遠是匆忙的,總是快的讓人無法思考,我想這就是聰明的中國為什么沒有那么多優(yōu)秀開源框架的原因把。今天我又得到一個js開源框架,獲得源碼,它能做出yfiles一樣的效果,非常強大,也是老外寫的,牛的不行啊,我們平臺開發(fā)本來對前端要求很高的,要求圖形化實現,真正做圖形化才知道他的復雜度之高,以后我會介紹下這個圖形化的框架,他能實現畫圖,自定義工作流,還能為關系復雜的圖形進行布局合理的自動繪制,太牛了。
下個階段可能要回歸javascript了,java框架暫停一下
作者:夏天的森林
最近忙得要死,昨晚寫著寫著居然睡著了。哎,還是接著寫java框架吧。
??? 任何系統(tǒng)里,日志和一定的監(jiān)控是相當重要的,在一個軟件整個生命周期里維護永遠是大頭同時是痛苦的,而日志和監(jiān)控就是為后期維護提供了良好的基礎和手段,在java工程里面大多使用log4j來記錄系統(tǒng)日志,這個技術幾乎所有的java工程師都很熟悉,不太明白了,大家可以查查百度。這里我打算引入一個能監(jiān)控JDBC執(zhí)行語句的框架到我寫的java框架里面,這個框架非常的好用,他就是p6spy。
“如果優(yōu)化SQL語句,如何進行系統(tǒng)調優(yōu)”,這樣的問題我想很多程序員都聽到過,優(yōu)化和調優(yōu)是一個高難度的技術技能,只有具有扎實的技術功底和多年的項目經驗才把他做好。我以前遇到這樣的提問,思路很機械,都是從SQL語法,索引,分區(qū),算法來考慮,其實優(yōu)化和調優(yōu)程序環(huán)境的搭配也是很重要的,例如,現在做java企業(yè)級項目,大多使用了orm技術,很少會直接去用jdbc操作數據庫,而orm技術對jdbc的封裝讓最終執(zhí)行的sql語句的原型離程序員越來越遠,因此對jbdc執(zhí)行的原生態(tài)的sql語句的掌控是相當重要的。
?? 在這里我可以分享一下我的經驗。我現在比較推崇ibatis,它既實現了orm思想,又保留了sql語句的使用而不是像hibernate那樣對數據庫做完全面向對象的映射,而產生了很多新的東西,比如hql。這是一個簡便的設計,我覺得系統(tǒng)設計最佳的方案就是新老兼容。其實不同的程序員擅長的技術也不同,做java的程序員當然對java更熟悉,開發(fā)數據庫的程序員對數據庫很在行,假如你現在開發(fā)一個后臺數據庫數據量很大,業(yè)務操作很復雜的系統(tǒng),我這里會首推ibatis技術做orm層,因為這樣的大系統(tǒng)到了數據庫層面和dba打交道很多,對sql語句以及數據庫優(yōu)化很多,而ibatis直接寫sql語句的優(yōu)點就很明顯了,java程序員和dba的溝通和程序的交互也就方便多了。我最近做了幾個這樣的系統(tǒng),系統(tǒng)做完后我都會把重要的sql語句從系統(tǒng)里抽取出來給dba優(yōu)化,而dba優(yōu)化后我基本只要拷貝到程序里就可以,為整個工作帶來了便利。此外系統(tǒng)上線,如果操作數據庫報錯,在日志里提取sql語句,進行檢查,檢查后維護程序也是有很大的便利,最后用sql嵌入orm,程序架構的變遷所帶來的問題也會少很多,因此我以后開發(fā)系統(tǒng)orm技術的首選就是ibatis了。
?? 但是ibatis執(zhí)行jdbc是使用prepareStatement,所以最終打印出來的sql語句是下面的格式:
2011-10-13 11:17:36 Connection - {conn-100000} Connection 2011-10-13 11:17:36 Connection - {conn-100000} Preparing Statement: select t.username,t.password,t.enabled from users t where t.username = ? 2011-10-13 11:17:36 PreparedStatement - {pstm-100001} Executing Statement: select t.username,t.password,t.enabled from users t where t.username = ? 2011-10-13 11:17:36 PreparedStatement - {pstm-100001} Parameters: [sharpxiajun] 2011-10-13 11:17:36 PreparedStatement - {pstm-100001} Types: [java.lang.String]
我們拷貝出sql語句還要改寫,真是煩死人了,那么p6spy就能解決這個問題,它會把?替換成參數,看看我加入了p6spy后執(zhí)行的效果吧。
?
|statement| select t.username,t.password,t.enabled from users t where t.username = 'sharpxiajun' P6spy使用很簡單,只要完成下面步驟就行:
1.將p6spy.jar包放到應用的classpath所在的路徑中;
2.修改連接池或者連接配置的jdbc的驅動為p6spy所提供的保證后的驅動,com.p6spy.engine.spy.P6SpyDriver
3.修改spy.properties并將其放到類搜索目錄.
下面我們開始開發(fā)了。
1.新的工程結構圖如下:
新添兩個jar包:log4j-1.2.12.jar和p6spy.jar
2.將log4j.properties和spy.properties拷貝到src下面(暫時拷貝到src下面,其實應該放到conf下面,因為現在都是在本地測試,沒有發(fā)布到tomcat下面,就把兩個文件拷貝到默認的路徑下)
log4j.properties內容如下:
log4j.debug=true log4j.rootLogger=INFO,CONSOLE,STDOUT#-----CONSOLE----- log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout log4j.appender.CONSOLE.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %c{1} - %m%n#-----SQL LOG----- log4j.logger.java.sql.Connection=DEBUG log4j.logger.java.sql.Statement=DEBUG log4j.logger.java.sql.PreparedStatement=DEBUGlog4j.logger.com.ibatis=debug log4j.logger.com.ibatis.common.jdbc.SimpleDataSource=debug log4j.logger.com.ibatis.common.jdbc.ScriptRunner=debug log4j.logger.com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate=debug日志打印到控制臺里,SQL LOG下的配置表示打印出jbdc以及ibatis的日志。
spy.properties內容如下:
module.log=com.p6spy.engine.logging.P6LogFactory realdriver=oracle.jdbc.driver.OracleDriver deregisterdrivers=true executionthreshold= outagedetection=false outagedetectioninterval= filter=false include = exclude = sqlexpression = autoflush = true dateformat= includecategories= excludecategories=info,debug,result,batch stringmatcher= stacktrace=false stacktraceclass= reloadproperties=false reloadpropertiesinterval=60 useprefix=false appender=com.p6spy.engine.logging.appender.StdoutLogger append=true log4j.appender.STDOUT=org.apache.log4j.ConsoleAppender log4j.appender.STDOUT.layout=org.apache.log4j.SimpleLayout log4j.appender.STDOUT.layout.ConversionPattern=p6spy log4j.logger.p6spy=DEBUG,STDOUT這里只要修改下realdriver=oracle.jdbc.driver.OracleDriver就行。
3.接下來我們只要更改下數據庫的驅動就行了,修改下constants.properties,內容如下:
#db.driverClass = oracle.jdbc.driver.OracleDriver db.driverClass = com.p6spy.engine.spy.P6SpyDriver db.user = sharpxiajun db.password = sharpxiajun db.jdbcUrl = jdbc:oracle:thin:@127.0.0.1:1521:orcl#db.driverClass = com.mysql.jdbc.Driver #db.user = root #db.password = root #db.jdbcUrl = jdbc\:mysql\://localhost\:3306/sq_xidi?useUnicode\=true&characterEncoding\=utf-84.最后修改下USERS.xml配置文件,讓查詢方法接收到參數,如下:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN" "http://ibatis.apache.org/dtd/sql-map-2.dtd"> <sqlMap namespace="USERS"><select id="queryUserList" parameterClass="java.util.Map" resultClass="java.util.HashMap">select t.username,t.password,t.enabled from users t<dynamic prepend="where"><isNotEmpty prepend="and" property="username">t.username = #username#</isNotEmpty> </dynamic></select> </sqlMap>執(zhí)行結果如下:
log4j: Parsing for [root] with value=[INFO,CONSOLE,STDOUT]. log4j: Level token is [INFO]. log4j: Category root set to INFO log4j: Parsing appender named "CONSOLE". log4j: Parsing layout options for "CONSOLE". log4j: Setting property [conversionPattern] to [%d{yyyy-MM-dd HH:mm:ss} %c{1} - %m%n]. log4j: End of parsing for "CONSOLE". log4j: Parsed "CONSOLE" options. log4j: Parsing appender named "STDOUT". log4j:ERROR Could not find value for key log4j.appender.STDOUT log4j:ERROR Could not instantiate appender named "STDOUT". log4j: Parsing for [com.ibatis.common.jdbc.SimpleDataSource] with value=[debug]. log4j: Level token is [debug]. log4j: Category com.ibatis.common.jdbc.SimpleDataSource set to DEBUG log4j: Handling log4j.additivity.com.ibatis.common.jdbc.SimpleDataSource=[null] log4j: Parsing for [java.sql.Connection] with value=[DEBUG]. log4j: Level token is [DEBUG]. log4j: Category java.sql.Connection set to DEBUG log4j: Handling log4j.additivity.java.sql.Connection=[null] log4j: Parsing for [com.ibatis] with value=[debug]. log4j: Level token is [debug]. log4j: Category com.ibatis set to DEBUG log4j: Handling log4j.additivity.com.ibatis=[null] log4j: Parsing for [java.sql.Statement] with value=[DEBUG]. log4j: Level token is [DEBUG]. log4j: Category java.sql.Statement set to DEBUG log4j: Handling log4j.additivity.java.sql.Statement=[null] log4j: Parsing for [com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate] with value=[debug]. log4j: Level token is [debug]. log4j: Category com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate set to DEBUG log4j: Handling log4j.additivity.com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate=[null] log4j: Parsing for [com.ibatis.common.jdbc.ScriptRunner] with value=[debug]. log4j: Level token is [debug]. log4j: Category com.ibatis.common.jdbc.ScriptRunner set to DEBUG log4j: Handling log4j.additivity.com.ibatis.common.jdbc.ScriptRunner=[null] log4j: Parsing for [java.sql.PreparedStatement] with value=[DEBUG]. log4j: Level token is [DEBUG]. log4j: Category java.sql.PreparedStatement set to DEBUG log4j: Handling log4j.additivity.java.sql.PreparedStatement=[null] log4j: Finished configuring. 初始化測試類.... 2011-10-13 11:17:35 XmlBeanDefinitionReader - Loading XML bean definitions from class path resource [conf/applicationContext.xml] 2011-10-13 11:17:35 GenericApplicationContext - Refreshing org.springframework.context.support.GenericApplicationContext@2808b3: startup date [Thu Oct 13 11:17:35 CST 2011]; root of context hierarchy 2011-10-13 11:17:35 PropertyPlaceholderConfigurer - Loading properties file from class path resource [conf/constants.properties] 2011-10-13 11:17:35 DefaultListableBeanFactory - Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1de17f4: defining beans [usersDao,userService,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,propertyConfigurer,myDataSource,sqlMapClient,sqlMapClientTemplate,transactionManager,methodServiceAdvisor,org.springframework.aop.config.internalAutoProxyCreator,baseServiceMethods,org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor#0]; root of factory hierarchy 2011-10-13 11:17:35 MLog - MLog clients using log4j logging. 2011-10-13 11:17:35 C3P0Registry - Initializing c3p0-0.9.1.2 [built 21-May-2007 15:04:56; debug? true; trace: 10] 2011-10-13 11:17:36 AbstractPoolBackedDataSource - Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, dataSourceName -> 2wyjv28i1xq0ktewu9ral|131303f, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> com.p6spy.engine.spy.P6SpyDriver, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, identityToken -> 2wyjv28i1xq0ktewu9ral|131303f, idleConnectionTestPeriod -> 0, initialPoolSize -> 3, jdbcUrl -> jdbc:oracle:thin:@127.0.0.1:1521:orcl, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 0, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 15, maxStatements -> 0, maxStatementsPerConnection -> 0, minPoolSize -> 3, numHelperThreads -> 3, numThreadsAwaitingCheckoutDefaultUser -> 0, preferredTestQuery -> null, properties -> {user=******, password=******}, propertyCycle -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, usesTraditionalReflectiveProxies -> false ] 2011-10-13 11:17:36 TransactionalTestExecutionListener - Began transaction (1): transaction manager [org.springframework.jdbc.datasource.DataSourceTransactionManager@6db33c]; rollback [false] 測試開始.... 進入到了方法攔截器。。。。 調用的service: cn.com.sharpxiajun.service.impl.UsersServiceImpl@23bdd1 調用的方法: public abstract java.util.List cn.com.sharpxiajun.service.UsersService.queryUsersList(java.util.Map) throws java.lang.Exception 參數是: {username=sharpxiajun} 2011-10-13 11:17:36 Connection - {conn-100000} Connection 2011-10-13 11:17:36 Connection - {conn-100000} Preparing Statement: select t.username,t.password,t.enabled from users t where t.username = ? 2011-10-13 11:17:36 PreparedStatement - {pstm-100001} Executing Statement: select t.username,t.password,t.enabled from users t where t.username = ? 2011-10-13 11:17:36 PreparedStatement - {pstm-100001} Parameters: [sharpxiajun] 2011-10-13 11:17:36 PreparedStatement - {pstm-100001} Types: [java.lang.String] |statement| select t.username,t.password,t.enabled from users t where t.username = 'sharpxiajun' 返回結果是: [] 攔截器執(zhí)行結束!! [] 測試結束!! |commit| 2011-10-13 11:17:36 TransactionalTestExecutionListener - Committed transaction after test execution for test context [[TestContext@1fac852 testClass = UsersServiceImplTest, locations = array<String>['classpath:conf/applicationContext.xml'], testInstance = cn.com.sharpxiajun.junittest.service.UsersServiceImplTest@1758cd1, testMethod = testQueryUserList@UsersServiceImplTest, testException = [null]]] 2011-10-13 11:17:36 GenericApplicationContext - Closing org.springframework.context.support.GenericApplicationContext@2808b3: startup date [Thu Oct 13 11:17:35 CST 2011]; root of context hierarchy 2011-10-13 11:17:36 DefaultListableBeanFactory - Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1de17f4: defining beans [usersDao,userService,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,propertyConfigurer,myDataSource,sqlMapClient,sqlMapClientTemplate,transactionManager,methodServiceAdvisor,org.springframework.aop.config.internalAutoProxyCreator,baseServiceMethods,org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor#0]; root of factory hierarchy看到了這句話了吧|statement| select t.username,t.password,t.enabled from users t where t.username = 'sharpxiajun' ,ok,寫完了。
總結下了:java框架的dao和service這塊寫完了,這個系列下一篇是針對前三篇的技術要點做一下比較詳細的解釋,做程序不僅要知其然還要知其所以然,這樣才能提高。不過下面的博文我會回到javascript,加固一下javascript我在學習中感覺比較難理解的基礎知識,之后要繼續(xù)研究jquery了,這個才是我的重點
作者:夏天的森林
總結
以上是生活随笔為你收集整理的java笔记:自己动手写javaEE的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android Memory Manag
- 下一篇: iOS内存优化及排查方法