Bootstrap

手写Spring框架

项目结构

├── pom.xml
├── spring-demo.iml
├── src
   └── main
       ├── java
       │   └── xyz
       │       └── suiwo
       │           ├── action
       │           │   ├── controller
       │           │   │   └── DemoController.java
       │           │   └── service
       │           │       ├── DemoService.java
       │           │       └── impl
       │           │           └── DemoServiceImpl.java
       │           └── framework
       │               ├── annotation
       │               │   ├── SWAutowried.java
       │               │   ├── SWController.java
       │               │   ├── SWRequestMapping.java
       │               │   ├── SWRequestParam.java
       │               │   └── SWService.java
       │               └── servlet
       │                   └── SWDispatcherServlet.java
       ├── resources
       │   └── application.properties
       └── web
           ├── WEB-INF
           │   └── web.xml
           └── index.jsp

所用依赖

<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>servlet-api</artifactId>
    <version>${servlet.api.version}</version>
    <scope>provided</scope>
</dependency>

web.xml配置如下

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <display-name>Suiwo Web Application</display-name>
    <servlet>
        <servlet-name>swmvc</servlet-name>
        <servlet-class>xyz.suiwo.framework.servlet.SWDispatcherServlet</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>swmvc</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
</web-app>

application.properties配置如下

scanPackage=xyz.suiwo.action

下面是几个我们自定义的注解类

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SWAutowried {
    String value() default "";
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SWController {
    String value() default "";
}
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SWRequestMapping {
    String value() default "";
}
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SWRequestParam {
    String value() default "";
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SWService {
    String value() default "";
}

下面我们新建几个controller类和service类并使用我们自定义的注解

@SWController
@SWRequestMapping("/get")
public class DemoController {

    @SWAutowried
    DemoService demoService;

    @SWRequestMapping("/method")
    public void get(HttpServletRequest request, HttpServletResponse response,
                    @SWRequestParam("name") String name){
        demoService.get();
        try {
            response.getWriter().write("my name is " + name);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
public interface DemoService {
    void get();
}
@SWService
public class DemoServiceImpl implements DemoService {
    public void get() {
        System.out.println("This is method get");
    }
}

最后让我们看一下我们的Dispatcher类

public class SWDispatcherServlet extends HttpServlet {

    private Properties properties = new Properties();

    private List<String> classNames = new ArrayList<>();

    private Map<String, Object> ioc = new HashMap<>();

    private Map<String, Method> handlerMapping = new HashMap<>();


    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doDispatch(req,resp);

        //注:当使用父类的doGet以及doPost可能会导致405错误
//        super.doGet(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
        doDispatch(req,resp);
//        doDispatch(req, resp);
    }

    @Override
    public void init(ServletConfig config)  {
        //1.加载配置文件
        doLoadConfig(config.getInitParameter("contextConfigLocation"));

        //2.扫描所有相关的类
        doScanner(properties.getProperty("scanPackage"));

        //3.初始化所有相关Class的实例,并且将其保存到IOC容器中
        doInstance();

        //4.自动化的依赖注入
        doAutowired();

        //5.初始化handlerMapping
        initHandlerMapping();

        System.out.println("初始化成功");
    }

    private void doLoadConfig(String location) {

        InputStream inputStream =null;
        try {
            inputStream = this.getClass().getClassLoader().getResourceAsStream(location);
            properties.load(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (null != inputStream) {
                    inputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    //进行扫描
    private void doScanner(String packageName) {
        URL url = this.getClass().getClassLoader()
                .getResource("/" + packageName.replaceAll("\\.", "/"));
        File classDir = new File(Objects.requireNonNull(url).getFile());
        for(File file : Objects.requireNonNull(classDir.listFiles())){

            //递归获取scan下所有的类,并将类名添加至classNames中用于之后实例化
            if(file.isDirectory()){
                doScanner(packageName + "." + file.getName());
            }else {
                String className = packageName + "." + file.getName().replace(".class","").trim();
                classNames.add(className);
            }
        }

    }

    //进行实例化
    private void doInstance() {
        if(classNames.isEmpty()){
            return;
        }
        for(String className : classNames){
            try {
                Class<?> clazz = Class.forName(className);

                //进行实例化
                //判断不是所有的都需要实例化,只有添加了例如Controller或者Service的注解才初始化
                if(clazz.isAnnotationPresent(SWController.class)){
                    //beanName beanId
                    //1.默认采用类名的首字母小写
                    //2.如果自定义了名字,默认使用自定义名字
                    //3.根据类型匹配,利用实现类的接口名作为Key
                    String beanName = toLowStr(clazz.getSimpleName());
                    ioc.put(beanName, clazz.newInstance());
                }else if(clazz.isAnnotationPresent(SWService.class)){
                    SWService swService = clazz.getAnnotation(SWService.class);
                    String beanName = swService.value();
                    if(!"".equals(beanName.trim())){
                        ioc.put(beanName, clazz.newInstance());
                        continue;
                    }

                    //获取对象所实现的所有接口
                    Class<?>[] interfaces = clazz.getInterfaces();
                    for(Class<?> i : interfaces){
                        ioc.put(i.getName(), clazz.newInstance());
                    }
                }
            } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {
                e.printStackTrace();
            }


        }
    }

    private void doAutowired() {
        if(ioc.isEmpty()){
            return;
        }

        for (Map.Entry<String, Object> entry : ioc.entrySet()){
            //在spring中没有隐私
            //咱们只认 @Autowried,获取所有属性
            Field[] fields = entry.getValue().getClass().getDeclaredFields();

            for(Field field : fields){
                if(!field.isAnnotationPresent(SWAutowried.class)){
                    continue;
                }

                SWAutowried swAutowried = field.getAnnotation(SWAutowried.class);

                String beanName = swAutowried.value().trim();
                //如果为空说明使用默认名字,所以使用getName获取默认名字
                if("".equals(beanName)){
                    beanName = field.getType().getName();
                }

                field.setAccessible(true);

                try {
                    //向属性set进已经实例化的对象
                    field.set(entry.getValue(), ioc.get(beanName));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }

    }

    private void initHandlerMapping() {
        if(!ioc.isEmpty()){
            for(Map.Entry<String, Object> entry : ioc.entrySet()){
                Class<?> clazz = entry.getValue().getClass();
                //HandlerMapping只认识SWController
                if(!clazz.isAnnotationPresent(SWController.class)){
                    continue;
                }
                String url = "";
                //获取类上的RequestMapping地址
                if(clazz.isAnnotationPresent(SWRequestMapping.class)){
                    SWRequestMapping swRequestMapping = clazz.getAnnotation(SWRequestMapping.class);
                    url = swRequestMapping.value();
                }
                Method[] methods = clazz.getMethods();
                for(Method method : methods){
                    if(!method.isAnnotationPresent(SWRequestMapping.class)){
                        continue;
                    }
                    //获取实际方法上的requestMapping
                    SWRequestMapping swRequestMapping = method.getAnnotation(SWRequestMapping.class);
                    String mUrl = url + swRequestMapping.value();
                    handlerMapping.put(mUrl, method);
                    System.out.println("Mapping : " + mUrl + " " + method);
                }

            }
        }
    }

    private void doDispatch(HttpServletRequest request, HttpServletResponse response) {
        String url = request.getRequestURI();
        String contextPath = request.getContextPath();
        url = url.replace(contextPath, "").replaceAll("/+", "/");
        if(!handlerMapping.containsKey(url)){
            try {
                response.getWriter().write("404");
                return;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        //获取当前路由对应的方法
        Method method = handlerMapping.get(url);
        System.out.println("获得对应的方法" + method);

        //获取方法列表
        Class<?>[] parameterTypes = method.getParameterTypes();
        Map<String, String[]> parameterMap = request.getParameterMap();

        Object[] paramValues = new Object[parameterTypes.length];
        for(int i = 0; i < parameterTypes.length; i++){
            Class parameterType = parameterTypes[i];
            if(parameterType == HttpServletRequest.class){
                paramValues[i] = request;
                continue;
            }else if(parameterType == HttpServletResponse.class){
                paramValues[i] = response;
            }else if(parameterType == String.class){
                for(Map.Entry<String, String[]> entry : parameterMap.entrySet()){
                    String value = Arrays.toString(entry.getValue()).replaceAll("\\[|\\]","")
                            .replaceAll(",\\s", ",");
                    paramValues[i++] = value;
                    if(i == parameterTypes.length){
                        break;
                    }
                }
            }
        }
        try{
            String beanName = toLowStr(method.getDeclaringClass().getSimpleName());
            method.invoke(this.ioc.get(beanName), paramValues);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    private String toLowStr(String str){
        char[] ch = str.toCharArray();
        ch[0] += 32;
        return String.valueOf(ch);
    }
}

此时我们就可以在浏览器中访问对应的网页了,至此一个简易的Spring框架就完成了

项目链接:https://github.com/ZhangJia97/Spring-demo

;