• 【转】Nutz | Nutz项目整合Spring实战


    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,在原有配置基础上加入如下配置

    <!--引入spring-->
        <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath*:applicationContext.xml</param-value>
            <!-- 默认是/WEB-INF/applicationContext.xml -->
        </context-param>
    
        <listener>
            <listener-class>
                org.springframework.web.context.ContextLoaderListener
            </listener-class>
        </listener>
    
        <servlet>
            <servlet-name>SpringMVC</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath*:SpringMVC-servlet.xml</param-value>
                <!-- 默认是/WEB-INF/[servlet名字]-servlet.xml -->
            </init-param>
            <load-on-startup>1</load-on-startup>
        </servlet>
    
        <servlet-mapping>
            <servlet-name>SpringMVC</servlet-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,加入如下配置

    <?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"
           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">
        <!-- 设置使用注解的类所在的jar包 -->
        <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>.jsp</value>
            </property>
    
        </bean>
    
    </beans>

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

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

    <?xml version="1.0" encoding="UTF-8"?>
    <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"/>
            <!-- 如果要使用Trans,移除springDaoRunner -->
            <property name="runner" ref="springDaoRunner"/>
        </bean>
        <bean id="springDaoRunner" class="org.nutz.integration.spring.SpringDaoRunner">
        </bean>
    
    </beans>

    property.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"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
                            http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
        <!-- 读取jdbc配置 -->
        <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
            <property name="locations">
                <list>
                    <!-- jdbc配置 -->
                    <value>classpath:properties/db.properties</value>
                </list>
            </property>
        </bean>
    </beans>

    transation.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"
           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"/>
        <!-- 如果要使用Trans,移除springDaoRunner -->
        <property name="runner" ref="springDaoRunner"/>
    </bean>
    <bean id="springDaoRunner" class="org.nutz.integration.spring.SpringDaoRunner">
    </bean>
    
    </beans>

    annotation.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"
           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">
    
        <!--spring 扫包   @Service .....-->
        <context:component-scan base-package="com.evanshare">
            <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
        </context:component-scan>
    
        <context:annotation-config/>
    
       <!-- 添加自定义注解,使spring自动注入-->
        <bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor">
            <property name="autowiredAnnotationTypes" >
            <set >
                <value >org.nutz.ioc.loader.annotation.Inject</value>
            </set>
            </property>
        </bean>
    </beans>

    注意:

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

     <!-- 添加自定义注解,使spring自动注入-->
        <bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor">
            <property name="autowiredAnnotationTypes" >
            <set >
                <value >org.nutz.ioc.loader.annotation.Inject</value>
            </set>
            </property>
        </bean>

    Tips

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

        /**
         * Set the 'autowired' annotation types, to be used on constructors, fields,
         * setter methods and arbitrary config methods.
         * <p>The default autowired annotation type is the Spring-provided
         * {@link Autowired} annotation, as well as {@link Value}.
         * <p>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<Class<? extends Annotation>> 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> T get(Class<T> 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:10856214@163.com
     */
    @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:10856214@163.com
     */
    @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<User> 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:10856214@163.com
     */
    @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<User> 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:10856214@163.com
     */
    @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<String> 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<String> getTables(String insql) throws JSQLParserException{
            net.sf.jsqlparser.statement.Statement statement = CCJSqlParserUtil.parse(insql);
            Select selectStatement = (Select)statement;
            TablesNamesFinder tablesNamesFinder = new TablesNamesFinder();
            List<String> 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"/>
        <!-- 如果要使用Trans,移除springDaoRunner -->
        <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<String> prefixs = new HashSet<String>();
        @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>

    菜单显示如下 

    测试

    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:10856214@163.com
     */
    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:10856214@163.com
     */
    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
    版权声明:本文为博主原创文章,未经博主允许不得转载。
  • 相关阅读:
    angularjs的$on、$emit、$broadcast
    angularjs中的路由介绍详解 ui-route(转)
    ionic入门教程-ionic路由详解(state、route、resolve)(转)
    Cocos Creator 加载使用protobuf第三方库,因为加载顺序报错
    Cocos Creator 计时器错误 cc.Scheduler: Illegal target which doesn't have uuid or instanceId.
    Cocos Creator 构造函数传参警告 Can not instantiate CCClass 'Test' with arguments.
    Cocos Creator 对象池NodePool
    Cocos Creator 坐标系 (convertToWorldSpaceAR、convertToNodeSpaceAR)
    Cocos Creator 常驻节点addPersistRootNode
    Cocos Creator 配合Tiled地图的使用
  • 原文地址:https://www.cnblogs.com/telwanggs/p/7570685.html
Copyright © 2020-2023  润新知