0
点赞
收藏
分享

微信扫一扫

通过搭建demo-spring-framework-v1,初识Spring框架

jjt二向箔 2022-07-13 阅读 81


文章目录

  • ​​1.代码地址​​
  • ​​2.思路梳理​​
  • ​​2.1.入口​​
  • ​​2.1.DispatcherServlet整个启动加载流程​​
  • ​​2.1.1.配置web.xml​​
  • ​​2.1.2.初始化阶段(容器Tomcat启动阶段)​​
  • ​​2.1.3.运行阶段【主要测试侧重于调用地址与调用方法的映射分配】​​
  • ​​3.实战搭建简单的Spring框架​​
  • ​​3.1.配置文件:web.xml​​
  • ​​3.2.创建自己的入口类:MyDispatcherServlet​​
  • ​​3.3.编写Spring的注解类​​
  • ​​3.3.1.MyRequestParam-->类比RequestParam​​
  • ​​3.3.2.MyComponent-->类比Component​​
  • ​​3.3.3.MyController-->类比Controller​​
  • ​​3.3.4.MyService-->类比Service​​
  • ​​3.3.5.关于注解的详细说明​​
  • ​​3.4.使用自定义的注解类进行项目开发​​
  • ​​3.4.1.@MyController的使用-->定义DemoController​​
  • ​​3.4.2.@MyService的使用-->IDemoService与DemoServiceImpl​​
  • ​​3.5.继续编写Spring加载入口与请求入口类:MyDispatcherServlet​​
  • ​​3.5.1.首先继承Tomcat的HttpServlet类​​
  • ​​3.5.2.Tomcat启动初始化容器信息​​
  • ​​3.5.2.1.备注:web.xml​​
  • ​​3.5.2.2.指定扫描Bean的路径:application.properties​​
  • ​​3.5.2.3.加载配置​​
  • ​​3.5.2.3.根据application.properties扫描项目中的所有类​​
  • ​​3.5.2.4.初始化IOC容器:扫描所有的相关类的实例​​
  • ​​3.5.2.5.DI:完善依赖类的属性依赖注入​​
  • ​​3.5.2.6.初始化调用URL与调用类和方法的关联关系​​
  • ​​3.6.调用地址与实际请求类方法的映射匹配​​
  • ​​3.6.1.概述​​
  • ​​3.6.2.doDispatcher方法详述​​
  • ​​3.7.初步进行Tomcat部署启动项目​​

1.代码地址

https://gitee.com/gaoxinfu_admin/open-source/tree/master/spring/spring-framework-5.0.2.RELEASE-%E4%B8%AD%E6%96%87%E6%B3%A8%E9%87%8A%E7%89%88/demo-spring-framework-v1

通过搭建demo-spring-framework-v1,初识Spring框架_xml

2.思路梳理

2.1.入口

1.我们平时在开发的过程中,都是需要配置DispatcherServlet,
这个类实际上负责的就是进行加载我们的项目中开发的类以及相关的配置东西,
2.这是整个Spring进行加载的一个入口,这两个非常重要的功能:
2.1.加载类和相关的属性配置
2.2.分发Http请求;

通过搭建demo-spring-framework-v1,初识Spring框架_xml_02

2.1.DispatcherServlet整个启动加载流程

2.1.1.配置web.xml

通过搭建demo-spring-framework-v1,初识Spring框架_java_03

<?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>Gupao Web Application</display-name>
<servlet>
<servlet-name>myservlet</servlet-name>
<servlet-class>com.gaoxinfu.demo.spring.framework.v1.servlet.MyDispatcherServlet</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>myservlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>

2.1.2.初始化阶段(容器Tomcat启动阶段)

通过搭建demo-spring-framework-v1,初识Spring框架_java_04

2.1.3.运行阶段【主要测试侧重于调用地址与调用方法的映射分配】

通过搭建demo-spring-framework-v1,初识Spring框架_java_05

3.实战搭建简单的Spring框架

通过搭建demo-spring-framework-v1,初识Spring框架_java_06

3.1.配置文件: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>Gupao Web Application</display-name>
<servlet>
<servlet-name>myservlet</servlet-name>
<servlet-class>com.gaoxinfu.demo.spring.framework.v1.servlet.MyDispatcherServlet</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>myservlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>

3.2.创建自己的入口类:MyDispatcherServlet

1.因为入口涉及到相关的加载配置,初始化IOC,DI注入等操作,需要我们首先编写号相关的注解等等操作;

3.3.编写Spring的注解类

1.我们知道Spring的注入注解类主要有下面几种,我们这里只弄几个比较常见的; 
标示注解类的:
@Controller
@Component
@Repository
@Service

标示注解请求路径的
@RequestMapping

标示DI注入的
@Autowired
@Qualifier
@Resource

1.备注我们这里的注解都是参考的spring源码中的直接,然后改成我们自己的定义的名字

3.3.1.MyRequestParam–>类比RequestParam

package com.gaoxinfu.demo.spring.framework.v1.annotation;

import java.lang.annotation.*;

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

}

3.3.2.MyComponent–>类比Component

package com.gaoxinfu.demo.spring.framework.v1.annotation;

import java.lang.annotation.*;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyComponent {

String value() default "";

}

3.3.3.MyController–>类比Controller

package com.gaoxinfu.demo.spring.framework.v1.annotation;

import java.lang.annotation.*;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyController {

String value() default "";

}

3.3.4.MyService–>类比Service

package com.gaoxinfu.demo.spring.framework.v1.annotation;

import java.lang.annotation.*;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyService {


String value() default "";

}

3.3.5.关于注解的详细说明

关注与注解中@Target,@Retention,@Documented的详细解释,大家可以参考下面的地址进行学习
​​​https://www.runoob.com/w3cnote/java-annotation.html​​

3.4.使用自定义的注解类进行项目开发

3.4.1.@MyController的使用–>定义DemoController

package com.gaoxinfu.demo.spring.framework.v1.controller;


import com.gaoxinfu.demo.spring.framework.v1.annotation.MyAutowired;
import com.gaoxinfu.demo.spring.framework.v1.annotation.MyRequestMapping;
import com.gaoxinfu.demo.spring.framework.v1.annotation.MyRequestParam;
import com.gaoxinfu.demo.spring.framework.v1.service.IDemoService;

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

@MyController
@MyRequestMapping("/demo")
public class DemoController {

@MyAutowired
IDemoService iDemoService;

@MyRequestMapping("/query")
public void query(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
@MyRequestParam("name") String name){
String result=iDemoService.get(name);
try {
httpServletResponse.getWriter().write(result);
} catch (IOException e) {
e.printStackTrace();
}
}
}

3.4.2.@MyService的使用–>IDemoService与DemoServiceImpl

package com.gaoxinfu.demo.spring.framework.v1.service;

public interface IDemoService {

public String get(String name);
}

package com.gaoxinfu.demo.spring.framework.v1.service.impl;

import com.gaoxinfu.demo.spring.framework.v1.annotation.MyService;
import com.gaoxinfu.demo.spring.framework.v1.service.IDemoService;

@MyService
public class DemoServiceImpl implements IDemoService {

@Override
public String get(String name) {
return "My Name Is "+name;
}
}

3.5.继续编写Spring加载入口与请求入口类:MyDispatcherServlet

3.5.1.首先继承Tomcat的HttpServlet类

1.可以认为继承HttpServlet,使得入口请求能够进入到该类MyDispatcherServlet中
包含get类型请求或者post类型请求

通过搭建demo-spring-framework-v1,初识Spring框架_spring_07

3.5.2.Tomcat启动初始化容器信息

@Override
public void init(ServletConfig config) throws ServletException {

//使用模板模式
//1.加载配置文件
this.reloadConfig(config);
//2.扫描所有的类
this.scanner(propertiesConfig.getProperty("scanPackage"));
//3.初始化IOC容器:扫描所有的相关类的实例
this.initIOC();
//4.完善依赖类的依赖注入
this.doAutowired();
//5.初始化调用URL与调用类和方法的关联关系
this.initHandlerMapping();
}

3.5.2.1.备注: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>Gupao Web Application</display-name>
<servlet>
<servlet-name>myservlet</servlet-name>
<servlet-class>com.gaoxinfu.demo.spring.framework.v1.servlet.MyDispatcherServlet</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>myservlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>

3.5.2.2.指定扫描Bean的路径:application.properties

scanPackage=com.gaoxinfu.demo.spring.framework.v1

通过搭建demo-spring-framework-v1,初识Spring框架_java_08

3.5.2.3.加载配置

/**
* 1.加载配置文件
* @param config
*/
private void reloadConfig(ServletConfig config) {
//这里的contextConfigLocation参数实际上就是我们的web.xml中的<init-param>的配置参数,通过key:contextConfigLocation
//获取application.properties文件中的原始的数据流,然后propertiesConfig.load(inputStream);加载到缓存中
InputStream inputStream=this.getClass().getClassLoader().getResourceAsStream(config.getInitParameter("contextConfigLocation"));
try {
propertiesConfig.load(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}

3.5.2.3.根据application.properties扫描项目中的所有类

this.scanner(propertiesConfig.getProperty("scanPackage"));

/**
* 2.扫描所有的类
* 这个就是拉取所有的.class结尾的类文件
* @param scanPackage
*/
private void scanner(String scanPackage) {
URL url=this.getClass().getClassLoader().getResource("/"+scanPackage.replaceAll("\\.","/"));
File parentFile=new File(url.getFile());

File[] childFile=parentFile.listFiles();
for (File file:childFile){
if (file.isDirectory()){
scanner(scanPackage+"."+file.getName());
}else{
if (!file.getName().endsWith(".class")){
continue;
}
String className=scanPackage+"."+file.getName().replaceAll(".class","");
classNameList.add(className);
}
}
}

3.5.2.4.初始化IOC容器:扫描所有的相关类的实例

这里注意下:我们认为只有使用了注解,我们才会注入到IOC容器,没有注解,不注入IOC容器

/**
* 3.初始化IOC容器:扫描所有的相关类的实例
* 所谓初始化IOC容器,实际上就是针对类,起个别名
* 放入到一个IOC容器,这里使用Map存储,key为别名,value是类
* 这里重点提示:
* 所谓别名我们都是通过注解来标示的,比如@MyController @MyService等等,如果没有特殊标示就是默认
*/
private void initIOC() {
if (classNameList.isEmpty()){
return;
}
for (String className:classNameList) {
try {
Class<?> clazz=Class.forName(className);
if (clazz.isAnnotationPresent(MyController.class)){
Object instance=clazz.newInstance();
String classSimpleName=clazz.getSimpleName().toLowerCase();
iocMap.put(classSimpleName,instance);
}else if (clazz.isAnnotationPresent(MyService.class)){
//1、默认的类名首字母小写
String classSimpleName=clazz.getSimpleName().toLowerCase();
MyService myService=clazz.getAnnotation(MyService.class);
if (!"".equalsIgnoreCase(myService.value())){
classSimpleName=myService.value();
}
//2、自定义命名
Object instance=clazz.newInstance();
iocMap.put(classSimpleName,instance);

//3.根据类型注入实现类,投机取巧的方式---实际上就是接口
for (Class<?> c:clazz.getInterfaces()) {
if (iocMap.containsKey(c.getName())){
//异常抛出提示客户换一个名字
throw new RuntimeException("The Bean " +c.getName() +" already register!");
}
iocMap.put(c.getName(),instance);
}
}else{
continue;
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}

}

3.5.2.5.DI:完善依赖类的属性依赖注入

/**
*
* 4.完善依赖类的依赖注入
* 这里所谓的doAutowired 主要是处理的我们在类中通过属性字段注入的类
* 比如:下面我们在EbpContRebuyUnderwriteAction类中注入的ContRebuyService属性
* @Component
* public class EbpContRebuyUnderwriteAction extends BaseAction{
*
* private static final String OPERATOR="原单EBP续保校验";
*
* @Resource
* @Qualifier("posContRebuyClient")
* private ContRebuyService contRebuyService;
*
* ......
* }
*/
private void doAutowired() {
for (Map.Entry<String,Object> entry:iocMap.entrySet()) {
Field[] fields=entry.getValue().getClass().getDeclaredFields();
for (Field field:fields){
if (!field.isAnnotationPresent(MyAutowired.class)){
continue;
}
MyAutowired myAutowired=field.getAnnotation(MyAutowired.class);
String classSimpleName= myAutowired.value().trim();
if ("".equalsIgnoreCase(classSimpleName)){
//如果没有配置别名,我们直接使用类的别名
classSimpleName=field.getType().getName();
}
//设置私有属性的访问权限 暴力访问
field.setAccessible(Boolean.TRUE);
try {
/*
* 这里的entry.getValue()如同EbpContRebuyUnderwriteAction对象
* iocMap.get(classSimpleName) 如同ContRebuyService实例
* 相当于在EbpContRebuyUnderwriteAction对象上注入ContRebuyService
* 使得在EbpContRebuyUnderwriteAction对象在构造的时候,ContRebuyService属性同时赋值
*/
//执行注入动作
field.set(entry.getValue(),iocMap.get(classSimpleName));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}

3.5.2.6.初始化调用URL与调用类和方法的关联关系

/**
*5.初始化调用URL与调用类和方法的关联关系
*
*/
private void initHandlerMapping() {

if (iocMap.isEmpty()){
return;
}
for (Map.Entry entry:iocMap.entrySet()) {
Class<?> clazz= entry.getValue().getClass();
if (!clazz.isAnnotationPresent(MyController.class)){
continue;
}
String baseUrl="";
if (clazz.isAnnotationPresent(MyRequestMapping.class)){
MyRequestMapping myRequestMapping=clazz.getAnnotation(MyRequestMapping.class);
baseUrl=myRequestMapping.value();
}

//默认获取所有的public方法
Method[] methods=clazz.getMethods();
for (Method method:methods){
if (!method.isAnnotationPresent(MyRequestMapping.class)){
continue;
}

MyRequestMapping myRequestMapping=method.getAnnotation(MyRequestMapping.class);
//如果有连续的//,替换成一个/
String url=(baseUrl+"/"+myRequestMapping.value()).replaceAll("/+","/");

handlingMappings.put(url,method);
}
}
}

3.6.调用地址与实际请求类方法的映射匹配

3.6.1.概述

为了方便起见,我们get。post请求最终都去调用doDispatcher方法

通过搭建demo-spring-framework-v1,初识Spring框架_spring_09

3.6.2.doDispatcher方法详述

private void doDispatcher(HttpServletRequest req, HttpServletResponse resp) throws IOException {
System.out.println("请求进来了。。。。。");
String url=req.getRequestURI();
System.out.println("Current URL = " +req.getRequestURL());
System.out.println("Current URI = " +url);
String contextPath=req.getContextPath();
url=url.replaceAll(contextPath,"").replaceAll("/+","/");
if (!this.handlingMappings.containsKey(url)){
resp.getWriter().write("404 NOT FOUND");
return;
}

Method method=this.handlingMappings.get(url);
//一个key对应着一个数组,URL里面一个key可以对应多个值
//http://localhost:8080/demo-spring-self-architecture/demo/query?name=gaoxinfu&name=tom
Map<String,String[]> params=req.getParameterMap();
Class<?>[] paramterTypes=method.getParameterTypes();
Object[] paramValues=new Object[paramterTypes.length];

for (int i=0;i<paramterTypes.length;i++){
Class paramterType=paramterTypes[i];
if (paramterType==HttpServletRequest.class){
paramValues[i]=req;
continue;
}
if (paramterType==HttpServletResponse.class){
paramValues[i]=resp;
continue;
}else if (paramterType==String.class){
Annotation[][] annotations= method.getParameterAnnotations();
for (Annotation annotation:annotations[i]) {

if (annotation instanceof MyRequestParam){
String paramValue=((MyRequestParam) annotation).value();
if (!"".equalsIgnoreCase(paramValue)){
String value=Arrays.toString(params.get(paramValue))
.replaceAll("\\[|\\]","") //删除[]等特殊字符
.replaceAll("\\s",","); //删除空格
paramValues[i]=value;
}
}
}
}

}
String beanName= method.getDeclaringClass().getSimpleName().toLowerCase();
try {
//利用方式进行请求方法的调用
method.invoke(iocMap.get(beanName),paramValues);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}

}

3.7.初步进行Tomcat部署启动项目

通过搭建demo-spring-framework-v1,初识Spring框架_xml_10


通过搭建demo-spring-framework-v1,初识Spring框架_java_11


通过搭建demo-spring-framework-v1,初识Spring框架_java_12


通过搭建demo-spring-framework-v1,初识Spring框架_java_13


通过搭建demo-spring-framework-v1,初识Spring框架_xml_14


验证我们的方法

​​http://localhost:8080/demo_spring_framework_v1_war/demo/query?name=gaoxinfu​​

通过搭建demo-spring-framework-v1,初识Spring框架_xml_15


举报

相关推荐

0 条评论