手写SpringMVC框架(2)

编写SpringMVC核心代码

前端控制器:DispatcherServlet

前端控制器主要是捕获来自浏览器、前端的请求,在服务器启动之前初始化spring容器中的组件。自定义DispatcherServlet类继承HttpServlet

public class DispatcherServlet extends HttpServlet {
	
    // Spring ioc容器对象
    private WebApplicationContext webApplicationContext;
    // 存储URI和对象的方法映射关系
    private List<HandlerMapping> handlerMappings = new ArrayList<>();
	
		/**
    * 加载DispatcherServlet类的初始方法
    */
    @Override
    public void init(ServletConfig config) {
        // classpath: springmvc.xml 读取初始化的参数
        String contextConfigLocation = config.getInitParameter("contextConfigLocation");
        // 1.创建spring容器
        webApplicationContext = new WebApplicationContext(contextConfigLocation);
        // 2.初始化spring容器
        webApplicationContext.refresh();
        // 3.初始化请求映射 /user/query ----> Controller ----> method ----> parameters
        initHandlerMappings();
        System.out.println("请求地址和控制器方法的映射关系:" + handlerMappings);
    }

		@Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException {
        this.doPost(req, resp);
    }

		@Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
        
    }
	
    /**
    * 初始化请求映射
    */
    private void initHandlerMappings() {
          // 判断容器中是否有bean对象
          if(webApplicationContext.iocMap.isEmpty()) {
              throw new ContextException("Spring容器为空");
          }
          for (Map.Entry<String, Object> entry: webApplicationContext.iocMap.entrySet()) {
              Class<?> clazz = entry.getValue().getClass();
              Object ctl = entry.getValue(); // 当前的对象
            	// 判断clazz是否有@Controller注解
              if(clazz.isAnnotationPresent(Controller.class)) {
                  // 处理有@Controller注解的类(下面讲解)
                  parseHandlerFromController(clazz, ctl);
              }
          }
      }
    
}

Spring容器对象:WebApplicationContext

创建WebApplicationContext类(Spring容器对象)

public class WebApplicationContext  {
	
	// 配置文件名
    private String contextConfigLocation;
    // 扫描指定包的类名列表
    private List<String> classNames = new ArrayList<>();
    // Spring的IOC容器
    public Map<String, Object> iocMap = new ConcurrentHashMap<>();

    public WebApplicationContext(String contextConfigLocation) {
        this.contextConfigLocation = contextConfigLocation;
    }
  
  	/**
     * 容器初始化
     */
    public void refresh(){
      	// 扫描配置文件扫描的包
        String basePackage = XmlParser.getBasePackage(contextConfigLocation.split(":")[1]);
        assert basePackage != null;
      	// 以','来分割出各个包名
        String[] basePackages = basePackage.split(",");
        // 判断存在
        if(basePackages.length > 0) {
            for(String pack: basePackages) {
                // com.example.controller
                // com.example.service
                try {
                    // 执行包扫描的方法,将每个包以参数放进去,去除首尾空格
                    executeScanPackage(pack.trim());
                    // 实例化对象
                    executeInstance();
                    // 实例化Spring容器中bean对象
                    executeAutowired();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            System.out.println("扫描的包:" + classNames);
            System.out.println("ioc容器中的对象:" + iocMap);
        }
    }
  
  	/**
     *
     * @param pack 扫描包
     */
    public void executeScanPackage(String pack) {
        // 获取当前包的类路径,用/去替换掉.
        URL url = this.getClass().getClassLoader().getResource("/" + pack.replaceAll("\\.", "/"));
        // 路径:/com/example/controller
        assert url != null;
      	// 获取文件路径
        String path = url.getFile();
        // new File()
        File dir = new File(path);
        // for循环去递归,扫描当前路径下的文件\文件夹
        for(File f: Objects.requireNonNull(dir.listFiles())) {
            if(f.isDirectory()) {
                // 文件夹(再去指定executeScanPackage方法)
                executeScanPackage(pack+"."+f.getName());
            } else {
                // 文件 获取className然后加入到容器的classNames列表中
                String className = pack+"."+f.getName().replaceAll(".class", "");
                classNames.add(className);
            }
        }
    }
  	
  	/**
     * 实例化bean对象
     */
    public void executeInstance() {
        // 判断容器中的classNames是否为空
        if(classNames.size() == 0) {
            // 没有要实例化的对象 抛出自定义异常
            throw new ContextException("没有要实例化的class");
        }
        for (String className : classNames) {
            try {
                // 通过反射获取当前类
                Class<?> clazz = Class.forName(className);
                // 判断当前类是否是Controller类(加@Controller注解)
                if(clazz.isAnnotationPresent(Controller.class)) {
                    // 控制层的类 com.springmvc.controller.UserController
                    // userController 控制层对象的名字
                    // System.out.println(clazz.getSimpleName()) 处理成首字母小写;
                    String beanName = clazz.getSimpleName().substring(0, 1).toLowerCase() + clazz.getSimpleName().substring(1);
                    // 将beanName放入iocMap中
                    // key(beanName) -> value((Object)beanName通过反射获取到对象类)
                    iocMap.put(beanName, clazz.newInstance());
                // 如上:  判断当前类是否是Service类(加@Service注解)
                } else if(clazz.isAnnotationPresent(Service.class)) {
                    // com.springmvc.controller.UserServiceImpl
                    // 获取Service(value="")的value值
                    Service serviceAnnotation = clazz.getAnnotation(Service.class);
                    String beanName = serviceAnnotation.value();
                    // 判断Service(value="")的value是否为空
                    if("".equals(beanName)) {
                        // 取当前Service类的接口
                        Class<?>[] interfaces = clazz.getInterfaces();
                        for (Class<?> c1 : interfaces) {
                            beanName = c1.getSimpleName().substring(0, 1).toLowerCase() + c1.getSimpleName().substring(1);
                            iocMap.put(beanName, clazz.newInstance());
                        }
                    } else {
                        // 在Service(value="us") beanName = us
                        iocMap.put(beanName, clazz.newInstance());
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    }
  
    /**
     * 实现spring容器中bean的注入(自动装配)
     */
    private void executeAutowired() throws IllegalAccessException {
				// 判断iocMap中是否为空
        if(iocMap.isEmpty()) {
            // 为空则抛出异常
            throw new ContextException("没有找到初始化bean对象");
        }
				// 遍历iocMap
        for (Map.Entry<String, Object> entry : iocMap.entrySet()) {
            String key = entry.getKey(); // beanName
            Object bean = entry.getValue(); // bean
            // 获取到当前对象里面定义的Field
            Field[] declaredFields =  bean.getClass().getDeclaredFields();
            for (Field declaredField : declaredFields) {
                // 如果字段有Autowired的注解
                if(declaredField.isAnnotationPresent(Autowired.class)) {
                    // 先判断是否有value
                    Autowired autowired = declaredField.getAnnotation(Autowired.class);
                    String beanName = autowired.value();
                    // 如果value为空
                    if("".equals(beanName)) {
                        Class<?> type = declaredField.getType();
                      	// 获取当前字段首字母小写(等价于获取iocMap中的key)
                        beanName = type.getSimpleName().substring(0, 1).toLowerCase() + type.getSimpleName().substring(1);
                    }
                    // 强制访问类的属性
                    declaredField.setAccessible(true);
                    // 属性注入 调用反射给属性注入值
                    declaredField.set(bean, iocMap.get(beanName));
                }
            }
        }
    }
  
}

自定义:ContextException

// 继承RuntimeException类重写getMessage方法
public class ContextException extends RuntimeException {

    public ContextException(String message) {
        super(message);
    }

    public ContextException(Throwable cause) {
        super(cause);
    }

    @Override
    public String getMessage() {
        return super.getMessage();
    }
}

处理@Controller类:parseHandlerFromController

/**
 * 通过控制器类解析请求映射
 * @param clazz Class
 */
private void parseHandlerFromController(Class<?> clazz, Object ctl) {
    String uriPrefix = "";
  	// 去判断Controller类是否有RequestMapping注解
    if(clazz.isAnnotationPresent(RequestMapping.class)){
        // 获取RequestMapping中的value值
        uriPrefix = clazz.getDeclaredAnnotation(RequestMapping.class).value();
    }
    // 获取当前类定义的方法
    Method[] declaredMethods = clazz.getDeclaredMethods();
    for (Method declaredMethod : declaredMethods) {
        // 判断是否有@ResponseBody注解(默认:false)
        boolean isResponseBody = false;
        if(declaredMethod.isAnnotationPresent(ResponseBody.class)) {
            // 有@ResponseBody注解:true
            isResponseBody = true;
        }
        // 判断方法上是否有@RequestMapping注解
        if(declaredMethod.isAnnotationPresent(RequestMapping.class)) {
            RequestMapping requestMapping = declaredMethod.getAnnotation(RequestMapping.class);
            // 获取RequestMapping的value
            String URI = uriPrefix + requestMapping.value();
            // 解析方法上的请求参数
            List<String> paramNameList = new ArrayList<>();
          	// 遍历方法上的参数
            for(Parameter parameter: declaredMethod.getParameters()) {
                // 判断是否有@RequestParam注解
                if(parameter.isAnnotationPresent(RequestParam.class)) {
                    // 控制当前方法形参是否为必须的 后期还需优化required = false的时候
                    if(parameter.getDeclaredAnnotation(RequestParam.class).required()) {
                      // 解析@RequestParam的value
                        paramNameList.add(parameter.getDeclaredAnnotation(RequestParam.class).value());
                    } else {
                        paramNameList.add(parameter.getName());
                    }
                } else {
                    if("HttpServletRequest".equals(parameter.getType().getSimpleName())){
                        // 处理方法参数是有HttpServletRequest
                        paramNameList.add("HttpServletRequest");
                    } else if("HttpServletResponse".equals(parameter.getType().getSimpleName())){
                        // 处理方法参数是有HttpServletResponse
                        paramNameList.add("HttpServletResponse");
                    } else {
                        paramNameList.add(parameter.getName());
                    }
                }
            }
            int size = paramNameList.size();
            String[] params = paramNameList.toArray(new String[size]);
            // new HandlerMapping() 然后添加在handlerMappings中
            HandlerMapping handlerMapping = new HandlerMapping(URI, ctl, declaredMethod, params, isResponseBody);
            handlerMappings.add(handlerMapping);
        }
    }
}

处理应映射器对象:HandlerMapping

@Data
@AllArgsConstructor
@NoArgsConstructor
public class HandlerMapping {

    private String uri; // 请求的URI
    private Object controller; // 对应的控制器
    private Method method; // 对应控制器执行的方法
    private String[] args; // 方法上的参数
    private boolean isResponseBody; // 方法是否加了@ResponseBody注解

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值