SpringMVC---->自我实现底层机制(吃透springMVC)

news2024/12/26 10:42:10

目录

配套代码在资源中(免费)

maven环境搭配 

注解注入的规范:

一.开发HongDisptcherServlet前端控制器

1.说明:

2.配置web.xml文件

3.检查前期工作是否成功

二.完成客户端/浏览器请求控制层

1.创建 自己的 Controller 和自定义注解

2.配置自己的spring容器文件

3.编写XMLParser工具类,可以解析hongspringmvc.xml

这里来说明一下SAXReader解析xml文件

4.开发自己的spring容器(HongWebApplicationContext)

4.1把指定的目录包括子目录下的 java 类的全路径扫描到集合中,比如 ArrayList 

4.2扫描service中的类

5.实例化对象到容器中

将扫描到的类, 在满足条件的情况下(即有相应的注解@Controller @Service...), 反射注

入到 ioc 容器

6.完成请求URL和控制器方法的映射关系(HongHandler)

完成

7.完成HongDispatcherServlet分发请求到对应控制器方法executeDispatch()

7.1通过request对象,返回HongHandler对象

7.2完成分发请求任务

思路:

三.从 web.xml 动态获取 hspspringmvc.xml

四.完成自定义@Service 注解功能

如果给某个类加上@Service, 则可以将其注入到我们的 Spring 容器

1.@Service注解:用于表示一个service对象,并注入到spring容器

2.Monster.java

3.MonsterServiceImpl/对象 作为service注入到spring容器

五.完成 Spring 容器对象的自动装配 -@Autowried

完成 Spring 容器中对象的注入/自动装配

1.完成属性的自动装配

思路:

六.完成控制器方法获取参数-@RequestParam

自定义@RequestParam 和 方法参数名获取参数

1.将方法的 HttpServletRequest 和 HttpServletResponse 参数封装到参数数组,进行反射调用

2.在方法参数 指定 @RequestParam 的参数封装到参数数组,进行反射调用

注解

将http请求参数封装到params数组中, 提示,要注意填充实参的时候,顺序问题

编写方法,返回请求参数是目标方法的第几个形参

3.在方法参数 没有指定 @RequestParam ,按照默认参数名获取值, 进行反射调用

通过方法返回的 String, 转发或者重定向到指定页面

编写方法, 得到目标方法的所有形参的名称,并放入到集合中返回

七.完成简单视图解析

通过方法返回的 String, 转发或者重定向到指定页面

思路

1.登录页面

2.失败页面

3.成功页面

处理妖怪登录的方法,返回要请求转发/重定向的字符串

    这里就是对返回的结果进行解析(判断是forward 还是 redirect)

   八.完成返回 JSON 格式数据-@ResponseBody 

1.@RequestBody的基本认识

简单说就是如果你想让这个方法以json的形式返回一个结果,就可以添加这个注解

2.需求说明:

3.代码

当读取到该注解

九.最后的总结


 

配套代码在资源中(免费)

maven环境搭配 

aa69962b883c458ebae9752bf6688c22.png


<!--    引入原生servlet依赖-->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>4.0.1</version>
<!--      scope表示引入的jar的作用范围,-->
<!--      provided:表示该项目在打包时,放到生产环境的时候,不需要带上servlet-api.jar-->
<!--      因为tomcat本身是由servlet包的,到时直接使用tomcat本身的servlet-api.jar,防止版本冲突-->
      <scope>provided</scope>
    </dependency>
<!--    引入dom4j,解析xml文件-->
    <dependency>
      <groupId>dom4j</groupId>
      <artifactId>dom4j</artifactId>
      <version>1.6.1</version>
    </dependency>
<!--    引入常用的工具jar包,该jar有很多常用的类-->
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-lang3</artifactId>
      <version>3.5</version>
    </dependency>
  </dependencies>

注解注入的规范:

首先我们会先去使用isAnnotationPresen()读取路径上有么有注解,有的话我们就会继续看看这个注解后面是否有value值,有就按照这个value值去注入到ioc中,没有就进行类名或者接口名首字母小写的转化,然后依然注入到ioc中

一.开发HongDisptcherServlet前端控制器

1.说明:

HongDisptcherServlet充当springmvc原生的DisptcherServlet

hongspringmvc充当spring容器配置文件

2.配置web.xml文件

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name>


<!--  配置HongDisptcherServlet-->
  <servlet>
    <servlet-name>HongDisptcherServlet</servlet-name>
    <servlet-class>com.hong.hongspringmvc.servlet.HongDisptcherServlet</servlet-class>
<!--    给HongDisptcherServlet配置参数,指定要操作的spring容器配置文件-->
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:hongspringmvc.xml</param-value>
    </init-param>
    
<!--    HongDisptcherServlet在tomcat启动时就自动加载-->
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>HongDisptcherServlet</servlet-name>
<!--    因为HongDisptcherServlet作为前端控制器,所以需要拦截所以请求-->
<!--    url-pattern对外提供访问Servlet的地址就是http://ip[域名]:port/过程路径/*-->
    <url-pattern>/</url-pattern>
  </servlet-mapping>

</web-app>

3.检查前期工作是否成功

public class HongDisptcherServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("调用成功");
    }

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

当启动tomcat的时候,我们随便输入一个最后的页面地址,就会发现调用成功,那么我们的第一步就先完成了。

二.完成客户端/浏览器请求控制层

b94163a9d7634d00986ccf2ad00a6a75.png        

目标:在自己的控制器这边写一个方法然后浏览器请求打过来之后,希望前端控制器能够进行分发处理到Controller的每个方法

1.创建 自己的 Controller 和自定义注解

@Controller
public class MonsterController {
    //springmvc是支持原生的servle api
    @RequestMapping("/list/monster")
    public void listMonster(HttpServletRequest request, HttpServletResponse response){
        //设置编码和返回类型
        response.setContentType("text/html;charset=utf-8");
        //获取Writer返回信息
        try {
            PrintWriter printWriter=response.getWriter();
            printWriter.write("<h1>妖怪列表信息</h1>");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

}
//注解用于标识一个控制器组件
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
public @interface Controller {
 String value() default  " ";
}
//注解用于标识一个控制器组件
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestMapping {
 String value() default  " ";
}

2.配置自己的spring容器文件

<?xml version="1.0" encoding="utf-8" ?>
<beans>
<!--    指定要扫描的基本包-->
    <component-scan base-package="com.hong.controller"></component-scan>
</beans>

3.编写XMLParser工具类,可以解析hongspringmvc.xml

public class XMLParser {

    public static String getBasePackage(String xmlFile) {

        //这个解析的过程,是前面讲过的
        SAXReader saxReader = new SAXReader();
        //通过得到类的加载路径-》获取到spring配置文件.[对应的资源流]
        InputStream inputStream =
                XMLParser.class.getClassLoader().getResourceAsStream(xmlFile);
        try {
            //得到xmlFile文档
            Document document = saxReader.read(inputStream);
            Element rootElement = document.getRootElement();
            Element componentScanElement =
                    rootElement.element("component-scan");
            Attribute attribute = componentScanElement.attribute("base-package");
            String basePackage = attribute.getText();
            return basePackage;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }
}

这里来说明一下SAXReader解析xml文件

读取配置文件步骤如下:
1.首先实例化SAXReader对象,然后用Document对象获取配置文件的数据。    

  //实例化对象
        SAXReader read=new SAXReader();
        //获取配置文件的数据
        Document doc=read.read("src/web.xml");

2.获取根节点元素:  

//获取根节点元素
        String rootElement=doc.getRootElement().getName();
        System.out.println("根节点元素:"+rootElement);

3.获取根节点里某个属性的的属性值:                                                   

  //属性名
        String rootName=doc.getRootElement().attributeValue("name");
        System.out.println("根节点里name属性的属性值:"+rootName);

4.开发自己的spring容器(HongWebApplicationContext)

1.得到扫描类的全路径

2.注入相关的对象(Controller,Service...)

4.1把指定的目录包括子目录下的 java 类的全路径扫描到集合中,比如 ArrayList 

当tomcat启动的时候会先去加载前端控制器,在init()方法中创建我们自己的spring容器并初始化

通过所写的自己的spring容器中的init获得前端控制器的路径

 public void scanPackage(String pack) {

        //得到包所在的工作路径[绝对路径]
        //下面这句话的含义是 通过类的加载器,得到你指定的包对应的 工作路径[绝对路径]
        //比如 "com.hong.controller" => url 是 D:\hong_springmvc\hong-springmvc\target\hong-springmvc\WEB-INF\classes\com\hong\controller
        //细节说明: 1. 不要直接使用Junit测试, 否则 url null
        //             2. 启动tomcat来去测试(因为会调用前端控制器来初始化spring容器)
        URL url =
                this.getClass().getClassLoader()
                        .getResource("/" + pack.replaceAll("\\.", "/"));

        //System.out.println("url=" + url);
        //根据得到的路径, 对其进行扫描,把类的全路径,保存到classFullPathList

        String path = url.getFile();
        System.out.println("path= " + path);
        //在io中,把目录,视为一个文件
        File dir = new File(path);
        //遍历dir[文件/子目录]
        for (File f : dir.listFiles()) {
            if (f.isDirectory()) {//如果是一个目录,需要递归扫描
                scanPackage(pack + "." + f.getName());
            } else {
                //说明:这时,你扫描到的文件,可能是.class, 也可能是其它文件
                //就算是.class, 也存在是不是需要注入到容器
                //目前先把文件的全路径都保存到集合,后面在注入对象到容器时,再处理
                String classFullPath =
                        pack + "." + f.getName().replaceAll(".class", "");
                classFullPathList.add(classFullPath);
            }
        }

4.2扫描service中的类

//编写方法,完成自己的spring容器的初始化
    public void init() {
        //这里是写的固定的spring容器配置文件.?=>做活
        String basePackage = XMLParser.getBasePackage("hongspringmvc.xml");
        //这时basePackage => com.hong.controller,com.hong.service
        String[] basePackages = basePackage.split(",");
        //遍历basePackages, 进行扫描
        if (basePackages.length > 0) {
            for (String pack : basePackages) {
                scanPackage(pack);//扫描
            }
        }
        System.out.println("扫描后的= classFullPathList=" + classFullPathList);
        //将扫描到的类, 反射到ico容器
        executeInstance();
        System.out.println("扫描后的 ioc容器= " + ioc);

        //完成注入的bean对象,的属性的装配
        executeAutoWired();
        System.out.println("装配后 ioc容器= " + ioc);
    }

5.实例化对象到容器中

将扫描到的类, 在满足条件的情况下(即有相应的注解@Controller @Service...), 反射注

入到 ioc 容器

//定义属性ioc, 存放反射生成的Bean对象 /Controller/Service
    public ConcurrentHashMap<String, Object> ioc =
            new ConcurrentHashMap<>();

编写方法,将扫描到的类, 在满足条件的情况下,反射到ioc容器 

//编写方法,将扫描到的类, 在满足条件的情况下,反射到ioc容器
    public void executeInstance() {
        //判断是否扫描到类
        //classFullPathList前面所写的类的集合
        if (classFullPathList.size() == 0) {//说明没有扫描到类
            return;
        }
        try {
            //遍历classFullPathList,进行反射
            for (String classFullPath : classFullPathList) {
                Class<?> clazz = Class.forName(classFullPath);
                //说明当前这个类有@Controller
                if (clazz.isAnnotationPresent(Controller.class)) {
                    //得到类名首字母小写
                    String beanName = clazz.getSimpleName().substring(0, 1).toLowerCase() +
                            clazz.getSimpleName().substring(1);
                    ioc.put(beanName, clazz.newInstance());
                } //如果有其它的注解,可以扩展 ,

6.完成请求URL和控制器方法的映射关系(HongHandler)

将配置的 @RequestMapping url 和 对应的 控制器 - 方法 映射关系保存到集合中

 

**
 * Created with IntelliJ IDEA.
 *
 * @Author: 海绵hong
 * @Date: 2022/10/29/16:13
 * @Description:对象记录请求的 url 和 控制器方法映射关系
 */
public class HongHandler {
    private String url;
    private Object controller;
    private Method method;

    public HongHandler() {
    }

    public HongHandler(String url, Object controller, Method method) {
        this.url = url;
        this.controller = controller;
        this.method = method;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public Object getController() {
        return controller;
    }

    public void setController(Object controller) {
        this.controller = controller;
    }

    public Method getMethod() {
        return method;
    }

    public void setMethod(Method method) {
        this.method = method;
    }

    @Override
    public String toString() {
        return "HongHandler{" +
                "url='" + url + '\'' +
                ", controller=" + controller +
                ", method=" + method +
                '}';
    }
}
/定义属性 handlerList , 保存hongHandler[url和控制器方法的映射]
    private List<HongHandler> handlerList =
            new ArrayList<>();
编写方法,完成url 和 控制器方法的映射
//编写方法,完成url 和 控制器方法的映射
    private void initHandlerMapping() {
        if (hongWebApplicationContext.ioc.isEmpty()) {
            //判断当前的ioc容器是否为null
            return;
        }

        //遍历ioc容器的bean对象,然后进行url映射处理
        //java基础 map的遍历
        for (Map.Entry<String, Object> entry : hongWebApplicationContext.ioc.entrySet()) {
            //先取出注入的Object的class对象
            Class<?> clazz = entry.getValue().getClass();
            //如果注入的Bean是Controller
            if (clazz.isAnnotationPresent(Controller.class)) {
                //取出它的所有方法
                Method[] declaredMethods = clazz.getDeclaredMethods();
                //遍历方法
                for (Method declaredMethod : declaredMethods) {
                    //判断该方法是否有@RequestMapping
                    if (declaredMethod.isAnnotationPresent(RequestMapping.class)) {
                        //取出@RequestMapping值->就是映射路径
                        RequestMapping requestMappingAnnotation =
                                declaredMethod.getAnnotation(RequestMapping.class);
                        //这里小伙伴可以把工程路径+url
                        //getServletContext().getContextPath()
                        // /springmvc/monster/list
                        String url = requestMappingAnnotation.value();
                        //创建hongHandler对象->就是一个映射关系
                        HongHandler hongHandler = new HongHandler(url, entry.getValue(), declaredMethod);
                        //放入到handlerList
                        handlerList.add(hongHandler);
                    }
                }
            }
        }
    }

完成

根据initHandlerMapping()方法的处理,我们就可以将当前注入到ioc容器的bean对象(主要是Controller),把它的映射关系,已经放到了一个hongHandler(对象)中,并将hongHandler放到了一个集合中

7.完成HongDispatcherServlet分发请求到对应控制器方法executeDispatch()

当一个请求打过来之后先走到前端控制器,因为前端控制器的doget和doport方法是要拦截你所以请求的,在这个请求里面我是不是可以拿到这个url,然后到映射关系的集合initHandlerMapping()里面去找,这个url有没有,如果有我就去调这个executeDispatch()进行分发,拿不到说明你的地址是由问题的,返回一个404就欧克了。

7.1通过request对象,返回HongHandler对象

//编写方法,通过request对象,返回HongHandler对象
    //如果没有,就返回null
    private HongHandler getHongHandler(HttpServletRequest request) {
        //1.先获取的用户请求的uri 比如http://localhost:8080/springmvc/monster/list
        //  uri = /springmvc/monster/list
        //2. 这里小伙伴要注意得到uri 和 保存url 是有一个工程路径的问题
        // 两个方案解决 =>第一个方案: 简单 tomcat 直接配置 application context =>/
        // 第二个方案 保存 honghandler对象 url 拼接 getServletContext().getContextPath()
        String requestURI = request.getRequestURI();
        //遍历handlerList
        for (HongHandler hongHandler : handlerList) {
            if (requestURI.equals(hongHandler.getUrl())) {//说明匹配成功
                return hongHandler;
            }
        }
        return null;
    }

7.2完成分发请求任务

思路:

当我们调用这个方法的时候先去判断用户请求的路径是否存在,不存在就返回404,存在就开始在之前的hongHandler集合(ioc容器的bean对象(主要是Controller)的数据存入到了hongHandler的集合中hongHandler类型的集合)里面,这个时候executeDispatch()就会去分发找到对应的url。

通过调用invoke方法来执行对象的某个方法

 private void executeDispatch(HttpServletRequest request,
                                 HttpServletResponse response) {

        HongHandler hongHandler = getHongHandler(request);
        try {
            if (null == hongHandler) {//说明用户请求的路径/资源不存在
                response.getWriter().print("<h1>404 NOT FOUND</h1>");
            } else {//匹配成功, 反射调用控制器的方法
                HongHandler.getMethod()
                      .invoke(hongHandler.getController(),request,response);
            } catch (Exception e) {
            e.printStackTrace();
        }
    }

由executeDispatch()去找handler,然后反射到自己的处理器

三.从 web.xml 动态获取 hspspringmvc.xml

前面我们加载 hongspringmvc.xml 是硬编码, 现在做活, 从 web.xml

 

//获取到web.xml中的 contextConfigLocation
        /*
         <init-param>
          <param-name>contextConfigLocation</param-name>
          <param-value>classpath:hongspringmvc.xml</param-value>
        </init-param>
         */
        String configLocation =
                servletConfig.getInitParameter("contextConfigLocation");//拿到的是classpath:hongspringmvc.xml
//这里是写的固定的spring容器配置文件.?=>做活
        //String basePackage = XMLParser.getBasePackage("hongspringmvc.xml");
        String basePackage =
                XMLParser.getBasePackage(configLocation.split(":")[1]);

四.完成自定义@Service 注解功能

如果给某个类加上@Service, 则可以将其注入到我们的 Spring 容器

4ccd4146151845b18215a90ca207e6b8.png

- Service 类标注 @Service, 可以将对象注入到 Spring 容器中
- 并可以 通过接口名 支持多级 , 类名来获取到 Service Bean
 

1.@Service注解:用于表示一个service对象,并注入到spring容器

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

2.Monster.java

public class Monster {
    private Integer id;
    private String name;
    private String skill;
    private Integer age;

    public Monster(Integer id, String name, String skill, Integer age) {
        this.id = id;
        this.name = name;
        this.skill = skill;
        this.age = age;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSkill() {
        return skill;
    }

    public void setSkill(String skill) {
        this.skill = skill;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Monster{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", skill='" + skill + '\'' +
                ", age=" + age +
                '}';
    }
}

3.MonsterServiceImpl/对象 作为service注入到spring容器

public interface MonsterService{

    //增加方法-返回monster列表
    public List<Monster> listMonster();

    //增加方法,通过传入的name,返回monster列表
    public List<Monster> findMonsterByName(String name);
}
@Service
public class MonsterServiceImpl implements MonsterService {
    @Override
    public List<Monster> listMonster() {
        //这里就模拟数据->DB
        List<Monster> monsters =
                new ArrayList<>();
        monsters.add(new Monster(100, "牛魔王", "芭蕉扇", 400));
        monsters.add(new Monster(200, "老猫妖怪", "抓老鼠", 200));
        return monsters;
    }


}

现在在Service中也添加了数据,但是我们只是将@Service注解读取了,没有注入到ioc中,所以接下来我们需要将@service注解读入到ioc中。而之前我们的@Controller注解之后我们就可以添加另外一种情况了(就是读取到这个注解)

  else if (clazz.isAnnotationPresent(Service.class)) {//如果类有@Serivce注解

                    //先获取到Service的value值=> 就是注入时的beanName
                    Service serviceAnnotation =
                            clazz.getAnnotation(Service.class);

                    String beanName = serviceAnnotation.value();
                    if ("".equals(beanName)) {//说明没有指定value, 我们就使用默认的机制注入Service
                        //可以通过接口名/类名[首字母小写]来注入ioc容器
                        //1.得到所有接口的名称=>反射
                        Class<?>[] interfaces = clazz.getInterfaces();

                        Object instance = clazz.newInstance();
                        //2. 遍历接口,然后通过多个接口名来注入
                        for (Class<?> anInterface : interfaces) {
                            //接口名->首字母小写
                            String beanName2 = anInterface.getSimpleName().substring(0, 1).toLowerCase() +
                                    anInterface.getSimpleName().substring(1);
                            ioc.put(beanName2, instance);
                        }

                    } else {//如果有指定名称,就使用该名称注入即可
                        ioc.put(beanName, clazz.newInstance());
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

五.完成 Spring 容器对象的自动装配 -@Autowried

完成 Spring 容器中对象的注入/自动装配

spring会默认优先根据(被注解修饰的)属性类型去容器中找对应的组件(bean),找到就赋值;若找到多个相同类型的组件,再将属性的名称作为组件(bean)的id去容器中查找。

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

1.完成属性的自动装配

 public void executeAutoWired() {
        //判断ioc有没有要装配的对象
        if (ioc.isEmpty()) {
            return; //你也可以抛出异常 throw new RuntimeException("ioc 容器没有bean对象")
        }
        //遍历ioc容器中的所有注入的bean对象, 然后获取到bean的所有字段/属性,判断是否需要
        //装配
        /**
         * entry => <String,Object > String 就是你注入对象时名称 Object就是bean对象
         */
        for (Map.Entry<String, Object> entry : ioc.entrySet()) {

            //String key = entry.getKey();
            Object bean = entry.getValue();

            //得到bean的所有字段/属性
            Field[] declaredFields = bean.getClass().getDeclaredFields();
            for (Field declaredField : declaredFields) {
                //判断当前这个字段,是否有@AutoWired
                if (declaredField.isAnnotationPresent(AutoWired.class)) {//有@AutoWired

                    //的当前这个字段的@AutoWired
                    AutoWired autoWiredAnnotation = declaredField.getAnnotation(AutoWired.class);
                    String beanName = autoWiredAnnotation.value();
                    if ("".equals(beanName)) {//如果没有设置value,按照默认规则
                        //即得到字段类型的名称的首字母小写,作为名字来进行装配
                        Class<?> type = declaredField.getType();
                        beanName = type.getSimpleName().substring(0, 1).toLowerCase() +
                                type.getSimpleName().substring(1);
                    }
                    //如果设置value, 直接按照beanName来进行装配
                    //从ioc容器中获取到bean
                    if (null == ioc.get(beanName)) {//说明你指定的名字对应的bean不在ioc容器
                        throw new RuntimeException("ioc容器中, 不存在你要装配的bean");
                    }
                    //防止属性是private, 我们需要暴力破解
                    declaredField.setAccessible(true);
                    //可以装配属性
                    try {
                        declaredField.set(bean, ioc.get(beanName));
                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                }
            }

        }

    }

自动装配的特性:如待装配的类型对应的bean在IOC容器中有多个,则使用待装配的属性的属性名作为id值再进行查找,找到就装配,找不到就抛异常

思路:

1.就是先遍历ioc容器中的所有注入的bean对象, 然后获取到bean的所有字段/属性,判断是否需要,

2.判断当前这个字段,是否有@AutoWired

3.有则:如果没有设置value,按照默认规则:即得到字段类型的名称的首字母小写,作为名字来进行装配;

        如果设置value, 直接按照beanName来进行装配

4.装配:

declaredField.setAccessible(true);
                    //可以装配属性

六.完成控制器方法获取参数-@RequestParam

自定义@RequestParam 和 方法参数名获取参数

@RequestParam (value = "name" )就是为了获取到浏览器地址中的属性名(名字要相同),并装配到方法的参数中

 

在浏览器提交了一个请求打到了前端控制器支撑对这个注解的一个解析,然后在前端控制器完成一个映射

难点:将浏览器请求来的参数进行处理,考虑目标方法形参是多种形式问题

1.将方法的 HttpServletRequest HttpServletResponse 参数封装到参数数组,进行反射调用

//目标将: HttpServletRequest 和 HttpServletResponse封装到参数数组
                //1. 得到目标方法的所有形参参数信息[对应的数组]
                Class<?>[] parameterTypes =
                        hongHandler.getMethod().getParameterTypes();

                //2. 创建一个参数数组[对应实参数组], 在后面反射调用目标方法时,会使用到
                Object[] params =
                        new Object[parameterTypes.length];

                //3遍历parameterTypes形参数组,根据形参数组信息,将实参填充到实参数组

                for (int i = 0; i < parameterTypes.length; i++) {
                    //取出每一个形参类型
                    Class<?> parameterType = parameterTypes[i];
                    //如果这个形参是HttpServletRequest, 将request填充到params
                    //在原生SpringMVC中,是按照类型来进行匹配,这里简化使用名字来进行匹配
                    if ("HttpServletRequest".equals(parameterType.getSimpleName())) {
                        params[i] = request;
                    } else if ("HttpServletResponse".equals(parameterType.getSimpleName())) {
                        params[i] = response;
                    }
                }

2.在方法参数 指定 @RequestParam 的参数封装到参数数组,进行反射调用

    @Override
    public List<Monster> findMonsterByName(String name) {
        //这里老师就模拟数据->DB
        List<Monster> monsters =
                new ArrayList<>();
        monsters.add(new Monster(100, "牛魔王", "芭蕉扇", 400));
        monsters.add(new Monster(200, "老猫妖怪", "抓老鼠", 200));
        monsters.add(new Monster(300, "大象精", "运木头", 100));
        monsters.add(new Monster(400, "黄袍怪", "吐烟雾", 300));
        monsters.add(new Monster(500, "白骨精", "美人计", 800));


        //创建集合返回查询到的monster集合

        List<Monster> findMonsters =
                new ArrayList<>();
        //遍历monsters,返回满足条件
        for (Monster monster : monsters) {
            if (monster.getName().contains(name)) {
                findMonsters.add(monster);
            }
        }
        return findMonsters;
    }

    @Override
    public boolean login(String name) {
        //实际上会到DB验证->这里模拟
        if ("白骨精".equals(name)) {
            return true;
        } else {
            return false;
        }

注解

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

将http请求参数封装到params数组中, 提示,要注意填充实参的时候,顺序问题

//将http请求参数封装到params数组中, 提示,要注意填充实参的时候,顺序问题

                //1. 获取http请求的参数集合
                //解读
                //http://localhost:8080/monster/find?name=牛魔王&hobby=打篮球&hobby=喝酒
                //2. 返回的Map<String,String[]> String:表示http请求的参数名
                //   String[]:表示http请求的参数值,为什么是数组
                //
                //处理提交的数据中文乱码
                request.setCharacterEncoding("utf-8");
                Map<String, String[]> parameterMap =
                        request.getParameterMap();

                //2. 遍历parameterMap 将请求参数,按照顺序填充到实参数组params
                for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {

                    //取出key,这name就是对应请求的参数名
                    String name = entry.getKey();
                    //说明:这里只考虑提交的参数是单值的情况,即不考虑类似checkbox提示的数据
                    //    这里做了简化,如果小伙伴考虑多值情况,也不难..
                    String value = entry.getValue()[0];
                    //我们得到请求的参数对应目标方法的第几个形参,然后将其填充
                    //这里专门编写一个方法,得到请求的参数对应的是第几个形参
                    //1. API 2. java内力真正增加..3.忠告
                    int indexRequestParameterIndex =
                            getIndexRequestParameterIndex(hongHandler.getMethod(), name);
                    if (indexRequestParameterIndex != -1) {//找到对应的位置
                        params[indexRequestParameterIndex] = value;
                    } else {//说明并没有找到@RequestParam注解对应的参数,就会使用默认的机制进行配置[待..]

                        //思路
                        //1. 得到目标方法的所有形参的名称-专门编写方法获取形参名
                        //2. 对得到目标方法的所有形参名进行遍历,如果匹配就把当前请求的参数值,填充到params
                        List<String> parameterNames =
                                getParameterNames(hongHandler.getMethod());
                        for (int i = 0; i < parameterNames.size(); i++) {
                            //如果请求参数名和目标方法的形参名一样,说明匹配成功
                            if (name.equals(parameterNames.get(i))) {
                                params[i] = value;//填充到实参数组
                                break;
                            }
                        }
                    }
                }

编写方法,返回请求参数是目标方法的第几个形参

  //编写方法,返回请求参数是目标方法的第几个形参

    /**
     * @param method 目标方法
     * @param name   请求的参数名
     * @return 是目标方法的第几个形参
     */
    public int getIndexRequestParameterIndex(Method method, String name) {

        //1.得到method的所有形参参数
        Parameter[] parameters = method.getParameters();
        for (int i = 0; i < parameters.length; i++) {
            //取出当前的形参参数
            Parameter parameter = parameters[i];
            //判断parameter是不是有@RequestParam注解
            boolean annotationPresent = parameter.isAnnotationPresent(RequestParam.class);
            if (annotationPresent) {//说明有@RequestParam
                //取出当前这个参数的 @RequestParam(value = "xxx")
                RequestParam requestParamAnnotation =
                        parameter.getAnnotation(RequestParam.class);
                String value = requestParamAnnotation.value();
                //这里就是匹配的比较
                if (name.equals(value)) {
                    return i;//找到请求的参数,对应的目标方法的形参的位置
                }
            }
        }
        //如果没有匹配成功,就返回-1
        return -1;
    }

3.在方法参数 没有指定 @RequestParam ,按照默认参数名获取值, 进行反射调用

通过方法返回的 String, 转发或者重定向到指定页面

编写方法, 得到目标方法的所有形参的名称,并放入到集合中返回

 //编写方法, 得到目标方法的所有形参的名称,并放入到集合中返回

    /**
     * @param method 目标方法
     * @return 所有形参的名称, 并放入到集合中返回
     */
    public List<String> getParameterNames(Method method) {

        List<String> parametersList = new ArrayList<>();
        //获取到所以的参数名->这里有一个小细节
        //在默认情况下 parameter.getName() 得到的名字不是形参真正名字
        //而是 [arg0, arg1, arg2...], 这里我们要引入一个插件,使用java8特性,这样才能解决
        Parameter[] parameters = method.getParameters();
        //遍历parameters 取出名称,放入parametersList
        for (Parameter parameter : parameters) {
            parametersList.add(parameter.getName());
        }
        System.out.println("目标方法的形参列表=" + parametersList);
        return parametersList;
    }

引入插件加入pom.xml中

  <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.7.0</version>
    <configuration>
      <source>1.8</source>
      <target>1.8</target>
      <compilerArgs>
        <arg>-parameters</arg>
      </compilerArgs>
      <encoding>utf-8</encoding>
    </configuration>
  </plugin>
{//说明并没有找到@RequestParam注解对应的参数,就会使用默认的机制进行配置[待..]

                        //思路
                        //1. 得到目标方法的所有形参的名称-专门编写方法获取形参名
                        //2. 对得到目标方法的所有形参名进行遍历,如果匹配就把当前请求的参数值,填充到params
                        List<String> parameterNames =
                                getParameterNames(hongHandler.getMethod());
                        for (int i = 0; i < parameterNames.size(); i++) {
                            //如果请求参数名和目标方法的形参名一样,说明匹配成功
                            if (name.equals(parameterNames.get(i))) {
                                params[i] = value;//填充到实参数组
                                break;
                            }
                        }
                    }

七.完成简单视图解析

通过方法返回的 String, 转发或者重定向到指定页面

完成任务说明
- 用户输入 白骨精 , 可以登录成功 , 否则失败
- 根据登录的结果 , 可以重定向或者请求转发到 login_ok.jsp / login_error.jsp, 并显示妖

 

思路

在handler中会增加一个目标方法,专门处理登录,返回一个结果(String),进行请求转发还是重定向返回给前端控制器。然后前端处理器会去调用视图解析器,同时视图解析器也会返回一个结果。视图解析器对handler返回的结果进行解析,这个时候有两种结果:1.请求转发,2,重定向

再由视图解析器进行解析

1.登录页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登录页面</title>
</head>
<body>
<h1>登录页面</h1>
<form action="/monster/login" method="post">
    妖怪名: <input type="text" name="mName"><br/>
    <input type="submit" value="登录">
</form>
</body>
</html>

2.失败页面

<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
    <title>登录失败</title>
</head>
<body>
<h1>登录失败</h1>
sorry, 登录失败 ${requestScope.mName}
</body>
</html>

3.成功页面

<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
    <title>登录成功</title>
</head>
<body>
<h1>登录成功</h1>
欢迎你: ${requestScope.mName}
</body>
</html>

处理妖怪登录的方法,返回要请求转发/重定向的字符串

//处理妖怪登录的方法,返回要请求转发/重定向的字符串
    @RequestMapping("/monster/login")
    public String login(HttpServletRequest request,
                        HttpServletResponse response,
                        String mName) {

        System.out.println("--接收到mName---" + mName);
        //将mName设置到request域
        request.setAttribute("mName", mName);
        boolean b = monsterService.login(mName);
        if (b) {//登录成功!
            //return "forward:/login_ok.jsp";
            //测试重定向
            //return "redirect:/login_ok.jsp";
            //测试默认的方式-forward
            return "/login_ok.jsp";

        } else {//登录失败
            return "forward:/login_error.jsp";
        }
    }

    这里就是对返回的结果进行解析(判断是forward 还是 redirect)

 //这里就是对返回的结果进行解析=>原生springmvc 可以通过视图解析器来完成
                //这里直接解析,只要把视图解析的核心机制讲清楚就OK
                if (result instanceof String) {

                    String viewName = (String) result;
                    if(viewName.contains(":")){//说明你返回的String 结果forward:/login_ok.jsp 或者 redirect:/xxx/xx/xx.xx
                        String viewType = viewName.split(":")[0];//forward | redirect
                        String viewPage = viewName.split(":")[1];//是你要跳转的页面名
                        //判断是forward 还是 redirect
                        if("forward".equals(viewType)) {//说明你希望请求转发
                            request.getRequestDispatcher(viewPage)
                                    .forward(request,response);
                        } else if("redirect".equals(viewType)) {//说明你希望重定向
                            response.sendRedirect(viewPage);
                        }
                    } else {//默认是请求转发
                        request.getRequestDispatcher(viewName)
                                .forward(request,response);
                    }

                }

   八.完成返回 JSON 格式数据-@ResponseBody 

1.@RequestBody的基本认识

        @RequestBody主要用来接收前端传递给后端的json字符串中的数据的(请求体中的数据的);而最常用的使用请求体传参的无疑是POST请求了,所以使用@RequestBody接收数据时,一般都用POST方式进行提交。在后端的同一个接收方法里,@RequestBody与@RequestParam()可以同时使用,@RequestBody最多只能有一个,而@RequestParam()可以有多个。

        如果后端参数是一个对象,且该参数前是以@RequestBody修饰的,那么前端传递json参数时,必须满足以下要求:

        后端@RequestBody注解对应的类在将HTTP的输入流(含请求体)装配到目标类(即:@RequestBody后面的类)时,会根据json字符串中的key来匹配对应实体类的属性,如果匹配一致且json中的该key对应的值符合(或可转换为),这一条我会在下面详细分析,其他的都可简单略过,但是本文末的核心逻辑代码以及几个结论一定要看! 实体类的对应属性的类型要求时,会调用实体类的setter方法将值赋给该属性。

        json字符串中,如果value为""的话,后端对应属性如果是String类型的,那么接受到的就是"",如果是后端属性的类型是Integer、Double等类型,那么接收到的就是null。

        json字符串中,如果value为null的话,后端对应收到的就是null。

        如果某个参数没有value的话,在传json字符串给后端时,要么干脆就不把该字段写到json字符串中;要么写value时, 必须有值,null  或""都行。千万不能有类似"stature":,这样的写法,

简单说就是如果你想让这个方法以json的形式返回一个结果,就可以添加这个注解

2.需求说明:

1.在实际开发中,比如前后端分离的项目,通常是直接返回json数据给客户端/浏览器

2.客户端/浏览器接收到数据后,直接决定如何处理和显示

3.代码

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestParam {
    String value() default "";
}
/**
     * 编写方法,返回json格式的数据
     * 1. 梳理
     * 2. 目标方法返回的结果是给springmvc底层通过反射调用的位置
     * 3. 我们在springmvc底层反射调用的位置,接收到结果并解析即可
     * 4. 方法上标注了 @ResponseBody 表示希望以json格式返回给客户端/浏览器
     * 5. 目标方法的实参,在springmvc底层通过封装好的参数数组,传入..
     * @param request
     * @param response
     * @return
     */

    @RequestMapping("/monster/list/json")
    @ResponseBody
    public List<Monster> listMonsterByJson(HttpServletRequest request,
                                           HttpServletResponse response) {

        List<Monster> monsters = monsterService.listMonster();
        return monsters;
    }

当读取到该注解

else if(result instanceof ArrayList) {//如果是ArrayList

                    //判断目标方法是否有@ResponseBody
                    Method method = hongHandler.getMethod();
                    if(method.isAnnotationPresent(ResponseBody.class)) {
                        //把result [ArrayList] 转成json格式数据-》返回
                        //这里我们需要使用到java中如何将 ArrayList 转成 json(writeValueAsString())
                        //这里我们需要使用jackson包下的工具类可以轻松的搞定.
                            
                        ObjectMapper objectMapper = new ObjectMapper();
                        String resultJson =
                                objectMapper.writeValueAsString(result);

                        response.setContentType("text/html;charset=utf-8");
                        //这里简单的处理,就直接返回
                        PrintWriter writer = response.getWriter();
                        writer.write(resultJson);
                        writer.flush();
                        writer.close();

                    }

九.最后的总结

首先我们先开发的是前端控制器(HongDispatherServlet)

1.先将前端控制器配置到了web.xml文件中(其实是由tomcat来创建的)

2.在前端控制器里面创建了spring容器(init方法)并初始化(初始化的过程就是将我们control和service注入到容器)

3.而这个spring容器我们专门开发了一个HongWebApplicationContext,这个类里面我们进行了扫描,进行了注入

4.init方法我们拿到spring配置文件的包,然后进行一个扫描,拿到(注解)全部路径,然后在反射到ioc容器里面,还有就是完成bean对象属性的装配工作executeAutoWired();所以这个方法将bean对象注入到了ioc中并将依赖关系装配

5.接下来在前端控制器中的initHandlerMapping---完成映射处理,将映射对象保存到集合---遍历ioc容器的bean对象,然后进行url映射处理,将这个遍历对象保存到了handler集合中了

6.接下来就是最头疼的分发请求了需要对浏览器请求来的参数进行处理,考虑目标方法形参是多种形式问题---将其封装到参数数组,以反射调用的形式传递给目标方法

                这个方法具体做的:先判断用户请求的路径和资源是否存在。接着将将http请求参数封装到params数组中调用的时候传的就是这个实参数组。这就是为啥HttpServletRequest req, HttpServletResponse resp这两个形参是如何得到实参的。就是把你传过来的参数进行封装,扫描你这个目标方法有那些参数,然后一个一个给你填进去的。

7.当你将整个目标方法调用之后打到某个目标方法之后,就会返回一个结果,这个结果就会被适配器(代码中没有实现,但是必须知道有)去调用我们自己的处理器返回一个结果。

8.根据这个结果进行视图解析(视图解析器),就会去判断如果你是一个String需要请求转发或者重定向,如果你是一个ArrayList并且有@ResponseBody这个注解,就会返回json文件

10.部分分析示意图,不想画了

92d628fa070641b4a55e551213c0bac1.png

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2248.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【HTML+CSS+JS】模仿QQ登录界面

目录前言简介布局思路相关代码颜色渐变动画头像表单区域JS相关总结前言 学了HTML、CSS和JS有了一个月了&#xff0c;JS还未学完&#xff0c;偷懒写一个小项目&#xff0c;用了一个下午&#xff0c;顺便巩固一下所学知识。&#xff08;内容比较简陋&#xff0c;适合新手&#x…

基于火鹰优化算法的函数寻优算法

文章目录一、理论基础1、火鹰优化算法2、FHO算法伪代码二、仿真实验与结果分析三、参考文献一、理论基础 1、火鹰优化算法 文献[1]提出了火鹰优化算法(Fire Hawk Optimizer, FHO)作为一种新的元启发式算法&#xff0c;该算法基于啸鸢、麻鹰和褐隼的觅食行为&#xff0c;这些鸟…

【网络原理】网络编程Socket套接字基础知识汇总

目录 1.网络初始&#xff1a; 2.网络编程&#xff1a; 3.UDP数据报套接字&#xff1a; 4.TCP流套接字&#xff1a; 1.网络初始&#xff1a; 局域网&#xff08;LAN&#xff09;广域网&#xff08;WAN&#xff09;IP地址用于定位主机的网络地址。端口号可以标识主机中发送数…

数据结构六:堆

前言&#xff1a;上一篇我们讲了二叉树&#xff0c;你知道吗&#xff1f;堆的底层是一棵完全二叉树。这样说会不会就会觉得熟悉了。 目录 1.堆的概念及存储方式 2&#xff1a;堆的创建 2.1:向下调整 3.堆的插入和删除 3.1&#xff1a;堆的插入 3.2&#xff1a;堆的删除 …

基于Web的商城后台管理系统的设计与实现

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

Oracle和MySQL查询所有的表信息和字段信息

Oracle和MySQL查询所有的表信息和字段信息1. MySQL1.1 查询表1.2 查询字段1.2.1 方式1->SHOW FULL COLUMNS1.2.2 方式2->information_schema.COLUMNS1.3 查表和字段1.4 查表和字段-->转程Oracle需要的数据类型2. Oracle2.1 查表和字段的单表查询2.2 整理查表和字段的s…

超详细的JUnit单元测试介绍

前言 本文为JUnit单元测试相关知识&#xff0c;下边将对JUnit单元测试概念&#xff0c;JUnit优点&#xff0c;JUnit安装与使用&#xff0c;JUnit运行流程与常用注解&#xff0c;JUnit测试套件使用及参数化设置&#xff0c;JUnit断言等进行详尽介绍~ &#x1f4cc;博主主页&…

大数据Hadoop之——Apache Hudi 与 Presto/Trino集成

文章目录一、概述二、Trino 环境部署1&#xff09;安装JDK2&#xff09;安装python3&#xff09;安装Trino1、下载解压并配置环境变量2、修改配置3、启动服务4、测试验证三、在Hive中创建表关联Hudi表1&#xff09;添加jar包2&#xff09;创建库表关联Hudi四、Hudi 与 Trino集成…

SpringCloud Alibaba系列 Sentinel(三)

高并发下的微服务容错方案&#xff1f; 限流、熔断、降级 1&#xff1a;限流 在高并发系统中一定要用&#xff0c;高并发的所有请求进来&#xff0c;不是让每个请求都打到后台集群的&#xff0c;后台集群有它的消费能力&#xff0c;应该在它消费能力之内放行请求&#xff0c;…

Hadoop HA集群全是standBy解决办法

文章目录原理解决方案原理 hadoop集群配置HA后&#xff0c;会存在多个namenode&#xff0c;但是同一时间仅有一台NN为Active的状态&#xff0c;其他NN都是StandBy的状态。 上图是hadoop集群配置HA的原理图&#xff0c;从上图我们可以看到多个NN的状态切换&#xff0c;是依靠Z…

linux命令与makefile学习

linux命令与makefile学习文件权限通配符*常用命令makefilegcc与g区别&#xff1a;Linux上有一句话&#xff1a;一切皆文件 普通文件 “-” 目录文件 “d” &#xff08;directory&#xff09; 管道文件 “p” &#xff08;piping&#xff09; 链接文件“l” &#xff08;li…

SAP FICO银行账户余额查询表开发说明书(包括开发源代码、测试样例及FS)

程序说明 满足财务银行账户余额查询明细的需求; 支持财务实时查看银行余额数据。 筛选界面 序号 栏位标题 字段类型 是否必须 是否为范围

【pwn】2022 祥云杯 部分wp

【pwn】2022 祥云杯 部分wp 前言 又是一年的祥云杯&#xff0c;相比去年我啥也不会写&#xff0c;今年起码写了几个签到… 又被队友带飞咯 protool Google的Protobuf&#xff0c;参考学习连接 https://bbs.pediy.com/thread-270004.htm 发现了栈溢出&#xff0c;protobuf…

Unity技术手册-UGUI零基础详细教程-Toggle切换

往期文章分享点击跳转>《导航贴》- Unity手册&#xff0c;系统实战学习点击跳转>《导航贴》- Android手册&#xff0c;重温移动开发 本文约3千字&#xff0c;新手阅读需要7分钟&#xff0c;复习需要2分钟 【收藏随时查阅不再迷路】 &#x1f449;关于作者 众所周知&#…

2.6 Python 基本数据类型

1. 数据类型 类型是变量所指的内存中对象的类型. 内置的type()函数可以用来查询变量所指的对象类型。Python 3中有六个标准的数据类型: Numbers(数字), String(字符串), List(列表), Tuple(元组), Sets(集合), Dictionary(字典).2. Numbers 数字型 Python 有三种数字类型 in…

SpringMVC基本配置

小常规 springmvc的处理器对应的bean必须按照规范格式开发&#xff0c;为避免加入无效的bean可通过bean加载过滤器进行包含设定或排除设定&#xff0c;表现层bean标注通常设定为Controller在此发现图片没有加载出来回到程序去分析当发起一个请求以后DispatcherServlet配置拦截所…

【JVM技术专题】 深入分析class字节码指令方法调用详解「原理篇」

方法调用详解 ​ 调用目标在程序代码写好、编译器进行编译时就必须确定下来&#xff0c;这类方法的调用称为解析。 解析 ​ 在Java语言中符合**“编译期可知&#xff0c;运行期不可变”**这个要求的方法&#xff0c;主要包括静态方法和私有方法两大类&#xff0c;前者与类型…

【JavaScript】网页轮播图

目录HTML搭建功能实现小圆圈事件左右按钮事件自动播放轮播图也叫焦点图&#xff0c;是网页中比较常见的网页特效。功能&#xff1a;鼠标经过轮播图模块&#xff0c;左右按钮显示&#xff0c;离开隐藏左右按钮。点击右侧按钮一次&#xff0c;图片往左播放一张&#xff0c;以此类…

UACANet: Uncertainty Augmented Context Attention for Polyp Segmentation代码补充

上一篇看了文章创新点的代码&#xff0c;现在看一下train文件等其余的文件。 看主函数&#xff1a; import os import torch import argparse import tqdm import sysimport cv2 import torch.nn as nn import torch.distributed as distfrom torch.optim import Adam, SGD fr…

CVE-2022-21907 Microsoft Windows HTTP 协议栈远程代码执行漏洞复现

目录 0x01 声明&#xff1a; 0x02 简介&#xff1a; 0x03 漏洞概述&#xff1a; 0x04 影响版本&#xff1a; 0x05 环境搭建&#xff1a; 下载&#xff1a; 开启IIS&#xff1a; 0x06 漏洞复现&#xff1a; 利用POC&#xff1a; 0x07 流量分析&#xff1a; 客户端&am…