• Spring


    1. 配置文件

      src/main/resources 下创建XX.properties 文件,文件里加入数据库配置信息,base_package,jsp_path,asset_path(静态资源文件的基础路径)等信息。

    2. 加载配置

      加载配置文件中的配置信息,将jsp_path,asset_path配置项提供默认值,以配置文件中配置的信息优先。

    3. 类加载器

      来加载基础包名下的所有类,比如使用了某注解的类,或实现了某个接口的类,或继承了某父类的所有子类等。获取类加载器,加载类,获取指定包下的所有类。

      获取类加载器只需获取当前线程中的classLoader即可,加载类需要提供类名和是否初始化的标记,这里的初始化是指执行类的静态代码块。获取指定包名下的所有类,需要将包名转换为文件路径,读取class文件或jar包,获取指定的类名去加载类,将类放到Set中。

      

    public static ClassLoadder getClassLoader(){
        return Thread.currentThread().getContextClassLoader();
    }
    public static Class<?> loadClass(String className, boolean isInitialized) {
            Class<?> cls;
            try {
                cls = Class.forName(className, isInitialized, getClassLoader()); // 使用给定的类加载器加载类并返回Class对象
            } catch (ClassNotFoundException e) {
                throw new RuntimeException(e);
            }
            return cls;
    }
    public static Set<Class<?>> getClassSet(String packageName) {
            // 存放Class类的Set集合
            Set<Class<?>> classSet = new HashSet<Class<?>>();
            try {
                // getResource(String name) 查找具有给定名称的资源
                // Enumeration 接口的对象,它生成一系列元素,一次生成一个。连续调用 nextElement 方法将返回一系列的连续元素
                Enumeration<URL> urls = getClassLoader().getResources(packageName.replace(".", "/"));  
                while (urls.hasMoreElements()) { // 判断此枚举是否包含更多的元素
                    URL url = urls.nextElement(); // 返回此枚举的下一个元素
                    if (url != null) {
                        String protocol = url.getProtocol(); // 获取此 URL的协议名称
                        if(protocol.equals("file")) {
                            String packagePath = url.getPath().replaceAll("%20", ""); // getPath() 获取此 URL 的路径部分
                            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 jarEntryName = jarEntry.getName();
                                        if (jarEntryName.endsWith(".class")) {
                                            String className = jarEntryName.substring(0, jarEntryName.lastIndexOf(".")).replaceAll("/", ".");
                                            doAddClass(classSet, className);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return classSet;
    }
    /**
         * 递归遍历类或文件,判断是否是.class文件或者标准文件
         * @param classSet
         * @param packagePath
         * @param packageName
         */
        public static void addClass(Set<Class<?>> classSet, String packagePath, String packageName) {
            File[] files = new File(packagePath).listFiles(new FileFilter() {
                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()) { // 此抽象路径名表示的文件是否是一个标准文件
                    String className = fileName.substring(0, fileName.lastIndexOf("."));
                    if (StringUtil.isNotEmpty(packageName)) {
                        className = packageName + "." + className;
                    } else {
                        String subPackagePath = fileName;
                        if (StringUtil.isNotEmpty(packagePath)) {
                            subPackagePath = packagePath + "/" + subPackagePath;
                        }
                        String subPackageName = fileName;
                        if (StringUtil.isNotEmpty(packageName)) {
                            subPackageName = packageName + "." +subPackageName;
                        }
                        addClass(classSet, subPackagePath, subPackageName);
                    }
                }
            }
        }
        /**
         * 将类放入 Set<Class<?>> 中
         * @param classSet 存放Class类的Set集合
         * @param className 类名
         */
        public static void doAddClass(Set<Class<?>> classSet, String className) {
            Class<?> cls = loadClass(className, false);
            classSet.add(cls);
    }

    ClassHelper.class用于获取应用包名下所有Service类,Controller类,将带有@Controller,@Service注解的类所产生的对象,理解为由框架所管理的bean,获取应用包名下所有bean的方法,同样将bean放到Set中,先由上述代码获取所有类,然后从类中查找包含固定注解的类加入到Set中:

    /**
         * 定义Class类的Set集合(用于存放所加载的类)
         */
        private static final Set<Class<?>> CLASS_SET;
        
        static {
            String basePackage = ConfigHelper.getAppBasePackage();
            CLASS_SET = ClassUtil.getClassSet(basePackage);
        }
        
        /**
         * 获取应用包名下的所有的类
         * @return Set集合泛型为Class类
         */
        public static Set<Class<?>> getClassSet() {
            return CLASS_SET;
        }
        
        /**
         * 获取应用包名下的所有Service类
         * @return Set集合泛型为Class类
         */
        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类
         * @return Set集合泛型为Class类
         */
        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等)
         * @return Set集合泛型为Class类
         */
        public static Set<Class<?>> getBeanClassSet() {
            Set<Class<?>> beanClassSet = new HashSet<Class<?>>();
            beanClassSet.addAll(getServiceClassSet());
            beanClassSet.addAll(getControllerClassSet());
            return beanClassSet;
    }

    4. bean容器

      前边获取了所加载的类,但是无法通过类来实例化对象,因此要通过反射来对所加载的类进行实例化,bean类与bean实例的映射关系放到Map中。

    public final class ReflectionUtil {
        /**
         * 创建类的实例
         * @param cls Class类的实例
         * @return Object 类型的对象
         */
        public static Object newInstance(Class<?> cls) {
            Object instance = null;
            try {
                instance = cls.newInstance();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            return instance;
        } 
        /**
         * 调用方法
         * @param obj Object类的对象
         * @param method Method类的对象
         * @param args 可变的Object类型的参数
         * @return
         */
        public static Object invokeMethod(Object obj, Method method, Object... args) {
            Object result = null;
            method.setAccessible(true); // 如果获取到的method是private,不设置为true,就会报错
            try {
                method.invoke(obj, args);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
            return result;
        }
        /**
         * 设置成员变量的值    
         * @param obj Object类的对象
         * @param field Field类的对象
         * @param value Object类的对象
         */
        public static void setField(Object obj, Field field, Object value) {
            field.setAccessible(true); // 如果获取到的field是private,不设置为true,就会报错
            try {
                field.set(obj, value);
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }
    /**
         * 定义Bean映射(用于存放Bean类与Bean实例的映射关系)
         */
        private static final Map<Class<?>, Object> BEAN_MAP = new HashMap<Class<?>, Object>();
        
        static {
            Set<Class<?>> beanClassSet = ClassHelper.getBeanClassSet();
            for (Class<?> beanClass : beanClassSet) {
                Object obj = ReflectionUtil.newInstance(beanClass);
                BEAN_MAP.put(beanClass, obj);
            }
        }
        /**
         * 获取Bean映射
         * @return Map<Class<?>, Object> 对象
         */
        public static Map<Class<?>, Object> getBeanMap() {
            return BEAN_MAP;
        }
        /**
         * 获取Bean实例
         * @param cls Class类
         * @return
         */
        @SuppressWarnings("unchecked")
        public static <T> T getBean(Class<T> cls) { // <T>表明T是一个泛型,没有明确的类型
            if (!BEAN_MAP.containsKey(cls)) {
                throw new RuntimeException("can not get bean by class:" + cls);
            }
            return (T) BEAN_MAP.get(cls);
    }

    在bean map中存放了bean类与bean实例的映射关系,只需要调用getBean方法,传入一个bean类就能获取bean实例,这就是bean容器了。

    5. 依赖注入

      在Controller中定义了Service变量,如何在Controller的方法中调用Service成员变量的方法呢?如何实例化Service成员变量呢?通过框架自身来实例化,这种实例化过程就是IOC啦,先通过Bean Map(类和对象的映射关系),遍历这个映射关系,取出bean和bean实例,然后通过反射获取类中所有的成员变量,在循环中遍历当前成员变量,判断是否带有自定义注解,若带有自定义注解,则从bean map中取出bean实例,最后通过反射修改当前成员变量的值。

    public final class IocHelper {
        static {
            // 获取所有的 Bean 类与 Bean 实例之间的映射关系(简称 Bean Map)
            Map<Class<?>, Object> beanMap = BeanHelper.getBeanMap();
            if (CollectionUtil.isNotEmpty(beanMap)) {
                // 遍历 Bean Map
                for (Map.Entry<Class<?>, Object> beanEntry : beanMap.entrySet()) {
                    // 从 BeanMap 中获取 Bean 类与 Bean 实例
                    Class<?> beanClass = beanEntry.getKey();
                    Object beanInstance = beanEntry.getValue();
                    // 获取 Bean 类定义的所有成员变量(简称 Bean Field)
                    Field[] beanFields = beanClass.getDeclaredFields();
                    if (ArrayUtil.isNotEmpty(beanFields)) {
                        for (Field beanField : beanFields) {
                            // 判断当前 Bean Field 是否带有 Inject 注解
                            if (beanField.isAnnotationPresent(Inject.class)) {
                                // 在 Bean Map 中获取 Bean Field 对应的实例
                                Class<?> beanFieldClass = beanField.getType();
                                Object beanFieldInstance = beanMap.get(beanFieldClass);
                                if (beanFieldInstance != null) {
                                    // 通过反射初始化 BeanField 的值
                                    ReflectionUtil.setField(beanInstance, beanField, beanFieldInstance);
                                }
                            } 
                        }
                    }
                }
            }
        }
    }

    在静态块中实现相关逻辑,完成IOC容器的初始化工作,当这个类被加载的时候,就应该加载它的静态块,IOC框架所管理的bean都是单例的,由于bean map 中的对象都是事先创建好并放入这个bean容器的,所以所有的bean都是单例的。

    5. 加载Controller

      

  • 相关阅读:
    asp.net服务器控件button先执行js再执行后台的方法
    详谈ASP.NET的DataReader对象
    ASP.NET Web API的Controller是如何被创建的?
    富文本编辑器复制粘贴图片上传
    富文本编辑器的使用和图片上传,复制粘贴图片上传
    能够粘贴Word文档公式的编辑器
    可以粘贴Word公式的编辑器
    可以粘贴Word文档公式的编辑器
    可以粘贴Word文档中公式的编辑器
    openstack nova 深入
  • 原文地址:https://www.cnblogs.com/yangfei-beijing/p/7207110.html
Copyright © 2020-2023  润新知