Spring源码探究 | 三、初试-提炼Spring+MVC核心功能 [一]

提炼Spring+MVC核心功能

1、前言

在学习一门框架的源码之前,我们可以通过对该框架提供的开发者所需配置项、框架在项目中所承担的功能、框架在项目中的展现形式的了解来进行思考,思考如果通过上述的三点,自己来写这们框架,我们应该如何来实现,内部的思路是什么样的。这样我们自己梳理了一套相应的实现方式后,再带着自己的一套东西去看源码,看看源码是如何优雅的实现其框架的功能,从而在其中通过比较、琢磨来打磨自己的编码和思想。

2、梳理

  • 开发者所需配置项
  • 框架在项目中的展现形式
  • 框架在项目中所承担的功能

2.1 开发者所需配置项

我们以spring + springMVC为例,暂不提及spring boot。
首先我们需要引入spring的依赖包,紧接着去配置web.xml。

2.1.1 配置Spring Web的启动

有过Web开发经验的读者都知道可以在web.xml中配置自启动的Servlet或定义Web容器监听器(ServletContextListener),借助这两者中的任何一个,我们就可以完成启动Spring Web应用上下文的工作。
Spring分别提供了用于启动WebApplicationContext的Servlet和Web容器监听器:

org.springframework.web.context.ContextLoaderServlet
org.springframework.web.context.ContextLoaderListener

两者的内部都实现了启动WebApplicationContext实例的逻辑,我们只要根据Web容器的具体情况选择两者之一,并在web.xml中完成配置就可以了.
比如我们选择了Web容器监听器,那么在web.xml中配置为:

<listener>
	<description>启动spring容器</description>
	<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
2.1.2 配置Spring Web配置文件的扫描路径

ContextLoaderListener通过Web容器上下文参数contextConfigLocation获取Spring配置文件的位置。用户可以指定多个配置文件,用逗号、空格或冒号分隔均可。对于未带资源类型前缀的配置文件路径,WebApplicationContext默认这些路径相对于Web的部署根路径。当然,我们可以采用带资源类型前缀的路径配置,如"classpath*:/spring-*.xml"和上面的配置是等效的。

<context-param>
	<param-name>contextConfigLocation</param-name>
	<param-value>classpath:spring-context.xml</param-value>
</context-param>
2.1.3 spring MVC分发器和匹配规则配置

要想使用spring MVC,我们需要在web.mxl中引入DispatcherServlet,同时指明配置文件所在路径以及匹配规则。

<servlet>
	<servlet-name>spring-mvc</servlet-name>
	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
	<!-- 指定路径 -->
	<init-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:spring-mvc.xml</param-value>
	</init-param>
</servlet>

<servlet-mapping>
	<servlet-name>spring-mvc</servlet-name>
	<url-pattern>*.do</url-pattern>
</servlet-mapping>

上述配置表示,声明一个DispatcherServlet(DispatcherSerlet是前端控制器,核心功能是分发请求,分发给对应的java类),其配置文件的路径地址为resources/spring-mvc.xml,匹配规则为请求url的结尾为.do的均在DispatcherServlet中匹配、分发一次。

2.1.4 spring的xml配置文件

我们在项目的resources文件夹下创建一个spring-context.xml用于配置spring相关的配置信息,创建一个spring-mvc.xml用于配置springMVC相关的配置信息。
其中spring-mvc.xml中配置信息如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:mvc="http://www.springframework.org/schema/mvc" 
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:util="http://www.springframework.org/schema/util"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation=
            "
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd  
            http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd  
            http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd
            http://www.springframework.org/schema/tx 
            http://www.springframework.org/schema/tx/spring-tx.xsd 
            http://www.springframework.org/schema/aop 
            http://www.springframework.org/schema/aop/spring-aop.xsd
            "
            > 
   
    <context:component-scan base-package="com.spring.test.aop" />
    
    <mvc:annotation-driven />
</beans>  

其中最重要的是 <context:component-scan base-package=“com.spring.test.aop” /> ,其明确了配置spring的bean扫描路径,该路径下的注解声明(@Controller、@Service、@Component)的bean将被注册至bean容器中。

2.2 框架在项目中的展现形式

当我们完成上述的配置,就可以使用该框架提供的一些注解来完成相应的功能实现。

  • 常用的有类上声明的注解有@Controller、@Service、@Component这三类
  • 属性上声明的注解有@Autowired、@Resource
  • 方法上声明的注解有@RequestMapping、@PostMapping、@GetMapping
  • 方法参数上的注解有@RequestBody、@RequestParam。

比如以下的例子:

@Controller
@RequestMapping("/test")
public class TestController {
    
    @Autowired
    private Config Config;
    
    @RequestMapping("/testMethod.do")
    public void testMethod(@RequestBody Test test) {
        
    }
    
    @RequestMapping("/testMethodParam.do")
    public void testMethodParam(@RequestParam("param") String param) {
        
    }
}

2.3 框架在项目中所承担的功能

spring在项目中所承担的功能,即spring的核心功能便是 IOC、DI、AOP,并且作为一个粘合剂,能够集成一些外有的框架。
通过spring的核心功能,我们能够很方便的处理好bean与bean之间的依赖关系,不需要再关注创建bean和管理bean,我们声明好的bean交给spring管理,直接拿来使用即可。

3、模拟

模拟spring、spring MVC的核心功能,整体思路如下:
在这里插入图片描述

3.1 配置application.properties

为了方便解析,我们可以使用application.properties来代替spring-content.xml和spring-mvc.xml的配置,具体配置内容如下:

scanPackage=com.peng.demo

定义需要扫描的路径。

3.2 自定义注解类

我们根据spring的对外呈现的注解类,我们模拟自建相应的注解类
@PDController

import java.lang.annotation.*;

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

@PDService

import java.lang.annotation.*;

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

@PDAutowired

import java.lang.annotation.*;

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

@PDRequestMapping

import java.lang.annotation.*;

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

@PDRequestParam

import java.lang.annotation.*;

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

3.3 模拟创建DispatcherServlet

新建个PDDispatcherServlet,继承HttpServlet,重写init()、doGet()、doPost()方法。

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class PDDispatcherServlet extends HttpServlet{

    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
    }
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doGet(req, resp);
    }
    
    
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
    }
}

3.4 配置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">
	<servlet>
		<servlet-name>pdmvc</servlet-name>
		<servlet-class>com.peng.mvcframework.servlet.PDDispatcherServlet</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>pdmvc</servlet-name>
		<url-pattern>/*</url-pattern>
	</servlet-mapping>
</web-app>

我们使用自己的DispatchServlet以及application.properties。

3.5 配置注解,创建controller/service类

controller层

import java.io.IOException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.peng.demo.service.IDemoService;
import com.peng.mvcframework.annotation.PDAutowired;
import com.peng.mvcframework.annotation.PDController;
import com.peng.mvcframework.annotation.PDRequestMapping;
import com.peng.mvcframework.annotation.PDRequestParam;

@PDController
@PDRequestMapping("/demo")
public class DemoController {

  	@PDAutowired private IDemoService demoService;

	@PDRequestMapping("/query")
	public void query(HttpServletRequest req, HttpServletResponse resp,
					  @PDRequestParam("name") String name){
		String result = demoService.get(name);
		try {
			resp.getWriter().write(result);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@PDRequestMapping("/add")
	public void add(HttpServletRequest req, HttpServletResponse resp,
					@PDRequestParam("a") Integer a, @PDRequestParam("b") Integer b){
		try {
			resp.getWriter().write(a + "+" + b + "=" + (a + b));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

service层

public interface IDemoService {
	
	String get(String name);
}
import com.peng.demo.service.IDemoService;
import com.peng.mvcframework.annotation.PDService;

/**
 * 核心业务逻辑
 */
@PDService
public class DemoServiceImpl implements IDemoService{

	public String get(String name) {
		return "My name is " + name;
	}
}

至此,配置阶段就已经完成。

4、构思

下一步就是进行初始化阶段以及运行阶段的代码模拟实现。
在这里插入图片描述
由于篇幅问题,下一章我们继续来提炼、模拟DispatchServlet的init()方法的编写以及运行阶段如何通过DispatcherServlet来完成请求的转发。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值