项目结构图

手写Spring源码-LMLPHP

项目依赖

<dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>servlet-api</artifactId>
      <version>2.5</version>
    </dependency>
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.16.20</version>
    </dependency>
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-lang3</artifactId>
      <version>3.4</version>
    </dependency>
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>1.2.38</version>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>jsp-api</artifactId>
      <version>2.0</version>
    </dependency>
  </dependencies>

具体实现

    1. Annotation

       UVAutowired:

/*
 * @author uv
 * @date 2018/9/29 10:00
 * 注入
 */

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

    UVController

/*
 * @author uv
 * @date 2018/9/29 9:58
 *
 */

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

    UVRequestMapping

/*
 * @author uv
 * @date 2018/9/29 9:59
 *
 */

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

    UVRequestParam

/*
 * @author uv
 * @date 2018/9/29 10:59
 *
 */

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

   UVResponseBody

/*
 * @author uv
 * @date 2018/9/30 14:06
 *
 */

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.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface UVResponseBody {

}

   UVService

/*
 * @author uv
 * @date 2018/9/29 9:57
 *
 */

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

   2. core

    MethodHandler

/*
 * @author uv
 * @date 2018/9/30 10:33
 *
 */

import java.lang.reflect.Method;
import java.util.List;
import lombok.Data;

@Data
public class MethodHandler {

    //方法所在的类
    private Object object;

    private Method method;
    //参数顺序
    private List<String> params;
    //参数列表中是否有UVModel,如果有则modelIndex为参数列表的索引,否则为-1
    private Integer modelIndex = -1;
}

  UVModel

/*
 * @author uv
 * @date 2018/9/30 15:06
 *
 */

import java.util.HashMap;
import java.util.Set;

public class UVModel extends HashMap<String, Object>{


    public void addAttribute(String key, Object value) {
        super.put(key, value);
    }
    public Object getAttribute(String key) {
        return super.get(key);
    }
    @Override
    public Set<Entry<String, Object>> entrySet() {
        return super.entrySet();
    }
}

3. servlet 

/*
 * @author uv
 * @date 2018/9/28 19:51
 * 调度中心,分发请求,IOC
 */

import com.alibaba.fastjson.JSON;
import com.spring.annotation.UVAutowried;
import com.spring.annotation.UVController;
import com.spring.annotation.UVRequestMapping;
import com.spring.annotation.UVRequestParam;
import com.spring.annotation.UVResponseBody;
import com.spring.annotation.UVService;
import com.spring.core.MethodHandler;
import com.spring.core.UVModel;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.net.URL;
import java.util.ArrayList;
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 org.apache.commons.lang3.StringUtils;

public class UVDispatcherServlet extends HttpServlet {

    //spring配置文件
    private Properties properties = new Properties();
    //存放所有带注解的类
    private List<String> classNameList = new ArrayList<>();
    //IOC容器
    private Map<String, Object> IOC = new HashMap<>();
    //url 到controller方法的映射
    private Map<String, MethodHandler> urlHandler = new HashMap<>();

    @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 {
        //6、处理请求,执行相应的方法
        doHandler(req, resp);
    }

    @Override
    public void init() throws ServletException {

        System.out.println("servlet开始初始化");
        //1、加载配置文件 spring-config.properties,获取扫描路径
        doLoadConfig();
        //2、扫描配置的路径下的带有注解的类
        doScanner(properties.getProperty("basepackage"));
        //3、初始化所有的类,被放入到IOC容器中
        doPutIoc();
        //4、实现@UVAutowried自动注入
        doAutowried();
        //5、初始化HandlerMapping,根据url映射不同的controller方法
        doMapping();
        System.out.println("servlet初始化完成");
    }

    //1、加载配置文件 spring-config.properties,获取扫描路径
    private void doLoadConfig() {
        //ServletConfig:代表当前Servlet在web.xml中的配置信息
        ServletConfig config = this.getServletConfig();
        String configLocation = config.getInitParameter("contextConfigLocation");
        InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(configLocation);
        try {
            properties.load(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //2、扫描配置的路径下的带有注解的类
    private void doScanner(String path) {
        //java文件
        if (path.endsWith(".class")) {
            //获取到带有包路径的类名
            String className = path.substring(0, path.lastIndexOf(".class"));
            //扫描的类
            classNameList.add(className);
            return;
        }
        URL url = this.getClass().getClassLoader().getResource("/" + path.replaceAll("\\.", "/"));
        //是包路径,继续迭代
        File file = new File(url.getFile());
        File[] files = file.listFiles();
        for (File f : files) {
            doScanner(path + "." + f.getName());
        }
    }

    //3、初始化所有的类,被放入到IOC容器中
    private void doPutIoc() {
        if (classNameList.isEmpty()) {
            return;
        }
        try {
            for (String className : classNameList) {
                //反射获取实例对象
                Class<?> clazz = Class.forName(className);
                //IOC容器key命名规则:1.默认类名首字母小写  2.使用用户自定义名,如 @UVService("abc") 3.如果service实现了接口,可以使用接口作为key

                //controler,service注解类
                if (clazz.isAnnotationPresent(UVController.class)) {
                    UVController uvController = clazz.getAnnotation(UVController.class);
                    String beanName = uvController.value().trim();
                    //如果用户没有定义名称,使用名首字母小写
                    if (StringUtils.isBlank(beanName)) {
                        beanName = lowerFirstCase(clazz.getSimpleName());
                    }
                    IOC.put(beanName, clazz.newInstance());
                } else if (clazz.isAnnotationPresent(UVService.class)) {
                    UVService uvService = clazz.getAnnotation(UVService.class);
                    String beanName = uvService.value().trim();
                    //如果用户没有定义名称,使用名首字母小写
                    if (StringUtils.isBlank(beanName)) {
                        beanName = lowerFirstCase(clazz.getSimpleName());
                    }
                    Object object = clazz.newInstance();
                    //将实例化的对象放到到容器中
                    IOC.put(beanName, object);
                    //如果service实现了接口,可以使用接口作为key
                    //取到service实现的接口
                    Class<?>[] interfaces = clazz.getInterfaces();
                    for (Class<?> interf : interfaces) {
                        IOC.put(lowerFirstCase(interf.getSimpleName()), object);
                    }
                } else {
                    continue;
                }
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    //4、实现@UVAutowried自动注入
    private void doAutowried() {
        if (IOC.isEmpty()) {
            return;
        }
        for (Entry<String, Object> entry : IOC.entrySet()) {
            //获取变量
            Field[] fields = entry.getValue().getClass().getDeclaredFields();
            for (Field field : fields) {
                //private、protected修饰的变量可访问
                field.setAccessible(true);

                if (!field.isAnnotationPresent(UVAutowried.class)) {
                    continue;
                }

                String beanName = field.getAnnotation(UVAutowried.class).value().trim();
                //如果value为空,则使用根据变量名注入,否则根据定义的value注入
                if (StringUtils.isBlank(beanName)) {
                    beanName = field.getType().getName();
                }
                try {
                    //向obj对象的这个Field设置新值value,依赖注入
                    field.set(entry.getValue(), IOC.get(beanName));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    //5、初始化HandlerMapping,根据url映射不同的controller方法
    private void doMapping() {
        if (IOC.isEmpty()) {
            return;
        }
        for (Entry<String, Object> entry : IOC.entrySet()) {
            Class<?> clazz = entry.getValue().getClass();
            //判断是否是controller
            if (!clazz.isAnnotationPresent(UVController.class)) {
                continue;
            }
            String startUrl = "/";
            //判断controller类上是否有UVRequestMapping注解,如果有则拼接url
            if (clazz.isAnnotationPresent(UVRequestMapping.class)) {
                UVRequestMapping requestMapping = clazz.getAnnotation(UVRequestMapping.class);
                String value = requestMapping.value();
                if (!StringUtils.isBlank(value)) {
                    startUrl += value;
                }
            }
            //遍历controller类中UVRequestMapping注解修饰的方法,添加到urlHandler中,完成url到方法的映射
            Method[] methods = clazz.getDeclaredMethods();
            for (Method method : methods) {
                if (!method.isAnnotationPresent(UVRequestMapping.class)) {
                    continue;
                }
                UVRequestMapping annotation = method.getAnnotation(UVRequestMapping.class);
                String url = startUrl + "/" + annotation.value().trim();
                //解决多个/重叠的问题
                url = url.replaceAll("/+", "/");

                MethodHandler methodHandler = new MethodHandler();
                //放入方法
                methodHandler.setMethod(method);
                try {
                    //放入方法所在的controller
                    methodHandler.setObject(clazz.newInstance());
                } catch (Exception e) {
                    e.printStackTrace();
                }
                //放入方法的参数列表
                List<String> params = doParamHandler(method, methodHandler);
                methodHandler.setParams(params);
                urlHandler.put(url, methodHandler);
            }
        }
    }

    //6、处理请求,执行相应的方法
    private void doHandler(HttpServletRequest request, HttpServletResponse response) throws IOException {
        boolean jsonResult = false;
        String uri = request.getRequestURI();
        PrintWriter writer = response.getWriter();
        //没有映射的url,返回404
        if (!urlHandler.containsKey(uri)) {
            writer.write("404 Not Found");
            return;
        }
        //获取url对应的method包装类
        MethodHandler methodHandler = urlHandler.get(uri);
        //处理url的method
        Method method = methodHandler.getMethod();
        //method所在的controller
        Object object = methodHandler.getObject();
        //method的参数列表
        List<String> params = methodHandler.getParams();

        //如果controller或这个方法有UVResponseBody修饰,返回json
        if (object.getClass().isAnnotationPresent(UVResponseBody.class) || method.isAnnotationPresent(UVResponseBody.class)) {
            jsonResult = true;
        }
        List<Object> args = new ArrayList<>();
        for (String param : params) {
            //从request中获取参数,然后放入参数列表
            String parameter = request.getParameter(param);
            args.add(parameter);
        }
        //参数列表是否是否有model对象
        UVModel model = new UVModel();
        if (methodHandler.getModelIndex() != -1) {
            //将model对象注入到参数中
            args.set(methodHandler.getModelIndex(), model);
        }
        try {
            //执行方法,处理,返回结果
            Object result = method.invoke(object, args.toArray());
            //返回json(使用阿里的fastJson)
            if (jsonResult) {
                writer.write(JSON.toJSONString(object));
            } else { //返回视图
                //如果存在model,则处理model存的值,将其写入request域中
                if (methodHandler.getModelIndex() != -1) {
                    doModelHandler(model, request);
                }
                doResolveView((String) result, request, response);
            }
        } catch (Exception e) {
            e.printStackTrace();
            //方法执行异常,返回500
            writer.write("500 Internal Server Error");
            return;
        }

    }

    //8、视图解析,返回视图
    private void doResolveView(String indexView, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        //视图前缀
        String prefix = properties.getProperty("view.prefix");
        //视图后缀
        String suffix = properties.getProperty("view.suffix");
        String view = (prefix + indexView + suffix).trim().replaceAll("/+", "/");
        request.getRequestDispatcher(view).forward(request, response);
    }

    //处理字符串首字母小写
    private String lowerFirstCase(String str) {
        char[] chars = str.toCharArray();
        //ascii码计算
        chars[0] += 32;
        return String.valueOf(chars);
    }

    //处理method的参数
    /**
     在Java 8之前的版本,代码编译为class文件后,方法参数的类型是固定的,但参数名称却丢失了,
     这和动态语言严重依赖参数名称形成了鲜明对比。
     现在,Java 8开始在class文件中保留参数名,给反射带来了极大的便利。
     但是!!!!换成JDK8以后,也配置了编辑器,但是参数名始终不对,所以就暂时所有参数使用用@UVRequestParam
     **/
    private List<String> doParamHandler(Method method, MethodHandler methodHandler) {
        //参数名与顺序对应
        List<String> params = new ArrayList<>();
        Parameter[] parameters = method.getParameters();
        for (int i = 0; i < parameters.length; i++) {
            //是否有UVRequestParam注解修饰,如果有这是用注解定义的参数名,否则使用原参数名
            if (parameters[i].isAnnotationPresent(UVRequestParam.class)) {
                String paramName = parameters[i].getAnnotation(UVRequestParam.class).value().trim();
                params.add(paramName);
            } else {
                params.add(parameters[i].getName());
            }
            try {
                //如果参数列表中有UVmodel对象,记录索引
                if (parameters[i].getType().isInstance(new UVModel())) {
                    methodHandler.setModelIndex(i);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return params;
    }

    //处理model对象,放入request域中
    private void doModelHandler(UVModel model, HttpServletRequest request) {
        for (Entry<String, Object> entry : model.entrySet()) {
            request.setAttribute(entry.getKey(), entry.getValue());
        }
    }
}

4. Controller

import com.spring.annotation.UVAutowried;
import com.spring.annotation.UVController;
import com.spring.annotation.UVRequestMapping;
import com.spring.annotation.UVRequestParam;
import com.spring.annotation.UVResponseBody;
import com.spring.core.UVModel;
import com.uv.entity.User;
import com.uv.service.UserService;

/*
 * @author uv
 * @date 2018/9/29 10:46
 *
 */
@UVController
@UVRequestMapping("user")
public class UserController {

    @UVAutowried
    private UserService userService;

    @UVRequestMapping("user")
    @UVResponseBody
    public User getUser() {
        return userService.getUser();
    }
    /**
     在Java 8之前的版本,代码编译为class文件后,方法参数的类型是固定的,但参数名称却丢失了,
     这和动态语言严重依赖参数名称形成了鲜明对比。
     现在,Java 8开始在class文件中保留参数名,给反射带来了极大的便利。
     但是!!!!换成JDK8以后,也配置了编辑器,但是参数名始终不对,所以就暂时所有参数使用用@UVRequestParam
     **/
    @UVRequestMapping("hello")
    public String hello(@UVRequestParam("name") String name, @UVRequestParam("model") UVModel model) {
        model.addAttribute("name", name);
        return "hello";
    }

}

 User

import lombok.AllArgsConstructor;
import lombok.Data;

/*
 * @author uv
 * @date 2018/9/29 10:39
 *
 */
@Data
@AllArgsConstructor
public class User {

    private String id;

    private String name;

    private int age;

}

  UserSerive 和 UserServiceImpl

import com.uv.entity.User;

/*
 * @author uv
 * @date 2018/9/29 10:38
 *
 */
public interface UserService {

    public User getUser();

}
import com.spring.annotation.UVService;
import com.uv.entity.User;
import com.uv.service.UserService;

/*
 * @author uv
 * @date 2018/9/29 10:38
 *
 */
@UVService
public class UserServiceImpl implements UserService{

    public User getUser() {
        User user = new User("1", "Tom",18);
        return user;
    }

}

spring-config.properties

#指定扫描路径
basepackage=com.uv

#视图解析器
view.prefix=/WEB-INF/jsp/
view.suffix=.jsp

 web.xml

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name>

  <!--启动时加载的servlet,servlet为单实例多线程-->
  <servlet>
    <servlet-name>myspring</servlet-name>
    <servlet-class>com.spring.servlet.UVDispatcherServlet</servlet-class>
    <!-- 指定Spring的配置文件 -->
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>
        spring-config.properties
      </param-value>
    </init-param>
    <!--启动后立即加载servlet-->
    <load-on-startup>1</load-on-startup>
  </servlet>
  <!--配置映射到servlet的路径-->
  <servlet-mapping>
    <servlet-name>myspring</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>

  hello.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
  <title>Title</title>
</head>
<body>
<h1>Hello Tom!</h1>
</body>
</html>

 

10-04 16:21