写这篇博客前,我有个技术难题想请教大家,不知道谁有很好的建议,做过互联网的童鞋应该都知道,有点规模的大公司都会做用户行为分析系统,而且有些大公司还会提供专业的用户行为分析解决方案例如:百度分析,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里才有的,有兴趣的童鞋可以学习下哈。

前一篇博文里有三位童鞋留言了,第一位童鞋问道我提出的那个技术难题,我得到一个答案,但是我比较怀疑这个方法的技术实现,以后我会验证下,还有位童鞋问道源码,我现在还没有写完,写完后我会把源码发到博客里的,最后一位童鞋的问题我要着重讲讲。其实开起这个系列时我是想过用什么题目,例如用ssh或者 ssi等等,但是这种命名就局限了,因为这里面每一个单词都是指一个技术框架,而我想用到的框架比较多,这样的标题不能代表我写的所有内容。用 javaEE是有理由的,javaEE是j2ee的新名称,(注意:为了严谨我下面的理解是我自己经验得来的理解,写下面内容时候我没有查阅相关资料,假如不正确,大家可以直接指出),sun公司出品的java语言,当然现在是甲骨文的产品了,一共包括三大部分,j2se,j2ee和j2me,而j2ee 是什么了?是sun运用java语言为企业级开发提供的一套解决方案,j2ee也可以说是一套框架,这个框架里面主要是定义了java为企业级开发提供了那些技术,但是这些技术只是提供了接口和规范,而非是具体实现,最开始sun出品了ejb,但是ejb太繁琐,最后就有类似ssh框架的实现,但是 j2ee的范畴很大,我们平时开发时候只是实现了其中的部分功能,例如一个页面发出请求到后台程序运算最后查询数据库,返回数据到页面展示,这是j2ee 的一个子集实现,j2ee还有很多,比如jms,webservice,xml等等技术,我这里想写的框架绝不是简单的针对页面请求响应,我还想引入很多功能到这些框架,比如webservice,mq,velocity,spring的调度,ant,缓存等等,要不我写一个大众化的框架放到博客里意义不大,就是重复。而且后面那些技术并不是能常用到,但很有可能会成为你面试的软肋,因为有些现在我用过忘了,有些你没用过别人会给你印象减分,这是功力的说法了,但是接触的技术越到你做项目的余地越大,所以我用javaEE左标题,我想做一个尽量完善的j2ee规范实现的子集。
  转入正题还是要闲话下,最近在为公司的平台做权限开发设计,由于人力和时间的原因这个功能复杂度的要求被降低了,就算降低了,这次权限设计还是和我以前的经验比较起来有很大不同,我们做的是到程序方法级别的控制和数据级别的权限控制,数据级别的权限控制我以后再说了,这个主要是从系统设计块实现,技术没有特别之处。前者会用到spring的aop,这个和我现在写的这个系列有关,所以我这里要提到这个问题。
  今天我将写service层,然后写service的测试类,然后我会加入一个拦截器:拦截service的请求,这个拦截器是针对方法的拦截,这个拦截器里面我们可以知道调用到了那个service类,那个method,可以截获到传入的参数,也能截获到返回值。我在公司的项目里的aop就会拦截到 service的方法,大家也许会很奇怪,为什么不做到action而是service,哎,这个没法子,我们前台用的是flex,而flex调用 java跟rpc很像,就是flex直接调用service的方法,因此控制层在前台,和前台的耦合度太高,只得做service方法级别的拦截了。
我是按下面顺序开发的:
1.先看看我新的目录结构

还要加入三个jar包,都是AspectJ相关的,如下图:

2.在cn.com.sharpxiajun.service包下新建接口UsersService,代码如下:

 package cn.com.sharpxiajun.service;import java.util.List;
import java.util.Map;public interface UsersService {public List<Map<String, Object>> queryUsersList(Map<String, Object> map) throws Exception;}

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("拦截器执行结束!!");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><!--配置规则,满足以下规则的将拦截,第一个*表示所有返回类型,第二个表示service包下的所有class,第三个表示所有方法--><aop:pointcut id="baseServiceMethods" expression="execution(* cn.com.sharpxiajun.service.*.*(..))"/><!-- 符合上面规则的拦截器都会调用到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}]
拦截器执行结束!!
[{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]]]

看来达到我们预期的结果了!!!

总结下了:这样写程序蛮有成就感,而且一步步来感觉很清晰。工作永远是匆忙的,总是快的让人无法思考,我想这就是聪明的中国为什么没有那么多优秀开源框架的原因把。今天我又得到一个js开源框架,获得源码,它能做出yfiles一样的效果,非常强大,也是老外写的,牛的不行啊,我们平台开发本来对前端要求很高的,要求图形化实现,真正做图形化才知道他的复杂度之高,以后我会介绍下这个图形化的框架,他能实现画图,自定义工作流,还能为关系复杂的图形进行布局合理的自动绘制,太牛了。

下个阶段可能要回归javascript了,java框架暂停一下

作者:夏天的森林

最近忙得要死,昨晚写着写着居然睡着了。哎,还是接着写java框架吧。
    任何系统里,日志和一定的监控是相当重要的,在一个软件整个生命周期里维护永远是大头同时是痛苦的,而日志和监控就是为后期维护提供了良好的基础和手段,在java工程里面大多使用log4j来记录系统日志,这个技术几乎所有的java工程师都很熟悉,不太明白了,大家可以查查百度。这里我打算引入一个能监控JDBC执行语句的框架到我写的java框架里面,这个框架非常的好用,他就是p6spy。

 “如果优化SQL语句,如何进行系统调优”,这样的问题我想很多程序员都听到过,优化和调优是一个高难度的技术技能,只有具有扎实的技术功底和多年的项目经验才把他做好。我以前遇到这样的提问,思路很机械,都是从SQL语法,索引,分区,算法来考虑,其实优化和调优程序环境的搭配也是很重要的,例如,现在做java企业级项目,大多使用了orm技术,很少会直接去用jdbc操作数据库,而orm技术对jdbc的封装让最终执行的sql语句的原型离程序员越来越远,因此对jbdc执行的原生态的sql语句的掌控是相当重要的。

在这里我可以分享一下我的经验。我现在比较推崇ibatis,它既实现了orm思想,又保留了sql语句的使用而不是像hibernate那样对数据库做完全面向对象的映射,而产生了很多新的东西,比如hql。这是一个简便的设计,我觉得系统设计最佳的方案就是新老兼容。其实不同的程序员擅长的技术也不同,做java的程序员当然对java更熟悉,开发数据库的程序员对数据库很在行,假如你现在开发一个后台数据库数据量很大,业务操作很复杂的系统,我这里会首推ibatis技术做orm层,因为这样的大系统到了数据库层面和dba打交道很多,对sql语句以及数据库优化很多,而ibatis直接写sql语句的优点就很明显了,java程序员和dba的沟通和程序的交互也就方便多了。我最近做了几个这样的系统,系统做完后我都会把重要的sql语句从系统里抽取出来给dba优化,而dba优化后我基本只要拷贝到程序里就可以,为整个工作带来了便利。此外系统上线,如果操作数据库报错,在日志里提取sql语句,进行检查,检查后维护程序也是有很大的便利,最后用sql嵌入orm,程序架构的变迁所带来的问题也会少很多,因此我以后开发系统orm技术的首选就是ibatis了。

但是ibatis执行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后执行的效果吧。

 |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并将其放到类搜索目录.

下面我们开始开发了。

1.新的工程结构图如下:

新添两个jar包:log4j-1.2.12.jar和p6spy.jar

2.将log4j.properties和spy.properties拷贝到src下面(暂时拷贝到src下面,其实应该放到conf下面,因为现在都是在本地测试,没有发布到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-8

4.最后修改下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>

执行结果如下:

 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'
返回结果是:
[]
拦截器执行结束!!
[]
测试结束!!
|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我在学习中感觉比较难理解的基础知识,之后要继续研究jquery了,这个才是我的重点

作者:夏天的森林

java笔记:自己动手写javaEE相关推荐

  1. php怎么自己写框架,PHP学习笔记,自己动手写个MVC的框架

    最新在大家自己的博客的过程中,发现各种开源的博客系统都或多或少的用起来别扭.于是想动手自己写个博客系统.既然写,就想好好写.那就先写个MVC框架.一点一点来.写的过程中有很多想法.还希望大家能够多多指 ...

  2. iOS学习笔记-自己动手写RESideMenu

    代码地址如下: http://www.demodashi.com/demo/11683.html 很多app都实现了类似RESideMenu的效果,RESideMenu是Github上面一个stars ...

  3. java笔记:自己动手写javaEE框架(七)--使用JSON和Ajax技术

    今天我要将json和ajax引入到我所写的框架,不过今天用到的技术有部分不是我框架最终使用到的技术,比如ajax技术,我用到的是最为原始的ajax技术,这次算是对老技术的回顾,不过不管技术如何演进,对 ...

  4. java 手编线程池_死磕 java线程系列之自己动手写一个线程池

    欢迎关注我的公众号"彤哥读源码",查看更多源码系列文章, 与彤哥一起畅游源码的海洋. (手机横屏看源码更方便) 问题 (1)自己动手写一个线程池需要考虑哪些因素? (2)自己动手写 ...

  5. java 同步锁_死磕 java同步系列之自己动手写一个锁Lock

    问题 (1)自己动手写一个锁需要哪些知识? (2)自己动手写一个锁到底有多简单? (3)自己能不能写出来一个完美的锁? 简介 本篇文章的目标一是自己动手写一个锁,这个锁的功能很简单,能进行正常的加锁. ...

  6. 《自己动手写操作系统》读书笔记——初识保护模式

    <自己动手写操作系统>读书笔记--初识保护模式 http://www.cnblogs.com/pang123hui/archive/2010/11/27/2309930.html 书本第三 ...

  7. 《自己动手写Docker》学习笔记2

    <自己动手写Docker>学习笔记2 1 前言 由于本人毕业设计与云原生领域相关,因此最近在学习Docker相关知识,<自己动手写Docker>涵盖了Docker底层的各类知识 ...

  8. 自己动手写Docker学习笔记

    零.前言 本文为<自己动手写 Docker>的学习,对于各位学习 docker 的同学非常友好,非常建议买一本来学习. 书中有摘录书中的一些知识点,不过限于篇幅,没有全部摘录 (主要也是懒 ...

  9. 自己动手写java虚拟机_自己动手写操作系统(要了解的知识点)

    自己动手写操作系统(开篇) 自己动手写操作系统(字符显示) 说明:Intel 8086 或者不同的处理器,开机寄存器数据可能不一样,但是大致原理差不多 了解过计算机启动的同学肯定知道,当计算机启动的时 ...

最新文章

  1. darknet53网络结构及配置文件对比
  2. 转载LINQ优点 自己学习用的
  3. pb连接多个数据库 有关问题2
  4. 学习 Ruby on Rails 真的很爽!
  5. 为什么要学C语言及C语言存在的意义,新手经常犹豫学不学C语言
  6. acrgis api for javaScript中的portal查询
  7. php循环输出标题,php-如何通过循环在同一标题下分组数据
  8. Ajax — 新闻列表
  9. 每日一问:Android 滑动冲突,你们都是怎样处理的
  10. linux编辑音频文件,Linux 上的最佳音频编辑工具推荐
  11. 最长递增子序列 两种做法
  12. linux 源码搭建lnmp_LINUX CENTOS 6.5下源码搭建LNMP
  13. (PTA)数据结构(作业)10、树
  14. 本园的下学期工作计划
  15. 电脑DNS修改后被重新还原的解决方法
  16. 重要!SpringBoot一个非常蛋疼的无法启动的问题解决
  17. 易语言json置入_易语言取置JSON文本使用方法-易语言学习-猴子技术宅
  18. ES搜索结果中各个字段介绍,hits,took,timeout
  19. Java获取今天是几号
  20. P3400【仓鼠窝 】

热门文章

  1. 【Flutter】Flutter 混合开发 ( Flutter 与 Native 通信 | 在 Flutter 端实现 MethodChannel 通信 )
  2. 【Flutter】Flutter 混合开发 ( 关联 Android 工程与 Flutter 工程 | 安卓页面中嵌入 Flutter 页面 | 安卓中启动 Flutter 页面 )
  3. 【数据挖掘】基于密度的聚类方法 - DBSCAN 方法 ( DBSCAN 原理 | DBSCAN 流程 | 可变密度问题 | 链条现象 | OPTICS 算法引入 | 聚类层次 | 族序概念 )
  4. 网络基础 + 简易服务端和客户端
  5. 软件测试模型以及测试方法
  6. 前后端数据交互的几个方法
  7. WPF-MVVM学习心德(WinForm转WPF心德)
  8. maven 常用插件3
  9. Android 利用an框架快速实现网络请求(含下载上传文件)
  10. js_加入收藏夹功能