簡單實現JFinal注解配置Controller,Model

本人小菜一枚,表达能力也不是很好,哪里写了不好的地方请大神评论下。

首先写Controller对映的注解,这里我将其命名为C

这个注解现在比较简单些

package net.zz.annotation;
import java.lang.annotation.*;

/**
* Created by ZaoSheng on 2015/5/24.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
public @interface C {
String value();
}

首先写Model对映的注解,这里我将其命名为M这个注解现在也是比较简单些,呵呵!

package net.zz.annotation;

import java.lang.annotation.*;

/**
* Created by ZaoSheng on 2015/5/28.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
public @interface M
{
String value() default "";//这里默认为Model的名字小写
String id() default "id";
}

注解写好了,接下来得写一个对类的扫描器.这里我写了也比较简单,主要只是为了实现这个Demo

package net.zz.plugin;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

public class Scan {
private HttpServletRequest request;
public Scan() {
}

public Scan(HttpServletRequest request) {
this.request = request;
}

public Set<Class<?>> getClasses(String pack) {

// 第一个class类的集合
Set<Class<?>> classes = new LinkedHashSet<Class<?>>();
// 是否循环迭代
boolean recursive = true;
// 获取包的名字 并进行替换
String packageName = pack;
String packageDirName = packageName.replace('.', '/');

// 定义一个枚举的集合 并进行循环来处理这个目录下的things
Enumeration<URL> dirs;
try {
dirs = Thread.currentThread().getContextClassLoader().getResources(
packageDirName);
// 循环迭代下去
while (dirs.hasMoreElements()) {
// 获取下一个元素
URL url = dirs.nextElement();
// 得到协议的名称
String protocol = url.getProtocol();
// 如果是以文件的形式保存在服务器上
if ("file".equals(protocol)) {

// 获取包的物理路径
String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
// 以文件的方式扫描整个包下的文件 并添加到集合中
findAndAddClassesInPackageByFile(packageName, filePath, recursive, classes);
} else if ("jar".equals(protocol)) {
// 如果是jar包文件
// 定义一个JarFile

JarFile jar;
try {
// 获取jar
jar = ((JarURLConnection) url.openConnection())
.getJarFile();
// 从此jar包 得到一个枚举类
Enumeration<JarEntry> entries = jar.entries();
// 同样的进行循环迭代
while (entries.hasMoreElements()) {
// 获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件
JarEntry entry = entries.nextElement();
String name = entry.getName();
// 如果是以/开头的
if (name.charAt(0) == '/') {
// 获取后面的字符串
name = name.substring(1);
}
// 如果前半部分和定义的包名相同
if (name.startsWith(packageDirName)) {
int idx = name.lastIndexOf('/');
// 如果以"/"结尾 是一个包
if (idx != -1) {
// 获取包名 把"/"替换成"."
packageName = name.substring(0, idx)
.replace('/', '.');
}
// 如果可以迭代下去 并且是一个包
if ((idx != -1) || recursive) {
// 如果是一个.class文件 而且不是目录
if (name.endsWith(".class")
&& !entry.isDirectory()) {
// 去掉后面的".class" 获取真正的类名
String className = name.substring(
packageName.length() + 1, name
.length() - 6);
try {
// 添加到classes
classes.add(Class
.forName(packageName + '.'
+ className));
} catch (ClassNotFoundException e) {
// log
// .error("添加用户自定义视图类错误 找不到此类的.class文件");
e.printStackTrace();
}
}
}
}
}
} catch (IOException e) {
// log.error("在扫描用户定义视图时从jar包获取文件出错");
e.printStackTrace();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}

return classes;
}

public void findAndAddClassesInPackageByFile(String packageName,
String packagePath, final boolean recursive, Set<Class<?>> classes) {
// 获取此包的目录 建立一个File
File dir = new File(packagePath);
// 如果不存在或者 也不是目录就直接返回
if (!dir.exists() || !dir.isDirectory()) {
// log.warn("用户定义包名 " + packageName + " 下没有任何文件");
return;
}
// 如果存在 就获取包下的所有文件 包括目录
File[] dirfiles = dir.listFiles(new FileFilter() {
// 自定义过滤规则 如果可以循环(包含子目录) 或则是以.class结尾的文件(编译好的java类文件)
public boolean accept(File file) {
return (recursive && file.isDirectory())
|| (file.getName().endsWith(".class"));
}
});
// 循环所有文件
for (File file : dirfiles) {
// 如果是目录 则继续扫描
if (file.isDirectory()) {
findAndAddClassesInPackageByFile(packageName + "."
+ file.getName(), file.getAbsolutePath(), recursive,
classes);
} else {
// 如果是java类文件 去掉后面的.class 只留下类名
String className = file.getName().substring(0,
file.getName().length() - 6);
try {
// 添加到集合中去
//classes.add(Class.forName(packageName + '.' + className));
//经过回复同学的提醒,这里用forName有一些不好,会触发static方法,没有使用classLoader的load干净
Class<?> cls=Thread.currentThread().getContextClassLoader().loadClass(packageName + '.' + className);

classes.add(cls);
} catch (ClassNotFoundException e) {
// log.error("添加用户自定义视图类错误 找不到此类的.class文件");
e.printStackTrace();
}
}
}
}

public static void getName(String path) {
File file = new File(path);
if (file.isDirectory()) {
File[] dirFile = file.listFiles();
for (File f : dirFile) {
if (f.isDirectory())
getName(f.getAbsolutePath());
else {
if (f.getName().endsWith(".class"))
System.out.println(f.getAbsolutePath());
}
}
}
}

}

这个简单的扫描起就这样写完了,接下来就是最重要的JFinal配置的部分,实现注解处理适配的部分

编写一个抽象类JFinalConfig,这个配置类跟之前写的差不多,我们继承com.jfinal.config.JFinalConfig这个类,并实现下面这几个方法

public void configRoute(Routes me)
public void configPlugin(Plugins me)

编写三个抽象方法

/**
* Scan control
* @param controlPackage 存储需要扫描的control包
*/
protected abstract void controlSscan(List<String> controlPackage);

/**
* Scan basePackage
* @param basePackage 存储需要扫描的model包
*/
protected abstract void componentSscan(List<String> basePackage);

/**
* 设置数据源
* @param showSql 是否显示SQL, true为现实,默认为false
* @return IDataSourceProvider
*/
protected abstract IDataSourceProvider setDataSource ();

controlSscan方法主要用于设置controller的扫描,componentSscan也是一样的意思。
setDataSource 这个方法配置数据源。
接下来一个是用于开发者进行需要时覆盖的方法,用于活动记录的插件添加
/**
* 这里进行附加的活动记录的添加,
* @param arp 活动记录插件
*/
public void addActiveRecord(ActiveRecordPlugin arp){}

下面是对应的具体代码。

package net.zz.config;
import com.jfinal.config.*;
import com.jfinal.core.Controller;
import com.jfinal.plugin.IPlugin;
import com.jfinal.plugin.activerecord.ActiveRecordPlugin;
import com.jfinal.plugin.activerecord.IDataSourceProvider;
import com.jfinal.plugin.activerecord.Model;
import com.jfinal.plugin.c3p0.C3p0Plugin;
import net.zz.annotation.C;
import net.zz.annotation.M;
import net.zz.plugin.Scan;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
/**
* Created by ZaoSheng on 2015/5/24.
*/
public abstract class JFinalConfig extends com.jfinal.config.JFinalConfig{
private static final List<String> controlPackage = new ArrayList<String>();
private static final List<String> basePackage = new ArrayList<String>();
/**
* Scan control
* @param controlPackage 存储需要扫描的control包
*/
protected abstract void controlSscan(List<String> controlPackage);
/**
* Scan basePackage
* @param basePackage 存储需要扫描的model包
*/
protected abstract void componentSscan(List<String> basePackage);
/**
* 设置数据源
* @param showSql 是否显示SQL, true为现实,默认为false
* @return IDataSourceProvider
*/
protected abstract IDataSourceProvider setDataSource (boolean showSql);
/**
* controller
* @param me
*/
@Override
public void configRoute(Routes me) {
controlSscan(controlPackage);//获取需要扫描的包
//扫描器
Scan driven = new Scan();
for (String pake : controlPackage){
Set<Class<?>> clazzs = driven.getClasses(pake);
for (Class<?> clazz : clazzs) {
System.out.println(clazz.getName());
if (clazz.isAssignableFrom(com.jfinal.core.Controller.class)) {
C con = clazz.getAnnotation(C.class);
if (null != con) {
me.add(con.value(), (Class<? extends Controller>) clazz);
}
}
}
}
}
/**
* model
* @param me
*/
@Override
public void configPlugin(Plugins me) {
componentSscan(basePackage);

IDataSourceProvider iDataSourceProvider = setDataSource();
try {
me.add((IPlugin) iDataSourceProvider);
}catch (Exception e){
throw new RuntimeException("is not IPlugin type");
}
ActiveRecordPlugin arp = new ActiveRecordPlugin(iDataSourceProvider);

addActiveRecord(arp); // 加入附加的活动记录
Scan driven = new Scan();
for (String pake : controlPackage){
Set<Class<?>> clazzs = driven.getClasses(pake);
for (Class<?> clazz : clazzs) {
System.out.println(clazz.getName());
if (clazz.isAssignableFrom(com.jfinal.plugin.activerecord.Model.class)) {
M model = clazz.getAnnotation(M.class);
if (null != model) {
arp.addMapping(model.value(), model.id(), (Class<? extends Model<?>>) clazz);
}
}
}
}
me.add(arp);
}
/**
* 这里进行附加的活动记录的添加,
* @param arp 活动记录插件
*/
protected void addActiveRecord(ActiveRecordPlugin arp){
// arp.setShowSql(true);//设置是sql显示开关
}
}

接下来是简单实现Config配置的例子

package net.zz.config;
import com.jfinal.plugin.activerecord.IDataSourceProvider;
import net.mzzo.inter.JsonCrossDomain;
import net.mzzo.inter.Login;
import com.jfinal.config.Constants;
import com.jfinal.config.Handlers;
import com.jfinal.config.Interceptors;
import com.jfinal.plugin.c3p0.C3p0Plugin;
import net.zz.config.JFinalConfig;
import java.util.List;
public class ZZConfig extends JFinalConfig {
public void configConstant(Constants me) {
loadPropertyFile("config.properties");
me.setDevMode(getPropertyToBoolean("devMode", false));
}
public void configHandler(Handlers me) {

}
@Override
public void controlSscan(List<String> list) {
list.add("net.zz.controler");
list.add("net.zz.util");
}
@Override
public void componentSscan(List<String> list) {
list.add("net.mzzo.model");
}
@Override
public IDataSourceProvider setDataSource() {
showSql = true;
C3p0Plugin c3p0Plugin = new C3p0Plugin(getProperty("jdbcUrl"), getProperty("user"), getProperty("password"), getProperty("driverClass"), getPropertyToInt("maxPoolSize"), getPropertyToInt("minPoolSize"), getPropertyToInt("initialPoolSize"), getPropertyToInt("maxIdleTime"), getPropertyToInt("acquireIncrement"));
return c3p0Plugin;
}
public void configInterceptor(Interceptors me) {
me.add(new JsonCrossDomain());
me.add(new Login());
}
}

//接下来就是Controller的例子

@C("/users")
public class UsersControl extends Controller {

}

下面就是Model的例子

@M("users")
public class Users extends Model<Users> {

}

再次说下表达不是很好。
接下来的想法是将JFinal的Model部分稍微包装一下,github地址:https://github.com/cnzzs/zjf

本文出自 “7031393” 博客,请务必保留此出处http://7041393.blog.51cto.com/7031393/1680314

更多相关文章
一周排行