美文网首页
从零写一个Java WEB框架(四)框架的演进

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

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

    本篇

    • 项目现在也跑起来了,而且Server和Dao层都已经封装好了,都可以专心处理各自的业务逻辑,耦合度也低了很多。
    • 但是项目还是有很多需要改进的地方。
      例如:
    1. Controller 层 的耦合度还是非常高。首先请求由doGet()和doPost()获取,然后从消息头里面获取url或者参数,然后进行逻辑判断是需要进行哪些业务。当业务多的时候,Controller层就会变得很臃肿,而且耦合度很高。


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

    框架实现

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


    image.png
    • annotation : 元注解。自定义的一些注解。
    • config:配置,主要存放定义了框架配置的Key.(根据key来获取properties文件的values)
    • Helper: 对工具类,配置类 的封装。
    • Util : 工具类。

    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类,类加载工具类。是框架的核心,实现控制反转的第一步。

    相关文章

      网友评论

          本文标题:从零写一个Java WEB框架(四)框架的演进

          本文链接:https://www.haomeiwen.com/subject/sxyveftx.html