从零写一个Java WEB框架(四)框架的演进

2018-06-16  本文已影响24人  蓝汝丶琪
  • 该系列,其实是对《架构探险》这本书的实践。本人想记录自己的学习心得所写下的。
  • 从一个简单的Servlet项目开始起步。对每一层进行优化,然后形成一个轻量级的框架。
  • 每一篇,都是针对项目的不足点进行优化的。
  • 项目已放上github

本篇

  1. Controller 层 的耦合度还是非常高。首先请求由doGet()和doPost()获取,然后从消息头里面获取url或者参数,然后进行逻辑判断是需要进行哪些业务。当业务多的时候,Controller层就会变得很臃肿,而且耦合度很高。


    image.png
  2. 层与层之间的调用还需要手动New对象,这里也可以实现"控制反转"的思想。

框架实现

项目结构
我还是决定在原来的项目上进行开发。所以前期框架的封装代码会与业务代码放在同一个包下,等开发完成,再抽取出来,我觉得这样会更加容易理解。
项目结构:


image.png

ClassUtil类
一个类的加载器,该类主要根据类名,或者包名来加载类。

public class ClassUtil {

    private static final Logger log = LoggerFactory.getLogger(ClassUtil.class);


    /*
    *  获取类加载器
    * */
    public static ClassLoader getClassLoader() {
        return Thread.currentThread().getContextClassLoader();
    }

    /*
    *  加载类
    * */

    public static Class<?> loadClass(String className, boolean isInitialized) {
        Class<?> cls=null;
        try {
            cls = Class.forName(className, isInitialized, getClassLoader());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return cls;
    }

    /*
    *  获取指定包名下的所有类
    * */

    public static Set<Class<?>> getClassSet(String packageName) {

        Set<Class<?>> classSet = new HashSet<>();

        try {
            // 获取到包名下所有类的URL
            Enumeration<URL> urls =
                    getClassLoader().getResources(packageName.replace(".", "/"));

            // 开始遍历
            while (urls.hasMoreElements()) {
                URL url=urls.nextElement();
                if (url != null) {
                    String protocol = url.getProtocol();
                    if (protocol.equals("file")) {
                        String packagePath = url.getPath().replaceAll("%20", " ");
                        addClass(classSet, packagePath, packageName);
                    } else if (protocol.equals("jar")) {
                        JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();

                        if (jarURLConnection != null) {
                            JarFile jarFile = jarURLConnection.getJarFile();
                            if (jarFile != null) {
                                Enumeration<JarEntry> jarEntries = jarFile.entries();
                                while (jarEntries.hasMoreElements()) {
                                    JarEntry jarEntry = jarEntries.nextElement();
                                    String name = jarEntry.getName();
                                    if (name.endsWith(".class")) {
                                        String className = name.substring(0, name.lastIndexOf(".")).replaceAll("/", ".");
                                        doAddClass(classSet,className);
                                    }

                                }


                            }
                        }
                    }

                }

            }
        } catch (IOException e) {
            log.error("获取类失败",e);
            e.printStackTrace();
        }
        return classSet;
    }

    private static void addClass(Set<Class<?>> classSet, String packagePath, String packageName) {
        //在该路径下获取所有文件
        //FileFilter过滤器,只要class文件和文档。
        File[] files = new File(packagePath).listFiles(new FileFilter() {
            @Override
            public boolean accept(File file) {
                return (file.isFile() && file.getName().endsWith(".class")) || file.isDirectory();
            }
        });

        //遍历每个文件
        for (File file : files) {
            String fileName = file.getName();
            if (file.isFile()) {
                //去掉.class 后缀
                String className = fileName.substring(0, fileName.lastIndexOf("."));
                //如果包名不是空的 则加上包名
                if (StringUtils.isNotEmpty(packageName)) {
                    className = packageName + "." + className;
                }

                //加载类
                doAddClass(classSet, className);
            } else {
                //这里是对file 是文件夹 进行的操作
                String subPackagePath = fileName;
                if (StringUtils.isNotEmpty(packagePath)) {
                    subPackagePath = packagePath + "/" + subPackagePath;
                }

                String subPackageName = fileName;
                if (StringUtils.isNotEmpty(packageName)) {
                    subPackageName = packageName + "." + subPackageName;
                }
                addClass(classSet,subPackagePath,subPackageName);
            }
        }

    }

    //真正的加载类
    private static void doAddClass(Set<Class<?>> classSet, String className) {
        Class<?> cls = loadClass(className, false);
        classSet.add(cls);
    }



}

创建元注解


image.png

Action 注解


/*
*  Action 方法注解
*
* */

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Action {

    /*
     *  请求类型与路径
     * */
    String value();

}

定义配置文件的key ConfigConstant
定义这些key,根据这些key去获取properties文件的values;

/*
*  定义配置文件的key
* */
public enum ConfigConstant {
    CONFIG_FILE("config.properties"),


    JDBC_DRIVER("jdbc.driver"),
    JDBC_URL("jdbc.url"),
    JDBC_USERNAME("jdbc.username"),
    JDBC_PASSWORD("jdbc.password"),

    APP_BASE_PACKAGE("base_package"),
    APP_JSP_PATH("jsp_path"),
    ASSET_PATH("asset_path"),
    ;


    private final String value;

    ConfigConstant(String value) {
        this.value = value;
    }

    public String getValue() {
        return value;
    }

}

ClassHelper 类操作的组手类
主要是将类根据注解来进行分类


/*
 *  类操作 助手类
 * */
public class ClassHelper {

    /*
     *  定义类集合(用于存放所加载的类)
     * */
    private static final Set<Class<?>> CLASS_SET;

    static {
        String basePackage = ConfigHelper.getAppBasePackage();
        CLASS_SET = ClassUtil.getClassSet(basePackage);
    }


    /*
     *  获取应用包下的所有类
     * */
    public static Set<Class<?>> getClassSet() {
        return CLASS_SET;
    }

    /*
     *
     *  获取应用包名下所有Service类
     * */
    public static Set<Class<?>> getServiceClassSet() {
        Set<Class<?>> classSet = new HashSet<Class<?>>();
        for (Class<?> cls : CLASS_SET) {
            if (cls.isAnnotationPresent(Service.class)) {
                classSet.add(cls);
            }
        }
        return classSet;
    }


    /*
     *  获取应用包下所有Controller类
     * */
    public static Set<Class<?>> getControllerClassSet() {
        Set<Class<?>> classSet = new HashSet<Class<?>>();
        for (Class<?> cls : CLASS_SET) {
            if (cls.isAnnotationPresent(Controller.class)) {
                classSet.add(cls);
            }
        }
        return classSet;
    }

    /*
     *  获取应用包下所有Bean类 (包括: Service,Controller等)
     *
     * */
    public static Set<Class<?>> getBeanClassSet() {
        Set<Class<?>> beanClassSet = new HashSet<>();
        beanClassSet.addAll(getServiceClassSet());
        beanClassSet.addAll(getControllerClassSet());
        return beanClassSet;
    }

}

ConfigHelper 类
获取配置文件的values

/*
*  配置加载类
*  加载属性文件
* */
public class ConfigHelper {

    private static final Properties CONFIG_PROPS =
            PropsUtil.loadProps(ConfigConstant.CONFIG_FILE.getValue());

    /*
    *  获取JDBC 驱动
    * */

    public static String getJdbcDriver() {
        return PropsUtil.getString(CONFIG_PROPS, ConfigConstant.JDBC_DRIVER.getValue());
    }

    /*
    *  获取JDBC URL
    * */

    public static String getJdbcUrl() {
        return PropsUtil.getString(CONFIG_PROPS, ConfigConstant.JDBC_URL.getValue());
    }

    /*
    *  获取 JDBC 用户名
    * */
    public static String getJdbcUsername() {
        return PropsUtil.getString(CONFIG_PROPS, ConfigConstant.JDBC_USERNAME.getValue());
    }

    /*
    *
    *  获取 JDBC 密码
    * */

    public static String getJdbcPassword() {
        return PropsUtil.getString(CONFIG_PROPS, ConfigConstant.JDBC_PASSWORD.getValue());
    }


    /*
    *
    *  获取基础包
    * */
    public static String getAppBasePackage() {
        return PropsUtil.getString(CONFIG_PROPS, ConfigConstant.APP_BASE_PACKAGE.getValue());
    }


    /*
    *  获取应用JSP路径
    * */

    public static String getAppJspPath() {
        return PropsUtil.getString(CONFIG_PROPS, ConfigConstant.APP_JSP_PATH.getValue(), "/WEB-INF/view/");
    }

    /*
     *  获取静态资源路径
     * */
    public static String getAppAssetPath() {
        return PropsUtil.getString(CONFIG_PROPS, ConfigConstant.ASSET_PATH.getValue(), "/asset/");
    }
}

总结

框架的基础就准备好了。
核心是ClassUtil类,类加载工具类。是框架的核心,实现控制反转的第一步。

上一篇下一篇

猜你喜欢

热点阅读