300行代码手写 spring+spring mvc

本文主要写spring 的主干部分,麻雀虽小,五脏俱全

使用注解方式来手写spring,直借助servlet.jar包

项目体系结构是

红线部分为书写主要内容,其他类是测试,先看一下我们的测试类,为区分和spring的注解冲突,自己开发注解

package sdibt.lxj.demo.controller;

import sdibt.lxj.annotation.MyAutowired;
import sdibt.lxj.annotation.MyController;
import sdibt.lxj.annotation.MyRequestMapping;
import sdibt.lxj.demo.service.ITestService;

@MyController
@MyRequestMapping("/test")
public class TestController {
	
	@MyAutowired
	private ITestService testService;
	
	
	
	@MyRequestMapping("/add")
	public void add(String name){
		
		testService.add(name);
	}
	
	@MyRequestMapping("/query")
	public void query(String name, String age){
		
		testService.query(name, age);
	}
}

注解

package sdibt.lxj.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;

/**
 * 
 * Title:MyController
 * @author lxj
 * package:sdibt.lxj.annotation
 * date:2018年10月12日 下午4:43:02
 * version:1.0
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyController {
	String value() default "";
}
package sdibt.lxj.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;

/**
 * 
 * Title:MyService
 * @author lxj
 * package:sdibt.lxj.annotation
 * date:2018年10月12日 下午4:43:14
 * version:1.0
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyService {
	String value() default "";
}
package sdibt.lxj.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;

/**
 * 
 * Title:MyAutowired
 * @author lxj
 * package:sdibt.lxj.annotation
 * date:2018年10月12日 下午4:42:59
 * version:1.0
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAutowired {
	String value() default "";
}
package sdibt.lxj.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;

/**
 * 
 * Title:MyRequestMapping
 * @author lxj
 * package:sdibt.lxj.annotation
 * date:2018年10月12日 下午4:43:07
 * version:1.0
 */
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyRequestMapping {
	String value() default "";
}

在使用的spring的时候,通常需要配置一个application.xml文件,这里用application.properties代替,主要是获取要扫描的包

还要配置web.xml文件,tomcat启动时加载DispatcherServlet

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
  <display-name>MySpring</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
  </welcome-file-list>
  <servlet>
  	<servlet-name>mymvc</servlet-name>
  	<servlet-class>sdibt.lxj.mvcfarmework.MyDispatcherServlet</servlet-class>
  	<init-param>
  		<param-name>contextConfigLocation</param-name>
  		<param-value>application.properties</param-value>
  	</init-param>
  	<load-on-startup>1</load-on-startup>
  </servlet>
  
  <servlet-mapping>
  	<servlet-name>mymvc</servlet-name>
  	<url-pattern>/*</url-pattern>
  </servlet-mapping>
</web-app>

下面就是最重要的一个类,DispatcherServlet

package sdibt.lxj.mvcfarmework;
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.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import sdibt.lxj.annotation.MyAutowired;
import sdibt.lxj.annotation.MyController;
import sdibt.lxj.annotation.MyRequestMapping;
import sdibt.lxj.annotation.MyService;
/**
 * 
 * Title:MyDispatcherServlet
 * @author lxj
 * package:sdibt.lxj.mvcfarmework
 * date:2018年10月12日 下午4:07:17
 * version:1.0
 */
public class MyDispatcherServlet extends HttpServlet{
	//配置文件
	private Properties contextConfig = new Properties();
	//存放class
	private List<String> classNames = new ArrayList<String>();
	//ioc容器
	private Map<String,Object> ioc = new HashMap<String,Object>();
	//请求与方法映射
	private Map<String, Method> handlerMapping = new HashMap<String,Method>();
	
	@Override
	public void init(ServletConfig config) throws ServletException {
		// init
		//1.加载配置文件
		doLoadConfig(config.getInitParameter("contextConfigLocation"));
		
		//2.解析配置文件,扫描所有先关的类
		doScanner(contextConfig.getProperty("scanPackage"));
		
		//3.初始化所有的相关的类,并且放入到IOC中
		doInstance();
		
		//4.完成自动化的依赖注入(DI)
		doAutowired();
		
		//5.创建HanlerMapping将url和method建立对应关系
		initHandlerMapping();		
		
		System.out.println("---------------初始化完成--------------");
	}
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		doPost(req,resp);
	}
	
	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {			
		try {
			//找到对应的方法执行
			doDispatch(req,resp);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			resp.getWriter().write("500 Exception"+Arrays.toString(e.getStackTrace()));
			e.printStackTrace();
		}
	}

	/**
	 * 加载配置文件
	 * @param contextConfigLocation
	 */
	private void doLoadConfig(String contextConfigLocation) {
		// TODO Auto-generated method stub
		//从类路径下去的properties
		InputStream in = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
		try {
			//加载配置文件到contextConfig
			contextConfig.load(in);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
			if(null != in){
				try {
					in.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
	/**
	 * 解析配置文件,扫描所有先关的类
	 * @param package
	 */
	private void doScanner(String scanPackage) {
		// TODO Auto-generated method stub
		//将文件下的"."转换成文件路径
		URL url = this.getClass().getClassLoader().getResource("/"+scanPackage.replaceAll("\\.", "/"));
		//获取扫描的路径 转换成文件
		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;
				}
				
				String className = (scanPackage+"."+file.getName().replace(".class", "")).trim();			
				classNames.add(className);
			}			
		}		
		
	}
	
	/**
	 * 加载扫描的类到ioc
	 */
	private void doInstance() {
		// TODO Auto-generated method stub
		if(classNames.isEmpty()){return;}
		
		try {
			//遍历所有的class文件
			for (String className : classNames) {
				//拿到class文件的类信息
				Class<?> clazz = Class.forName(className);
				//只有打了注解的类的才可以放入到ioc中
				if(clazz.isAnnotationPresent(MyController.class)){
					//获取类名,但是类名第一个字母是大写,需要将第一个字母小写
					String beanName =  lowerFirstCase( clazz.getSimpleName());
					ioc.put(beanName, clazz.newInstance());
					
				}else if(clazz.isAnnotationPresent(MyService.class)){					
					//要把实现类注入给接口,有三种情况
					//1.用类名首字母小写,默认赋值
					//2.自定义命名
					MyService service = clazz.getAnnotation(MyService.class);
					String beanName = service.value();
					//如果没自定义命名,就证明使用默认赋值
					if("".equals(beanName.trim())){
						beanName = lowerFirstCase(clazz.getSimpleName());
					}
					Object instance = clazz.newInstance();
					ioc.put(beanName,instance);
					//3.用接口的全称作为key,用接口的实现类实例作为值
					//拿到改类的所有接口
					Class<?>[] interfaces = clazz.getInterfaces();
					for (Class<?> i : interfaces) {
							//如果ioc中存在该名称,抛出异常(该接口有多个实现类,必须只能有一个实现类才可以赋值)
							if(ioc.containsKey(i.getName())){
								throw new Exception("The beaName is exists!");
							}
							ioc.put(i.getName(), instance);
						}	
									
				}else{
					continue;
				}
				
			}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}		
		
	}
	/**
	 * DI注入
	 */
	private void doAutowired() {
		// TODO Auto-generated method stub
		if(ioc.isEmpty()){ return; }
		
		//遍历ioc,对每个对象进行DI		
		for (Map.Entry<String, Object> entry : ioc.entrySet()) {
			//拿到该类的所有属性
			Field[] fields = entry.getValue().getClass().getDeclaredFields();
			
			for (Field field : fields) {
				//如果该属性没有加Autowired注解,不对该属性进行操作
				if(!field.isAnnotationPresent(MyAutowired.class)){ continue; }
				
				MyAutowired autowired = field.getAnnotation(MyAutowired.class);
				String beanName = autowired.value();
				
				if("".equals(beanName)){
					beanName = field.getType().getName();
				}
				
				//强制注入
				field.setAccessible(true);
				
				try {
					field.set(entry.getValue(), ioc.get(beanName));
				} catch (IllegalArgumentException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}				
				
			}
		}
		
	}
	/**
	 * 初始化,url与方法映射
	 */
	private void initHandlerMapping() {
		// TODO Auto-generated method stub
		if(ioc.isEmpty()){ return;}
		//遍历ioc
		for (Map.Entry<String, Object> entry : ioc.entrySet()) {
			Class<?> clazz = entry.getValue().getClass();
			
			//判断该类是不是Controller
			if(!clazz.isAnnotationPresent(MyController.class)){ continue; }
			
			String namespace = "";
			//获取该类的namespace
			if(clazz.isAnnotationPresent(MyRequestMapping.class)){ 
				MyRequestMapping equestMapping = clazz.getAnnotation(MyRequestMapping.class);
				
				namespace = equestMapping.value();
				
			}
			//获取该类的所有公开方法
			Method[] methods = clazz.getMethods();
			//遍历所有的方法
			for (Method method : methods) {
				//只有加了requestMapping注解的方法才可以放入到mapping中
				
				if(!method.isAnnotationPresent(MyRequestMapping.class)){ continue ;}
				//获取该方法上的mapping
				MyRequestMapping requestMapping = method.getAnnotation(MyRequestMapping.class);
				String value = requestMapping.value();
				
				String url = ("/"+namespace+"/"+value).replaceAll("/+", "/");
				
				handlerMapping.put(url, method);
			}
			
		}		
		
	}

	/**
	 * 将类名的第一个字母大写变成小写
	 * @param simpleName
	 * @return
	 */
	private String lowerFirstCase(String simpleName) {
		// TODO Auto-generated method stub
		char [] chars = simpleName.toCharArray();
		chars[0]+=32;
		return String.valueOf(chars);
	}
	
	/**
	 * 运行阶段,,将请求的url找到对应的方法执行
	 * @param req
	 * @param resp
	 * @throws Exception
	 */
	private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
		// TODO Auto-generated method stub
		if(this.handlerMapping.isEmpty()){ return ;}
		
		//获取请求的路径,将路径转换成handlerMapping中的key
		String url = req.getRequestURI();
		String contextPath = req.getContextPath();
		url = url.replace(contextPath, "").replaceAll("/+", "/");
		
		if(!this.handlerMapping.containsKey(url)){
			resp.getWriter().write("404 Not Found");
			return ;
		}
		
		//如果url与handlerMapping中的key对应,就执行方法
		Method method = this.handlerMapping.get(url);
		//获取请求的所有的参数
		Map<String, String[]> params = req.getParameterMap();
		String[] paramsStr = new String[params.size()] ;
		int i=0;
		for (Map.Entry<String, String[]> param : params.entrySet()) {
			paramsStr[i]=param.getValue()[0];
			i++;
		}
		//拿到controller对象
		String beanName = lowerFirstCase(method.getDeclaringClass().getSimpleName());
		//执行该方法
		method.invoke(ioc.get(beanName), paramsStr);
	}	
}

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值