Nutz | Nutz项目整合Spring实战

  • Nutz项目整合Spring实战
  • 前言
    • Github地址
  • 背景
  • 实现步骤
    • 加入springMvc与Spring 相关配置
    • 新增Spring相关配置
    • 新增SpringIocProvider
    • 重写Nutz IOC注解
    • 新增spring相关controller与service
    • 事务托管给Spring
    • 修改ResourceFilter
    • 新增基于Spring代码实现的菜单
  • 测试
    • 改造前后兼容测试
    • 事务测试
      • 新建NutzTransTestService

Nutz项目整合Spring实战

前言

本教程主要基于本人Nutz框架的 Demo进行改造,目的是使原有Nutz demo代码可以正常运行,新增spring代码也可以正常运行,并且Nutz框架原有代码与spring新增代码可以相互引用,从而达到整合后的新代码与旧代码可以兼容,请配合项目源码进行操作。

Demo功能

登陆模块
查看用户列表
新增用户
通过自定义sql自动导出报表

Github地址

https://github.com/Evan43789596/evanshare.git

背景

某公司原有项目采用的是Nutz框架,无奈是Nutz框架整合其他主流框架会相对繁琐,加上Nutz对事务支持也不是很友好,而且也不便于团队成员使用和学习市面主流技术,于是我们想把Nutz与spring框架整合,在不影响原有项目的前提下,新代码均使用spring实现,也就意味着把nutz的东西都转移到spring去管理了。

目标

  1. 原有nutz的业务代码不需要改动能正常运行
  2. 新做的spring的代码可以正常运行
  3. nutz可以引用spring的代码
  4. spring可以引用nutz的代码

方案

  1. 保留两套MVC,Spring MVC与Nutz MVC共存,原有旧代码继续走Nutz MVC
  2. Nutz与Spring统一使用Spring IOC容器进行管理,均从Spring上下文查找bean
  3. 原有Nutz配置搬到Spring ,由Spring统一加载与管理,AOP与事务均使用Spring实现

思路

  1. Nutz Mvc 与 Spring Mvc相互独立存在,项目原有代码继续使用Nutz Mvc,新建Spring代码采用Spring Mvc,根据请求路径中的前缀/spring/进行切换。
  2. 原有Nutz项目的bean与新做的spring相关bean统一交给springIOC容器管理,覆盖NutzIOC注解(IocBean、Inject、InjectName)使这些注解标注的类实例交由Spring管理
  3. 创建SpringIocProvider实现Ioc、IocProvider,使原有Nutz代码与新代码上下文统一使用applicationContext
  4. 通过Spring AutowiredAnnotationBeanPostProcessor类中的setAutowiredAnnotationTypes方法,可使开发者扩展自定义注解交给spring去自动注入(如:Inject),从而实现spring的代码也能使用Inject注解
    把原有nutz项目的所有配置统一交由spring去加载,这样后面的事情都按照spring的方式去做即可。

文件变更

  • 在com.evanshare包下,新增spring相关Controller与service,其功能与原Nutz部分相同
  • 新增SpringIocProvider
  • 修改MainModule原有IocBy的类型,改为SpringIocProvider
  • 新增org.nutz包,用于复写Nutz Ioc相关注解
  • 修改ResourceFilter,新增对/spring/过滤
  • 修改web.xml,加入spring相关配置
  • 在resource目录下新增application.xml、SpringMVC-servlet.xml以及其他相关.xml,把原有Nutz配置搬到 .xml
  • 对com.evanshare.module原有nutz部分module引入spring相关service与注解

实现步骤

1.加入springMvc与Spring 相关配置

打开WEB-INF下的web.xl,在原有配置基础上加入如下配置


    <context-param>
        <param-name>contextConfigLocationparam-name>
        <param-value>classpath*:applicationContext.xmlparam-value>
        
    context-param>

    <listener>
        <listener-class>
            org.springframework.web.context.ContextLoaderListener
        listener-class>
    listener>

    <servlet>
        <servlet-name>SpringMVCservlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
        <init-param>
            <param-name>contextConfigLocationparam-name>
            <param-value>classpath*:SpringMVC-servlet.xmlparam-value>
            
        init-param>
        <load-on-startup>1load-on-startup>
    servlet>

    <servlet-mapping>
        <servlet-name>SpringMVCservlet-name>
        <url-pattern>/spring/*url-pattern>
    servlet-mapping>

2.新增Spring相关配置

在resource目录下,创建applicationContext.xml,加入如下配置

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">

    <import resource="conf/*.xml" />

beans>

在resource目录下,创建SpringMVC-servlet.xml,加入如下配置


<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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                        http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
                        http://www.springframework.org/schema/context
                        http://www.springframework.org/schema/context/spring-context-4.0.xsd">
    
    <context:component-scan base-package="com.evanshare" />
    <bean
            class="org.springframework.web.servlet.view.InternalResourceViewResolver">

        <property name="prefix">
            <value>/WEB-INF/jsp/value>
        property>

        <property name="suffix">
            <value>.jspvalue>
        property>

    bean>

beans>

在Resource下,分别创建jdbc.xml、nutDao.xml、property.xml、transation.xml、annotation.xml

jdbc.xml


<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                        http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
    
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${db.driver}"/>
        <property name="url" value="${db.url}"/>
        <property name="username" value="${db.username}" />
        <property name="password" value="${db.password}"/>
        <property name="testWhileIdle" value="true"/>
        <property name="validationQuery" value="${db.validationQuery}"/>
        <property name="maxActive" value="${db.maxActive}"/>
        <property name="connectionProperties" value="druid.stat.slowSqlMillis=2000"/>
    bean>

beans>

nutzDao.xml


<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                        http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">

    <bean id="dao" class="org.nutz.dao.impl.NutDao">
        <property name="dataSource" ref="dataSource"/>
        
        <property name="runner" ref="springDaoRunner"/>
    bean>
    <bean id="springDaoRunner" class="org.nutz.integration.spring.SpringDaoRunner">
    bean>

beans>

property.xml


<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                        http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
    
    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations">
            <list>
                
                <value>classpath:properties/db.propertiesvalue>
            list>
        property>
    bean>
beans>

transation.xml


<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                        http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">

<bean id="dao" class="org.nutz.dao.impl.NutDao">
    <property name="dataSource" ref="dataSource"/>
    
    <property name="runner" ref="springDaoRunner"/>
bean>
<bean id="springDaoRunner" class="org.nutz.integration.spring.SpringDaoRunner">
bean>

beans>

annotation.xml


<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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                        http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
                        http://www.springframework.org/schema/context
                        http://www.springframework.org/schema/context/spring-context-4.0.xsd">

    
    <context:component-scan base-package="com.evanshare">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    context:component-scan>

    <context:annotation-config/>

   
    <bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor">
        <property name="autowiredAnnotationTypes" >
        <set >
            <value >org.nutz.ioc.loader.annotation.Injectvalue>
        set>
        property>
    bean>
beans>

注意:

以下annotation.xml中的这段配置,是为了让spring能识别我们的自定义注解,并且能为我们用自定义注解(Nutz的@Inject)标识的对象注入,我们如果想加多几个自定义注解,只需要像一下配置那样把全限定类名加入到属性中即可

 
    <bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor">
        <property name="autowiredAnnotationTypes" >
        <set >
            <value >org.nutz.ioc.loader.annotation.Injectvalue>
        set>
        property>
    bean>

Tips

为什么我会知道?参考Spring AutowiredAnnotationBeanPostProcessor源码注释可以看到,其实它本身就是支持开发者自由扩展的

    /**
     * Set the 'autowired' annotation types, to be used on constructors, fields,
     * setter methods and arbitrary config methods.
     * 

The default autowired annotation type is the Spring-provided * {@link Autowired} annotation, as well as {@link Value}. *

This setter property exists so that developers can provide their own * (non-Spring-specific) annotation types to indicate that a member is * supposed to be autowired. */ public void setAutowiredAnnotationTypes(Set> autowiredAnnotationTypes) { Assert.notEmpty(autowiredAnnotationTypes, "'autowiredAnnotationTypes' must not be empty"); this.autowiredAnnotationTypes.clear(); this.autowiredAnnotationTypes.addAll(autowiredAnnotationTypes); }

3.新增SpringIocProvider,

具体SpringIocProvider代码请查看项目源码,主要看下面这段代码:

public class SpringIocProvider implements IocProvider, Ioc {

    protected  ApplicationContext applicationContext;


    @Override
    public Ioc create(NutConfig config, String[] args) {
        if (config == null || Lang.length(args) > 0)
            applicationContext = new ClassPathXmlApplicationContext(args);
        else
            applicationContext = (ApplicationContext) config.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
        return this;
    }

@SuppressWarnings("unchecked")
    @Override
    public  T get(Class classZ) throws IocException {
        InjectName injectName = classZ.getAnnotation(InjectName.class);
        if (injectName != null && !Strings.isBlank(injectName.value()))
            return (T) applicationContext.getBean(injectName.value());
        return (T) applicationContext.getBean(applicationContext.getBeanNamesForType(classZ)[0]);
    }
    }

从以上代码可以看出,该create和get方法会返回一个applicationContext,也就是说,Nutz Ioc如果是使用该iocProvider去创建,那么Nutz 在查找bean的时候将会从spring的上下文中去查找了,从而达到了我们想要的spring管理bean的效果,可对比Nutz原生ComboIocProvider.

在主模块声明SpringIocProvider
在MainModule上加入@IocBy(type=SpringIocProvider.class,args={})

@ChainBy(args="conf/mvc/evanshare-mvc-chain.js")
@SetupBy(value=MainSetup.class)
//@IocBy(type=CustomComboIocProvider.class,args={"*js","conf/ioc/","*anno","com.evanshare","*tx"})
@IocBy(args = {} ,type = SpringIocProvider.class)
@Modules(scanPackage=true)
@Fail("jsp:jsp.500")
@Localization(value="msg/", defaultLocalizationKey="zh-CN")
public class MainModule {   
}

意味着nutz的所有bean都将会从spring上下文中查找

4.重写Nutz IOC注解

为了使项目中原有nutz IOC注解标识的类能交给spring容器管理,我们需要做一些小的改动,就是在原注解上加入spring的注解@Component,这样的话,spring就会帮我们将这些类纳入到spring容器管理.需要重写的注解有:@Inject、@IocBean、@InjectName,以下取IocBean作为示例,其他同理。

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Component
public @interface IocBean {

5.新增spring相关controller与service

在com.evanshare包下,新建Spring相关Controller: SpUserController、SpSqlController,新建Spring相关Service:SpUserService、SpSqlService

作用

SpUserController:基于spring实现,用于用户操作相关模块,作用与Nutz demo中的UserModule相同,用于用户登录、添加用户、用户列表展示

SpSqlController:基于Spring实现,用于Sql查询相关模块,作用与Nutz demo中的SqlController中的SqlModule相同,用于根据sql导出报表

SpUserService:基于Spring实现,用于用户相关处理逻辑,
与Nutz demo中的UserService作用相同,可互相替换使用

SpSqlService:基于Spring实现,用于sql相关处理逻辑,
与Nutz demo中的SqlService作用相同,可互相替换使用

SpUserController

/**
 *
 * 基于spring实现的Sql Controller
 * Created by liangyh on 2017/1/24.
 * Email:[email protected]
 */
@Controller
public class SpSqlController {
    @Autowired
   private SpSqlService spSqlService;
    //注入nutz service
    @Inject
    private SqlService sqlService;

    /**
     * 跳转到sql查询页面
     * @return
     */
    @RequestMapping(value = "/sql/sql_query")
    public String forwardToSqlQuery(){

        return "sql/sql_query";

    }

    /**
     * 根据自定义sql到处报表
     * @param querySql
     * @param resp
     * @throws Exception
     */
    @RequestMapping("/sql/exportData")
    public void exportSqlData(@RequestParam(value = "querySql",required = false) String querySql, HttpServletResponse resp) throws Exception {
        spSqlService.exportExcelBySql(querySql,resp);
        //调用Nutz service去到处报表
       // sqlService.exportExcelBySql(querySql,resp);
    }

SpUserController

/**
 * 基于spring实现的用户controller
 * Created by liangyh on 2017/1/28.
 * Email:[email protected]
 */
@RestController
public class SpUserController {
 @Autowired
    private SpUserService spUserService;
    //注入nutz的service
    @Inject
    private UserService userService;

    /**
     * 用户登录
     *
     * @param name
     * @param password
     * @param session
     * @return
     */
    @RequestMapping("/user/login")
    public String login(@RequestParam("username") String name, @RequestParam("password") String password, HttpSession session) {
        User user = spUserService.findByNameAndPsw(name,password);
        if (user == null) {
            return "/page/error/500";
        } else {
            session.setAttribute("user", user);
            return "jsp/common/index_menu";
        }
    }


    /**
     * 获取用户列表
     *
     * @param session
     * @return
     */
    @RequestMapping("/user/list")
    public String list(@RequestParam(value = "pageNum",required = false) String pageNum,@RequestParam(value = "numPerPage",required = false) String numPerPage ,@RequestParam(value = "keyword",required = false) String keyword,HttpSession session,HttpServletRequest req) {
        Integer page = StringUtils.isNotBlank(pageNum)?Integer.parseInt(pageNum):0;
        Integer perPageNum = StringUtils.isNotBlank(numPerPage)?Integer.parseInt(numPerPage):0;
        //调用Nutz service获取用户列表
       // QueryResult result  = userService.getUserList(page, perPageNum,keyword);
        QueryResult result  = spUserService.getUserList(page, perPageNum,keyword);
        List users =  result.getList(User.class);
        PagerModel pager = new PagerModel();
        pager.setCurrentPage(page);
        pager.setNumPerPage(perPageNum);
        pager.setPageNumShown(result.getPager().getPageSize());
        pager.setTotalCount(result.getPager().getRecordCount());
        pager.setPageSize(result.getPager().getPageSize());
        req.setAttribute("users", users);
        req.setAttribute("pager", pager);
        return "user/user_list";
    }

SpUserService

/**
 * 基于spring 的service
 * Email:[email protected]
 */
@Service
public class SpUserService {
    @Autowired
  private Dao dao;


    /**
     * 获取用户列表
     * @param pageNumber
     * @param pageSize
     * @param keyword
     * @return
     */
    public QueryResult getUserList(int pageNumber,int pageSize,String keyword){
        if(StringUtils.isEmpty(keyword)){
            keyword = "";
        }
        Pager pager = dao.createPager(pageNumber, pageSize);
        List userList =dao.query(User.class, Cnd.where("name","like","%"+keyword+"%"), pager);
        return new QueryResult(userList,pager);
    }

    /**
     * 根据姓名和密码查找用户
     * @param name
     * @param password
     * @return
     */
    public User findByNameAndPsw(String name,String password){
        return dao.fetch(User.class, Cnd.where("name", "=", name).and("password", "=", password));
    }

    public void print(){
        System.out.println("I AM NUTZ SERVICE");
    }

    /**
     * 获取用户总数
     * @return
     */
    public Integer getTotalUserCount(){

        return dao.count(User.class);
    }

    /**
     * 添加用户
     * @param user
     * @return
     */
    public User addUser(User user) {
        user.setCreateTime(new Date());
        user.setUpdateTime(new Date());
        return dao.insert(user);

    }

}

SpSqlService

/**
 * Created by liangyh on 2017/1/28.
 * Email:[email protected]
 */
@Service
public class SpSqlService {

    private final Logger logger = Logger.getLogger(SqlModule.class);

    @Autowired
    private Dao dao;

    /**
     * 根据自定义sql导出报表
     * @param insql 自定义sql
     * @param resp 响应实体
     * @throws SQLException
     * @throws IOException
     * @throws FileNotFoundException
     * @throws JSQLParserException
     */
    public void exportExcelBySql(String insql,HttpServletResponse resp)
            throws Exception {
        // 11:创建一个excel
        SXSSFWorkbook book = new SXSSFWorkbook(1000);
        java.sql.ResultSet rs = null;
        List tables =  getTables(insql);
        DataSource datasource =  Mvcs.getIoc().get(DruidDataSource.class, "dataSource");
        Connection conn = null;
        Statement st =null;
        try {
            conn = datasource.getConnection();
            // 3:声明st
            double maxRowNum;
            int sheets;
            ResultSetMetaData rsmd;
            int cols;
            long startTime;
            long endTime;
            st = conn.createStatement();

            //结果集总行数
            double totalcount = 137713;

            /* excel单表最大行数是65535 */
            maxRowNum = 60000;

            sheets = (int) Math.ceil(totalcount / maxRowNum);
            rs = st.executeQuery(insql);
            // 3:rsmd
            rsmd = (ResultSetMetaData) rs.getMetaData();
            // 4:分析一共多少列
            cols = rsmd.getColumnCount();
            startTime = System.currentTimeMillis();

            //写入excel文件数据信息
            for (int i = 0; i < sheets; i++) {
                logger.info("<=======正在导出报表=========>");

                // 12:创建sheet
                SXSSFSheet sheet = book.createSheet(" " + (i + 1) + " ");

                // 13:创建表头
                SXSSFRow row = sheet.createRow(0);

                for (int j = 0; j < cols; j++) {
                    String cname = rsmd.getColumnName(j + 1);

                    SXSSFCell cell = row.createCell(j);
                    cell.setCellValue(cname);

                }
                // 显示数据
                for (int t = 0; t < maxRowNum; t++) {
                    if (!rs.next()) {
                        break;
                    }
                    // 14:保存数据
                    row = sheet.createRow(sheet.getLastRowNum() + 1);

                    for (int j = 0; j < cols; j++) {
                        SXSSFCell cell = row.createCell(j);
                        cell.setCellValue(rs.getString(j + 1));
                    }
                }
            }

            resp.reset();
            resp.setContentType("multipart/form-data"); //自动识别
            resp.setHeader("Content-Disposition", "attachment;filename=data.xls");
            book.write(resp.getOutputStream());
            endTime = System.currentTimeMillis();
            logger.info("导出报表所用时间为:" + (endTime - startTime));
        }catch (Exception ex){
            logger.info(String.format("导出报表异常,Sql=【%s】,异常信息{}",insql),ex);

        }finally {
            if(book!=null){
                book.close();
            }
            if(rs!=null){
                rs.close();
            }
            if(st!=null){
                st.close();
            }
            if(conn!=null){
                conn.close();
            }
        }
    }

    /**
     * 获取对应的表
     * @param insql 自定义sql
     * @return
     * @throws JSQLParserException
     */
    private  List getTables(String insql) throws JSQLParserException{
        net.sf.jsqlparser.statement.Statement statement = CCJSqlParserUtil.parse(insql);
        Select selectStatement = (Select)statement;
        TablesNamesFinder tablesNamesFinder = new TablesNamesFinder();
        List result = tablesNamesFinder.getTableList(selectStatement);
        return result;
    }

猜想

基于Spring实现的SpUserService与基于Nutz实现的UserService相互替换使用,Spring实现的Controller既然调用spring的service,也能调用nutz的service(SpSqlService与SqlService同理)

6.事务托管给Spring

在org.nutz下创建SpringDaoRunner,SpringDaoRunner继承NutDaoRunner,重写了_run方法,通过DataSourceUtils去获取connection,使得事务可以统一交给Spring去管理

public class SpringDaoRunner extends NutDaoRunner {

    @Override
    public void _run(DataSource dataSource, ConnCallback callback) {

        Connection con = DataSourceUtils.getConnection(dataSource);
        try {
            callback.invoke(con);
        } catch (Exception e) {
            if (e instanceof RuntimeException)
                throw (RuntimeException) e;
            else
                throw new RuntimeException(e);
        } finally {
            DataSourceUtils.releaseConnection(con, dataSource);
        }
    }
}

回顾之前的nutDao.xml配置,该配置作用是使用spring管理事务

<bean id="dao" class="org.nutz.dao.impl.NutDao">
    <property name="dataSource" ref="dataSource"/>
    
    <property name="runner" ref="springDaoRunner"/>
bean>
<bean id="springDaoRunner" class="org.nutz.integration.spring.SpringDaoRunner">
bean>

7.修改ResourceFilter

在ResourceFilter中的init方法中,新增对请求路径中/spring/过滤

/**
 * 对资源文件进行过滤
 * @author lyh
 *
 */
public class ResourceFilter extends NutFilter {
    protected Set prefixs = new HashSet();
    @Override
    public void init(FilterConfig conf) throws ServletException {
        super.init(conf);
        prefixs.add(conf.getServletContext().getContextPath() + "/druid/");
        //对请求路径带有/spring/过滤
        prefixs.add(conf.getServletContext().getContextPath() + "/spring/");
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
            throws IOException, ServletException {
         if (req instanceof HttpServletRequest) {
                String uri = ((HttpServletRequest) req).getRequestURI();
                for (String prefix : prefixs) {
                    if (uri.startsWith(prefix)) {
                        chain.doFilter(req, resp);
                        return;
                    }
                }
            }
            super.doFilter(req, resp, chain);
    }


}

为什么要过滤?

为了应对基于spring controller请求可以走Spring Mvc,所以在ResourceFilter那进行了过滤,避免被NutFilter进行拦截和调用到Nutz MVC的处理方法,这样请求可以顺利进入DispatcherServlet

8.新增基于Spring代码实现的菜单

在原Nutz demo菜单基础上,新增两个指向Spring处理类的菜单,只想Spring处理类的Url前缀都加上/spring/,用于区分请求是走Spring MVC还是Nutz Mvc

    <ul class="tree treeFolder">
                            <li><a href="<%=request.getContextPath() %>/user/list" target="navTab" rel="user_list">用户列表a>li>
                                ul>
                            li>

                        ul>
                        <ul class="tree treeFolder">
                            <li><a href="<%=request.getContextPath() %>/sql/sql_query" target="navTab" rel="sql_query">SQL查询a>li>
                                ul>
                            li>

                        ul>
                        <ul class="tree treeFolder">
                            <li><a href="<%=request.getContextPath() %>/spring/user/list" target="navTab" rel="spring_user_list">基于Srping实现用户列表a>li>
                        ul>
                        li>

                        ul>
                        <ul class="tree treeFolder">
                            <li><a href="<%=request.getContextPath() %>/spring/sql/sql_query" target="navTab" rel="springsql_query">基于Spring实现SQL查询a>li>
                        ul>
                        li>

                        ul>

菜单显示如下
Nutz | Nutz项目整合Spring实战_第1张图片

测试

1.改造前后兼容测试

测试案例

  1. 浏览器输入:localhost:8080/evanshare,跳转到登陆页面,输入:admin 123456
  2. 点击资管管理-用户列表
  3. 点击资管管理-Sql查询
  4. 点击资管管理-基于Springle实现用户列表
  5. 点击资管管理-基于Springle实现Sql查询

预期结果

  1. 能正常登陆,进入管理后台
  2. 能正常打开用户列表
  3. 输入select * from tb_at_user ,能正常导出报表
  4. 能正常打开用户列表
  5. 输入select * from tb_at_user ,能正常导出报表

2.事务测试

新建NutzTransTestService

在com.evanshare.spring.service包下,新建NutzTransTestService

package com.evanshare.spring.service;

import org.nutz.dao.Dao;
import org.springframework.transaction.annotation.Transactional;

/**
 * 该类用于事务测试用途
 * Created by liangyh on 2017/1/28.
 * Email:[email protected]
 */
public class NutzTransTestService {

    protected Dao dao;

    public void setDao(Dao dao) {
        this.dao = dao;
    }

    @Transactional(rollbackFor=Exception.class)
    public void doUserClear() {
        dao.clear("tb_at_user");
        throw new RuntimeException();
    }

}

在Test目录下com.evanshare.spring.service包新建NutzTransTestServiceTest

package com.evanshare.spring.service;

import org.junit.Test;
import org.nutz.dao.Dao;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.util.Assert;

import static org.junit.Assert.*;

/**
 * NUtz事务测试
 * Created by liangyh on 2017/1/28.
 * Email:[email protected]
 */
public class NutzTransTestServiceTest extends Assert {


    @Test
    public void doUserClear() throws Exception {
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        NutzTransTestService nutzTest = ctx.getBean("transTest", NutzTransTestService.class);

        Dao dao = ctx.getBean("dao", Dao.class);
        int count = dao.count("tb_at_user");
        assertTrue(count > 0);
        try {
            nutzTest.doUserClear();
        } catch (Exception e) {
            // 里面主动抛出异常
        }
        assertEquals(count, dao.count("tb_at_user"));
        ctx.close();
    }

}

测试案例

  1. 执行NutzTransTestServiceTest下的doUserClear方法

预期结果

  • NutzTransTestService有一个spring事务注解的方法,里面清除了t_user表,然后抛出了异常
  • 日志中显示, spring的事务进行了rollback, 可以看到rollback等字眼
  • 执行doUserClear前后的tb_at_user表记录数相同,所以事务真的是回滚了

执行日志如下:

22:17:31.483 [main] DEBUG org.nutz.dao.impl.sql.run.NutDaoExecutor - DELETE FROM tb_at_user 
22:17:31.503 [main] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager - Initiating transaction rollback
22:17:31.503 [main] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager - Rolling back JDBC transaction on Connection [com.mysql.jdbc.JDBC4Connection@2228db21]
22:17:31.511 [main] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager - Releasing JDBC Connection [com.mysql.jdbc.JDBC4Connection@2228db21] after transaction
22:17:31.511 [main] DEBUG org.springframework.jdbc.datasource.DataSourceUtils - Returning JDBC Connection to DataSource
22:17:35.556 [main] DEBUG org.springframework.jdbc.datasource.DataSourceUtils - Fetching JDBC Connection from DataSource
22:17:35.556 [main] DEBUG org.nutz.dao.impl.sql.run.NutDaoExecutor - SELECT COUNT(*) FROM tb_at_user 
22:17:35.558 [main] DEBUG org.springframework.jdbc.datasource.DataSourceUtils - Returning JDBC Connection to DataSource

你可能感兴趣的:(解决方案,聊聊框架开发)