浅谈对SpringMVC框架的理解及实现

浅谈对SpringMVC框架的理解及实现

本文是简单分析SpringMVC源码及运行原理后,搭建的简易版SpringMVC框架。可实现正常工作中常用到的基本功能。主要是用于加深对SpringMVC框架的理解。

SpringMVC的工作流程
  1. 用户发送请求至DispatcherServlet
  2. DispatcherServlet收到请求调用HandlerMapping处理器映射器
  3. DispatcherServlet通过HandlerAdapter处理器适配器调用处理器
  4. 执行处理器(Controller)
  5. Controller执行完成返回ModelAndView
  6. HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServle
Spring的两大核心思想就是IOC(控制反转),DI(依赖注入),AOP(面向切面编程)。

那么
为什么Controller和Service要加注解。
Spring是如何实现依赖注入。
SpringMVC是如何将不同类型、不同个数的参数进行传递的。
SpringMVC是如何通过请求地址找到相应的Controller和方法的。

SpringMVC的实现原理

从任意web项目的web.xml可以看到,项目启动时,首先加载的是 org.springframework.web.servlet.DispatcherServlet,从SpringMVC的工作流程可以看到。SpringMVC的最核心的控制器,就是DispatcherServlet,也就是一个Servlet。那么DispatcherServlet在启动时,做了哪些事。

以下代码,均为本人查看源码后,对于SpringMVC的理解,搭建的简易版框架,与源码有一定差距,主要是用于理解SpringMVC。如有不实之处,请予以指出。

源码下载地址:

https://download.csdn.net/download/geniuslesserpanda/10778632

此项目为Maven项目,引用jar包:servlet-api.jar.

一、注解的声明

通常我们用的注解有
@Controller
@Service
@Autowired
@RequestMapping
@RequestParam

我们需要实现将这几种注解声明出来,每种注解的Target,即作用域不同,可自行修改。例如:

@Target({java.lang.annotation.ElementType.TYPE}) //作用于类上
@Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到
@Documented//表明该注解将被包含在javadoc中    @Inherited:说明子类可以继承父类中的该注解
public @interface Controller {
   String value() default "";
}
二、DispatcherServlet 初始化工作和依赖注入的实现。

1.声明集合对象,用于后期使用。

public class DispatcherServlet extends HttpServlet{
	
	private static final long serialVersionUID = 1L;
	
	//存放所有.class名称
	private List<String> classNames = new ArrayList<String>();
	
	//存放所有带@Controller、@Service类的实体
	private Map<String,Object> beans = new HashMap<String,Object>();
	
	//请求地址和方法的映射关系
	private Map<String,Object> handleMapping = new HashMap<String,Object>();

2.获取配置的 base-package 路径,加载该路径下的所有.class,将类名放至内存集合List中,下面简称classNames。

	/**
	 * 加载该路径下的所有类
	 * 此参数通常通过spring配置文件的 base-package 定义
	 * @param basePackage
	 */
	private void scanPackage(String basePackage){
		URL url = this.getClass().getClassLoader().getResource("/"+basePackage.replaceAll("\\.", "/"));
		String path = url.getPath();
		File baseFile = new File(path);
		String[] fileArray = baseFile.list();
		for(String fileName:fileArray){
			File file = new File(path+"/"+fileName);
			if(file.isDirectory()){
				scanPackage(basePackage+"."+fileName);
			}else{
				classNames.add(basePackage+"."+fileName);
			}
		}
	}

3.遍历.class集合对象(classNames),将带有@Controller,@Service注解的类实例化,放至内存集合Map中。下面简称 beans 。
@Controller 的 key: @RequestMapping 的值 value:Controller的实例
@Service 的 key:@Service的值 value:Service的实例

/**
    * 将带有@Controller、@Service注解的类实体化
    */
   private void instance(){
   	
   	if(classNames.size()<=0){
   		System.out.println("----扫描basePackage失败----");
   		return;
   	}
   	
   	for(String className:classNames){
   		try {
   			className = className.replace(".class", "");
   			Class<?> clzz = Class.forName(className);
   			if(clzz.isAnnotationPresent(Controller.class)){
   				RequestMapping mapping = clzz.getAnnotation(RequestMapping.class);
   				String key = mapping.value();
   				Object value = clzz.newInstance();
   				beans.put(key, value);
   			}else if(clzz.isAnnotationPresent(Service.class)){
   				Service service = clzz.getAnnotation(Service.class);
   				String key = service.value();
   				Object value = clzz.newInstance();
   				beans.put(key, value);
   			}else{
   				continue;
   			}
   		} catch (ClassNotFoundException e) {
   			e.printStackTrace();
   		} catch (InstantiationException e) {
   			e.printStackTrace();
   		} catch (IllegalAccessException e) {
   			e.printStackTrace();
   		}
   	}
   }

4.依赖注入。
遍历所有的beans,拿到带有@Controller 注解的类的实体。
拿到该 Controller 下的所有带@Resource @Autowired 等所有需要注入变量。
通过注解的 value,去内存集合Map(benas)中相应的实体。
打开变量的权限。将实体注入到 Controller的变量中。

/**
    * 依赖注入
    */
   private void ioc(){
   	if(beans.size()<=0){
   		System.out.println("----扫描Controller、Service失败----");
   		return;
   	}
   	for (Map.Entry<String, Object> entry : beans.entrySet()) {
   		try {
   			Object instance = entry.getValue();//获取bean实例
   			Class<?> clzz = instance.getClass();
   			if(clzz.isAnnotationPresent(Controller.class)){
   				Field[] fieldArray = clzz.getDeclaredFields();
   				for(Field f:fieldArray){
   					if(f.isAnnotationPresent(Autowired.class)){
   						Autowired auto = f.getAnnotation(Autowired.class);
   						String key = auto.value();
   						Object vaule = beans.get(key);
   						f.setAccessible(true);
   						f.set(instance, vaule);
   					}else{
   						continue;
   					}
   				}
   			}else{
   				continue;
   			}
   		} catch (IllegalArgumentException e) {
   			e.printStackTrace();
   		} catch (IllegalAccessException e) {
   			e.printStackTrace();
   		}
   	}
   }

5.建立请求地址和Method的映射关系。
遍历.class集合对象(classNames),拿到所有带@Controller注解的类。拿到Controller的请求路径,及@RequestMapping注解的值,下面简称 path。
拿到该 Controller 下所有带有@RequestMapping注解的方法,并获取到注解的值,下面简称 methodPath。
将 path+methodPath 作为key,Method 作为value,放至内存队列,下面简称 handleMapping.

/**
    * 建立请求地址和Method的映射关系
    */
   private void HandlerMapping(){
   	
   	for(String className:classNames){
   		try {
   			className = className.replace(".class", "");
   			Class<?> clzz = Class.forName(className);
   			if(clzz.isAnnotationPresent(Controller.class)){
   				RequestMapping requestMapping = clzz.getAnnotation(RequestMapping.class);
   				String path = requestMapping.value();
   				Method[] methods = clzz.getMethods();
   				for(Method method:methods){
   					if(method.isAnnotationPresent(RequestMapping.class)){
   						RequestMapping mapping = method.getAnnotation(RequestMapping.class);
   						String methodPath = mapping.value();
   						handleMapping.put(path+methodPath, method);
   					}else{
   						continue;
   					}
   				}
   			}
   		} catch (ClassNotFoundException e) {
   			e.printStackTrace();
   		}
   	}
   }

6.用户请求达到DispatcherServlet后,即可通过请求地址找到对应的Method。然后通过策略模式,解析请求参数,并调用对应的Method。

@Override
   protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
   	
   	String uri = req.getRequestURI();
   	String context = req.getContextPath();
   	String url = uri.replace(context, "");
   	String controller = "/"+url.split("/")[1];
   	
   	Method method = (Method) handleMapping.get(url);
   	HandlerAdapterImpl adapter = (HandlerAdapterImpl) beans.get(HANDELADAPTER);
   	Object[] param = adapter.hand(req, resp, method, beans);
   	
   	Object instance = beans.get(controller);
   	
   	try {
   		method.invoke(instance, param);
   	} catch (IllegalAccessException e) {
   		e.printStackTrace();
   	} catch (IllegalArgumentException e) {
   		e.printStackTrace();
   	} catch (InvocationTargetException e) {
   		e.printStackTrace();
   	}
   	
   }

参数解析器主要代码:

@Service("requestParamArgumentResolver")
public class RequestParamArgumentResolver implements ArgumentResolver{

   public boolean support(Class<?> clzz, int paramIndex, Method method) {
       
       Annotation[][] an = method.getParameterAnnotations();
       
       Annotation[] paramAns = an[paramIndex];
       
       for (Annotation paramAn : paramAns) {
       	if (RequestParam.class.isAssignableFrom(paramAn.getClass())) {
               return true;
           }
       }
       return false;
   }

   public Object argumentResolver(HttpServletRequest request, HttpServletResponse resp, Class<?> clzz, int paramIndex,
   		Method method) {
   	
   	Annotation[][] annots = method.getParameterAnnotations();
   	
   	Annotation[] paramAns = annots[paramIndex];
   	
   	for(Annotation ann:paramAns){
   		if(RequestParam.class.isAssignableFrom(ann.getClass())){
   			RequestParam param = (RequestParam) ann;
   			String key = param.value();
   			return request.getParameter(key);
   		}
   	}
   	return null;
   }
}

处理器主要代码:

@Service("handlerAdapter")
public class HandlerAdapterImpl implements HandlerAdapterService{

   public Object[] hand(HttpServletRequest request, HttpServletResponse resp, Method method,
   		Map<String, Object> beans) {
   	
   	Class<?>[] paramType = method.getParameterTypes();
   	
   	Object[] args = new Object[paramType.length];
   	
   	Map<String,Object>  argumentResolvers = getBeansOfType(ArgumentResolver.class, beans);
   	
   	int paramIndex = 0;
   	int i = 0;
   	
   	for(Class<?> clzz:paramType){
   		for(Map.Entry<String, Object> entry:argumentResolvers.entrySet()){
   			ArgumentResolver resolver = (ArgumentResolver) entry.getValue();
   			if(resolver.support(clzz, paramIndex, method)){
   				args[i++] = resolver.argumentResolver(request, resp, clzz, paramIndex, method);
   			}
   		}
   		paramIndex++;
   	}
   	return args;
   }
   
   private Map<String,Object> getBeansOfType(Class<?> clzz,Map<String,Object> beans){
   	
   	Map<String,Object> argumentResolvers = new HashMap<String,Object>();
   	
   	for(Map.Entry<String, Object> entry:beans.entrySet()){
   		
   		Class<?>[] clzzArray = entry.getValue().getClass().getInterfaces();
   		
   		for(Class<?> cl:clzzArray){
   			if(clzz.isAssignableFrom(cl)){
   				argumentResolvers.put(entry.getKey(), entry.getValue());
   			}
   		}
   	}
   	return argumentResolvers;
   }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值