关于spring:Spring-中毒太深离开-Spring-居然连最基本的接口都不会写了

4次阅读

共计 16882 个字符,预计需要花费 43 分钟才能阅读完成。

前言

随着 Spring 的崛起以及其性能的欠缺,当初可能绝大部分我的项目的开发都是应用 Spring(全家桶)来进行开发,Spring 也的确和其名字一样,是开发者的春天,Spring 解放了程序员的双手,而等到 SpringBoot 进去之后配置文件大大减少,更是进一步解放了程序员的双手,然而也正是因为 Spring 家族产品的弱小,使得咱们习惯了面向 Spring 开发,那么如果有一天没有了 Spring,是不是感觉心里一空,可能一下子连最根本的接口都不会写了,尤其是没有接触过 Servlet 编程的敌人。因为退出没有了 Spring 等框架,那么咱们就须要利用最原生的 Servlet 来本人实现接口门路的映射,对象也须要本人进行治理。

Spring 能帮咱们做什么

Spring 是为解决企业级利用开发的复杂性而设计的一款框架,Spring 的设计理念就是:简化开发。

在 Spring 框架中,所有对象都是 bean,所以其通过面向 bean 编程(BOP),联合其核心思想依赖注入(DI)和面向切面 ((AOP)编程,Spring 实现了其平凡的简化开发的设计理念。

管制反转(IOC)

IOC 全称为:Inversion of Control。管制反转的基本概念是:不必创建对象,然而须要形容创建对象的形式。

简略地说咱们原本在代码中创立一个对象是通过 new 关键字,而应用了 Spring 之后,咱们不再须要本人去 new 一个对象了,而是间接通过容器外面去取进去,再将其主动注入到咱们须要的对象之中,即:依赖注入。

也就说创建对象的控制权不在咱们程序员手上了,全副交由 Spring 进行治理,程序要只须要注入就能够了,所以才称之为管制反转。

依赖注入(DI)

依赖注入(Dependency Injection,DI)就是 Spring 为了实现管制反转的一种实现形式,所以有时候咱们也将管制反转间接称之为依赖注入。

面向切面编程(AOP)

AOP 全称为:Aspect Oriented Programming。AOP 是一种编程思维,其外围结构是方面(切面),行将那些影响多个类的公共行为封装到可重用的模块中,而使本来的模块内只需关注本身的个性化行为。

AOP 编程的罕用场景有:Authentication(权限认证)、Auto Caching(主动缓存解决)、Error Handling(对立错误处理)、Debugging(调试信息输入)、Logging(日志记录)、Transactions(事务处理)等。

利用 Spring 来实现 Hello World

最原生的 Spring 须要较多的配置文件,而 SpringBoot 省略了许多配置,相比拟于原始的 Spring 又简化了不少,在这里咱们就以 SpringBoot 为例来实现一个简略的接口开发。

  • 1、新建一个 maven 我的项目,pom 文件中引入依赖(省略了少部分属性):
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.4.0</version>
    <relativePath/>
</parent>
<dependencies>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
  • 2、新建一个 HelloController 类:
package com.lonely.wolf.note.springboot.demo;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/hello")
public class HelloController {@GetMapping("/demo")
    public String helloWorld(String name){return "Hello:" + name;}
}
  • 3、最初新建一个 SpringBoot 启动类:
package com.lonely.wolf.note.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication(scanBasePackages = "com.lonely.wolf.note.springboot")
class MySpringBootApplication {public static void main(String[] args) {SpringApplication.run(MySpringBootApplication.class, args);
    }
}
  • 4、当初就能够输出测试门路:http://localhost:8080/hello/d… 双子孤狼 进行测试,失常输入:Hello:双子孤狼。

咱们能够看到,利用 SpringBoot 来实现一个简略的利用开发非常简单,能够不须要任何配置实现一个简略的利用,这是因为 SpringBoot 外部曾经做好了约定(约定优于配置思维),包含容器 Tomcat 都被默认集成,所以咱们不须要任何配置文件就能够实现一个简略的 demo 利用。

如果没有了 Spring

通过下面的例子咱们能够发现,利用 Spring 来实现一个 Hello World 非常简单,然而如果没有了 Spring,咱们又该如何实现这样的一个 Hello World 接口呢?

基于 Servlet 开发

在还没有框架之前,编程式基于原始的 Servlet 进行开发,上面咱们就基于原生的 Servlet 来实现一个简略的接口调用。

  • 1、pom 文件引入依赖,须要留神的是,package 属性要设置成 war 包,为了节俭篇幅,这里没有列出 pom 残缺的信息:
<packaging>war</packaging>
<dependencies>
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>servlet-api</artifactId>
    <version>2.4</version>
</dependency>

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.7</version>
</dependency>

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.72</version>
</dependency>
</dependencies>
  • 2、在 src/main 上面新建文件夹 webapp/WEB-INF,而后在 WEB-INF 上面新建一个 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/j2ee" xmlns:javaee="http://java.sun.com/xml/ns/javaee"
         xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
         version="2.4">
    <display-name>Lonely Wolf Web Application</display-name>
    <servlet>
        <servlet-name>helloServlet</servlet-name>
        <servlet-class>com.lonely.wolf.mini.spring.servlet.HelloServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>helloServlet</servlet-name>
        <url-pattern>/hello/*</url-pattern>
    </servlet-mapping>
</web-app>

这外面定义了 selvlet 和 servlet-mapping 两个标签,这两个标签必须一一对应,下面的标签定义了 servlet 的地位,而上面的 servlet-mapping 文件定义了门路的映射,这两个标签通过 servlet-name 标签对应。

  • 3、新建一个 HelloServlet 类继承 HttpServlet:
package com.lonely.wolf.mini.spring.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 原始 Servlet 接口编写,个别须要实现 GET 和 POST 办法,其余办法能够视具体情况选择性继承
 */
public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doPost(request,response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {response.setContentType("text/html;charset=utf-8");
        response.getWriter().write("Hello:" + request.getParameter("name"));
    }
}
  • 4、执行 maven 打包命令,确认胜利打包成 war 包:

  • 5、RUN–>Edit Configurations,而后点击左上角的 + 号,新建一个 Tomcat Server,如果是第一次配置,默认没有 Tomcat Server 选项,须要点击底部的 xx more items…:

  • 6、点击左边的 Deployment,而后依照下图顺次点击,最初在弹框内找到下面打包好的 war 包文件:

  • 7、选中之后,须要留神的是,上面 Application Context 默认会带上 war 包名,为了不便,咱们须要把它删掉,即不必上下文门路,只保留一个根门路 /(当然上下文也能够保留,然而每次申请都要带上这一部分),再抉择 Apply,点击 OK,即可实现部署:

  • 8、最初咱们在浏览器输出申请门路 http://localhost:8080/hello?n… 双子孤狼,即可失去返回:Hello:双子孤狼。

下面咱们就实现了一个简略的 基于 Servlet 的接口开发,能够看到,配置十分麻烦,每减少一个 Servlet 都须要减少对应的配置,所以才会有许多框架的呈现来帮咱们简化开发,比方原来很风行的 Struts2 框架,当然当初除了一些比拟老的我的项目,个别咱们都很少应用,而更多的是抉择 Spring 框架来进行开发。

模拟 Spring

Spring 的源码体系十分宏大,大部分人对其源码都敬而远之。的确,Spring 毕竟通过了这么多年的迭代,功能丰富,我的项目宏大,不是一下子就能看懂的。尽管 Spring 难以了解,然而其最外围的思维依然是咱们下面介绍的几点,接下来就基于 Spring 最外围的局部来模仿,本人入手实现一个超级迷你版本的 Spring(此版本并不蕴含 AOP 性能)。

 另外,举荐下 Spring boot 的用户权限管理系统开源我的项目:https://gitee.com/yoodb/jing-xuan
  • 1、pom 依赖和下面放弃不变,而后 web.xml 作如下扭转,这里会拦挡所有的接口 /*,而后多配置了一个参数,这个参数其实也是为了更形象的模仿 Spring:
<servlet>
    <servlet-name>myDispatcherServlet</servlet-name>
    <servlet-class>com.lonely.wolf.mini.spring.v1.MyDispatcherServlet</servlet-class>
    <init-param>
        <param-name>defaultConfig</param-name>
        <param-value>application.properties</param-value>
    </init-param>
</servlet>

<servlet-mapping>
<servlet-name>myDispatcherServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
  • 2、在 respurces 上面新建一个配置文件 application.properties,用来定义扫描的根本门路:
basePackages=com.lonely.wolf.mini.spring
  • 3、创立一些相干的注解类:
package com.lonely.wolf.mini.spring.annotation;import java.lang.annotation.*;@Target({ElementType.FIELD})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface WolfAutowired {String value() default "";}package com.lonely.wolf.mini.spring.annotation;import java.lang.annotation.*;@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface WolfController {String value() default"";}package com.lonely.wolf.mini.spring.annotation;import java.lang.annotation.*;@Target({ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface WolfGetMapping {String value() default "";}package com.lonely.wolf.mini.spring.annotation;import java.lang.annotation.*;@Target({ElementType.PARAMETER})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface WolfRequestParam {String value() default"";}package com.lonely.wolf.mini.spring.annotation;import java.lang.annotation.*;@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface WolfService {String value() default "";}
  • 4、这个时候最外围的逻辑就是 MyDispatcherServlet 类了:
package com.lonely.wolf.mini.spring.v1;

import com.lonely.wolf.mini.spring.annotation.*;
import com.lonely.wolf.mini.spring.v1.config.MyConfig;
import org.apache.commons.lang3.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.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;

public class MyDispatcherServlet extends HttpServlet {private MyConfig myConfig = new MyConfig();
    private List<String> classNameList = new ArrayList<String>();

    private Map<String,Object> iocContainerMap = new HashMap<>();
    private Map<String,HandlerMapping> handlerMappingMap = new HashMap<>();

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doPost(request,response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        try {this.doDispatch(request, response);
        } catch (Exception e) {e.printStackTrace();
        }
    }

    private void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception{String requestUrl = this.formatUrl(request.getRequestURI());
        HandlerMapping handlerMapping = handlerMappingMap.get(requestUrl);
        if (null == handlerMapping){response.getWriter().write("404 Not Found");
            return;
        }

        // 获取办法中的参数类型
        Class<?>[] paramTypeArr = handlerMapping.getMethod().getParameterTypes();
        Object[] paramArr = new Object[paramTypeArr.length];

        for (int i=0;i<paramTypeArr.length;i++){Class<?> clazz = paramTypeArr[i];
            // 参数只思考三种类型,其余不思考
            if (clazz == HttpServletRequest.class){paramArr[i] = request;
            }else if (clazz == HttpServletResponse.class){paramArr[i] = response;
            } else if (clazz == String.class){Map<Integer,String> methodParam = handlerMapping.getMethodParams();
                paramArr[i] = request.getParameter(methodParam.get(i));
            }else{System.out.println("暂不反对的参数类型");
            }
        }
        // 反射调用 controller 办法
        handlerMapping.getMethod().invoke(handlerMapping.getTarget(), paramArr);
    }

    private String formatUrl(String requestUrl) {requestUrl = requestUrl.replaceAll("/+","/");
        if (requestUrl.lastIndexOf("/") == requestUrl.length() -1){requestUrl = requestUrl.substring(0,requestUrl.length() -1);
        }
        return requestUrl;
    }

    @Override
    public void init(ServletConfig config) throws ServletException {
        //1. 加载配置文件
        try {doLoadConfig(config.getInitParameter("defaultConfig"));
        } catch (Exception e) {System.out.println("加载配置文件失败");
            return;
        }

        //2. 依据获取到的扫描门路进行扫描
        doScanPacakge(myConfig.getBasePackages());

        //3. 将扫描到的类进行初始化,并存放到 IOC 容器
        doInitializedClass();

        //4. 依赖注入
        doDependencyInjection();

        System.out.println("DispatchServlet Init End...");
    }

    private void doDependencyInjection() {if (iocContainerMap.size() == 0){return;}
        // 循环 IOC 容器中的类
        Iterator<Map.Entry<String,Object>> iterator = iocContainerMap.entrySet().iterator();

        while (iterator.hasNext()){Map.Entry<String,Object> entry = iterator.next();
            Class<?> clazz = entry.getValue().getClass();
            Field[] fields = clazz.getDeclaredFields();

            // 属性注入
            for (Field field : fields){
                // 如果属性有 WolfAutowired 注解则注入值(临时不思考其余注解)if (field.isAnnotationPresent(WolfAutowired.class)){String value = toLowerFirstLetterCase(field.getType().getSimpleName());// 默认 bean 的 value 为类名首字母小写
                    if (field.getType().isAnnotationPresent(WolfService.class)){WolfService wolfService = field.getType().getAnnotation(WolfService.class);
                        value = wolfService.value();}
                    field.setAccessible(true);
                    try {Object target = iocContainerMap.get(beanName);
                        if (null == target){System.out.println(clazz.getName() + "required bean:" + beanName + ",but we not found it");
                        }
                        field.set(entry.getValue(),iocContainerMap.get(beanName));// 初始化对象,前面注入
                    } catch (IllegalAccessException e) {e.printStackTrace();
                    }
                }
            }

            // 初始化 HanderMapping
            String requestUrl = "";
            // 获取 Controller 类上的申请门路
            if (clazz.isAnnotationPresent(WolfController.class)){requestUrl = clazz.getAnnotation(WolfController.class).value();}

            // 循环类中的办法,获取办法上的门路
            Method[] methods = clazz.getMethods();
            for (Method method : methods){
                // 假如只有 WolfGetMapping 这一种注解
                if(!method.isAnnotationPresent(WolfGetMapping.class)){continue;}
                WolfGetMapping wolfGetMapping = method.getDeclaredAnnotation(WolfGetMapping.class);
                requestUrl = requestUrl + "/" + wolfGetMapping.value();// 拼成实现的申请门路

                // 不思考正则匹配门路 /xx/* 的状况,只思考齐全匹配的状况
                if (handlerMappingMap.containsKey(requestUrl)){System.out.println("反复门路");
                    continue;
                }

                Annotation[][] annotationArr = method.getParameterAnnotations();// 获取办法中参数的注解

                Map<Integer,String> methodParam = new HashMap<>();// 存储参数的程序和参数名
                retryParam:
                for (int i=0;i<annotationArr.length;i++){for (Annotation annotation : annotationArr[i]){if (annotation instanceof WolfRequestParam){WolfRequestParam wolfRequestParam = (WolfRequestParam) annotation;
                            methodParam.put(i,wolfRequestParam.value());// 存储参数的地位和注解中定义的参数名
                            continue retryParam;
                        }
                    }
                }

                requestUrl = this.formatUrl(requestUrl);// 次要是避免门路多了 / 导致门路匹配不上
                HandlerMapping handlerMapping = new HandlerMapping();
                handlerMapping.setRequestUrl(requestUrl);// 申请门路
                handlerMapping.setMethod(method);// 申请办法
                handlerMapping.setTarget(entry.getValue());// 申请办法所在 controller 对象
                handlerMapping.setMethodParams(methodParam);// 申请办法的参数信息
                handlerMappingMap.put(requestUrl,handlerMapping);// 存入 hashmap
            }
        }
    }

    /**
     * 初始化类,并放入容器 iocContainerMap 内
     */
    private void doInitializedClass() {if (classNameList.isEmpty()){return;}
        for (String className : classNameList){if (StringUtils.isEmpty(className)){continue;}
            Class clazz;
            try {clazz = Class.forName(className);// 反射获取对象
                if (clazz.isAnnotationPresent(WolfController.class)){String value = ((WolfController)clazz.getAnnotation(WolfController.class)).value();
                    // 如果间接指定了 value 则取 value,否则取首字母小写类名作为 key 值存储类的实例对象
                    iocContainerMap.put(StringUtils.isBlank(value) ? toLowerFirstLetterCase(clazz.getSimpleName()) : value,clazz.newInstance());
                }else if(clazz.isAnnotationPresent(WolfService.class)){String value = ((WolfService)clazz.getAnnotation(WolfService.class)).value();
                    iocContainerMap.put(StringUtils.isBlank(value) ? toLowerFirstLetterCase(clazz.getSimpleName()) : value,clazz.newInstance());
                }else{System.out.println("不思考其余注解的状况");
                }
            } catch (Exception e) {e.printStackTrace();
                System.out.println("初始化类失败,className 为" + className);
            }
        }

    }

    /**
     * 将首字母转换为小写
     * @param className
     * @return
     */
    private String toLowerFirstLetterCase(String className) {if (StringUtils.isBlank(className)){return "";}
        String firstLetter = className.substring(0,1);
        return firstLetter.toLowerCase() + className.substring(1);
    }

    /**
     * 扫描包下所有文件获取全限定类名
     * @param basePackages
     */
    private void doScanPacakge(String basePackages) {if (StringUtils.isBlank(basePackages)){return;}
        // 把包名的. 替换为 /
        String scanPath = "/" + basePackages.replaceAll("\\.","/");
        URL url = this.getClass().getClassLoader().getResource(scanPath);// 获取到以后包所在磁盘的全门路
        File files = new File(url.getFile());// 获取以后门路下所有文件
        for (File file : files.listFiles()){// 开始扫描门路下的所有文件
            if (file.isDirectory()){// 如果是文件夹则递归
                doScanPacakge(basePackages + "." + file.getName());
            }else{// 如果是文件则增加到汇合。因为下面是通过类加载器获取到的文件门路,所以实际上是 class 文件所在门路
                classNameList.add(basePackages + "." + file.getName().replace(".class",""));
            }
        }

    }

    /**
     * 加载配置文件,公众号:Java 精选
     * @param configPath - 配置文件所在门路
     */
    private void doLoadConfig(String configPath) {InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(configPath);
        Properties properties = new Properties();
        try {properties.load(inputStream);
        } catch (IOException e) {e.printStackTrace();
            System.out.println("加载配置文件失败");
        }

        properties.forEach((k, v) -> {
            try {Field field = myConfig.getClass().getDeclaredField((String)k);
                field.setAccessible(true);
                field.set(myConfig,v);
            } catch (Exception e) {e.printStackTrace();
                System.out.println("初始化配置类失败");
                return;
            }
        });
    }
}
  • 5、这个 Servlet 相比拟于下面的 HelloServlet 多了一个 init 办法,这个办法中次要做了以下几件事件:

1)初始化配置文件,拿到配置文件中配置的参数信息(对应办法:doLoadConfig)。

2)拿到第 1 步加载进去的配置文件,获取到须要扫描的包门路,而后将包门路进行转换成理论的磁盘门路,并开始遍历磁盘门路下的所有 class 文件,最终通过转换之后失去扫描门路下的所有类的全限定类型,存储到全局变量 classNameList 中(对应办法:doScanPacakge)。

3)依据第 2 步中失去的全局变量 classNameList 中的类通过反射进行初始化(须要留神的是只会初始化加了指定注解的类)并将失去的对应关系存储到全局变量 iocContainerMap 中(即传说中的 IOC 容器),其中 key 值为注解中的 value 属性,如 value 属性为空,则默认取首字母小写的类名作为 key 值进行存储(对应办法:doInitializedClass)。

4)这一步比拟要害,须要对 IOC 容器中的所有类的属性进行赋值并且须要对 Controller 中的申请门路进行映射存储,为了确保最初能顺利调用 Controller 中的办法,还须要将办法的参数进行存储。对属性进行映射时只会对加了注解的属性进行映射,映射时会从 IOC 容器中取出第 3 步中曾经初始化的实例对象进行赋值,最初将申请门路和 Controller 中办法的映射关系存入变量 handlerMappingMap,key 值为申请门路,value 为办法的相干信息(对应办法:doDependencyInjection)。

  • 6、存储申请门路和办法的映射关系时,须要用到 HandlerMapping 类来进行存储:
package com.lonely.wolf.mini.spring.v1;

import java.lang.reflect.Method;
import java.util.Map;

// 省略了 getter/setter 办法
public class HandlerMapping {
    private String requestUrl;
    private Object target;// 保留办法对应的实例
    private Method method;// 保留映射的办法
    private Map<Integer,String> methodParams;// 记录办法参数
}
  • 7、初始化实现之后,因为拦挡了 /*,所以调用任意接口都会进入 MyDispatcherServlet,而且最终都会执行办法 doDispatch,执行这个办法时会拿到申请的门路,而后和全局变量 handlerMappingMap 进行匹配,匹配不上则返回 404,匹配的上则取出必要的参数进行赋值,最初通过反射调用到 Controller 中的相干办法。
  • 8、新建一个 HelloController 和 HelloService 来进行测试:
package com.lonely.wolf.mini.spring.controller;

import com.lonely.wolf.mini.spring.annotation.WolfAutowired;
import com.lonely.wolf.mini.spring.annotation.WolfController;
import com.lonely.wolf.mini.spring.annotation.WolfGetMapping;
import com.lonely.wolf.mini.spring.annotation.WolfRequestParam;
import com.lonely.wolf.mini.spring.service.HelloService;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WolfController
public class HelloController {
    @WolfAutowired
    private HelloService helloService;

    @WolfGetMapping("/hello")
    public void query(HttpServletRequest request,HttpServletResponse response, @WolfRequestParam("name") String name) throws IOException {response.setContentType("text/html;charset=utf-8");
        response.getWriter().write("Hello:" + name);
    }
}
package com.lonely.wolf.mini.spring.service;

        import com.lonely.wolf.mini.spring.annotation.WolfService;

@WolfService(value = "hello_service")// 为了演示是否失常取 value 属性
public class HelloService {}
  • 9、输出测试门路:http://localhost:8080////hell… 双子孤狼,进行测试发现能够失常输入:Hello:双子孤狼。

下面这个例子只是一个简略的演示,通过这个例子只是心愿在没有任何框架的状况下,咱们也能晓得如何实现一个简略的利用开发。例子中很多细节都没有进行解决,仅仅只是为了体验一下 Spring 的核心思想,并理解 Spring 到底帮忙咱们做了什么,实际上 Spring 能帮咱们做的事件远比这个例子中多得多,Spring 体系宏大,设计优雅,通过了多年的迭代优化,是一款十分值得钻研的框架。

正文完
 0