java笔记:自己动手写javaEE

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

    @Override
    public 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 {

    @Override
    public 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 extends
        AbstractTransactionalJUnit4SpringContextTests {
    
    @Autowired
    private UsersService usersService = null;
    
    public UsersServiceImplTest()
    {
        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>();
        map.put("username", "sharpxiajun");
        try {
            List<Map<String, Object>> list = usersService.queryUsersList(map);
            System.out.println(list);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.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=DEBUG


log4j.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了,这个才是我的重点

作者:夏天的森林


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值