手写spring核心原理Version1

此篇博文主要通过一个简单的demo演示spring的核心原理,例如spring是如何根据@RequestMapping、@Autowired等注解进行路径匹配和自动注入的。 源码地址:https://github.com/yanzxu/spring-learning/tree/master/spring5/src/main/java/com/xyz/demo/core

自定义注解

@MyAutowired
package com.xyz.demo.core.annotaion;
import java.lang.annotation.*;

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAutowired {
    String value() default "";
}
@MyController
package com.xyz.demo.core.annotaion;
import java.lang.annotation.*;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyController {
    String value() default "";
}
@MyRequestMapping
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyRequestMapping {
    String value() default "";
}
@MyRequestParam
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyRequestParam {
    String value() default "";
}
@MyService
package com.xyz.demo.core.annotaion;
import java.lang.annotation.*;

@Target({ElementType.TYPE})
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyService {
    String value() default "";
}

自定义Controller

DemoController

package com.xyz.demo.core.controller;

import com.xyz.demo.core.annotaion.MyAutowired;
import com.xyz.demo.core.annotaion.MyController;
import com.xyz.demo.core.annotaion.MyRequestMapping;
import com.xyz.demo.core.annotaion.MyRequestParam;
import com.xyz.demo.core.service.DemoServiceImpl;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@MyController
@MyRequestMapping("/demo")
public class DemoController {
    @MyAutowired
    private DemoServiceImpl demoService;

    @MyRequestMapping("/query")
    public void query(HttpServletRequest request, HttpServletResponse response, @MyRequestParam("name") String name){
        String result = demoService.getName(name);

        try {
            response.getWriter().write(result);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

自定义Service

DemoService

package com.xyz.demo.core.service;

public interface DemoService {
     String getName(String name);
}

service实现类

package com.xyz.demo.core.service;
import com.xyz.demo.core.annotaion.MyService;

@MyService
public class DemoServiceImpl implements DemoService{
    @Override
    public String getName(String name){
        return "My name is: " + name;
    }
}

自定义Dispatcher

MyDispatcherServlet

Dispatcher是该demo的核心类

package com.xyz.demo.core;

import com.xyz.demo.core.annotaion.MyAutowired;
import com.xyz.demo.core.annotaion.MyController;
import com.xyz.demo.core.annotaion.MyRequestMapping;
import com.xyz.demo.core.annotaion.MyService;
import org.springframework.util.StringUtils;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
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.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

public class MyDispatcherServlet extends HttpServlet {
    private Map<String, Object> resourceHolder = 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) {
            resp.getWriter().write("500 Exception " + Arrays.toString(e.getStackTrace()));
        }
    }

    private void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        String url = request.getRequestURI().replace(request.getContextPath(), "").replaceAll("/+", "/");

        if (!resourceHolder.containsKey(url)) {
            response.getWriter().write("404 not found: " + url);
            return;
        }

        Method method = (Method) resourceHolder.get(url);

        Map<String, String[]> params = request.getParameterMap();
        method.invoke(resourceHolder.get(method.getDeclaringClass().getName()),
                request, response, params.get("name")[0]);
    }

    @Override
    public void init(ServletConfig config) throws ServletException {
        InputStream inputStream = null;

        try {
            Properties properties = new Properties();
            inputStream = this.getClass().getClassLoader().getResourceAsStream(config.getInitParameter("contextConfigLocation"));
            properties.load(inputStream);
            String scanPackage = properties.getProperty("scanPackage");

            scanPackages(scanPackage);

            for (String className : resourceHolder.keySet()) {
                if (!className.contains(".")) {
                    continue;
                }

                Class<?> clazz = Class.forName(className);
                if (clazz.isAnnotationPresent(MyController.class)) {
                    resourceHolder.put(className, clazz.newInstance());
                    String baseUrl = "";
                    if (clazz.isAnnotationPresent(MyRequestMapping.class)) {
                        baseUrl = clazz.getAnnotation(MyRequestMapping.class).value();
                    }

                    Method[] methods = clazz.getMethods();
                    for (Method method : methods) {
                        if (!method.isAnnotationPresent(MyRequestMapping.class)) {
                            continue;
                        }

                        String url = (baseUrl + "/" + method.getAnnotation(MyRequestMapping.class).value()).replaceAll("/+", "/");
                        resourceHolder.put(url, method);
                        System.out.println("==== add resource to holder, key: " + url + "  value: " + method);
                    }
                } else if (clazz.isAnnotationPresent(MyService.class)) {
                    String beanName = clazz.getAnnotation(MyService.class).value();
                    if (StringUtils.isEmpty(beanName)) {
                        beanName = clazz.getName();
                    }

                    Object instance = clazz.newInstance();
                    resourceHolder.put(beanName, inputStream);

                    for (Class<?> i : clazz.getInterfaces()) {
                        resourceHolder.put(i.getName(), instance);
                    }
                }
            }

            for (Object object : resourceHolder.values()) {
                if (object == null) {
                    continue;
                }

                Class<?> clazz = object.getClass();
                if (clazz.isAnnotationPresent(MyController.class)) {
                    Field[] fields = clazz.getDeclaredFields();
                    for (Field field : fields) {
                        if (!field.isAnnotationPresent(MyAutowired.class)) {
                            continue;
                        }

                        String beanName = field.getAnnotation(MyAutowired.class).value();
                        if (StringUtils.isEmpty(beanName)) {
                            beanName = field.getType().getName();
                        }

                        field.setAccessible(true);
                        try {
                            field.set(resourceHolder.get(clazz.getName()), resourceHolder.get(beanName));
                        } catch (IllegalAccessException e) {
                            System.out.println("Exception: 自动注入bean失败,beanName: " + beanName);
                        }
                    }
                }
            }
        } catch (Exception e) {
            System.out.println("Error: init dispatcher失败");
            e.printStackTrace();
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private void scanPackages(String packagePath) {
        URL resource = this.getClass().getClassLoader().getResource("/" + packagePath.replaceAll("\\.", "/"));
        File[] files = new File(resource.getFile()).listFiles();

        for (File file : files) {
            if (file.isDirectory()) {
                scanPackages(packagePath + "." + file.getName());
            } else {
                if (file.getName().endsWith(".class")) {
                    String className = packagePath + "." + file.getName().replace(".class", "");
                    resourceHolder.put(className, null);
                }
            }
        }
    }
}
  • 该demo的源码已上传至GitHub
  • 该demo只是最初的原始版本,下篇博文手写spring核心原理Version2将运用设计模式对其进行重构
  • 该demo源码参考自书籍《Spring 5核心原理与30个类手写实战》
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值