本文主要写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);
}
}