使用 Hasor 从数据库查询显示到页面上

    在 Hasor 的体系中开发 Web 应用程序需要至少 Hasor-Core、Hasor-Web 两个模块来共同完成。其中 Hasor-Core 为软件提供基本支持例如 IoC/Aop , Hasor-Web 提供请求转发。

    Hasor 的  Web MVC 支持是 Hasor-Web 软件包位于“net.hasor.web”的20多个核心类负责提供支持。Hasor 通过这20多个核心类提供了动态注册 Servlet、Filter 功能。读者可以先不深入它们。

    本文主要介绍 Controller 插件,该插件位于“net.hasor.plugins.controller”软件包。这个插件提供我们 MVC 模式中控制器方面的支持。

    MVC 模式中有三个主要点,它们分别是:M 模型层、V 视图层、C 控制器。

    在开发过程中通常模型用来编写业务逻辑;视图用于展现数据;控制器用于处理请求响应并将数据派发给视图用于显示。

    下面就看一看 Hasor 是如何完成这一切工作的,首先定义一个控制器,在控制器中新增一个userList方法作为Action,在Action中通过 JdbcTemplate 类查询数据库返回一个 List 到 request 属性中(请求地址:“/mgr/user/userList.do”即可):

import net.hasor.plugins.controller.AbstractController;
import net.hasor.plugins.controller.Controller;
/**
 * 
 * @version : 2013-12-23
 * @author 赵永春([email protected])
 */
@Controller("/mgr/user")
public class UserAction extends AbstractController {
    @Inject
    private JdbcTemplate jdbcTemplate;
    //
    @Forword
    public String userList() {
        ListuserList = jdbcTemplate.queryForList("select * from TB_User",
                                                        UserBean.class);
        this.setAttr("userList", userList);
        return "/mgr/user/userList.jsp";
    }
}

    下面这段代码的意思是注入一个与默认数据源绑定的数据库操作接口,使用这个接口操作数据库时都将针对默认数据源:

@Inject
    private JdbcTemplate jdbcTemplate;
   下面我们看一下如何配置默认数据源,Hasor 在使用数据库方面需要配置文件的支持。下面是例子程序的配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<config xmlns="http://project.hasor.net/hasor/schema/main">
  <!-- 数据源配置 -->
  <hasor-jdbc>
    <dataSourceSet default="localDB">
      <!-- 名称为 localDB 的内存数据库,数据库引擎使用 HSQL -->
      <dataSource name="localDB" dsFactory="net.hasor.plugins.datasource.factory.C3p0Factory">
        <driver>org.hsqldb.jdbcDriver</driver>
        <url>jdbc:hsqldb:mem:aname</url>
        <user>sa</user>
        <password></password>
      </dataSource>
    </dataSourceSet>
  </hasor-jdbc>
</config>

    从上面这段配置中可以看出 Hasor 是支持配置多个数据源的,每个数据源都要有一个具体的名字。如果配置的数据源要作为默认数据源,需要在“<dataSourceSet default="localDB">”的 default 属性上标出默认数据源的名字。

    @Forword 注解的含义是当 Action 执行完毕将请求转发到 Action 返回值所表示的地址中,下面是展示页面的 JSP 源码,您可以配上 JSTL 标准标签去优化这个页面:

<%@page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8" import="java.util.*"%>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<!-- 以下三个资源,保存在 Jar包中 -->
<title>Demo</title>
</head>
<body>
<%List userList= (List)request.getAttribute("userList"); %>
<%for (Object user : userList){ %>
<%request.setAttribute("user", user); %>
	<b>UUID</b>:${user.userUUID},<b>loginName</b>:${user.loginName}<br>
<%}%>
</body>
</html>

最后作为一个 Web 工程 web.xml 的配置文件如下:

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:javaee="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4">
  <listener>
    <listener-class>net.hasor.web.startup.RuntimeListener</listener-class>
  </listener>
  <filter>
    <filter-name>runtime</filter-name>
    <filter-class>net.hasor.web.startup.RuntimeFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>runtime</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
</web-app>

-------------------------------------------------------

    下面就分析一下 Hasor 是如何进行工作的,首先 Hasor 在启动初始化阶段扫描所有类并将标记了 @Controller 注解的类收集到一起,然后在启动阶段将其通过 Guice 创建出来。在这一过程完成依赖注入。

    Hasor 在收集创建控制器类时不会马上创建 Controller 的实例,它会通过一个代理类(ControllerInvoke)进行延迟加载。ControllerInvoke 类的主要职责就是延迟初始化控制器,并保证控制器在调用时是线程安全的。这个代理类还有一个重要的职责,负责调用最终的 action 方法。

    在收集 Controller 过程中,Hasor 还会根据注解中配置的内容对收集结果进行分组。分组的依据就是@Controller 注解中配置的值。用于标记分组的配置信息被称为“命名空间”。每个命名空间中可以存有若干 Action 定义,这些 Action 就是具体的类方法。在上面例子中 “userList”方法就是一个 action 定义。

    Hasor 会使用 ControllerNameSpace 类保存分组信息,分组的创建和保存全部是由 ControllerServlet 中央 控制器类处理。中央控制器在 Hasor init 过程中会扫描类路径并构建这些信息。

    由于处理起来并不是很复杂这部分代码就没有做过多的封装设计。

    当请求进入中央控制器时,中央控制器会用过字符串匹配方式找到对应的命名空间(ControllerNameSpace)。然后从命名空间中获取 Action对象(ControllerInvoke) 在通过 invoke 方法调用Action获取返回值。

    Action 返回值对于 Controller 插件来说毫无用处。因此处理 Action 返回值部分的功能就交给了其它插件去实现,至此 Controller 插件要关注的目标就更加单一。

-------------------------------------------------------

    有的同学可能想问,这样简单的 MVC 控制器如何实现 Action 拦截器呢?

    答案就是通过 Guice 的 Aop,与 JFinal 不依赖 IoC模型不同的是 Hasor 需要依赖 IoC/Aop 容器。因此通过挂载 控制器上的 Aop 可以方便的实现 Action 拦截器。

    而实现拦截器的关键代码Controller 插件也不用关心了,这样就更加使 Controller 插件的工作目标单一。越单一的功能维护起来反而更加简单轻松。

    关于Action拦截器 Controller 插件还是做了一些简单的封装,下面是一个Action 拦截器代码:

class ActionLogInterceptor extends ControllerInterceptor {
    public Object invoke(ControllerInvocation invocation) throws Throwable {
        try {
            HttpServletRequest reqest = invocation.getRequest();
            Hasor.logInfo("req:%s.", reqest.getRequestURI());
            return invocation.proceed();
        } catch (Exception e) {
            throw e;
        }
    }
}

   在 Hasor 中使用 Action 拦截器有三个方式:

    1.只对一个 Action生效的拦截器,声明这种拦截器需要在 Action 方法上通过 @Aop 注解实现。
    2.对一个控制器类中所有 Action 方法生效,这种方式是在类上通过标记 @Aop 注解实现。
    3.对所有 Action 方法生效,这种方式需要通过注册全局 Aop 来实现。

    那么既然如此,就向 Guice 注册一个全局 Aop 把。

    注册全局Aop 首先我们要得到 Guice 的 Binder 接口,然后通过 bindInterceptor 方法注册一个 Aop 切面。如果大家还记得,前面在有关 Aop 的博文中我已讲过如何使用这个方法(http://my.oschina.net/u/1166271/blog/178369)。

    接下来就是如何获取到 Binder 接口。 我们知道 Hasor 在启动时需要经过 init 阶段,在这阶段 Hasor 负责装载插件。那么我们就通过插件获取 Guice 的 Binder 接口完成这个功能把。下面是代码:

@Plugin
public class ActionLog implements HasorPlugin {
    public void loadPlugin(ApiBinder apiBinder) {
        apiBinder.getGuiceBinder().bindInterceptor(AopMatchers.annotatedWith(Controller.class),//
                AopMatchers.any(), new ActionLogInterceptor());
    }
}

再次启动应用程序,在地址栏输入 “/mgr/user/userList.do”在控制台就可以看到输出的日志了。

----------------------------------------------------------------
目前的开发代码存放于(包括Demo程序)
    Github:    https://github.com/zycgit/hasor
    git@OSC: http://git.oschina.net/zycgit/hasor

非常感谢您百忙之中抽出时间来看这一系博文。可以通过Maven 中央仓库网站  http://search.maven.org/ 搜索 Hasor 下载 hasor 的相关代码。

你可能感兴趣的:(使用 Hasor 从数据库查询显示到页面上)