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