Java自定义框架初步(一)

来源:互联网 发布:扫描仪专用软件 京瓷 编辑:程序博客网 时间:2024/05/18 03:11

最近想自己学习写框架,看过一些资料,决定从JavaWeb开始。近来用到SpringBoot写过一些模拟接口,我对这种框架很感兴趣,几行代码就可以写出很实用的数据接口。

框架设计除了需要巧妙的思路之外,还需要准备三种技术:

1.反射;

2.自定义注解;

3.资源文件的读写。一般框架常用xml文件做配置,也可以使用properties属性文件。

我是用Idea做工具。

一、首先创建一个Java项目,勾选Web Application。目的是进行调试。成熟的框架应该是独立的。

二、导入几个包,包括gson和java6,java6会在创建servlet时提示下载,另外还要org.apache.commons.io包,用来读取post提交的请求数据,通过maven就可以下载。

三,创建文件结构如图:


四、实现三个自定义注解

@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface KFController {    String value() default "/";}

@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface RequestUrl {    String value() default "/";}


@Target(ElementType.PARAMETER)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface RequestBody {    String value() default "";}

五、实现核心类

public class KfServlet extends javax.servlet.http.HttpServlet {

    @Override    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {        //字符集处理        request.setCharacterEncoding("UTF-8");        response.setCharacterEncoding("UTF-8");        response.setContentType("text/html;charset=UTF-8");        response.setHeader("Content-type", "application/json;charset=UTF-8");        PrintWriter out = response.getWriter();        /**         * 目前没有设计GET方法请求KV方式         * 严格的处理要判断请求方式,然后根据method判断是否可以使用RequestBody注解         * 这里只进行一点初步的设计         */        //获得请求字符串        String requestBody = getRequestBodyString(request);        //获得请求路径        String requestURI = request.getRequestURI();        //去掉开头的"/"        if (requestURI.startsWith("/")) {            requestURI = requestURI.substring(1, requestURI.length());        }        //去掉末尾的"/"        if (requestURI.endsWith("/")) {            requestURI = requestURI.substring(0, requestURI.length() - 1);        }        //分割剩余的路径字符串,三段为合法:项目名+控制器+方法        String[] paths = requestURI.split("/");        String controllerName = "", methodName = "";        boolean findController = false, findMethod = false;//控制器和方法的标记        if (paths.length == 3) {//长度为3才合法            controllerName = paths[1].toLowerCase();//控制器名            methodName = paths[2].toLowerCase();//方法名            String packageName = "com.kf";//基本包名,正确做法应该是在配置文件或者全局变量中设置            //通过"一条大红龙"的工具类扫描包下的所有类            //正确做法应该是在扫描的时候用解析的控制器名去匹配            List<Class<?>> classes = ClassUtil.getClasses(packageName);            //遍历扫描的类            //获得类上面的KFController注解            for (Class clazz : classes) {                KFController kfControllerAnno = (KFController) clazz.getAnnotation(KFController.class);                //判断KFController注解是否为空                if (kfControllerAnno != null&& kfControllerAnno.value().toLowerCase().equals(controllerName)) {                    findController = true;//标记找到了控制器                    try {                        //获得Controller类下的所有方法                        Method[] methods = clazz.getMethods();                        for (Method method : methods) {                            //获取方法上的RequestUrl注解                            RequestUrl requestUrlAnno = method.getAnnotation(RequestUrl.class);                            //判断RequestUrl注解是否为空                            if (requestUrlAnno != null&&requestUrlAnno.value().toLowerCase().equals(methodName)) {                                findMethod = true;//标记找到了方法                                //获得方法的所有参数                                Parameter[] parameters = method.getParameters();                                //无参                                if (parameters == null || parameters.length == 0) {                                    //执行无参数方法,返回                                    out.write((String) method.invoke(clazz.newInstance()));                                    return;                                } else {                                    //有参                                    for (Parameter parameter : parameters) {                                        //获得参数RequestBody注解                                        Annotation requestBodyAnno = parameter.getAnnotation(RequestBody.class);                                        //判断RequestBody注解是否为空                                        if (requestBodyAnno != null) {                                            //判断如果使用了RequestBody注解,就把RequestBody赋值给它                                            //把请求body转化为对象类型                                            Object obj = new Gson().fromJson(requestBody, parameter.getType());                                            //执行带一个用TequestBody修饰过的参数的方法                                            out.write((String) method.invoke(clazz.newInstance(), obj));                                            return;                                        } else {                                            out.write("404错误 对不起,没有找到符合注解的相应的请求方法");                                        }                                    }                                }                            }                        }                    } catch (Exception e) {                        e.printStackTrace();                    } finally {                        if (!findMethod) {                            out.write("404错误 对不起,没有找到相应的请求方法");                        }                        if (out != null) {                            out.close();                        }                    }                }            }            if (!findController) {                out.write("404错误 对不起,没有找到相应的控制器");            }        } else {            out.write("404 URL不合法");        }        if (out != null) {            out.close();        }    }    public static String getRequestBodyString(HttpServletRequest request) throws IOException {        String requestBody = "";        if ("POST".equals(request.getMethod())) {            requestBody = IOUtils.toString(request.getInputStream(), "UTF-8");        } else {            String queryString = request.getQueryString();            if (queryString != null && queryString.length() != 0) {                requestBody = queryString.replace("%22", "\"");            }        }        return requestBody;    }}


六、里面用到我从http://blog.csdn.net/jdzms23/article/details/17550119找到的工具类


    /**     * 取得某个接口下所有实现这个接口的类     */    public static List<Class> getAllClassByInterface(Class c) {        List<Class> returnClassList = null;        if (c.isInterface()) {            // 获取当前的包名            String packageName = c.getPackage().getName();            // 获取当前包下以及子包下所以的类            List<Class<?>> allClass = getClasses(packageName);            if (allClass != null) {                returnClassList = new ArrayList<Class>();                for (Class classes : allClass) {                    // 判断是否是同一个接口                    if (c.isAssignableFrom(classes)) {                        // 本身不加入进去                        if (!c.equals(classes)) {                            returnClassList.add(classes);                        }                    }                }            }        }        return returnClassList;    }    /*    * 取得某一类所在包的所有类名 不含迭代    */    public static String[] getPackageAllClassName(String classLocation, String packageName) {        //将packageName分解        String[] packagePathSplit = packageName.split("[.]");        String realClassLocation = classLocation;        int packageLength = packagePathSplit.length;        for (int i = 0; i < packageLength; i++) {            realClassLocation = realClassLocation + File.separator + packagePathSplit[i];        }        File packeageDir = new File(realClassLocation);        if (packeageDir.isDirectory()) {            String[] allClassName = packeageDir.list();            return allClassName;        }        return null;    }    /**     * 从包package中获取所有的Class     *     * @param packageName     * @return     */    public static List<Class<?>> getClasses(String packageName) {        //第一个class类的集合        List<Class<?>> classes = new ArrayList<Class<?>>();        //是否循环迭代        boolean recursive = true;        //获取包的名字 并进行替换        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) {                                            e.printStackTrace();                                        }                                    }                                }                            }                        }                    } catch (IOException e) {                        e.printStackTrace();                    }                }            }        } catch (IOException e) {            e.printStackTrace();        }        return classes;    }    /**     * 以文件的形式来获取包下的所有Class     *     * @param packageName     * @param packagePath     * @param recursive     * @param classes     */    public static void findAndAddClassesInPackageByFile(String packageName, String packagePath, final boolean recursive, List<Class<?>> classes) {        //获取此包的目录 建立一个File        File dir = new File(packagePath);        //如果不存在或者 也不是目录就直接返回        if (!dir.exists() || !dir.isDirectory()) {            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));                } catch (ClassNotFoundException e) {                    e.printStackTrace();                }            }        }    }}

七、创建两个model,一个是用于打包数据的UserModel,一个是用于解析请求数据的UserRequst

public class UserModel {    public long id;    public String name;    public boolean gender;    public int age;    public UserModel(long id, String name, boolean gender, int age) {        this.id = id;        this.name = name;        this.gender = gender;        this.age = age;    }}

public class UserRequest {    public long id;    public String name;    public String passWord;}

八、创建两个controller,目的是进行测试区别

@KFController("test")public class TestController {    @RequestUrl("test")    public String test() {        return "我就是test宝哥";    }    @RequestUrl("gettest")    public String gettest() {        UserModel userModel = new UserModel(1, "Test Chen", true, 38);        return new Gson().toJson(userModel);    }    @RequestUrl("bodyTest")    public String bodyTest(@RequestBody String body) {        return body;    }}

@KFController("user")public class UserController {    @RequestUrl("getUserInfo")    public String getUserInfo() {        return "我就是宝哥";    }    @RequestUrl("getUser")    public String getUser() {        return new Gson().toJson(new UserModel(1, "Kaly Chen", true, 38));    }    @RequestUrl("getUserBody")    public String getUserBody(@RequestBody UserRequest body) {        return new Gson().toJson(new UserModel(body.id, body.name, true, 38));    }}

九、还有web.xml需要配置,让所有的请求都从KfServlet经过,然后分发

    <servlet>        <servlet-name>KfServlet</servlet-name>        <servlet-class>com.kf.core.KfServlet</servlet-class>        <load-on-startup>1</load-on-startup>    </servlet>    <servlet-mapping>        <servlet-name>KfServlet</servlet-name>        <url-pattern>/</url-pattern>    </servlet-mapping>

十、测试

1.浏览器输入http://192.168.1.101:8080/kfdemo/user/getuserbody?{%22id%22:19,%22name%22:%22chenfabao%22,%22passWord%22:%22123456%22}


2.PostMan测试



下一次,我们考虑去操作数据库,并实现ORM