《架构探险》第三章 项目核心实现
1 package cn.m4.chapter3.controller; 2 3 4 5 import cn.ease4j.annotation.Action; 6 import cn.ease4j.annotation.Controller; 7 import cn.ease4j.annotation.Inject; 8 import cn.ease4j.bean.Data; 9 import cn.ease4j.bean.Param; 10 import cn.ease4j.bean.View; 11 import cn.m4.chapter3.entity.Customer; 12 import cn.m4.chapter3.service.MyService; 13 14 import java.util.List; 15 16 @Controller 17 public class MyController { 18 19 @Inject 20 private MyService myService; 21 22 @Action("get:/customer_create") 23 public Data getCustomerList(Param param){ 24 List<Customer> customerList = myService.getCustomerById(); 25 return new Data(customerList); 26 } 27 }
2.2 框架核心的具体搭建
<dependencies> <!--01 Servlet依赖--> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <!--02 JSP依赖--> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>2.2</version> <scope>provided</scope> </dependency> <!--03 JSTL依赖--> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> <scope>runtime</scope> </dependency> <!--junit--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <!--04 SLF4J--> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.7</version> </dependency> <!--05 mysql--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.35</version> <scope>runtime</scope> </dependency> <!--06 Json--> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.4.4</version> </dependency> <!--添加两个Apache Commons依赖,用于提供常用工具类--> <!--07 Apach Commons Lang--> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.3.2</version> </dependency> <!--08 Apache Commons Collections--> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-collections4</artifactId> <version>4.0</version> </dependency> <!--09 Apache Commons DBUtils--> <dependency> <groupId>commons-dbutils</groupId> <artifactId>commons-dbutils</artifactId> <version>1.6</version> </dependency> <!--10 数据库连接池--> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-dbcp2</artifactId> <version>2.0.1</version> </dependency> <!-- cglib --> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>2.2.2</version> </dependency>
2.2.2 第二步、提供获取并处理框架配置的工具类:ConfigConstant、ConfigHelper。
package cn.ease4j.constant; /** * 存放配置常量名称 * @author Zephyr */ public interface ConfigConstant { //配置文件名常量 String CONFIG_FILE = "ease.properties"; //数据库连接常量 String JDBC_DRIVER = "ease.framework.jdbc.driver"; String JDBC_URL = "ease.framework.jdbc.url"; String JDBC_USERNAME = "ease.framework.jdbc.username"; String JDBC_PASSWORD = "ease.framework.jdbc.password"; //各文件资源路径 String APP_BASE_PACKAGE = "ease.framework.app.base_package"; String APP_JSP_PATH = "ease.framework.app.jsp_path"; String APP_ASSET_PATH = "ease.framework.app.asset_path"; }
/** * 提取配置的助手类 * @author Zephyr */ public final class ConfigHelper { private static final Properties CONFIG_PROPS = PropUtil.loadProps(ConfigConstant.CONFIG_FILE); /** * 获取jdbc驱动 * @return */ public static String getJdbcDriver(){ return PropUtil.getString(CONFIG_PROPS,ConfigConstant.JDBC_DRIVER); } /** * 获取jdbc URL * @return */ public static String getJdbcUrl(){ return PropUtil.getString(CONFIG_PROPS,ConfigConstant.JDBC_URL); } /** * 获取jdbc用户名 * @return */ public static String getJdbcUsername(){ return PropUtil.getString(CONFIG_PROPS,ConfigConstant.JDBC_USERNAME); } /** * 获取jdbc密码 * @return */ public static String getJdbcPassword(){ return PropUtil.getString(CONFIG_PROPS,ConfigConstant.JDBC_PASSWORD); } /** * 获取应用的基本包路径 * @return */ public static String getAppBasePackage(){ return PropUtil.getString(CONFIG_PROPS,ConfigConstant.APP_BASE_PACKAGE); } /** * 获取应用jsp路径(含默认值) * @return */ public static String getAppJspPath(){ return PropUtil.getString(CONFIG_PROPS,ConfigConstant.APP_JSP_PATH,"/WEB-INF/view/"); } /** * 获取静态资源的路径(含默认值) * @return */ public static String getAppAssetPath(){ return PropUtil.getString(CONFIG_PROPS,ConfigConstant.APP_ASSET_PATH,"/asset/"); } }
/** * 属性文件工具类 * @author Zephyr */ public class PropUtil { //获取日志对象 public static final Logger LOGGER= LoggerFactory.getLogger(PropUtil.class); /** * 加载配置文件 * @param fileName * @return */ public static Properties loadProps(String fileName){ Properties props=null; InputStream is=null; try { is=Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName); if(null==is){ throw new FileNotFoundException(fileName+" is not found"); } props=new Properties(); props.load(is); }catch (IOException e){ LOGGER.error("load properties file failure",e); }finally { if(is!=null){ try { is.close(); } catch (IOException e) { LOGGER.error("close inputstream failure",e); } } } return props; } /** * 获取字符型属性,指定默认值 * @param prop * @param key * @param defaultValue * @return */ public static String getString(Properties prop, String key,String defaultValue){ String value=defaultValue; if(prop.containsKey(key)){ value=prop.getProperty(key); } return value; } /** * 获取字符型属性,默认值为空 * @param prop * @param key * @return */ public static String getString(Properties prop, String key){ return getString(prop,key,""); } /** * 获取整型属性,指定默认值 * @param prop * @param key * @param defaultValue * @return */ public static Integer getInt(Properties prop,String key,Integer defaultValue){ Integer value = defaultValue; if(prop.containsKey(key)){ value=CastUtil.castInt(prop.getProperty(key)); } return value; } /** * 获取整型属性,默认值0 * @param prop * @param key * @return */ public static Integer getInt(Properties prop,String key){ return getInt(prop,key,0); } /** * 获取布尔型属性,指定默认值 * @param prop * @param key * @param defaultValue * @return */ public static Boolean getBoolean(Properties prop,String key,Boolean defaultValue){ Boolean value=defaultValue; if(prop.containsKey(key)){ value=CastUtil.castBoolean(prop.getProperty(key)); } return value; } /** * 获取布尔型属性,默认值false * @param prop * @param key * @return */ public static Boolean getBoolean(Properties prop,String key){ return getBoolean(prop,key,false); } }
2.2.3 第三步、提供让框架具备类加载功能的工具类以及相关注解:ClassUtil、ClassHelper、@Controller、@Service、@Action、@Inject
1、ClassUtil中包含三个方法:getClassLoader、loadClass、getClassSet
getClassLoader是获取当前的类加载器,其实就一行代码:Thread.currentThread().getContextClassLoader();
loadClass用于加载类,核心代码也只有一行,用的是Class.forName,但用的是另一种不常见的形式,public static Class<?> forName(String name,boolean initialize,ClassLoader loader);三个参数的含义分别是class名、是否初始化类(后面的使用中填false)、类加载器(直接用方法类中的getClassLoader());
getClassSet方法体比较长。输入参数是String packageName(类似于xx.xx.xx的形式),返回参数是Set<Class<?>>,实现的功能就是根据包名,递归获取本包以及子包中的class文件的名称以及jar包中的class文件的名称,使用工具类中的loadClass方法加载并放入Set集合并返回。并且用到了2个私有方法:addClass、doAddClass。addClass用于递归获取当前包或子包中的class文件,并调用doAddClass方法;而doAddClass方法则用于加载类(调用loadClass方法)并将生成的类放入Set。
/** * 类操作的工具类 * @author Zephyr Lai */ public class ClassUtil { private static final Logger LOGGER = LoggerFactory.getLogger(ClassUtil.class); /** * 获取类加载器 * @return */ public static ClassLoader getClassLoader(){ return Thread.currentThread().getContextClassLoader(); } /** * 加载类(返回class对象) * @param className * @param ifInitialize 通常赋值为false * @return */ public static Class<?> loadClass(String className,boolean ifInitialize){ Class<?> clz = null; try { clz = Class.forName(className,ifInitialize,getClassLoader()); } catch (ClassNotFoundException e) { LOGGER.error("load class failure",e); throw new RuntimeException(e); } return clz; } /** * 获取指定包下的所有类 * @param packageName * @return */ public static Set<Class<?>> getClassSet(String packageName){ //用于存储类名的集合 Set<Class<?>> classSet = new HashSet<Class<?>>(); try { String path=packageName.replace(".", "/"); ClassLoader classLoader = getClassLoader(); Enumeration<URL> urls = getClassLoader().getResources(path); if(!urls.hasMoreElements()){ System.out.println("no element !"); } while(urls.hasMoreElements()){ URL url = urls.nextElement(); //非空校验 if(null != url){ String protocol = url.getProtocol(); if(protocol.equals("file")){ String packagePath = url.getPath().replace("%20", " "); addClass(classSet,packagePath,packageName); } else if(protocol.equals("jar")){ JarURLConnection jarURLConnection = (JarURLConnection)url.openConnection(); if(null !=jarURLConnection){ JarFile jarFile = jarURLConnection.getJarFile(); if(null != jarFile){ Enumeration<JarEntry> jarEntries = jarFile.entries(); while(jarEntries.hasMoreElements()){ JarEntry jarEntry = jarEntries.nextElement(); String name = jarEntry.getName(); if(name.endsWith(".class")){ //去掉.class后缀并且将所有的"/"替换为"." String className = name.substring(0,name.lastIndexOf(".")).replaceAll("/","."); doAddClass(classSet,className); } } } } } } } } catch (IOException e) { e.printStackTrace(); LOGGER.error("get class set failure",e); } return classSet; } /** * 递归获取包以及子包下的所有class文件,获取class名并加载 * @param classSet * @param packagePath * @param packageName */ private 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; } doAddClass(classSet,className); } else{ //子目录 String subPackagePath = fileName; if(StringUtil.isNotEmpty(packagePath)){ subPackagePath = packagePath + "/" + subPackagePath; } //子包 String subPackageName = fileName; if(StringUtil.isNotEmpty(packageName)){ subPackageName=packageName+"."+subPackageName; } //递归,在子目录、子包中查找class文件 addClass(classSet,subPackagePath,subPackageName); } } } /** * 仅生成class对象,不初始化实例 * @param classSet * @param className */ private static void doAddClass(Set<Class<?>> classSet, String className) { System.out.println(Arrays.toString(classSet.toArray())); Class<?> clz = loadClass(className, false); classSet.add(clz); } }
2、4个框架中需要提供的注解:@Controller、@Service、@Action、@Inject,目录结构如图

/** * 控制器注解 * @author Zephyr Lai */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Controller { }
/** * 服务类注解 * @author Zephyr Lai */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Service { }
/** * 依赖注入注解 * @author Zephyr Lai */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Inject { }
/** * Action方法注解 * 类似于Spring中的@RequestMapping * @author Zephyr Lai */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Action { /** * 请求类型与路径 * 类似于Spring中的@RequestMapping(value=" ") * @return */ String value(); }
3、ClassHelper包括1个静态代码块以及3个公有的静态方法。
静态代码块用于获取应用中所有的class(Set<Class<?>>形式)
getClassSet:直接返回静态代码块中的Set
getServiceClassSet:提取代码块中被@Service注解的class,放入新的Set并返回
getControllerClassSet:提取代码块中被@Controller注解的class,放入新的Set并返回
getBeanClassSet:提取代码块中被@Controller或者@Service注解的class,放入新的Set并返回(官话:获取被框架管理的Class(即:获取Bean类))
2.2.4 第四步、提供Bean容器的相关工具类(核心功能是:实例化对象):ReflectionUtil、BeanHelper
1、ReflectionUtil包含三个方法:方别用于类、方法、域。
newInstance:核心代码就一行 class.newInstance();
invokeMethod:核心代码两行 method.setAccessible(true);//设置为方法可以被反射机制调用
methos.invoke(obj,args);//反射执行方法
setField:跟工具类中的invokeMethod方法很类似,核心代码两行 field.setAccessible(true); //设置为域可以被反射机制调用
field.set(obj,value); //向obj这个对象中的field设置新的value
2、BeanHelper包含1个private static final 的 Map<Class<?>,Object>、1个静态代码块、2个公有的静态方法。
静态代码块的作用是调用ClassHelper中的getBeanClassSet();获取又框架管理的bean类,然后遍历调用ReflectionUtil中的newInstance方法初始化实例并放入map
getBeanMap:获取完整的map
getBean:根据键在map中获取对应的bean实例
package cn.ease4j.util; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /** * 反射工具类 * @author Zephyr Lai */ public class ReflectionUtil { private static final Logger LOGGER = LoggerFactory.getLogger(ReflectionUtil.class); /** * 创建实例 * @param clz * @return */ public static <T> T getInstance(Class<T> clz){ T instance= null; try { instance= clz.newInstance(); } catch (Exception e) { LOGGER.error("get instance error",e); throw new RuntimeException(e); } return instance; } /** * 调用方法 * @param obj * @param method * @param params * @return */ public static Object invokeMethod(Object obj, Method method,Object... params){ Object result ; try { //方法设置为可以被反射机制调用 method.setAccessible(true); result=method.invoke(obj,params); } catch (Exception e) { LOGGER.error("method invoke failure",e); throw new RuntimeException(e); } return result; } /** * 设置成员变量的值 * (将在IOCHelper中使用) * @param obj * @param field * @param value */ public static void setField(Object obj, Field field,Object value){ //todo try { //方法设置为可以被反射机制调用 field.setAccessible(true); //为obj这个对象中的field的值设置为value field.set(obj,value); } catch (Exception e) { LOGGER.error("set field failure",e); throw new RuntimeException(e); } } }
1 package cn.ease4j.helper; 2 3 import cn.ease4j.util.ClassUtil; 4 import cn.ease4j.util.ReflectionUtil; 5 import com.fasterxml.jackson.databind.util.BeanUtil; 6 import com.sun.corba.se.impl.ior.OldJIDLObjectKeyTemplate; 7 import org.slf4j.Logger; 8 import org.slf4j.LoggerFactory; 9 10 import java.util.HashMap; 11 import java.util.Map; 12 import java.util.Set; 13 14 /** 15 * 定义Bean映射,用于存放Bean类与Bean实例的映射关系 16 * @author Zephyr Lai 17 */ 18 public class BeanHelper { 19 public static final Logger LOGGER = LoggerFactory.getLogger(BeanHelper.class); 20 /** 21 * 用于存放class--bean的键值对 22 */ 23 public static final Map<Class<?>,Object> BEAN_MAP= new HashMap<Class<?>,Object>(); 24 25 /** 26 * 静态代码块用于初始化BEAN_MAP 27 * clz1 -- obj1 28 * clz2 -- obj2 29 */ 30 static{ 31 //获取由框架管理的bean 32 Set<Class<?>> beanSet = ClassHelper.getBeanClassSet(); 33 for (Class<?> beanClass : beanSet) { 34 Object obj = ReflectionUtil.getInstance(beanClass); 35 BEAN_MAP.put(beanClass,obj); 36 } 37 } 38 39 /** 40 * 获取整个Bean映射 41 * @return 42 */ 43 public static Map<Class<?>,Object> getBeanMap(){ 44 return BEAN_MAP; 45 } 46 47 /** 48 * 根据class名称从映射中获取Bean实例 49 * @param clz 50 * @param <T> 51 * @return 52 */ 53 public static <T> T getBean(Class<T> clz){ 54 if(!BEAN_MAP.containsKey(clz)){ 55 throw new RuntimeException("cannot get bean by class:"+clz.getName()); 56 } 57 for (Map.Entry<Class<?>, Object> entry : BEAN_MAP.entrySet()) { 58 Class<?> key = entry.getKey(); 59 Object value = entry.getValue(); 60 System.out.println(1); 61 } 62 return (T) BEAN_MAP.get(clz); 63 } 64 }
2.2.5 第五步、实现框架的依赖注入(DI)功能(人话:实例化Bean中带@Inject注解的域,并设置到Bean实例中),相关工具类:IocHelper
1、IocHelper的实现逻辑:通过BeanHelper获取所有的bean Map(存放的是class<-->obj),通过反射获取bean实例中所有的成员变量,然后遍历这些成员变量,查看是否带有@Inject注解,如果带有@Inject注解,则将成员变量实例化,并设置到Bean Map的Bean实例中
package cn.ease4j.helper; import cn.ease4j.annotation.Inject; import cn.ease4j.util.ArrayUtil; import cn.ease4j.util.CollectionUtil; import cn.ease4j.util.ReflectionUtil; import java.lang.reflect.Field; import java.util.Map; /** * 依赖注入助手类 * @author Zephyr Lai */ public final class IocHelper { static { //获取整个Bean映射 Map<Class<?>, Object> beanMap = BeanHelper.getBeanMap(); if(CollectionUtil.isNotEmpty(beanMap)){ //Map遍历 for (Map.Entry<Class<?>, Object> beanEntity : beanMap.entrySet()) { Class<?> beanClass = beanEntity.getKey(); Object beanInstance = beanEntity.getValue(); //获取class对象的所有域(属性) Field[] declaredFields = beanClass.getDeclaredFields(); if(ArrayUtil.isNotEmpty(declaredFields)){ for (Field field : declaredFields) { //匹配@Inject注解 if(field.isAnnotationPresent(Inject.class)){ //获取域的class对象 Class<?> type = field.getType(); //生成域的实例 Object fieldInstance = ReflectionUtil.getInstance(type); if(null != fieldInstance){ //将域实例设置进bean实例 ReflectionUtil.setField(beanInstance,field,fieldInstance); } } } } } } } }
package cn.ease4j.util; import org.apache.commons.lang3.ArrayUtils; /** * 数组工具类 * @author Zephyr Lai */ public class ArrayUtil { public static Boolean isNotEmpty(Object[] array){ return ArrayUtils.isNotEmpty(array); } public static Boolean isEmpty(Object[] array){ return ArrayUtils.isEmpty(array); } }
2.2.6 第六步、提供用于加载Controller的工具y以及相关类:Request、Handler、ControllerHelper
1、Request是请求类,封装了请求方法(String)、请求路径(String)
2、Handler是处理类,用于封装Action信息,包括controllerClass(Class<?>)、actionMethod(Method)
一张图理清Request与Handler的关系:

3、ControllerHelper包含一个private static final Map<Request,Hander>、1个静态代码块、1个公有的静态方法getHander
map中存放请求与处理器之间的映射关系
静态代码块则负责从现有的Controller Bean中封装Request与Handler的映射关系,封装到Map中并返回
getHandler 则根据请求方法和请求路径从map中提取对应的处理器(Handler)
1 package cn.ease4j.helper; 2 3 import cn.ease4j.annotation.Action; 4 import cn.ease4j.annotation.Inject; 5 import cn.ease4j.bean.Handler; 6 import cn.ease4j.bean.Request; 7 import cn.ease4j.util.ArrayUtil; 8 import cn.ease4j.util.CollectionUtil; 9 import cn.ease4j.util.ReflectionUtil; 10 11 import java.lang.reflect.Field; 12 import java.lang.reflect.Method; 13 import java.util.HashMap; 14 import java.util.Map; 15 import java.util.Set; 16 17 /** 18 * 控制器助手类 19 * @author Zephyr Lai 20 */ 21 public final class ControllerHelper { 22 //请求--处理器 映射 23 public static final Map<Request,Handler> ACTION_MAP = new HashMap<Request, Handler>(); 24 25 static{ 26 //获取整个Bean映射 27 Map<Class<?>, Object> beanMap = BeanHelper.getBeanMap(); 28 if(CollectionUtil.isNotEmpty(beanMap)){ 29 //Map遍历 30 for (Map.Entry<Class<?>, Object> beanEntity : beanMap.entrySet()) { 31 Class<?> beanClass = beanEntity.getKey(); 32 Object beanInstance = beanEntity.getValue(); 33 //获取class对象的所有域(属性) 34 Field[] declaredFields = beanClass.getDeclaredFields(); 35 if(ArrayUtil.isNotEmpty(declaredFields)){ 36 for (Field field : declaredFields) { 37 //匹配@Inject注解 38 if(field.isAnnotationPresent(Inject.class)){ 39 //获取域的class对象 40 Class<?> type = field.getType(); 41 //生成域的实例 42 Object fieldInstance = ReflectionUtil.getInstance(type); 43 if(null != fieldInstance){ 44 //将域实例设置进bean实例 45 ReflectionUtil.setField(beanInstance,field,fieldInstance); 46 } 47 } 48 } 49 } 50 } 51 } 52 53 54 //获取所有的Controller Bean 55 Set<Class<?>> controllerSet = ClassHelper.getControllerClassSet(); 56 if(null != controllerSet && controllerSet.size()>0){ 57 for (Class<?> c : controllerSet) { 58 //获取class对象中的所有方法 59 Method[] methods = c.getDeclaredMethods(); 60 if(null != methods && methods.length>0){ 61 for (Method m : methods) { 62 //匹配@Action注解 63 if(m.isAnnotationPresent(Action.class)){ 64 Action action = m.getAnnotation(Action.class); 65 String mapping = action.value(); 66 //正则匹配 67 if(mapping.matches("\\w+:/\\w*")){ 68 String[] arrays = mapping.split(":"); 69 String requestMethod = arrays[0]; 70 String requestPath = arrays[1]; 71 Request request = new Request(requestMethod, requestPath); 72 Handler handler = new Handler(c, m); 73 //封装到 请求--处理器的映射 74 ACTION_MAP.put(request,handler); 75 } 76 } 77 } 78 } 79 } 80 } 81 } 82 83 /** 84 * 根据请求方法(GET、POST),请求路径(../../..), 85 * 获取某个Hander(包括controller class以及对应的method) 86 * @param requestMethod 87 * @param requestPath 88 * @return 89 */ 90 public static Handler getHandler(String requestMethod,String requestPath){ 91 for (Map.Entry<Request, Handler> entity : ACTION_MAP.entrySet()) { 92 Handler value = entity.getValue(); 93 Request request = entity.getKey(); 94 System.out.println(value.getActionMethod()+"------------"+value.getControllerClass()); 95 } 96 return ACTION_MAP.get(new Request(requestMethod,requestPath)); 97 } 98 }
2.2.7 第七步、提供初始化框架的工具类:LoaderHelper(加载ClassHelper、BeanHelper、IocHelper、ControllerHelper)
没啥好说的,就遍历一下,调用4次ClassUtil#loadClass
1 package cn.ease4j; 2 3 import cn.ease4j.annotation.Controller; 4 import cn.ease4j.helper.BeanHelper; 5 import cn.ease4j.helper.ClassHelper; 6 import cn.ease4j.helper.ControllerHelper; 7 import cn.ease4j.helper.IocHelper; 8 import cn.ease4j.util.ClassUtil; 9 10 /** 11 * 助手加载器 12 * @author Zephyr Lai 13 */ 14 public class HelperLoader { 15 /** 16 * 依次加载各个助手(Helper) 17 */ 18 public static void init(){ 19 Class[] classList={ 20 ClassHelper.class, 21 BeanHelper.class, 22 IocHelper.class, 23 ControllerHelper.class 24 }; 25 for (Class clz : classList) { 26 ClassUtil.loadClass(clz.getName(),false); 27 } 28 } 29 }
2.2.8 第八步、提供框架中请求转发器DispatcherServlet(人话:就是写个框架中的Servlet)
(未完。。。)

浙公网安备 33010602011771号