用300行代码手写提炼Spring的核心原理(V1)

基本思路

先来介绍一下Mini 版本的Spring 基本实现思路,如下图所示:

用300行代码手写提炼Spring的核心原理(V1)_第1张图片

自定义配置

为了解析方便,我们用 config.properties 来代替 application.xml 文件,具体配置内容如下:

#config
scanPackage=com.hezhiqin

配置 web.xml 文件

大家都知道,所有依赖于 web 容器的项目,都是从读取 web.xml 文件开始的。我们先配置好 web.xml 中的内容。




  HZQmvc
  
  	hzqmvc
  	com.hezhiqin.mvcframwork.servlet.HZQDispathcherServlet
  	
  		contextConfigLocation
  		config.properties
  	
  	1
  
  
  		hzqmvc
  		/*
  

其中 HZQDispathcherServlet是有自己模拟 Spring 实现的核心功能类

自定义 Annotation

自定义Controller注释

package com.hezhiqin.mvcframework.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface HZQController {
	String value() default "";
}

自定义Service注释

package com.hezhiqin.mvcframework.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface HZQService {
	String value() default "";
}

自定义Autowired注释

package com.hezhiqin.mvcframework.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface HZQAutowired {
	String value() default "";
}

自定义RequestMapping注释

package com.hezhiqin.mvcframework.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface HZQRequestMapping {
	String value() default "";
}

自定义RequestParam注释

package com.hezhiqin.mvcframework.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface HZQRequestParam {
	String value() default "";
}

配置Service层

package com.hezhiqin.service;

public interface IDemoService {
	public String  get(String name);
}
package com.hezhiqin.service.impl;

import com.hezhiqin.mvcframework.annotation.HZQService;
import com.hezhiqin.service.IDemoService;

@HZQService
public class DemoServiceImpl implements IDemoService {

	public String get(String name) {
		return "My name is " + name;
	}

	

}

配置Controller层

package com.hezhiqin.controller;

import java.io.IOException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.hezhiqin.mvcframework.annotation.HZQAutowired;
import com.hezhiqin.mvcframework.annotation.HZQController;
import com.hezhiqin.mvcframework.annotation.HZQRequestMapping;
import com.hezhiqin.mvcframework.annotation.HZQRequestParam;
import com.hezhiqin.service.IDemoService;

@HZQController
@HZQRequestMapping("/demo")
public class DemoAction {
	
	@HZQAutowired private IDemoService demoService;
	
	@HZQRequestMapping("/query")
	public void query(HttpServletRequest req, HttpServletResponse resp,
			@HZQRequestParam("name") String name) {
		String result = demoService.get(name);
		try {
			resp.getWriter().write(result);
		} catch (IOException e) {
			e.printStackTrace();
		}

	}


}

逻辑很简单,一目了然,至此,配置阶段就已经完成

容器初始化

实现 V1 版本所有的核心逻辑全部写在一个 init()方法中。

代码有点长 仔细看注释

主要流程:

1.init()初始化时根据配置的需要扫描的包进行扫描,将包下所有的类放入容器中

2.先识别@HZQController,将其获取Controller上的RequestMapping地址作为baseUrl

3.获取@HZQController里面所有方法,获取上的RequestMapping地址作为和baseUrl进行拼接,

4.将url地址和方法作为K,V放入容器中;

5.如果是@HZQService,将@HZQService标注的类实例化,并将其父类接口类和实例K,V放入容器中

6.解析容器,获取Controller的参数,如果没有HZQAutowired修饰的属性则跳过,如果有的话从容器中获取并注入

7.请求时调用doDispatch方法,根据url和参数(name)来反射执行方法;

package com.hezhiqin.mvcframwork.servlet;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.hezhiqin.mvcframework.annotation.HZQAutowired;
import com.hezhiqin.mvcframework.annotation.HZQController;
import com.hezhiqin.mvcframework.annotation.HZQRequestMapping;
import com.hezhiqin.mvcframework.annotation.HZQService;

/**
 * @author Administrator
 *所有的核心逻辑卸载init方法中
 */
public class HZQDispathcherServlet  extends HttpServlet{
	
	//新建一个“容器”
	private Map mapping = new HashMap();


	

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		// TODO Auto-generated method stub
		this.doPost(req,resp);
	}

	

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		try {
			doDispatch(req,resp);
		} catch (Exception e) {
			resp.getWriter().write("500 Exception " + Arrays.toString(e.getStackTrace()));
		}

	}



	private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
		String url = req.getRequestURI();//请求的相对路径
		System.out.println("url"+url);
		String contextPath = req.getContextPath();//项目根路径
		System.out.println("contextPath"+contextPath);
		url = url.replace(contextPath, "").replaceAll("/+", "/");
		System.out.println("url"+url);
		if (!this.mapping.containsKey(url)) {//如果不存在这个路径则返回404
			resp.getWriter().write("404 Not Found!!");
			return;
		}
		Method method = (Method) this.mapping.get(url);
        Map params = req.getParameterMap();
        method.invoke(this.mapping.get(method.getDeclaringClass().getName()),new Object[]{req,resp,params.get("name")[0]});
	}



	@Override
	public void init(ServletConfig config) throws ServletException {//初始化
		InputStream is = null;
		try {
			//获取配置信息,定位到需要扫描的包下面
			Properties configContext = new Properties();
			is = this.getClass().getClassLoader().getResourceAsStream(config.getInitParameter("contextConfigLocation"));
			configContext.load(is);
			String scanPackage = configContext.getProperty("scanPackage");
			//扫描配置的路径,将所有相关的类放入容器中
			doScanner(scanPackage);

			System.out.println("classNameLIST" +mapping.keySet());
			String[] StringList =new String[0];
			//循环容器中的类
			String[] classNameList = mapping.keySet().toArray(StringList);
			
			for (String className : classNameList) {
				
				System.out.println("className" +className);
				if(!className.contains(".")){continue;}
				Class clazz = Class.forName(className);
				if (clazz.isAnnotationPresent(HZQController.class)) {//如果是@HZQController
					mapping.put(className,clazz.newInstance());
					String baseUrl = "";
					if (clazz.isAnnotationPresent(HZQRequestMapping.class)) {//获取Controller上的RequestMapping地址
						HZQRequestMapping requestMapping =clazz.getAnnotation(HZQRequestMapping.class);
						baseUrl = requestMapping.value();
					}
					Method[] methods = clazz.getMethods();//获取Controller里面所有方法
					for (Method method : methods) {
						if (!method.isAnnotationPresent(HZQRequestMapping.class)) {continue; }//如果没有@HZQRequestMapping则跳过
						HZQRequestMapping requestMapping =
								method.getAnnotation(HZQRequestMapping.class);
						String url = (baseUrl + "/" + requestMapping.value()).replaceAll("/+", "/");
						mapping.put(url, method);
						System.out.println("Mapped " + url + "," + method);

					}
				}else if(clazz.isAnnotationPresent(HZQService.class)) {//如果是@HZQService
					HZQService service = clazz.getAnnotation(HZQService.class);
					String beanName = service.value();
					if("".equals(beanName)){beanName = clazz.getName();}
					Object instance = clazz.newInstance();
					mapping.put(beanName,instance);
					for (Class i : clazz.getInterfaces()) {//获取接口类
						mapping.put(i.getName(),instance);
					}

				}else {
					continue;
				}
			}
			
			System.out.println("MAP"+mapping);
			for (Object object : mapping.values()) {//获取容器内所有的实例
				
				if(object == null){continue;}
				Class clazz = object.getClass();
				if(clazz.isAnnotationPresent(HZQController.class)){
					Field [] fields = clazz.getDeclaredFields();//获取Controller的参数
					for (Field field : fields) {
						if(!field.isAnnotationPresent(HZQAutowired.class)){continue; }//如果没有HZQAutowired修饰的属性则跳过
						HZQAutowired autowired = field.getAnnotation(HZQAutowired.class);
						String beanName = autowired.value();
						if("".equals(beanName)){beanName = field.getType().getName();}
						field.setAccessible(true);
						try {
							field.set(mapping.get(clazz.getName()),mapping.get(beanName));//注入
						} catch (IllegalAccessException e) {
							e.printStackTrace();
						}

					}

				}

			}


		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			if(is != null){
				try {is.close();} catch (IOException e) {
					e.printStackTrace();
				}

			}

		}
		
	
	}



	/**
	 * @param scanPackage
	 * 扫描配置的路径,将所有相关的类放入容器中
	 */
	private void doScanner(String scanPackage) {
		URL url = this.getClass().getClassLoader().getResource("/" +
				scanPackage.replaceAll("\\.","/"));//将扫描的包的.换成/
		System.out.println("url"+url.toString());//
		File classDir = new File(url.getFile());
		for (File file : classDir.listFiles()) {
			if (file.isDirectory()) {//如果是一个文件
				 doScanner(scanPackage + "." +file.getName());
			}else {
				if(!file.getName().endsWith(".class")){continue;}//不是一个class文件则跳过
				String clazzName = (scanPackage + "." + file.getName().replace(".class",""));//获取classpath
				mapping.put(clazzName,null);//写入Key
				System.out.println("mapping"+mapping);
			}
		}

		
	}
	
	

	
	
}

测试

用300行代码手写提炼Spring的核心原理(V1)_第2张图片

你可能感兴趣的:(Spring源码剖析)