手写spring框架

本文介绍了作者在学习tom老师手写Spring直播后,自己动手实现Spring框架的过程。文章涵盖了配置阶段、初始化阶段和运行阶段的详细步骤,包括web.xml的配置、注解的使用、IOC容器的初始化、DI操作、MVC的实现等。虽然未实现AOP功能,但完整地展示了Spring的基础工作流程。
摘要由CSDN通过智能技术生成

听了tom老师的手写spring直播之后,今日用代码实现了手写spring框架,并没有实现aop功能
在这里插入图片描述
1)配置阶段
配置 web.xml:
MDispatchServlet
设定 init-param: contextConfigLocation = applicationContext.properties

设定 url-pattern: /*
配置 Annotation: @MController @MService @MAutowired @MRequestMapping
2)初始化阶段
IOC:
调用 init() 方法: 加载配置文件
IOC 容器初始化: Map<String, Object>
扫描相关的类: scan-package=“com.matao.spring”
创建实例化并保存到容器: 同过反射机制将类实例化放入 IOC 容器中
DI:
进行 DI 操作: 扫描 IOC 容器中的实例,给没有赋值的属性自动赋值
MVC:
初始化 HandlerMapping: 将一个 URL 和一个 Method 进行一对一的关联映射 Map<String, Method>
3)运行阶段
调用 doGet() / doPost() 方法: Web 容器调用 doGet() / doPost() 方法,获得 request/response 对象
匹配 HandleMapping: 从 request 对象中获得用户输入的 url,找到其对应的 Method
反射调用 method.invoker(): 利用反射调用方法并返回结果
response.getWrite().write(): 将返回结果输出到浏览器

项目:
在这里插入图片描述

pom文件:`

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.bjsxt</groupId>
	<artifactId>springmvc-ac</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>war</packaging>
	<!--添加servlet依赖 -->
	<dependencies>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>javax.servlet-api</artifactId>
			<version>3.1.0</version>
			<scope>provided</scope>
		</dependency>
	</dependencies>
	
	 <build>
      <plugins>
         <!-- 配置Tomcat插件 -->
         <plugin>
            <groupId>org.apache.tomcat.maven</groupId>
            <artifactId>tomcat7-maven-plugin</artifactId>
            <configuration>
               <path>/</path>
               <port>8080</port>
            </configuration>
         </plugin>
      </plugins>
   </build>
</project>

如果项目启动报ClassCastException异常,要加上 provided

web.xml文件代码:

<?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_2_5.xsd"
	version="2.5">
	
	
	<servlet>
		<servlet-name>mmvc</servlet-name>
		<servlet-class>com.matao.spring.MDispatchServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<!--you can't use classpath*: -->
			<param-value>application.properties</param-value>
		</init-param>
	</servlet>
	<servlet-mapping>
		<servlet-name>mmvc</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>
</web-app>

application.properties代码

application.properties

注解: MAutowired.java代码

/**
 * 
 */
package com.matao.spring.annotion;

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 MAutowired {
	String value() default "";

}

MController.java代码

/**
 * 
 */
package com.matao.spring.annotion;

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 MT
 *
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MController {
	String value() default "";
}

MRequsetMapping.java代码

/**
 * 
 */
package com.matao.spring.annotion;

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 MT
 *
 */
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MRequsetMapping {
	String value() default "";
}

MService.java代码:

/**
 * 
 */
package com.matao.spring.annotion;

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 MT
 *
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MService {
	String value() default "";

}

service接口代码:

/**
 * 
 */
package com.matao.spring.test.service;

/**
 * @author MT
 *
 */
public interface ITestService {

	/**
	 * @return
	 */
	public String listClassName();
}

serviceImpl代码:

/**
 * 
 */
package com.matao.spring.test.service;

import com.matao.spring.annotion.MService;

/**
 * @author MT
 *
 */
@MService
public class TestServiceImpl implements ITestService{

	/* (non-Javadoc)
	 * @see com.matao.spring.test.service.ITestService#listClassName()
	 */
	@Override
	public String listClassName() {
		// TODO Auto-generated method stub
		return "classname is found";
	}

}

Controller代码:

/**
 * 
 */
package com.matao.spring.test;

import java.io.IOException;

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

import com.matao.spring.annotion.MAutowired;
import com.matao.spring.annotion.MController;
import com.matao.spring.annotion.MRequsetMapping;
import com.matao.spring.test.service.ITestService;


/**
 * @author MT
 *
 */
@MController
@MRequsetMapping("/test")
public class MyController {
	
	    @MAutowired
	    ITestService testService;

	    /**
	     * 测试方法 /test/query
	     *
	     * @param req  请求体
	     * @param resp 响应体
	     */
	    @MRequsetMapping("/query")
	    public void query(HttpServletRequest req, HttpServletResponse resp) {

	        if (req.getParameter("username") == null) {
	           try {
	                resp.getWriter().write("param username is null");
	            } catch (IOException e) {
	                e.printStackTrace();
	            }
	        } else {

	            String paramName = req.getParameter("username");
	            try {
	                resp.getWriter().write("param username is " + paramName);
	            } catch (IOException e) {
	                e.printStackTrace();
	            }
	            System.out.println("[INFO-req] New request param username-->" + paramName);
	        }
	    }

	    /**
	     * 测试方法 /test/listClassName
	     *
	     * @param req  请求体
	     * @param resp 响应体
	     */
	    @MRequsetMapping("/listClassName")
	    public void listClassName(HttpServletRequest req, HttpServletResponse resp) {
	        String str = testService.listClassName();
	        System.out.println("testXService----------=-=-=>" + str);
	        try {
	            resp.getWriter().write(str);
	        } catch (IOException e) {
	            e.printStackTrace();
	        }
	    }

}

MDisptchServlet代码,主要代码

/**
 * 
 */
package com.matao.spring;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
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.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
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 com.matao.spring.annotion.MAutowired;
import com.matao.spring.annotion.MController;
import com.matao.spring.annotion.MRequsetMapping;
import com.matao.spring.annotion.MService;

/**
 * @author MT
 *
 */
public class MDispatchServlet extends HttpServlet{
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	/**
	 * 属性配置文件
	 */
	private Properties contextConfig = new Properties();
	private List<String> classNameList = new ArrayList<>();
	
	/**
	 * IOC容器
	 */
	Map<String, Object> iocMap = new HashMap<>();
	
	Map<String, Method> handlerMapping = new HashMap<>();

	@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 {
			doDispatch(req,resp);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			resp.getWriter().write("500 Exception Detail\n " + Arrays.toString(e.getStackTrace()));
		} 
	}


	/**
	 * @param req
	 * @param resp
	 * @throws InvocationTargetException 
	 * @throws IllegalArgumentException 
	 * @throws IllegalAccessException 
	 */
	private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		String url = req.getRequestURI();
		
		String contextPath = req.getContextPath();
		
		url = url.replaceAll(contextPath, "").replaceAll("/+", "/");
		
		if (!this.handlerMapping.containsKey(url)) {
			try {
				resp.getWriter().write("404 NOT FOUND");
				return;
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
		}
		
		Method method = this.handlerMapping.get(url);
		
		String beanName = toLowerFirstCase(method.getDeclaringClass().getSimpleName());
		
		method.invoke(iocMap.get(beanName), req,resp);
	}

	@Override
	public void init(ServletConfig config) throws ServletException {
		//加载配置文件
		doLoadConfig(config.getInitParameter("contextConfigLocation"));
		
		//扫描相关类
		doScanner(contextConfig.getProperty("scan-package"));
		
		//初始化IOC容器,将所有的相关类实例化 并放入到IOC容器中
		doInstance();
		
		//依赖注入
		doAutowired();
		
		//初始化HandlerMapping,实现一对一
		initHandlerMapping();
		
		System.out.println("Spring IOC is start");
		
		 //打印数据
        doTestPrintData();
	}

	/**
	 * 实现初始化HandlerMapping一对一映射功能
	 */
	private void initHandlerMapping() {
		System.out.println("initHandlerMapping");
		if (iocMap.isEmpty()) {
			System.out.println("ioc is empty");
			return;
		}
		
		for(Entry<String, Object> entry : iocMap.entrySet()){
			Class<?> clazz = entry.getValue().getClass();
			if (!clazz.isAnnotationPresent(MController.class)) {
				continue;
			}
			
			String baseUrl = "";
			
			if (clazz.isAnnotationPresent(MRequsetMapping.class)) {
				
				MRequsetMapping mRequestMapping = clazz.getAnnotation(MRequsetMapping.class);
				
				baseUrl = mRequestMapping.value();
			}
			
			for(Method method : clazz.getMethods()){
				if (!method.isAnnotationPresent(MRequsetMapping.class)) {
					continue;
				}
				
				MRequsetMapping mRequestMapping = method.getAnnotation(MRequsetMapping.class);
				String url = (baseUrl + mRequestMapping.value().replaceAll("/+", "/"));
				handlerMapping.put(url, method);
				
			}
		}
		
	}

	/**
	 * 实现依赖注入功能
	 */
	private void doAutowired() {
		if (iocMap.isEmpty()) {
			return;
		}
		
		for(Entry<String,Object> entry : iocMap.entrySet()){
			Field[] fields = entry.getValue().getClass().getDeclaredFields();
			
			for(Field field : fields){
				if (!field.isAnnotationPresent(MAutowired.class)) {
					continue;
				}
				
				MAutowired mAutowired = field.getAnnotation(MAutowired.class);
				String beanName = mAutowired.value().trim();
				
				//获取XAutowired注解的值
				if ("".equals(beanName)) {
					beanName = field.getType().getName();
				}
				
				//只要加了注解 都要加载 不管是private 还是protect
				
				field.setAccessible(true);
				
				try {
					field.set(entry.getValue(), iocMap.get(beanName));
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
		
	}

	/**
	 * 实现初始化容器功能
	 */
	private void doInstance() {
		System.out.println("doinstance");
		if (classNameList.isEmpty()) {
			System.out.println("classNamelist is empty");
			return;
		}
		
		try {
			
		for(String className : classNameList){
			
				Class<?> clazz = Class.forName(className);
				
			if (clazz.isAnnotationPresent(MController.class)) {
				//将类名首字母小写,默认bean是将首字母小写
				String beanName = toLowerFirstCase(clazz.getSimpleName());
				System.out.println("beanname is " + beanName );
				Object instance = clazz.newInstance();
				
				//添加到IOC容器中
				iocMap.put(beanName, instance);
			}else if (clazz.isAnnotationPresent(MService.class)) {
				String beanName = toLowerFirstCase(clazz.getSimpleName());
				//如果注解中含有自定义名称
				MService mService = clazz.getAnnotation(MService.class);
				if (!"".equals(mService.value())) {
					beanName = mService.value();
				}
				
				Object instance = clazz.newInstance();
				iocMap.put(beanName, instance);
				
				//寻找类的接口
				for(Class<?> i : clazz.getInterfaces()){
					if (iocMap.containsKey(i.getName())) {
						 throw new Exception("The Bean Name Is Exist.");
					}
					
					iocMap.put(i.getName(), instance);
				}
			
			}	
		}
		
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		
		
	}

	/**
	 * 实现扫描相关类功能
	 * @param property
	 */
	private void doScanner(String scanPackage) {
		//com.matao.spring... ==> /com/matao/spring/...
		URL url = this.getClass().getClassLoader().getResource(scanPackage.replaceAll("\\.", "/"));
		System.out.println("page is " + url);
		//判断url
		if (url == null) {
			return;
		}
		//获取路径下的文件
		File classPath = new File(url.getFile());
		for (File file : classPath.listFiles()) {
			if (file.isDirectory()) {
				//递归遍历子目录
				System.out.println("is directory");
				doScanner(scanPackage + "." + file.getName());
			}else {
				if (!file.getName().endsWith(".class")) {
					continue;
				}
				
				String className = (scanPackage + "." + file.getName().replace(".class", ""));
				
				//保存
				classNameList.add(className);
			}
		}
		
	}

	/**
	 * 实现加载配置文件功能
	 * @param initParameter
	 */
	private void doLoadConfig(String contextConfigLocation) {
		
		InputStream is = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
		
		try {
			contextConfig.load(is);
			System.out.println("property file has been loaded in properties");
		} catch (IOException e) {
			e.printStackTrace();
		}finally {
			if (is != null) {
				try {
					is.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
	/**
	 * 实现将首字母小写方法
	 * @param className
	 * @return
	 */
	private String toLowerFirstCase(String className){
		char[] chars = className.toCharArray();
		chars[0] += 32;
		return String.valueOf(chars);
	}
	
	 /**
     * 、打印数据
     */
    private void doTestPrintData() {

        System.out.println("[classNameList]-->");
        for (String str : classNameList) {
            System.out.println(str);
        }

        System.out.println("[iocMap]-->");
        for (Map.Entry<String, Object> entry : iocMap.entrySet()) {
            System.out.println(entry);
        }

        System.out.println("[handlerMapping]-->");
        for (Map.Entry<String, Method> entry : handlerMapping.entrySet()) {
            System.out.println(entry);
        }
    }


}

测试结果:
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值