1.在 core 包中定义 ClassScanner,并定义 scanClass 方法,目的是给定一个包路径,扫描出该包下面的所有类
主要的流程是:
@1.拿到改包路径下的所有资源,遍历
@2.如果是 jar 包,就继续遍历这个 jar 包,把 jar 包的需要的 class 扫描进来,也就是以我们包名的开头的.class 文件
@3.jar 包中的 jarentryname 的格式是 cn/chenforcode/xxx.class,所以必须转换成包名,然后才可以用类加载器进行扫描。
3.创建 handlerManager 来管理 mappingHandler
4.创建 resolveMappingHandler 方法,对给出的类列表进行遍历
/**
* @Author <a href="http://www.chenforcode.cn">PKUCoder</a>
* @Date 2019/11/6 4:50 下午
* @Param [classList]
* @Return void
* @Description 对给出的一个类列表遍历,解析其中的方法
**/
public static void resolveMappingHandler(List<Class<?>> classList) {
for (Class<?> cls : classList) {
if (cls.isAnnotationPresent(Controller.class)) {
parseHandlerFromController(cls);
}
}
}
5.创建 parseHandlerFromController 方法,对某个类中的方法进行解析
/**
* @Author <a href="http://www.chenforcode.cn">PKUCoder</a>
* @Date 2019/11/6 5:17 下午
* @Param [cls]
* @Return void
* @Description 对某个类中的方法进行解析
**/
private static void parseHandlerFromController(Class<?> cls) {
Method[] methods = cls.getDeclaredMethods();
for (Method method : methods) {
//如果没有被requestMapping注解修饰就不用解析了
if (!method.isAnnotationPresent(RequestMapping.class)) {
continue;
}
//得到mapping需要的参数
//获取uri
String uri = method.getDeclaredAnnotation(RequestMapping.class).value();
//获取参数
List<String> paramNameList = new ArrayList<>();
//遍历参数,如果带有了requestParam注解的话,就进行解析
for (Parameter parameter: method.getParameters()) {
if (parameter.isAnnotationPresent(RequsetParam.class)) {
//得到所有的参数名称,加入一个list
paramNameList.add(parameter.getDeclaredAnnotation(RequsetParam.class).value());
}
}
//将参数list转化成参数数组
String[] args = paramNameList.toArray(new String[paramNameList.size()]);
//构造一个mappingHandler,注意这个handler是方法级别的,每一个方法都对应着一个handler
MappingHandler mappingHandler = new MappingHandler(uri, method, cls, args);
//加入到handler的集合中
HandlerManager.mappingHandlerList.add(mappingHandler);
}
}
6.接下来是要在 dispatcherservlet 中使用 handler
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
for (MappingHandler handler: HandlerManager.mappingHandlerList) {
try {
if (handler.handle(req, res)) {
return;
}
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
7.编写 handler 的 handle 方法,即开始处理请求
public boolean handle(ServletRequest req, ServletResponse res) throws IllegalAccessException, InstantiationException, InvocationTargetException, IOException {
String requestURI = ((HttpServletRequest)req).getRequestURI();
//如果uri不相等,说明这个handler是不能处理的
if (!uri.equals(requestURI)) {
return false;
}
//开始处理请求
Object[] parameters = new Object[args.length];
//初始化参数
for (int i = 0; i < args.length; i++) {
parameters[i] = args[i];
}
Object ctl = controller.newInstance();
//利用反射调用controller,这个response就相当于controller执行完返回的结果
Object response = method.invoke(ctl, parameters);
res.getWriter().println(response.toString());
return true;
}
8.这个时候可以捋一捋。。首先 dispatcherServlet 已经封装在了服务器中,并以一个“/”路径进行拦截,也就是说,每一个请求都会经过这个 servlet,并通过他的 service 方法。然后在这个方法里,会进行这次请求的 uri 比对,根据这次请求来的 uri,在所有的 handlermapping 中遍历,如果能够找到一个 hanlermapping 与之相对应,那么就让这个 handlermapping 进行处理,即调用 handle 方法。同时呢,在这个方法里,给 controller 实例化一个对象,然后利用 method 的 invoke 反射机制调用 controller,得到处理结果,放入 response 并返回。
9.这个时候重新打包项目,运行,已经能够响应 controller 的请求了。
10.写到这里我仍让有个疑问,mappinghandler 中保存的参数,我觉得应该仅仅是参数的名称的一个数组吧,如果仅仅把名称传入 invoke,是如何调用的呢???那个真正的参数值是什么时候传递过来的呢。。
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于