做一个java web应用,开发速度和运行效率是不容忽视的,下面简单的介绍一下apache mvc的运用:
第一步,创建数据库和表结构,以mysql为例,运行建表语句:
- DROP TABLE IF EXISTS T_USER;
- CREATE TABLE T_USER (
- AGE bigint(20),
- NAME varchar(500),
- ID bigint(20) NOT NULL AUTO_INCREMENT,
- PRIMARY KEY (ID)
- ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
很简单的一张表,三个字段。
第二步,创建web工程,取名为test,在src目录下创建一个文件夹config,用来存放配置文件,右键单击设置为:Build Path --> Use as Source Folder,
在src目录下创建一个包,名为com.zl,用来存放java源代码, 在com.zl这个包下创建一个Class,名为:CreateCode, 准备生成java代码,同时准备好以下jar包:
第三步,生成java代码,在CreateCode里写一个main方法,如下:
- public static void main(String[] args) throws Exception {
- Class.forName("com.mysql.jdbc.Driver");
- Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?characterEncoding=utf-8", "root", "123456");
- CreateJavaCode.getBean().create(
- connection, // 数据库连接
- new String[] { "T_USER" }, // 表的名字, 这是个数组,可以放多个表名
- new String[] { "会员" }, // 表的描述,和表名一一对应
- "D:\\project\\2013\\test\\src\\", // 生成的java代码存放目录
- "com.zl", // 包名根目录
- "/config/jdbc/" // 生成的jdbc映射文件存放的目录
- );
- }
运行main方法,控制台正常输出日志,无异常,表示java代码都已经生成好了,一个数据库表生成3个java文件和一个xml文件,
java文件包括Entity(实体类), Model(mvc中的m,即业务逻辑层), Controll(mvc中的c,即控制器层), xml文件就是jdbc映射文件,
此时你刷新工程,这些代码文件好比从天而降,突然出现并编译完毕,如图:
第四步,完善工程配置,首先在config目录下放入三个配置文件:
context.xml,内容如下:
- <?xml version="1.0" encoding="UTF-8"?>
- <beans>
- <interceptor name="myInterceptor" class="com.zl.control.MyInterceptor"/>
- <control package="com.zl.control" interceptor="myInterceptor"/>
- <config name="db" path="/jdbc.properties" />
- <bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
- <property name="driverClass" config="db.jdbc.driverClassName" />
- <property name="jdbcUrl" config="db.jdbc.url" />
- <property name="user" config="db.jdbc.username" />
- <property name="password" config="db.jdbc.password" />
- </bean>
- <bean name="jdbcConfig" class="org.apache.commons.mvc.entity.JdbcConfig">
- <property name="dataSource" ref="dataSource" />
- <property name="jdbcFolder" value="/jdbc/" />
- <property name="showSql" value="true" />
- <property name="databaseType" value="mysql" />
- <property name="createSqlFile" value="true" />
- </bean>
- </beans>
配置说明:这个文件是apache mvc的核心配置了,
interceptor 配置了控制器层的拦截器
control 配置了控制器所在的位置
config 配置了一个属性文件
bean name="dataSource" 配置了一个数据库连接池
bean name="jdbcConfig" 即mvc的jdbc配置
dataSource指定连接池
jdbcFolder指定jdbc映射文件所在的目录
showSql 每次访问数据库,是否显示sql语句
databaseType 指定数据库类型,目前只支持mysql和oracle两种
createSqlFile 是否在classpath下备份创建表结构的sql文件
再放入,jdbc.properties,内容如下:
- jdbc.driverClassName=com.mysql.jdbc.Driver
- jdbc.url=jdbc:mysql://localhost:3306/test?characterEncoding=utf-8
- jdbc.username=root
- jdbc.password=123456
再放入,log4j.properties,内容如下:
- #log4j.rootLogger=WARN, RollingFile
- log4j.rootLogger=INFO, Console, RollingFile
- #Console
- log4j.appender.Console=org.apache.log4j.ConsoleAppender
- log4j.appender.Console.layout=org.apache.log4j.PatternLayout
- log4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n
- #RollingFile
- log4j.appender.RollingFile=org.apache.log4j.DailyRollingFileAppender
- log4j.appender.RollingFile.layout=org.apache.log4j.HTMLLayout
- log4j.appender.RollingFile.File=/tmp/log/pm.html
- log4j.appender.RollingFile.DatePattern='.'yyyy-MM-dd
- log4j.logger.org.directwebremoting=WARN
- log4j.logger.org.getahaed=WARN
最后修改 web.xml, 内容如下:
- <?xml version="1.0" encoding="UTF-8"?>
- <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
- http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
- <display-name>test</display-name>
- <servlet>
- <servlet-name>mvc</servlet-name>
- <servlet-class>com.zl.control.InitServlet</servlet-class>
- <init-param>
- <param-name>config</param-name>
- <param-value>/WEB-INF/classes/context.xml</param-value>
- </init-param>
- <load-on-startup>1</load-on-startup>
- </servlet>
- <servlet-mapping>
- <servlet-name>mvc</servlet-name>
- <url-pattern>*.htm</url-pattern>
- </servlet-mapping>
- <welcome-file-list>
- <welcome-file>index.jsp</welcome-file>
- </welcome-file-list>
- </web-app>
可以看出,web.xml很简洁,就一个servlet的配置,servlet中指定了mvc核心配置的路径context.xml,
下面是com.zl.control.InitServlet的内容:
- package com.zl.control;
- import javax.servlet.ServletException;
- import org.apache.commons.codec.CharEncoding;
- import org.apache.commons.mvc.entity.FrameConfig;
- import org.apache.commons.mvc.web.ControlServlet;
- public class InitServlet extends ControlServlet {
- private static final long serialVersionUID = 1L;
- @Override
- public void init() throws ServletException {
- super.init();
- FrameConfig.encoding = CharEncoding.UTF_8;
- FrameConfig.viewPath = "/WEB-INF/view/";
- FrameConfig.viewSuffix = ".jsp";
- FrameConfig.showRequestInfo = true;
- }
- }
org.apache.commons.mvc.web.ControlServlet为apache mvc的核心servlet
FrameConfig.encoding 指定全局的编码格式
FrameConfig.viewPath 指定视图所在的位置
FrameConfig.viewSuffix 指定视图的后缀名
FrameConfig.showRequestInfo 是否显示每次请求的信息
第五步,测试工程运行
首先在tomcat中部署工程,部署方式有两种,
第1种,通过eclipse把工程复制到tomcat下的webapps目录下
第2种,修改tomcat的配置文件,注意看server.xml:
- <?xml version='1.0' encoding='utf-8'?>
- <Server port="8005" shutdown="SHUTDOWN">
- <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
- <Listener className="org.apache.catalina.core.JasperListener" />
- <Listener className="org.apache.catalina.mbeans.ServerLifecycleListener" />
- <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
- <GlobalNamingResources>
- <Resource name="UserDatabase" auth="Container"
- type="org.apache.catalina.UserDatabase"
- description="User database that can be updated and saved"
- factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
- pathname="conf/tomcat-users.xml" />
- </GlobalNamingResources>
- <Service name="Catalina">
- <Connector port="8088" protocol="HTTP/1.1"
- connectionTimeout="20000"
- redirectPort="8443" />
- <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
- <Engine name="Catalina" defaultHost="localhost">
- <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
- resourceName="UserDatabase"/>
- <Host name="localhost" appBase="webapps"
- unpackWARs="true" autoDeploy="true"
- xmlValidation="false" xmlNamespaceAware="false">
- <Context path="/test" docBase="D:\project\2013\test\WebRoot"></Context>
- </Host>
- </Engine>
- </Service>
- </Server>
<Context path="/test" docBase="D:\project\2013\test\WebRoot"></Context>关键是这一行
在tomcat中给工程配置虚拟路径就可以了,就完成了部署,这样部署之后,就不存在有重新部署的说法,一次即可
接下来就要启动tomcat了,此时,控制台会输出以下日志:
- 七月 31, 2013 6:02:43 下午 org.apache.catalina.core.AprLifecycleListener init
- 信息: The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: D:\work\jdk1.7.0_01\bin;D:\work\Tomcat6\bin
- 七月 31, 2013 6:02:43 下午 org.apache.coyote.http11.Http11Protocol init
- 信息: Initializing Coyote HTTP/1.1 on http-8088
- 七月 31, 2013 6:02:43 下午 org.apache.catalina.startup.Catalina load
- 信息: Initialization processed in 398 ms
- 七月 31, 2013 6:02:43 下午 org.apache.catalina.core.StandardService start
- 信息: Starting service Catalina
- 七月 31, 2013 6:02:43 下午 org.apache.catalina.core.StandardEngine start
- 信息: Starting Servlet Engine: Apache Tomcat/6.0.18
- 七月 31, 2013 6:02:43 下午 org.apache.catalina.loader.WebappClassLoader validateJarFile
- 信息: validateJarFile(D:\project\2013\test\WebRoot\WEB-INF\lib\javaee.jar) - jar not loaded. See Servlet Spec 2.3, section 9.7.2. Offending class: javax/servlet/Servlet.class
- 2013-07-31 18:02:44,125 [main] INFO [com.mchange.v2.log.MLog] - MLog clients using log4j logging.
- 2013-07-31 18:02:44,222 [main] INFO [com.mchange.v2.c3p0.C3P0Registry] - Initializing c3p0-0.9.1.2 [built 21-May-2007 15:04:56; debug? true; trace: 10]
- 2013-07-31 18:02:44,423 [main] INFO [org.apache.commons.mvc.bean.ConfigLoader] - db
- 2013-07-31 18:02:44,423 [main] INFO [org.apache.commons.mvc.bean.BeanLoader] - dataSource, jdbcConfig
- 2013-07-31 18:02:44,423 [main] INFO [org.apache.commons.mvc.bean.InterceptorLoader] - myInterceptor
- 2013-07-31 18:02:44,423 [main] INFO [org.apache.commons.mvc.bean.ControlLoader] - /other, /user, /tckOrder, /tckOrderItem
- 2013-07-31 18:02:44,442 [main] INFO [org.apache.commons.mvc.bean.CreateSqlFile] - jdbcConfig.sql
- 2013-07-31 18:02:44,455 [main] INFO [org.apache.commons.mvc.webservice.WebserviceLoader] - user
- 七月 31, 2013 6:02:44 下午 org.apache.coyote.http11.Http11Protocol start
- 信息: Starting Coyote HTTP/1.1 on http-8088
- 七月 31, 2013 6:02:44 下午 org.apache.jk.common.ChannelSocket init
- 信息: JK: ajp13 listening on /0.0.0.0:8009
- 七月 31, 2013 6:02:44 下午 org.apache.jk.server.JkMain start
- 信息: Jk running ID=0 time=0/20 config=null
- 七月 31, 2013 6:02:44 下午 org.apache.catalina.startup.Catalina start
- 信息: Server startup in 898 ms
你会很惊奇的发现,使用了apache mvc之后,我们的项目启动一下会这么神速,不到一秒钟的时间(如果是ssh框架的话,最少要20秒)
接下来就去访问工程中的Control,首先看看apache mvc给我们生成的代码:
- package com.zl.control;
- import org.apache.commons.mvc.annotation.Control;
- import org.apache.commons.mvc.entity.View;
- import org.apache.commons.mvc.entity.Pager;
- import org.apache.commons.mvc.util.ParamUtil;
- import org.apache.log4j.Logger;
- import com.zl.entity.TUser;
- import com.zl.model.TUserModel;
- /**
- * 会员的Control
- * @version 1.0
- */
- @Control(name = "/user")
- public class TUserControl {
- private static final Logger log = Logger.getLogger(TUserControl.class);
- /**
- * 分页列表
- * @return
- */
- public View list() {
- TUser bean = ParamUtil.paramObject(TUser.class);
- Pager<TUser> pager = new TUserModel().pager(bean, ParamUtil.paramInt("pageNo"), ParamUtil.paramInt("pageSize"));
- log.info(pager);
- return View.get("index").setAttr("pager", pager);
- }
- }
一个普通的class,头部有一个注解,我们就是用这个注解的name来访问这个Control了,Control中每个以public修饰的方法都是可以访问的,比如:public View list() 的访问路径为:http://localhost:8080/test/user/list.htm,test为上下文路径(工程应用名称),user为Control名称,list就是方法名称了
org.apache.commons.mvc.entity.View就是mvc中的v,可以设置转发路径和属性。如果方法的返回类型不是View,那么Control不会有转发动作,而是输出这个结果,自定义数据类型将会转为json格式再输出
特别说明一下org.apache.commons.mvc.util.ParamUtil,该类的功能就是用于接收客户端请求的参数,可以接收基本数据类型,也可以接收自定义数据类型,
比如:TUser bean = ParamUtil.paramObject(TUser.class); 接收客户端请求的参数自动封装成了一个对象
接下来该去看看Model的代码了:
- package com.zl.model;
- import org.apache.commons.mvc.bean.BaseModel;
- import org.apache.commons.mvc.entity.Pager;
- import org.apache.commons.mvc.util.JdbcUtil;
- import org.apache.commons.mvc.util.SqlUtil;
- import com.zl.entity.TUser;
- /**
- * 会员的model
- * @version 1.0
- */
- public class TUserModel extends BaseModel<TUser> {
- private JdbcUtil<TUser> jdbc = new JdbcUtil<TUser>(TUser.class);
- /**
- * 重写父类该方法才能拥有父类的所有功能
- */
- @Override
- public JdbcUtil<TUser> getJdbc() {
- return jdbc;
- }
- /**
- * 分页列表
- * @param bean
- * @param pageNo
- * @param pageSize
- * @return
- */
- public Pager<TUser> pager(TUser bean, int pageNo, int pageSize) {
- SqlUtil sqlUtil = SqlUtil.get(bean, null, null);
- String sql = sqlUtil.getSelectSql() + sqlUtil.getWherePropSql() + sqlUtil.getDescSql();
- Pager<TUser> pager = jdbc.getPage(sql, sqlUtil.getArgs().toArray(), pageNo, pageSize);
- return pager;
- }
- }
Model的功能就是与数据库交互和处理业务逻辑,单表的增删改查在org.apache.commons.mvc.bean.BaseModel中实现了,只要你的Model继承该类就行,不需要再写一遍
由于是泛型操作,BaseModel并不知道当前实体是什么类型,所以必须重写public JdbcUtil getJdbc()方法
一个线程中多次更新数据库的时候,这些操作都是在同一个事务中的,apache mvc已经处理好了这个问题
org.apache.commons.mvc.util.JdbcUtil封装了对数据库操作的所有方法,要求泛型操作,它可以让你的代码变得非常简洁,基本上你要做的操作,这里都已经有了
org.apache.commons.mvc.util.SqlUtil是为了帮你处理及拼接sql语句,该类可以满足单张表所有增删改查的sql语句
org.apache.commons.mvc.entity.Pager为apache mvc中定义的一个分页实体类
接下来去看看实体类和jdbc映射文件吧:
- package com.zl.entity;
- import java.io.Serializable;
- /**
- * 会员实体类
- * @version 1.0
- */
- public class TUser implements Serializable {
- private static final long serialVersionUID = 1L;
- private long id;// BIGINT
- private String name;// VARCHAR
- private long age;// INT
- public long getId() {
- return id;
- }
- public void setId(long id) {
- this.id = id;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public long getAge() {
- return age;
- }
- public void setAge(long age) {
- this.age = age;
- }
- }
- <?xml version="1.0" encoding="UTF-8" ?>
- <sqlMap namespace="TUser">
- <!-- xmlName属性有些不准确,需要稍微修改 -->
- <resultMap class="com.zl.entity.TUser" xmlName="{table:'T_USER',primaryKey:'ID',create:'native'}">
- <result column="ID" property="id" />
- <result column="NAME" property="name" />
- <result column="AGE" property="age" />
- </resultMap>
- <sql xmlName="query">
- select * from T_USER where ID = ?
- </sql>
- </sqlMap>
这个映射文件包含根节点 namespace和两种类型的子节点:resultMap和sql
resultMap就是把表中的字段和实体的属性一一对应, xmlName="{table:'T_USER',primaryKey:'ID',create:'native'}" 这个create表示主键生成机制,apache mvc中支持三个方式:
public static final String ID_CREATE_NATIVE = "native";//自动增长的id生成机制
public static final String ID_CREATE_SEQUENCE = "sequence";//根据sequence的id生成机制
public static final String ID_CREATE_CUSTOM = "custom";//自定义的id生成机制
sql标签就是把一些复杂的sql语句(比如多表连接查询,多分支查询)存放在这里,在java代码中取出这里的sql语句,用SqlUtil.sql("TUser.query")即可
再看看拦截器的代码:
- package com.zl.control;
- import java.lang.reflect.Method;
- import org.apache.commons.mvc.entity.View;
- import org.apache.commons.mvc.thread.MvcException;
- import org.apache.commons.mvc.web.Interceptor;
- import org.apache.log4j.Logger;
- /**
- * 拦截器
- * @version 1.0
- */
- public class MyInterceptor implements Interceptor {
- private static final Logger log = Logger.getLogger(MyInterceptor.class);
- public View advance(Method method) throws MvcException {
- log.info(method);
- return null;
- }
- }
如果在拦截器中需要使用request和response,它们都在org.apache.commons.mvc.thread.HttpThread中,已经放入了线程池,
如果本次访问可以通过拦截,你就return null, 如果返回一个真实的View,apache mvc就会在拦截器里做出转发动作,目标url就进不去了,从而实现了拦截的效果
我们再看看如何在main方法中运行apache mvc:
- public static void main(String[] args) throws Exception {
- BeanContainer.init("context.xml");
- ConnectThread.set(new JdbcUtil<TUser>(TUser.class).getConnNotCommit());
- TUserModel model = new TUserModel();
- Pager<TUser> pager = model.pager(new TUser(), 1, 15);
- System.out.println(pager.getList().size());
- System.out.println(SqlUtil.sql("TUser.query"));
- ConnectThread.remove();
- }
BeanContainer.init("context.xml");就是加载配置文件
ConnectThread.set(new JdbcUtil<TUser>(TUser.class).getConnNotCommit());就是给当前线程放入一个连接
接下来就是你的操作了
ConnectThread.remove();最后就是把连接从线程中移除
很方便吧,就三步,你试试在main方法中运行或测试spring,配置文件要写一大堆,没这么方便哦
再来看看apache mvc中如何使用webservice
首先新建一个用来发布webservice的class,名字为UserWebservice,代码如下:
- package com.zl.control;
- import java.util.List;
- import org.apache.commons.mvc.annotation.Webservice;
- import org.apache.commons.mvc.annotation.WebserviceMethod;
- import com.zl.entity.TUser;
- import com.zl.model.TUserModel;
- /**
- * 发布webservice
- * @version 1.0
- */
- @Webservice(name = "user")
- public class UserWebservice {
- @WebserviceMethod
- public List<TUser> getUsers() {
- return new TUserModel().getBeans();
- }
- @WebserviceMethod
- public TUser findByName(String name) {
- List<TUser> list = new TUserModel().findByProperty("name", "=", name);
- return list.size() > 0 ? list.get(0) : null;
- }
- @WebserviceMethod
- public boolean save(TUser user) {
- user = new TUserModel().add(user);
- return user.getId() > 0l;
- }
- }
@Webservice(name = "user")表示这个class发布成webservice之后的名称
@WebserviceMethod 表示这个方法是可以在客户端(调用方)被webservice调用的
主要就是两个注解,其它没什么特别之处,这个webservice允许List,Map,自定义数据类与客户端之间的传递
class已经准备好了,接下来就是修改web.xml了,加入配置:
- <servlet>
- <servlet-name>ws</servlet-name>
- <servlet-class>org.apache.commons.mvc.webservice.WebserviceServlet</servlet-class>
- <init-param>
- <param-name>package</param-name>
- <param-value>com.zl.control</param-value>
- </init-param>
- <load-on-startup>2</load-on-startup>
- </servlet>
- <servlet-mapping>
- <servlet-name>ws</servlet-name>
- <url-pattern>/webservice</url-pattern>
- </servlet-mapping>
又是一个servlet,由此可见servlet在java web中是非常重要的
这里指定的package也就是要发布的webservice的class所在的包,你可以发布多个class,但这些class必须一个包下
接着重新启动项目,在浏览器地址栏输入:http://localhost:8088/test/webservice,按下回车键,如果在浏览器显示以下内容,表示webservice发布成功:
然后我们在另外一个工程里新建一个class,名字为TestWebservice,写入代码:
- public static void main(String[] args) {
- // 首先创建一个webservice客户端,参数依次为:webservice的url, webservice的名称, webservice的方法, 参数列表, 返回类型, 泛型的类型(不需要泛型就传入null)
- WebserviceClient client = new WebserviceClient("http://localhost:8088/test/webservice", "user", "getUsers", null, List.class, TUser.class);
- List<TUser> list = client.execute();// 调用webservice
- System.out.println(list);// 得到了服务端返回的数据
- client.setWsMethod("findByName");// 测试另外一个方法
- client.setArgs(new Object[] { "ssd" });// 给定参数
- client.setReturnType(TUser.class);// 指定返回类型
- TUser user = client.execute();// 调用webservice
- System.out.println(user);// 得到了服务端返回的数据
- user = new TUser();
- user.setName(ToolUtil.getRandom(5));// 准备数据
- client.setWsMethod("save");// 测试另外一个方法
- client.setArgs(new Object[] { user });// 给定参数
- client.setReturnType(Boolean.class);// 指定返回类型
- boolean isSave = client.execute();// 调用webservice
- System.out.println(isSave);// 得到了服务端返回的数据
- }
运行这个main方法,注意看控制台的输出,得到了自己想要的结果,表示调用webservice圆满成功!
有了apache mvc之后,webservice不再神秘,就这么几步,轻松的搞定了,并且功能十分强大,可以实现发布方与调用方之间非常复杂的数据交互。
与axis2,xfire,cxf相比,这种webservice需要支持的jar更少,发布和调用更简单,可用的数据类型更丰富。
结语:在java web框架中,我用过struts1,struts2,hibernate,spring,spring mvc,webwork,ibatis,mybatis,dwr,虽然谈不上精通,但说很熟悉了,一点也不为过,
给我的感觉是这些框架太啰嗦了,jar一大堆,运行效率让人堪忧,主要是开发起来有些慢,每个文件都要手动新建,项目启动一下需要很长时间,框架中异常全是英文的,英文水平菜一点的初学者真的很吃力,但apache mvc在这些方面已经有了很大的该进,完全符合国人的编程习惯,有了apache mvc,你的开发变得十分轻松,如果数据库有很多表,这些代码都一次性生成,真不知道要省掉多少事情,节约多少时间,而且生成的代码准确无误,远比手写的正确率高!对于新手来说,你再也不需要搭建那一堆罗里吧嗦的ssh框架了,ssh要多少配置多少jar包,apache mvc才需要几个,相比之下,apache mvc的运行效率远远高于ssh,因为apache mvc将服务器缓存机制运用的非常得体,如果有疑问,你可以做压力(或多线程)测试。