写这篇博客前,我有个技术难题想请教大家,不知道谁有很好的建议,做过互联网的童鞋应该都知道,有点规模的大公司都会做用户行为分析系统,而且有些大公司还会提供专业的用户行为分析解决方案例如:百度分析,google analysis。用户行为分析就是当用户访问某个网站的页面,会有专门系统记录用户的相关信息以及使用状况,然后分析这些数据用来指导网站的运营,我们现在遇到一个问题:如果某的访客访问了www.a.com页面,我们怎么知道这个用户访问过www.b.com页面,a页面和b页面毫无关系,比如:某个未知访客访问QQ主页,他只要打开了QQ页面我就知道他是否访问过sina的页面,听说有人把这个做出来了,但是我还没想到,哪位高手能想到解决方案吗?
说到用户行为分析,这个对于互联网公司来说相当的重要系统,以后有时间我会开一个系列从技术到业务的角度讲讲用户行为分析系统是啥样子,大伙好好交流下。感兴趣的童鞋多多关注哈。
转入正题前还闲话几句,本来开博客是想系统研究javascript技术,但是最近工作比较忙,而且大量工作都是java开发任务(我们公司前端工程师太不专业了,哎),因此javascript现在只得暂停下,毕竟晚上的时间还是太少。因此我想就我现在做的技术开一个新系列:自己动手写javaEE框架,这个和我现在工作有关比较好收集资料,这个系列我的想法很大,我想写如下内容:
1. struts2+ibatis+spring+js
2. springmvc+ibatis+spring+js
3. flex+ibatis+spring
如果以上写完还有时间的话,我会把ibatis换成hibernate,也许还会自己封装一个js框架或者更加深入讲解flex技术。哎,东西挺多了,能写完不???天知道了。
由于内容比较多,我的博客里只做简单讲解,不做深入分析。好下面开始了。
我第一个写的是struts2+ibatis+spring+js。框架结构是页面+action+service+dao+数据库,数据库为oracle(公司电脑装的是oracle)或者mysql(家里电脑装的是mysql)。
今天任务是搭建好spring框架,spring里面集成好ibatis框架,然后编写dao层,最后一个重要任务是加入junit测试框架,方便以后开发的单元测试(junit测试框架是本博文的亮点,我想许多做java童鞋看完本篇博客还是很有启发的,因为我发现很多朋友都不太会做跟spring相关的单元测试,这个会了能提升不少开发效率)。我是按下面步骤写的:
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开发的工程师都很清楚,我就不具体讲解,实际使用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里才有的,有兴趣的童鞋可以学习下哈。