ZolltyMVC框架简介
(——根据PPT文档整理)
ZolltyMVC是基于 Java 语言的极速 WEB 框架,核心设计目标是开发迅速、代码量少、学习简单、轻量级、易扩展。
它在传统MVC框架的基础上做了大量优化和创新设计,以适应软件开发的个性化需求,提升了系统性能,具有更好的可扩展性。
如同Spring、StrutsMVC、JFinal、Argo(58同城自主开发的框架)一样,ZolltyMVC也完全开源,在Github、Google Code等都能找到。
ZolltyMVC自从2013年发布第一个稳定版之后,目前已经更新到了 1.3.0.RELEASE 版本,在多个项目中运行良好。
经过长时间的运作与运行,证明ZolltyMVC是一个可靠的、高效的web框架。
详细介绍参见:
1、GitHub README.md:https://github.com/zollty-org/zollty-mvc
2、《ZolltyMVC Framework Introduction》
示例代码 a controller demo
@Controller
@CBefore({PermissionCheck.class}) // before controller method execution
public class HelloWorldController {
// 属性注入,支持按类型注入
@Inject
private DiService diService;
@RequestMapping("/lesson1/hello-jsp")
public View helloJsp() {
// Return a JSP View
return new JspView("/lesson1/hello.jsp");
}
@RequestMapping("POST:/user/{userName}") // Only allow POSTmethod
public View helloSomeOne(@URIParam("userName") String userName) {
// Get userName from URI
return new JsonView("{\"name\": \""+userName+"\"}");
}
}
ZolltyMVC功能和特点
1. Web层:它是一个通用纯Servlet请求控制转发器(RESTful URL 路由,代码简洁、效率非常高),基于原生RESTful设计,且支持各种定制化URL方案。扩展功能支持拦截器配置,支持ModelDriven(视图层VO自动封装),支持多视图模板(Jsp View、JSON View等)。
2. Bean:它是一个轻量级IOC/DI框架,可以独立应用于Standard Java,强大的支持各种形式加载Bean,强大的可扩展性和可集成性。(只支持单实例,功能比Spring的要精简很多,但是一般够用了)
3. 对于中小型项目,它完全可以替代SpringMVC、Spring+Struts,已经经过多个企业级生产项目的考验,高效稳定运行于Tomcat、Jetty、WebSphere、JBOSS服务器。
4. 小巧,代码量少。只有几百kb,比Spring要精简很多,而且常用功能一应俱全,并增加了一些nice的功能。
与Spring框架功能的对比
下图是Spring框架的主要功能:
ZolltyMVC去掉了其中的ORM、JEE的部分,并且将AOP和Web合并,只支持有限的AOP
JavaEE中的两个MVC模型
MVC Model 1
即传统的JSP+JavaBean,目前已经很少用了。
MVC Model 2
该模型的标准是:
JSP(视图View,用户界面)
+Servlet(控制器Controller,输入处理)
+JavaBean(模型Model,功能逻辑)。
由于Java EE中的JavaBean(EJB2.0)太复杂了,所以该模型最流行的一个版本是SSH,即Struts+Spring+Hibernate。
控制反转(IOC )
public class PersonService {
private PersonDao pDao = new PersonDaoImpl();
public void save(Person person){
pDao.save(person);
}
}
PersonDao 是在应用内部创建及维护的。所谓控制反转就是应用本身不负责依赖对象的创建及维护,依赖对象的创建及维护是由外部容器负责的。这样控制权就由应用转移到了外部容器,控制权的转移就是所谓反转。
依赖注入 DI ( Dependency Injection )
当我们把依赖对象交给外部容器负责创建,那么PersonServiceBean 类可以改成如下:
public class PersonService {
private PersonDao personDao ;
//通过构造器函数,让容器把创建好的依赖对象注入进来
//也可以使用setter方法进行注入
public PersonService(PersonDao personDao){
this.personDao=personDao;
}
public void save(Person person){
personDao.save(person);
}
}
所谓依赖注入就是指:在运行期,由外部容器动态地将依赖对象注入到组件中。
ZolltyMVC注入依赖对象
1、基本类型对象注入:
<bean id="stu2" class="com.tavsky.Student">
//属性setter方法注入
<property name="name" value="" />
</bean>
2、注入其他bean:
<bean id="stu2" class="com.tavsky.Student">
//属性setter方法注入
<property name="stuDao" ref="stuDao" />
</bean>
<bean id="stuDao" class="com.tavsky.StuDao" />
依赖注入-自动装配
在Java代码中使用@Autowired或@Resource注解方式进行装配,这两个注解的区别是:@Autowired 默认按类型装配,@Resource默认按名称装配,当找不到与名称匹配的bean才会按类型装配。
@Autowired
private PersonDao personDao;//用于字段上
@Autowired注解是按类型装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它required属性为false。
@Resource注解和@Autowired一样,也可以标注在字段或属性的setter方法上,但它默认按名称装配。名称可以通过@Resource的name属性指定,如果没有指定name属性,当注解标注在字段上,即默认取字段的名称作为bean名称寻找依赖对象,当注解标注在属性的setter方法上,即默认取属性名作为bean名称寻找依赖对象。
@Resource(name=“personDaoImpl”)
private PersonDao personDao;//用于字段上
ZolltyMVC中的用法如下:
@Inject(“personDaoImpl”)
private PersonDao personDao; //用于字段上
RESTful URI路由
1、URI路由
基本实现方式是:Servlet控制转发(超级Servlet)
2、RESTful 架构风格
REST,即表述性状态转移(Representational State Transfer)
要深入理解REST,需要理解REST的五个关键词:
资源(Resource)
资源的表述(Representation)
状态转移(State Transfer)
统一接口(Uniform Interface)
超文本驱动(Hypertext Driven)
资源是一种看待服务器的方式,即,将服务器看作是由很多离散的资源组成。
资源的表述可以有多种格式,例如HTML/XML/JSON/图片/视频/音频等等。
统一接口,例如HTTP/1.1协议。
7个HTTP方法:GET/POST/PUT/DELETE/PATCH/HEAD/OPTIONS
ZolltyMVC支持原生RESTful,URL风格更简洁、优雅,比如:
http://www.oschina.net/code
http://www.oschina.net/blog
http://www.oschina.net/code/tag/jquery
http://www.oschina.net/code/list?lang=java
对应ZolltyMVC的后端Controller代码:
// RESTful风格URL路由-限定为POST方式
@RequestMapping("POST:/admin/login")
public View login(@HttpParam("loginvo") LoginVO loginvo){
// ……省略
return new JspView("/admin/main.jsp"); // 返回到JSP视图层
}
URI路由与SpringMVC的区别
ZolltyMVC的控制器,是基于原生Servlet进行设计的。有类似于SpringMVC的地方,但是效率比SpringMVC要高很多!
比如,因为SpringMVC要支持多文件上传,其逻辑就比ZolltyMVC的HTTP请求处理逻辑要复杂许多了。我对比了一下两者的算法复杂度,SpringMVC的算法复杂度至少是ZolltyMVC的n*10^2倍。
而且还有一点,SpringMVC采用的是AntPathMatcher的URL路径匹配算法,ZolltyMVC采用的是自己写的一个简洁的递归算法,实测它的匹配效率是ANT的几十倍,参见org.zollty.util.match.ZolltyPathMatcher.java, 及其单元测试用例。
另外,URI路由支持一些高级的“通配”功能。
例如:
@RequestMapping("GET:/user/{userName}")
public View hello(@URIParam("userName") String uName) {
return new TextView("Hello "+ uName);
}
这个“{}”号可以通配一个URI参数,而中括号“[]”可以通配多个“/”间隔的URI
视图模型
页面视图(View)
视图可以是JSP、HTML、XML、模板、静态文件等等,甚至可以扩展自定义的View。
例如我写了个ResourceView,读取jar包中META-INF目录下的html页面)。这样做会非常方便,编程更为灵活。
支持返回的类型包括(但不限于)如下类型:
JspView
HtmlView
TextView
JsonView
视图用法举例:
@RequestMapping("/admin/login")
public View login(){
// 返回到JSP 视图层
return new JspView("/admin/main.jsp");
}
ModelDriven - 视图层参数自动封装
举个例子说明:
@RequestMapping("/lesson1/hello-catvo")
public View hello(
@HttpParam("cat") CatVO cat) {
// 自动封装一个标准Bean对象
// 返回一个页面视图
return new TextView(
"Hello " + cat.getName() + ", Age: " + cat.getAge());
}
提示: @HttpParam不仅可以接收 String 类型的参数,还可以接收对象参数哦! 比如 UserVO ,它会自动把里面的值封装
访问URL示例: http://127.0.0.1:8080/zollty-mvc-demo/lesson1/hello-catvo?name=Kitty&age=27
面向切面编程(AOP)简介
AOP(Aspect Oriented Programming),也就是面向切面编程,作为面向对象编程的一种补充,专门用于处理系统中分布于各个模块(不同方法)中的交叉关注点的问题,在 Java EE 应用中,常常通过 AOP 来处理一些具有横切性质的系统级服务,如事务管理、安全检查、缓存、对象池管理等。常见AOP实现的途经是通过 AOP 框架自动创建的AOP代理(主要分为静态代理和动态代理)
举个例子:
有这样一个需求,对每个Controller方法记录其执行耗费的时间。
@RequestMapping("POST:/admin/login")
public View login(@HttpParam("loginvo") LoginVO loginvo){
// ……省略
return new JspView("/admin/main.jsp");
}
AOP和拦截器的类型
按执行顺序分:
在Controller的方法
执行之前:MvcBefore
执行之后、渲染之前:MvcBeforeRender、MvcAfterThrow
执行之后:MvcAfter
执行前后:MvcAround
拦截器按功能分为两类: 1)通用拦截器 2)业务拦截器
拦截器按作用范围大小分为三类(在三个不同地方定义的拦截器):
1)通用拦截器:在AOP类上用 @AOPMapping({"/admin/*"}) 定义的拦截器,作用范围为所有匹配的URI对应的controller method。
2)Controller拦截器:基于特定Controller来定义Controller层面的拦截器。在Controller类上用 如@CBefore({HxxxBefore.class})标注 的拦截器。
3)Controller Method拦截器:基于某个Controller的特定Method来定义方法层面的拦截器。在Controller类的@RequestMapping方法上用 如@CBefore({HxxxBefore.class})标注 的拦截器。
基于注解的AOP拦截器之 MvcBefore
MvcBefore 在执行 Controller Method 之前执行。
业务场景:
》Step 1. 权限检查:检查session是否过期。过期则直接返回错误视图。
》Step 2. 权限检查:检查是否有跨站点脚本攻击的非法参数,如果有则返回错误视图。
》Step 3. 预处理:读取请求信息、Cookie等,并做一些解析后存入请求对象中,方便后续流程使用。
》Step 4. 日志记录:记录请求信息。 可以做成异步处理。
可以有多个MvcBefore 与Controller的方法相关联。按照先后顺序执行这些MvcBefore拦截器。(Controller层面的拦截器,其执行顺序要先于Method层面的拦截器。通用拦截器,理应最先执行,然后才执行业务拦截器。)
执行顺序:通用拦截器、Controller拦截器、ControllerMethod拦截器。在每一级别上都是按从小到大先后顺序执行。
错误处理: 如果MvcBefore执行出错,可以返回一个View,MVC框架会提交这个View,终止后续执行(后面的MvcBefore等都不会执行了)。 如果MvcBefore抛出了未知异常,框架会catch异常并打印日志,并返回错误视图,终止后续执行。
基于注解的AOP拦截器之 MvcAround
MvcAround 在 执行Controller Method的前后 执行(把Controller Method包裹在MvcAround之中执行)。
业务场景:
》Step 1. 性能监控:记录处理时间,如果超时则打印log或者发送邮件。
》Step 2. 开关:如OpenSessionInView,在进入处理器打开Session,在完成后关闭Session。
定义了MvcAround的Controller的method,不会直接执行Controller的method,而是会调用MvcAround的方法, 在MvcAround的方法中再去调用Controller的method。 如果有多个MvcAround,则递归调用, 执行顺序:通用拦截器、Controller拦截器、ControllerMethod拦截器。在每一级别上都是按从小到大先后顺序执行。
错误处理: 如果MvcAround返回View则终止程序执行。 如果抛出了未知异常,框架会catch异常,如果定义了MvcAfterThrow则交由MvcAfterThrow进行处理,否则直接返回错误视图。
依赖注入(DI)高级用法
用方法注解 注入对象:
@Inject
public void setUser(User user) {
this.user = user;
}
还比如支持复合对象:
<bean id="user2" class="com.test.User >
<property name="name" value="zollty" />
<property name="age" value="26" />
<property name="students" >
<list>
<value>jack</value>
<value>lily</value>
</list>
</property>
</bean>
还比如支持从方法中获取一个对象实例:
<bean id="dataSource" class="org.test.JndiCreator#getDataSource" >
<property name="jndiName" value="jdbc/web"/>
</bean>
使用JdbcTemplate进行数据库操作
@Service
public class PersonServiceImpl implements PersonService {
private JdbcTemplate jdbcTemplate;
@Resource
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
public Person getPerson(Integer id){
RowMapper rowMapper = new RowMapper() {
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
Person person = new Person();
person.setId(rs.getInt("id"));
person.setName(rs.getString("name"));
return person;
}
};
return (Person) jdbcTemplate.queryForObject(
"select * from person where id=?", new Object[]{id},
new int[]{java.sql.Types.INTEGER}, rowMapper);
}
}
高级选项 配置
在classpath目录下,新建一个zollty-mvc.xml,内容类似下面:
//指定class的扫描路径(用于识别注解配置),支持多个配置,支持扫描jar包
<component-scan base-package="com.zollty.mvcdemo" />
//指定视图的路径的前缀及编码,可省略该配置,默认如下
<mvc view-path="/WEB-INF/views" view-encoding="UTF-8" />
//配置 排除拦截的URI 的前缀和后缀
<no-intercept prefix="/resources/,/pages/" suffix=".html,.js,.css"/>
//绑定外部的日志处理器(可以自定义),可省略该配置,默认为org.zollty.log.ConsoleLogger
<logger class="org.zollty.log.Log4jLogger" level="TRACE" />
//使用自定义的error页面,【可省略该配置,默认ZolltyMVC自动生成
<errorPage path="/error.jsp" />
<!-- 引入其他配置文件,【支持多个配置】 -->
<import resource="classpath:external-beans.xml" />
Web部署配置
对于Servlet 2.x以下版本的Web容器,需要配置web.xml,内容如下:
<listener>
<listener-class>org.zollty.framework.mvc.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>zolltyMVC</servlet-name>
<servlet-class>org.zollty.framework.mvc.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>zolltyMVC</servlet-name>
<url-pattern>/app/*</url-pattern>
</servlet-mapping>
Thanks! Q&A