jfinal初探

RT学习了一下jfinal 从一个快速入门案例开始学习

180524_zkEq_2338224.png

demo来自连接:http://www.jfinal.com/

180630_rU6x_2338224.png

ALT+Z用jetty启动web项目:

http://localhost:8080/jfinal_demo_for_maven/

180757_Nu4t_2338224.png

项目启动完毕

从工程目录结构看,jfinal也是三层架构MVC 

controller,service、dao层

其中DAO层和model层无缝连接了 ,从源码可以发现:

service引用dao层的时候:

package com.demo.blog;

import com.demo.common.model.Blog;
import com.jfinal.plugin.activerecord.Page;

/**
 * 本 demo 仅表达最为粗浅的 jfinal 用法,更为有价值的实用的企业级用法
 * 详见 JFinal 俱乐部: http://jfinal.com/club
 * 
 * BlogService
 * 所有 sql 与业务逻辑写在 Service 中,不要放在 Model 中,更不
 * 要放在 Controller 中,养成好习惯,有利于大型项目的开发与维护
 */
public class BlogService {
	
	/**
	 * 所有的 dao 对象也放在 Service 中
	 */
	private static final Blog dao = new Blog().dao();
	
	public Page<Blog> paginate(int pageNumber, int pageSize) {
		return dao.paginate(pageNumber, pageSize, "select *", "from blog order by id asc");
	}
	
	public Blog findById(int id) {
		return dao.findById(id);
	}
	
	public void deleteById(int id) {
		dao.deleteById(id);
	}
}

 Blog.java

/**
 * 本 demo 仅表达最为粗浅的 jfinal 用法,更为有价值的实用的企业级用法
 * 详见 JFinal 俱乐部: http://jfinal.com/club
 * 
 * Blog model.
 * 数据库字段名建议使用驼峰命名规则,便于与 java 代码保持一致,如字段名: userId
 */
@SuppressWarnings("serial")
public class Blog extends BaseBlog<Blog> {
	
}

BaseBlog<Blog>这个泛型类再点进去看:

package com.demo.common.model.base;

import com.jfinal.plugin.activerecord.Model;
import com.jfinal.plugin.activerecord.IBean;

/**
 * Generated by JFinal, do not modify this file.
 */
@SuppressWarnings({"serial", "unchecked"})
public abstract class BaseBlog<M extends BaseBlog<M>> extends Model<M> implements IBean {

	public M setId(java.lang.Integer id) {
		set("id", id);
		return (M)this;
	}

	public java.lang.Integer getId() {
		return get("id");
	}

	public M setTitle(java.lang.String title) {
		set("title", title);
		return (M)this;
	}

	public java.lang.String getTitle() {
		return get("title");
	}

	public M setContent(java.lang.String content) {
		set("content", content);
		return (M)this;
	}

	public java.lang.String getContent() {
		return get("content");
	}

}

BaseBlog最终是继承Model类的

注意这段,物理分页列表的实现

	public Page<Blog> paginate(int pageNumber, int pageSize) {
		return dao.paginate(pageNumber, pageSize, "select *", "from blog order by id asc");
	}

传入了页码、页面大小,选择的列,排序方式 

paginat(int pageNumber, int pageSize)方法在内部实现了分页字段填充、ResultSet拆解,实体类属性注入等操作(代码不贴了,就是操作JDBC),最后如你所料,返回的是一个带Blog泛型的Page对象

值得一提,在getConnection()的时候使用了ThreadLocal<Connection>

	/**
	 * Get Connection. Support transaction if Connection in ThreadLocal
	 */
	public final Connection getConnection() throws SQLException {
		Connection conn = threadLocal.get();
		if (conn != null)
			return conn;
		return showSql ? new SqlReporter(dataSource.getConnection()).getConnection() : dataSource.getConnection();
	}

SqlReporter这里使用了代理模式(JDK动态代理),有点意思~

 在JDBC 的prepareStatement()执行前加了切面,就是输出SQL日志,代码如下:

/** <a href="http://www.cpupk.com/decompiler">Eclipse Class Decompiler</a> plugin, Copyright (c) 2017 Chen Chao. */
/**
 * Copyright (c) 2011-2017, James Zhan 瑭规尝 (jfinal@126.com).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.jfinal.plugin.activerecord;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import com.jfinal.log.Log;

/**
 * SqlReporter.
 */
public class SqlReporter implements InvocationHandler {
	
	private Connection conn;
	private static boolean logOn = false;
	private static final Log log = Log.getLog(SqlReporter.class);
	
	SqlReporter(Connection conn) {
		this.conn = conn;
	}
	
	public static void setLog(boolean on) {
		SqlReporter.logOn = on;
	}
	
	@SuppressWarnings("rawtypes")
	Connection getConnection() {
		Class clazz = conn.getClass();
		return (Connection)Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{Connection.class}, this);
	}
	
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		try {
			if (method.getName().equals("prepareStatement")) {
				String info = "Sql: " + args[0];
				if (logOn)
					log.info(info);
				else
					System.out.println(info);
			}
			return method.invoke(conn, args);
		} catch (InvocationTargetException e) {
			throw e.getTargetException();
		}
	}
}



至此,jfinal的 ORM部分就看完了,不得不佩服jfinal的代码写的真的很简介,看起来并不难

视线转移到前端,看控制器部分,也就是MVC中的C。

一个典型的controller代码如下所示:

package com.demo.blog;

import com.jfinal.aop.Before;
import com.jfinal.core.Controller;
import com.demo.common.model.Blog;

@Before(BlogInterceptor.class)
public class BlogController extends Controller {
	
	static BlogService service = new BlogService();
	
	public void index() {
		setAttr("blogPage", service.paginate(getParaToInt(0, 1), 10));
		render("blog.html");
	}
}

setAttr没什么好说的,就是servlet基础知识中的request.setAttribute(name, value);

值得看的是最后一行的render("blog.html");很容易让人想起springMVC的 return "xxx.jsp" 

没错,这就是返回视图的意思!

render()方法点进去看源码:

	/**
	 * Render with view use default type Render configured in JFinalConfig
	 */
	public void render(String view) {
		render = renderManager.getRenderFactory().getRender(view);
	}

最后返回的render是一个 abstract class ,这里用了抽象工厂模式返回的是一个具体的视图实现类

Render的实现类有这么多,look~

185948_rDwP_2338224.png

但是还有不清楚的地方,按理说一个servlet处理完业务之后不是应该有类似如下的一些转发操作吗(重定向同理)

request.getRequestDispatcher(view).forward(request, response);

 

但是我点跟进

render("index.html");

这个源码并没有看到有这种操作啊 。原因在哪里呢 ,一时还看不出来,继续跟源码吧

我先启动工程运行到render("index.html")处打上断点,然后进入 com.jfinal.core.Controller

看到Factory类的具体实现类是renderFactory,这个renderFactory是IRenderFactory的一个实现类

172610_iGd2_2338224.png

熟悉工厂设计模式的朋友肯定想到了, 先找一下给抽象工厂注入具体实现的调用处

那么你或许能找到蛛丝马迹

在getRenderFactory()处按下 ctrl+shift+G,看看能搜出什么来

172952_pbY5_2338224.png

果然搜出来了 ,就在上图这个ActionHandler这里

继续跟源码进去看

找到这个

173302_PBdG_2338224.png

点进render()方法进去瞧一瞧,应该快水落石出了

强大的eclipse告诉我,有如下类实现了抽象类Render的抽象方法render()

173428_P7ht_2338224.png

我先选个JspRender进去看下实现吧,看到如下代码。这下终于跑不掉了吧

	public void render() {
		// 在 jsp 页面使用如下指令则无需再指字符集, 否则是重复指定了,与页面指定的不一致时还会出乱码
		// <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
		// response.setContentType(contentType);
		// response.setCharacterEncoding(encoding);
		
		try {
			if (isSupportActiveRecord)
				supportActiveRecord(request);
			request.getRequestDispatcher(view).forward(request, response);
		} catch (Exception e) {
			throw new RenderException(e);
		}
	}

其中 终于出现了我们熟悉的servletAPI:

request.getRequestDispatcher(view).forward(request, response);

问题解决但是还存在疑问

好好的代码怎么就莫名其妙的和这个什么handler勾搭上了? 

怎么调用的?答案就在就这个过滤器里面 

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="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/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  <filter>
		<filter-name>jfinal</filter-name>
		<filter-class>com.jfinal.core.JFinalFilter</filter-class>
		<init-param>
			<param-name>configClass</param-name>
			<param-value>com.demo.common.DemoConfig</param-value>
		</init-param>
	</filter>
	
	<filter-mapping>
		<filter-name>jfinal</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
</web-app>

好了,以上就是jfinal源码的一点小探索,重点就是分享一下阅读源码的思路

转载于:https://my.oschina.net/u/2338224/blog/1439291

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值