1.在web项目配置全局监听器,监听器写在框架下
<!-- 配置全局监听器 -->
<listener>
<listener-class>com.qfedu.mvc.framwork.listener.ContextListener</listener-class>
</listener>
2.框架启动首先找到AppConfig在哪里
反射
Class clazz = getConfigClazz(sce);
/**
* 找到config类的clazz对象
* @param sce
*/
private Class getConfigClazz(ServletContextEvent sce) {
//:读取全局的配置文件
ServletContext servletContext = sce.getServletContext();
String config = servletContext.getInitParameter("config");
if (StringUtils.isEmpty(config)){
//说明为配置config这个类
throw new MvcException("未配置Config类");
}
//配置了Config类读取出来,转化成class对象
Class<?> configClazz=null;
try {
configClazz = Class.forName(config);
} catch (ClassNotFoundException e) {
e.printStackTrace();
throw new MvcException("Config类的路径有误无法实施反射");
}
return configClazz;
}
3.找到AppConfig之后找注解的地址
//说明有对象
String value = getComponentScanPath(clazz);
private String getComponentScanPath(Class clazz) {
ComponentScan componentScan = (ComponentScan) clazz.getAnnotation(ComponentScan.class);
if(null==componentScan){
//说明为配置注解
throw new MvcException("未配置ComponentScan这个注解...");
}
//程序执行到这里,说明配置了注解
//取出控制器的扫描路径
String value = componentScan.value();
if(StringUtils.isEmpty(value)){
//说明未配置这个扫描路径
throw new MvcException("未配置这个控制器的扫描路径");
}
return value;
}
package com.qfedu.aunt.commons.config;
import com.qfedu.mvc.framwork.annotation.ComponentScan;
@ComponentScan("com.qfedu.aunt.commons.controller")//告诉程序需要找到的控制器的路径
public class AppConfig {
}
4.找到项目控制器controller的绝对路径的地址(项目部署绝对路径)
//获取到文件绝对路径:不是代码路径,而是项目部署路径
String absolutePath = getAbsolutePath(sce,value);
/**
* 获取部署的绝对路径
* @param sce
* @param value
* @return
*/
private String getAbsolutePath(ServletContextEvent sce,String value){
String realPath = sce.getServletContext().getRealPath("/");
value = value.replace(".","\\");
realPath=realPath+"WEB-INF\\classes\\"+value;
System.out.println("项目部署的根路径是:"+realPath);
return realPath;
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o5fLYW7h-1672318285598)(新建文本文档 (2)].assets/1672314828485.png)
5.继续找到Controller控制器下面的所有文件
//找到这个绝对路径下的所有文件
getFileByPath(absolutePath);
//递归方法找到所有的非文件夹文件
private void getFileByPath(String absolutePath) {
File file = new File(absolutePath);
File[] files = file.listFiles();
for (File f:files){
if(f.isFile()){
listFile.add(f);
}else {
getFileByPath(f.getAbsolutePath());
}
}
}
6.再找到所有的.class文件
//找到所有的class文件
List<File> classFile = getClassFileByListFile(listFile);
/**
* 通过所有遍历出来的文件,找到所有的class文件
* @param listFile
* @return
*/
private List<File> getClassFileByListFile(List<File> listFile) {
List<File> classFile = new ArrayList<File>();
//遍历找到的每一个文件
for (File f:listFile) {
//获取这个文件的绝对路径
String fileName = f.getName();
//判断这个文件名的结尾是不是 .class
if(fileName.endsWith(".class")){
classFile.add(f);
}
}
return classFile;
}
7.将所有的.class文件转换为class对象
为了找到controller注解
List<Class> clazzList = getClassFromClassFile(classFile,sce);
/**
* 通过class文件找到class所对应的clazz对象
* @param classFile
* @return
*/
private List<Class> getClassFromClassFile(List<File> classFile,ServletContextEvent sce) throws ClassNotFoundException {
List<Class> classList = new ArrayList<Class>();
//遍历文件,找到每一个class文件的包
for (File f:classFile) {
//找到文件的绝对路径
String absolutePath = f.getAbsolutePath();
//这个绝对路径要和谐掉前面的部署路径
//所有要去点部署路径
String realPath = sce.getServletContext().getRealPath("/");
//处理路径问题
realPath=realPath+"WEB-INF\\classes\\";
//在整个路径中要去掉这个部署的路径
//最终路径
String replace = absolutePath.replace(realPath, "");
//接下来去掉.class
replace = replace.substring(0,replace.lastIndexOf(".class"));
//将路径中的/换成我们的 .
String classPath = replace.replace("\\",".");
//ClassPath的路径直接反射获取class对象
Class<?> clazz = Class.forName(classPath);
classList.add(clazz);
}
return classList;
}
8.转换为class对象之后需要处理类映射关系
//处理映射关系
handlerrequestMapping(clazzList);
/**
* 处理映射关系
* @param clazzList
*/
private void handlerrequestMapping(List<Class> clazzList) throws IllegalAccessException, InstantiationException, InvocationTargetException {
//需要遍历类是否拥有Controller注解
for (Class clazz:clazzList){
Annotation annotation = clazz.getAnnotation(Controller.class);
if(annotation==null){
//说明是个普通类不需要解析
continue;
}
//类需要解析
//看类是否拥有@RequestMapping注解
RequestMapping requestMapping = (RequestMapping) clazz.getAnnotation(RequestMapping.class);
String reqPathAll = null;
if(requestMapping!=null){
//配置了注解
reqPathAll = requestMapping.value();
}
//找到类中的所有方法,判断这个方法是否拥有RequestMapping注解
Method[] declaredMethods = clazz.getDeclaredMethods();
//封装类的描述对象
Beandefinition beandefinition = new Beandefinition();
//设置类名
beandefinition.setTypeName(clazz.getName());
//类对象是谁
beandefinition.setObj(clazz.newInstance());
//类上的路径
beandefinition.setRegPath(reqPathAll);
if(requestMapping!=null){
beandefinition.setRequestMappingOrNot(true);
}
handlerMethodrequestMapping(reqPathAll,declaredMethods,beandefinition);
}
}
处理类方法的映射关系
/**
* 处理这个方法的映射关系
* @param reqPathAll
* @param declaredMethods
*/
private void handlerMethodrequestMapping(String reqPathAll,
Method[] declaredMethods,
Beandefinition beandefinition1) throws InvocationTargetException, IllegalAccessException {
//遍历这个类中的所有方法,找到这个方法是否拥有@RequestMapping这个注解
for (Method m:declaredMethods){
//TODO 进行深拷贝
Beandefinition beandefinition = new Beandefinition();
//做一个属性的拷贝
BeanUtils.copyProperties(beandefinition,beandefinition1);
//判断方法上是否拥有requestMapping注解
RequestMapping requestMapping = m.getAnnotation(RequestMapping.class);
if(requestMapping==null){
continue;
}
//执行说明拥有RequestMapping注解
String reqPath = requestMapping.value();
//类上的RequestMapping注解可以没有值,方法必须有,否则就报错
if(StringUtils.isEmpty(reqPath)){
throw new MvcException("方法上的RequestMapping必须有值,不能默认...");
}
//执行这里说明有值,组装最终的请求地址
String path = (reqPathAll+reqPath).trim();
//描述方法
BeanMethodDefinition beanMethodDefinition=new BeanMethodDefinition();
beanMethodDefinition.setMethod(m);
//设置方法参数的个数
beanMethodDefinition.setParameterCount(m.getParameterCount());
//方法上的请求路径
beanMethodDefinition.setReqPath(reqPath);
//将方法的描述放到类的描述中去
beandefinition.setBeanMethodDefinition(beanMethodDefinition);
//解决方法中参数的映射问题
//引用传递
handlerMethodParameterMapping(beanMethodDefinition,m);
//最终就可以把描述信息放到容器中去了
//找到容器
ConcurrentHashMap<String, Beandefinition> container = RquestMappingContainer.getContainer();
//向容器中写入数据
container.put(path,beandefinition);
}
}
处理参数的映射关系
/**
* 处理参数的映射关系
* @param beanMethodDefinition
* @param m
*/
private void handlerMethodParameterMapping(BeanMethodDefinition beanMethodDefinition, Method m) {
List<BeanParameterDefinition> beanParameterDefinitions = new ArrayList<>();
Parameter[] parameters = m.getParameters();
for (Parameter p:parameters) {
BeanParameterDefinition beanParameterDefinition = new BeanParameterDefinition();
beanParameterDefinition.setName(p.getName());
beanParameterDefinition.setType(p.getType().getName());
beanParameterDefinitions.add(beanParameterDefinition);
}
beanMethodDefinition.setBeanParameterDefinitions(beanParameterDefinitions);
}
;
beanParameterDefinition.setName(p.getName());
beanParameterDefinition.setType(p.getType().getName());
beanParameterDefinitions.add(beanParameterDefinition);
}
beanMethodDefinition.setBeanParameterDefinitions(beanParameterDefinitions);
}