学习Spring源码:手写实现spinrgmvc

1.关于spring ioc容器:

     spring ioc容器是什么呢?    我们可以理解为将东西存放起来的东西。比如将java对象存放在ioc容器中。  

     简单的说就是  ioc容器等于  ====>>>  Map<String,Object> 大集合

了解了容器,接下里就可以写代码了。用过Spring的都知道这些注解。这里不过多讲解。

     

这里我们主要是自己手写以上几个注解.通过在tomcat启动时创建 dispatcherServlet  init初始化时,完成注入;接下里会详细讲解。

  2.关于注解:

      算啦算啦,直接上代码. 有注释.

@Controller:

package com.springmvc.wh.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;

/**
 * Controller控制类注解
 * 命名可以自定义比如(WHController),这里为了可以更加直观体现
 * @author 文浩
 *
 */
@Target(ElementType.TYPE) // 该注解表示: 此注解作用范围只能在类上面
@Retention(RetentionPolicy.RUNTIME) // 该注解表示: 在系统运行时通过反射获取信息
@Documented // javaDoc
public @interface Controller {
	String value() default ""; // 表示可以在注解内传入参数,参数类型为String  默认为"";
}

   @Autowired:

package com.springmvc.wh.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;

/**
 * 自动注入注解
 * 
 * @author 文浩
 *
 */
@Target(ElementType.FIELD) // 表示作用范围只限于成员变量上
@Retention(RetentionPolicy.RUNTIME) // 系统运行时加载
@Documented // javaDoc
public @interface Autowired {
	String value() default "";
}

@RequestMapping:

package com.springmvc.wh.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;

/**
 * 映射路径
 * 
 * @author 文浩
 *
 */
@Target({ ElementType.TYPE, ElementType.METHOD }) // 表示作用范围为类,和方法
@Retention(RetentionPolicy.RUNTIME) // 运行加载
@Documented
public @interface RequestMapping {
	String value() default "";
}

 

@RequestParam :

package com.springmvc.wh.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;

/**
 * 参数注解
 * 
 * @author 文浩
 *
 */
@Target(ElementType.PARAMETER) // 作用范围为方法参数里
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestParam {
	String value() default "";
}

 

@Service

package com.springmvc.wh.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 Service {
	String value() default "";
}

 

3.到这里,注解已经写完了。接下里编写测试的代码.Service和Controller

    比较懒,还是直接上代码好了。

    Controller:

    

package com.springmvc.wh.controller;

import java.io.IOException;
import java.io.PrintWriter;

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

import com.springmvc.wh.annotation.Autowired;
import com.springmvc.wh.annotation.Controller;
import com.springmvc.wh.annotation.RequestMapping;
import com.springmvc.wh.annotation.RequestParam;
import com.springmvc.wh.service.DomeService;

@Controller()
@RequestMapping("/wh")
public class DemoController {

	@Autowired("domeService")
	private DomeService domeService;

	@RequestMapping("/Test")
	public void Test(HttpServletRequest request, HttpServletResponse response,
			@RequestParam("name") String name,
			@RequestParam("age") String age) {
		PrintWriter out;
		try {
			out = response.getWriter();
			String result = domeService.Test(name, age);
			out.write(result);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

}

Service:

package com.springmvc.wh.service;

public interface DomeService {
	public String Test(String name, String age);
}
package com.springmvc.wh.service.impl;

import com.springmvc.wh.annotation.Service;
import com.springmvc.wh.service.DomeService;

@Service("domeService")
public class DomeServiceImpl implements DomeService {

	public String Test(String name, String age) {
		return "name====>>" + name + "       ; age===>>" + age;
	}

}

  这里没有使用持久层框架. 所有直接return 了,不然应该连接Dao层。不多解释。

 

关键来了,注解写了,Controller写了,service也写了。

能映射了吗,能注入了吗。答:当然不行

因为我们缺少了最关键的部分,servlet呢?.  

没错,下一步,dispatcherServlet, 在tomcat启动时,完成注入。

 

4. dispatcherServlet

   要想在tomcat启动时完成注入应该怎么写呢。

      关于servlet生命周期,这里不在过多解释。

         当然是init()方法; 好了废话有点多,还是上代码吧。

package com.springmvc.wh.servlet;

import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

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.springmvc.wh.annotation.Autowired;
import com.springmvc.wh.annotation.Controller;
import com.springmvc.wh.annotation.RequestMapping;
import com.springmvc.wh.annotation.RequestParam;
import com.springmvc.wh.annotation.Service;
import com.springmvc.wh.controller.DemoController;

public class DispatcherServlet extends HttpServlet {
	// doScan()用于存储所有class路径
	private List<String> className = new ArrayList<String>();
	// doInstance() 存储实例化后的bean
	private Map<String, Object> beans = new HashMap<String, Object>();
	private Map<String, Object> handerMap = new HashMap<String, Object>();

	// <load-on-startup> 启动时调用
	/**
	 * 
	 */
	public void init(ServletConfig config) {
		// 扫描 com.spring.mvc.wh 下路径 获取所有class文件并创建实例
		// 得到Class<?>
		doScan("com.springmvc.wh");

		// 得到所有Class文件后,对这些class文件进行实例化
		doInstance();

		// 为实例化实例变量进行注入
		doAutowired();

		// 遍历方法获得映射路径
		doUrlMapping();
	}

	/**
	 * 遍历实例,得到@RequstMapping(...)里的路径
	 */
	public void doUrlMapping() {
		for (Map.Entry<String, Object> entry : beans.entrySet()) {

			Object insetance = entry.getValue();

			Class<?> clazz = insetance.getClass();

			if (clazz.isAnnotationPresent(Controller.class)) {
				RequestMapping reqMap = clazz.getAnnotation(RequestMapping.class);

				String calssPath = reqMap.value();
				// 得到类中所有方法
				Method[] methods = clazz.getMethods();
				// 遍历所有方法
				for (Method method : methods) {
					if (method.isAnnotationPresent(RequestMapping.class)) {
						RequestMapping reqMap1 = method.getAnnotation(RequestMapping.class);
						// 获取@ReuqstMapping注解中的value值
						String methodPath = reqMap1.value();

						handerMap.put(calssPath + methodPath, method);

					} else {
						continue;
					}
				}

			}

		}
	}

	/**
	 * 遍历实例化的实例,为成员变量进行注入
	 */
	public void doAutowired() {
		try {
			for (Map.Entry<String, Object> entry : beans.entrySet()) {
				Object instance = entry.getValue();
				Class<?> clazz = instance.getClass();
				// 如果是Controller 类下
				if (clazz.isAnnotationPresent(Controller.class)) {
					Field[] fields = clazz.getDeclaredFields();

					for (Field field : fields) {
						Autowired aotuwired = field.getAnnotation(Autowired.class);
						String key = aotuwired.value();
						Object value = beans.get(key);
						// 打开私有变量的权限
						field.setAccessible(true);
						field.set(instance, value);
					}
				} else {
					continue;
				}
			}
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	/**
	 * 遍历calssName集合中的文件,并且实例化
	 */
	public void doInstance() {
		// 遍历className里的所有calss文件
		for (String className : className) {
			String cn = className.replace(".class", "");
			try {
				Class<?> clazz = Class.forName(cn);
				// 判断calss文件上的注解是不是Controller注解
				if (clazz.isAnnotationPresent(Controller.class)) {
					Object value = clazz.newInstance();
					// map.put(key,value);
					RequestMapping reqMap = clazz.getAnnotation(RequestMapping.class);
					String key = reqMap.value();
					beans.put(key, value);
				} else if (clazz.isAnnotationPresent(Service.class)) {
					// 判断class文件上的注解是不是 Service
					Object value = clazz.newInstance();
					Service service = clazz.getAnnotation(Service.class);
					String key = service.value();
					beans.put(key, value);
				} else {
					continue;
				}

			} 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();
			}

		}

	}

	// mvc.xml ----》 basePackage
	public void doScan(String basePackage) {
		// basePackage == com.springmvc

		// 扫描编译好的所有的类路径
		URL url = this.getClass().getClassLoader().getResource("/" + basePackage.replaceAll("\\.", "/"));
		// 路径地址
		String fileStr = url.getFile();
		// 转换为文件类型,用于判断 是属于文件还是文件夹
		File file = new File(fileStr);
		// 获取basePackage 下所有的.calss
		String[] filesStr = file.list();

		for (String path : filesStr) {
			File filePath = new File(fileStr + path);
			// 判断路径是不是文件夹
			if (filePath.isDirectory()) {
				// 如果是路径 递归继续扫描
				doScan(basePackage + "." + path);
			} else {
				className.add(basePackage + "." + filePath.getName());
			}

		}

	}

	// 源码采用策略模式,这里粗略简写
	public static Object[] hand(HttpServletRequest req, HttpServletResponse resp, Method method) {

		// 拿到当前待执行的方法有哪些参数
		Class<?>[] paramClazzs = method.getParameterTypes();

		Object[] args = new Object[paramClazzs.length];

		int args_i = 0;
		int index = 0;

		for (Class<?> paramClazz : paramClazzs) {
			if (ServletRequest.class.isAssignableFrom(paramClazz)) {
				args[args_i++] = req;
			}
			if (ServletResponse.class.isAssignableFrom(paramClazz)) {
				args[args_i++] = resp;
			}
			// 从0-3判断有没有RequestParam注解,很明显paramClazz为0和1时不是
			// 当为2和3时为@requestParam,需要解析
			// [@Con.xxx.xxx.ReuqstParam(value = value)]
			Annotation[] paramAns = (Annotation[]) method.getParameterAnnotations()[index];
			if (paramAns.length > 0) {
				for (Annotation paramAn : paramAns) {
					if (RequestParam.class.isAssignableFrom(paramAn.getClass())) {
						RequestParam rp = (RequestParam) paramAn;
						// 找到注解的参数
						args[args_i++] = req.getParameter(rp.value());
					}
				}

			}
			index++;
		}

		return args;

	}

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

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		try {
			// 获取请求路径
			String uri = req.getRequestURI();
			String context = req.getContextPath();
			String path = uri.replace(context, ""); // 路径下所有key

			Method method = (Method) handerMap.get(path);

			DemoController insetance = (DemoController) beans.get("/" + path.split("/")[1]);

			Object[] args = hand(req, resp, method);

			method.invoke(insetance, args);
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

}

到这里就完成了。 可以测试下  :

好了。告辞

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值