前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Spring中毒太深,离开Spring我居然连最基本的接口都不会写了

Spring中毒太深,离开Spring我居然连最基本的接口都不会写了

作者头像
Java小咖秀
修改于 2021-04-03 02:07:53
修改于 2021-04-03 02:07:53
37100
代码可运行
举报
文章被收录于专栏:Java冰冻三尺Java冰冻三尺
运行总次数:0
代码可运行

来源:cnblogs.com/lonely-wolf/p/14127957.html

前言

随着 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 文件中引入依赖(省略了少部分属性):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<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 类:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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 启动类:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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/demo?name=双子孤狼 进行测试,正常输出:Hello:双子孤狼

我们可以看到,利用 SpringBoot 来完成一个简单的应用开发非常简单,可以不需要任何配置完成一个简单的应用,这是因为 SpringBoot 内部已经做好了约定(约定优于配置思想),包括容器 Tomcat 都被默认集成,所以我们不需要任何配置文件就可以完成一个简单的 demo 应用。

假如没有了 Spring

通过上面的例子我们可以发现,利用 Spring 来完成一个 Hello World 非常简单,但是假如没有了 Spring,我们又该如何完成这样的一个 Hello World 接口呢?

基于 Servlet 开发

在还没有框架之前,编程式基于原始的 Servlet 进行开发,下面我们就基于原生的 Servlet 来完成一个简单的接口调用。

1、pom 文件引入依赖,需要注意的是,package 属性要设置成 war 包,为了节省篇幅,这里没有列出 pom 完整的信息:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<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 文件:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<?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>

这里面定义了 selvletservlet-mapping 两个标签,这两个标签必须一一对应,上面的标签定义了 servlet 的位置,而下面的 servlet-mapping 文件定义了路径的映射,这两个标签通过 servlet-name 标签对应。

3、新建一个 HelloServlet 类继承 HttpServlet

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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?name=双子孤狼,即可得到返回:Hello:双子孤狼

上面我们就完成了一个简单的 基于Servlet 的接口开发,可以看到,配置非常麻烦,每增加一个 Servlet 都需要增加对应的配置,所以才会有许多框架的出现来帮我们简化开发,比如原来很流行的 Struts2 框架,当然现在除了一些比较老的项目,一般我们都很少使用,而更多的是选择 Spring 框架来进行开发。

模仿 Spring

Spring 的源码体系非常庞大,大部分人对其源码都敬而远之。确实,Spring 毕竟经过了这么多年的迭代,功能丰富,项目庞大,不是一下子就能看懂的。虽然 Spring 难以理解,但是其最核心的思想仍然是我们上面介绍的几点,接下来就基于 Spring 最核心的部分来模拟,自己动手实现一个超级迷你版本的 Spring(此版本并不包含 AOP 功能)。

1、pom 依赖和上面保持不变,然后 web.xml 作如下改变,这里会拦截所有的接口 /*,然后多配置了一个参数,这个参数其实也是为了更形象的模拟 Spring

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<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,用来定义扫描的基本路径:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
basePackages=com.lonely.wolf.mini.spring

3、创建一些相关的注解类:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.lonely.wolf.mini.spring.annotation;

import java.lang.annotation.*;

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WolfAutowired {
    String value() default "";
}
package com.lonely.wolf.mini.spring.annotation;

import java.lang.annotation.*;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WolfController {
    String value() default "";
}
package com.lonely.wolf.mini.spring.annotation;

import java.lang.annotation.*;

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WolfGetMapping {
    String value() default "";
}
package com.lonely.wolf.mini.spring.annotation;

import java.lang.annotation.*;

@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WolfRequestParam {
    String value() default "";
}
package com.lonely.wolf.mini.spring.annotation;

import java.lang.annotation.*;

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

4、这个时候最核心的逻辑就是 MyDispatcherServlet 类了:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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",""));
            }
        }

    }


    /**
     * 加载配置文件
     * @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 中方法的映射关系存入变量 handlerMappingMapkey 值为请求路径,value 为方法的相关信息 (对应方法:doDependencyInjection)。

6、存储请求路径和方法的映射关系时,需要用到 HandlerMapping 类来进行存储:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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、新建一个 HelloControllerHelloService 来进行测试:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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////hello?name=双子孤狼, 进行测试发现可以正常输出:Hello:双子孤狼

上面这个例子只是一个简单的演示,通过这个例子只是希望在没有任何框架的情况下,我们也能知道如何完成一个简单的应用开发。例子中很多细节都没有进行处理,仅仅只是为了体验一下 Spring 的核心思想,并了解 Spring 到底帮助我们做了什么,实际上 Spring 能帮我们做的事情远比这个例子中多得多,Spring 体系庞大,设计优雅,经过了多年的迭代优化,是一款非常值得研究的框架。

总结

本文从介绍 Spring 核心功能开始入手,从如何利用 Spring 完成一个应用开发,讲述到假如没有 Spring 我们该如何基于 Servlet 进行开发,最后再通过一个简单的例子体验了 Spring 的核心思想。

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
【Python 第3课】IDE
昨天的课发出去之后,有不少同学发来了反馈,有完成截屏的,也有遇到问题的。一些问题突然让我意识到,很多地方自己描述得不是很到位,会产生歧义,或者干脆就很难听懂。比如: 我自己不是Mac党,手边也没有Mac,所以不知道Mac上还有控制台(console)和终端(Terminal)之分。我想说的其实是终端。Mac的同学们可能要多自己摸索一下了。 另外我之前说了命令行下和在线编辑器两种输代码的方式,但其实这两种是不太一样的。(今天接下来会提到)我在文章里面的例子是在命令行里一行一行的输入得到的效果,有同学误以为全
Crossin先生
2018/04/16
6410
【React】653- 22 个让 React 开发更高效更有趣的工具
英文 | https://dev.to/jsmanifest/22-miraculous-tools-for-react-developers-in-2019-4i46 翻译 | https://www.leancloud.cn/
pingan8787
2020/07/16
2.1K0
【React】653- 22 个让 React 开发更高效更有趣的工具
加入Istio官方翻译组织的历程记录
作为曾经的程序猿,自己也一直“享受”着来自开源社区的那些无私分享。这些开源项目对自己的影响和启发还是很大的,之前就有想过如何去回馈开源社区,也开贡献过自己的一些项目,参与过一些开源项目,但都还是仅限国内的项目。不久前正好看到 Jimmy Song 在微信朋友圈发布 《Istio 官网翻译工作组成立暨志愿者招募》 的动态,没有任何的犹豫,下班后便联系 Jimmy 申请加入翻译工作,并在随后的时间完成自己的首次翻译,也成功被合并到了 Istio 官方仓库的主分支当中。如果你也有和我一样的想法,那么欢迎您也来一起加入,期待。
凡梦星尘
2024/11/20
610
加入Istio官方翻译组织的历程记录
嘀~正则表达式快速上手指南(上篇)
作为数据科学家,快速处理海量数据是他们的必备技能。有时候,这包括大量的文本语料库。例如,假设要找出在 Panama Papers(https://en.wikipedia.org/wiki/Panama_Papers) 泄密事件中邮件的发送方和接收方,我们需要详细筛查1150万封文档!我们可以手工完成上述任务,人工阅读每一封邮件,读取每一份最后发给我们的邮件,或者我们可以借助Python的力量。毕竟,代码存在的一个至关重要的理由就是自动处理任务。
AI研习社
2018/07/26
1.7K0
嘀~正则表达式快速上手指南(上篇)
Android Studio 4.1 中 Design Tools 的改进
Android Studio 中的 Design Tools Suite 提供了一整套开发工具包,使得开发者们能高效地进行 UI 设计、原型设计、构建和调试代码。这些工具包括 Layout Editor (排版编辑器)、Navigation Editor (Navigation 编辑器)、Motion Editor (动作编辑器)、Resource Manager (资源管理器) 和 Layout Inspector (布局检查器) 等。在 Android Studio 4.1 的迭代中,我们将重心侧重于听取并处理来自用户的反馈,并以此作为依据对现有工具进行改善,最终我们不仅重新设计了现有的一些交互方式,还新增了一些遗漏的功能。本篇文章会介绍我们针对 Android Studio 在 UX 方面做的一些改进,本文中所提到的内容您也可以在 What’s new in Design Tools Talk 这一视频中进行查看。
Android 开发者
2020/12/29
2.3K0
2023 最新最全 VSCode 插件推荐!
鱼皮最新原创项目教程,欢迎学习 大家好,我是鱼皮。今天给大家分享一些 2023 年前端必备的 VS Code 插件: 前端框架 ES7+ React/Redux/React-Native snippets 该插件提供了许多速记前缀来加速开发并帮助开发人员为 React、Redux、GraphQL 和 React Native 创建代码片段和语法。 例如,创建一个新文件并输入 rfce 然后按回车键,这将生成一个 React 函数组件,导入 React 并导出组件。 VSCode React Refact
程序员鱼皮
2023/02/27
3.1K0
2023 最新最全 VSCode 插件推荐!
如何真正成为一个在路上的Linuxer
Linux是工具,更像一个信仰。 写在前面 本文目的不是教你如何成为一个真正的Linuxer,也没有能力教你成为一个真正的linuxer,而是通过笔者的一些想法试图指引你真正踏上学习linux之路,成为一个真正意义的在路上的linuxer。 这有什么区别?是的,一个成熟的Linuxer能够游走于表面的命令提示符与内部代码细节实现之间,他们甚至也是linux内核源码的贡献者。这样 的能力是人人都期望的,但是事实是绝大部分在学习linux的人都还没能达到那样高深莫测的水准。而那绝大部分人当中又有很大一部分人
FB客服
2018/02/06
2.1K0
如何真正成为一个在路上的Linuxer
关于Android N的那些事
今年3月,Google破天荒提前半年发布了Android N开发者预览版。当然,作为一个不合格的谷粉并没有第一时间体验安装,因为至今仍然能够回忆起来去年今日此门中(雾)兴冲冲刷了Android M Preview的时候发现各种crash就连微信也(不出所料得)中招时自己一脸懵逼的心情。当然,为自己的机智而庆幸并没有过多久,很快就有微信好友(当然也是纯纯的谷粉)反馈微信又双叒叕在Android新版本下crash了……好吧这次我们的时间很充裕,因为5个preview之后才会发布最终release版本。令人失望(
微信终端开发团队
2018/01/29
1.4K0
关于Android N的那些事
堪比阿里插件的Android Studio插件集合(IDE通用)(上)
俗话说得好,工欲善其事,必先利其器。做开发时间长了,总想找一些快捷方法,只有拥有好用的工具,才能节省开发时间,提高工作效率。我就是一个 追(喜)求(欢)高(偷)效(懒) 的开发人员,正好趁着国庆假期,把平日用到的一些 Android Studio开发插件 整理了一下,分享给大家。
陈宇明
2020/12/15
1.5K0
堪比阿里插件的Android Studio插件集合(IDE通用)(上)
前端插件以及部分细分网址梳理
图来源网络 前端插件以及部分细分网址梳理 插件 parallel.js: 前后端通用的一个并行库 zepto: 用于现代浏览器的兼容 jQuery 的库 totoro: 稳定的跨浏览器测试工具 TheaterJS: 一个用于模拟人输入状态的 JS 库 stellar.js: 前端用于实现异步滚动效果的库,现已不再维护 skrollr: 另一款实现一步滚动的开源库,使用人数众多,可实现各种狂拽酷炫掉渣天的前端效果,看真相 Framework7: 前端框架,是开发人员可以基于 web 技术构建 IOS7 程
企鹅号小编
2018/02/05
5.8K0
前端插件以及部分细分网址梳理
22款好用的CLI工具
作者根据多年的终端使用经验,详细介绍了一些实用的 CLI 工具,希望它们能帮读者提高生产力。
深度学习与Python
2020/08/07
2.3K0
22款好用的CLI工具
Hexo博客主题之hexo-Theme-Matery的介绍
当你看到这里的时候,应该已经有一个自己的 Hexo 博客了。如果还没有的话,不妨使用 Hexo 和 Markdown 来写博客和文章。
Abalone
2022/07/14
1.2K0
Hexo博客主题之hexo-Theme-Matery的介绍
《HelloGitHub》第 67 期
这里有实战项目、入门教程、黑科技、开源书籍、大厂开源项目等,涵盖多种编程语言 Python、Java、Go、C/C++、Swift...让你在短时间内感受到开源的魅力,对编程产生兴趣!
HelloGitHub
2021/11/02
1.3K0
聚焦 Android 11: 大功告成
这是 #11WeeksOfAndroid 系列的最后一篇文章。感谢您在过去的时间里和我们一起深入探索 Android 开发的关键领域。下面来和我们一起回顾这些精彩内容吧:
Android 开发者
2022/09/23
2.1K0
25 个提升开发幸福感的 VSCode 扩展
我认为它如此受欢迎的原因很简单,因为 VSCode 提供了每个开发者想要的功能,包括他们甚至不知道自己需要的功能。这就是 VSCode 的秘密魅力——它总能让你大吃一惊。
一只图雀
2020/06/04
4.7K0
25 个提升开发幸福感的 VSCode 扩展
Python 最强编辑器详细使用教程
本文假设读者熟悉 Python 开发,且计算机中已安装某个版本的 Python。该教程将使用 Python 3.6 版本,屏幕截图和 demo 均来自 macOS 系统。由于 PyCharm 可在所有主流平台上运行,读者在其他系统中会看到略微不同的 UI 元素,可能需要调整某些命令。
python学习教程
2019/09/26
2.1K0
程序猿必须知道的一些有用的(外国)网站
原文:https://github.com/sdmg15/Best-websites-a-programmer-should-visit
IT阅读排行榜
2018/08/16
4K0
吐血推荐|2万字总结Mac所有应用程序、软件工具和相关资料
现在随着互联网的发展,越来越多的公司都鼓励Mac办公,属实MacOS系统对于我们的工作开发效率有很大提升,所以我们需要收集各种类别非常好用的 Mac 应用程序、软件以及工具。作为一个资深 Mac 用户,我需要它们帮助我快乐、高效的工作,同时也分享给你们。
浅羽技术
2020/12/07
6.2K1
吐血推荐|2万字总结Mac所有应用程序、软件工具和相关资料
推荐十款轻量的辅助开发工具
墨白推荐的这十款软件都是轻量级的辅助开发工具,除去idea和VMware Workstation这两款占内存的软件,其余的工具都是小而巧,在工作中是能够提高我们效率的开发神器.
框架师
2019/09/19
3.6K0
推荐十款轻量的辅助开发工具
海量的超赞 Linux 软件
来源:gitbooks 链接:见文末 Tux 这个仓库收集了对任何用户/开发者都超赞的 Linux 应用软件。 请随意 contribute / star / fork / pull request
小小科
2018/06/20
6.2K0
推荐阅读
相关推荐
【Python 第3课】IDE
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档