其他分享
首页 > 其他分享> > 37-面面01

37-面面01

作者:互联网

day37 - 面面项目第一天

学习目标

第一章-版本控制

知识点-版本控制相关的概念

1.目标

2.路径

  1. 什么是版本控制
  2. 为什么要进行版本控制
  3. 常见版本控制工具

3.讲解

3.1 什么是版本控制

​ 版本控制(Revision control)是一种软体工程技巧,籍以在开发的过程中,确保由不同人所编辑的同一档案(项目代码)都得到更新。 对我们开发的项目进行版本管理,管理的这些项目具有版本号,可以看到某一个版本的所有修改内容,也可以回退到以前的某一个版本。

一句话概括: 版本控制就是一个工具(软件), 它可以帮助我们管理项目资源,只要有一次写入动作,版本号就会往上提升

3.2 为什么要进行版本控制

  1. 回退代码到以前的某个阶段..
  2. 查看以往的代码修改记录及变化
  3. 协同开发时,合并同一文件中不同开发者写的代码
  4. 协同开发时定位修改代码的责任人 可以看到代码的修改记录
  5. 统计工作任务量
  6. 备份源代码
  7. 保护代码不被外泄

3.3 常见版本控制工具

4. 小结

  1. 什么版本控制

    ​ 是一种软体工程技巧, 确保由不同人所编辑的同一档案(项目代码)都得到更新。

  2. 为什么要使用版本控制

    ​ 工作里面要协同开发

  3. 常见的版本控制工具

    ​ SVN

    ​ Git

知识点-SVN介绍

1.目标

2.路径

  1. SVN概述
  2. SVN工作方式【重点】

3.讲解

3.1SVN概述

​ Svn(Subversion)是版本管理工具,在当前的开源项目里(J2EE),几乎95%以上的项目都用到了 SVN。Subversion 项目的初衷是为了替换当年开源社区最为流行的版本控制软件CVS,在CVS的功能的基础上有很多的提升同时也能较好的解决CVS系统的一些不足。

3.2 SVN工作方式【重点】

image-20191108150425278

4.小结

  1. SVN: 就是一个版本管理工具
  2. SVN工作方式

image-20191229092150203

实操-SVN的安装

1.目标

2.路径

  1. SVN服务器的安装
  2. SVN客户端的安装

3.讲解

3.1SVN服务器的安装

详情参考资料 【 01_VisualSVNServer安装步骤.html】

3.2SVN客户端的安装

详情参考资料 【02_svn客户端安装步骤.html】

唯一要注意的地方就是:这里面有一个选项一定要选择安装它。

4.小结

  1. 注意

​ 目录都不要有中文和空格

  1. 装客户端
    • 先装客户端软件
    • 再装语言包

实操-创建仓库和用户

1.目标

2.路径

  1. 创建仓库
  2. 创建用户

3.讲解

3.1创建仓库

详情参考资料 【03_VisualSVN创建仓库.html】

3.2创建用户

详情参考资料 【04_VisualSVN创建用户步骤.html】

4.小结

  1. 创建仓库

    image-20191229100617688

  2. 用户和组

    • 给用户或者组分配权限

实操-SVN功能操作【重点】

1.目标

2.路径

3.讲解

3.1检出代码(checkout)

可以看成是让我们目前的本地和服务器处于同步状态。服务器上有什么,我们本地就有什么!

不管仓库里面现在有什么,第一次跟这个仓库对话永远是检出代码: 检出的操作可以看成是让本地和服务器处于同样的状态。

  1. 拷贝路径

  2. 通过svn客户端检出代码



  3. 检出的结果

    .svn文件夹为版本控制的标记记录

3.2提交代码

  1. 新建文件

    新建完成后,文件会有一个?号图标,代表当前文件未纳入到版本控制中

  2. 添加到版本控制中




    添加操作完成后,文件图标会变为+号图标,代表当前文件已经纳入到版本控制中,但是没有上传到svn服务器中。

  3. 提交到版本控制器服务器




    提交操作完成后,图标会变为对钩,代表文件已经上传到版本控制服务器中。

3.3更新代码

Update,它是更新操作,可以将svn服务器上的内容更新到本地

3.4冲突解决

3.5更新|回退到某个版本

​ 选中文件,右键更新之某个版本

img

img

img

3.6 版本库的备份和还原【了解】





4.小结

4.1SVN图标

4.2 SVN使用规范

实操-在IDEA中使用SVN

1.目标

2.路径

  1. 配置SVN
  2. IDEA SVN使用

3.讲解

3.1 配置SVN

注意: 需要认证证书时,删除这个目录下所有内容C:\Users\Eric\AppData\Roaming\Subversion

选择File>Settings>Version Controller>Subversion,分别设置命令行客户端工具和svn配置信息存储目录。如下图:

图片42

3.2 IDEA SVN使用

3.2.1 浏览仓库

选中IDEA工具栏的VCS > Browse VCS Repository > Browse Subversion Repository

53494852574

此时会出现如下界面,我们点击+号,输入本地SVN地址,再点击OK即可将本地SVN地址加入进来。

53494868955

如果没有记住用户名和密码时,它就会弹出界面如下,需要我们输入正确的账号和密码方能实现仓库浏览。

53494881930

账号密码正确后,如下浏览:

53494890124

3.2.2 上传本地项目到SVN

在IDEA中,将本地项目共享到SVN,这个操作比较简单。

要做这个练习,我们先创建一个maven工程,这个工程还没有连接到SVN。然后再做以下操作

  1. 确保SVN功能已经开启:菜单:VCS > Enable Version Controller Integration

53497250306

  1. 选中Subversion,此时功能的颜色会变成黄色,表明SVN功能已经开启。

53497256751

  1. 2020版本的idea以后(2018版本可以忽略这个步骤),需要多增加这个步骤:

image-20210405182229659

  1. 共享操作:在项目上右键 > Subversion > Share Directory

image-20210405115843129

选择要共享的目标SVN地址,接着指定要共享的目标对象,点击Share之后,会在SVN创建一个对应的版本库文件,但该项目并未立刻提交。

53497291476

提交对应工程:选择对应工程 > Subversion > Commit Directory

53497308349

勾选要提交的内容,并填写上提交内容的注释信息,然后点击commit提交,提交完成后,项目就会被提交到SVN

53497320983

成功后再查询仓库,此时新的项目就出现了

shareproject

3.2.3 Add Commit

添加新文件时,idea会访问是否将新文件添加到SVN管理中

svn01

注意,此时的文件是没有上传到svn上的,需要通过commit file才行

svn02

如果文件有修改,也是要在项目上或者修改的文件上右击Subversion > Commit File

选择要提交的内容,并填写上注释,然后选中commit即可。

svn03

3.2.4 Update

如果需要更新服务器上的文件,选中要更新的项目并右键 > Subversion > Update Directory

svn04

一般直接点击OK即可,但如果需要选择历史版本,则勾上HEAD选项。

svn05

3.2.5 checkout 检出

​ 打开上面浏览的SVN目录信息,并选中任意一个项目,并右键,选中checkout,该功能是将SVN上的资源检出到本地。

53495300622

选中本地目录,用于存储从SVN服务器上检出的项目,目录选中后,直接点击OK,进入版本选择和存储目标地址选中

53495308012

接着我们选中要检出的项目存储目标地址,并选中要检出的版本信息,最后点击OK即可。

53495319604

按默认的就可以了

checkout2

这里我们直接勾选第一个,从现有资源创建项目。

53495339065

然后一路next,最后选择finish即可完成项目的导出。

checkout3

3.2.6 解决冲突

多个用户同时编辑一个文件并都直接执行提交时,容易导致冲突产生,如下:

53497404178

产生了冲突
我们在工程上执行更新操作

如果文件变更发生冲突,会看到如下界面,这里会有三个选项:

svn08

Accept Yours:接受你的版本,会以自己的版本为正确版本。

Accept Theirs:接受SVN上的版本,会把服务器的版本作为正确版本。

Merge:合并,需要将冲突手动排除。

svn09

svn10

最后还要把这个文件提交

4.小结

  1. 大家对着文档操作

第二章-自定义MVC框架【重点】

知识点-以模块为单位创建Servlet

1.目标

2.路径

  1. 复习 以模块为单位创建Servlet
  2. 使用反射优化

3.讲解

3.1以模块为单位创建Servlet

​ 传统方式的开发一个请求对应一个Servlet:这样的话会导致一个模块的Servlet过多,导致整个项目的Servlet都会很多.能不能做一个处理?让一个模块都用一个Servlet处理请求. 用户模块, 创建UserServlet

早期的servlet

​ 注册 ------ RegisterServlet

​ 登录 ----- LoginServlet

​ ...

模块化的Servlet

​ 注册:http://localhost:8080/day36/userServlet?method=register

​ 登录:http://localhost:8080/day36/userServlet?method=login

​ 激活:http://localhost:8080/day36/userServle?method=active

class UserServlet extend HttpServlet{
  
  	... doGet(HttpServletRequest request, HttpServletResponse response){
        
        //1.获得请求参数method
        String methodStr =  request.getParameter("method");
        
        //2.判断, 调用对应的方法
        if("regist".equals(methodStr)){
            //注册
           regist(request,response);
        }else if("login".equals(methodStr)){
            //登录
           login(request,response);
        }else if("active".equals(methodStr)){
            //激活
           active(request,response);
        }
  	}

  	public void regist(HttpServletRequest request, HttpServletResponse response){
      	//1. 接受请求参数
      	//2. 调用业务
      	//3. 响应
  	}
  	
  	 public void login(HttpServletRequest request, HttpServletResponse response){
      	//1. 接受请求参数
      	//2. 调用业务
    	//3. 响应
  	}
  	public void active(HttpServletRequest request, HttpServletResponse response){
      	//1. 接受请求参数
      	//2. 调用业务
     	//3. 响应
  	}
  
  
}

3.2使用反射优化

发现在上面的doGet方法里面,有大量的if语句,能不能不写if语句

​ 注册:http://localhost:8080/day31/userServlet?method=regist

​ 登录:http://localhost:8080/day31/userServlet?method=login

​ 激活:http://localhost:8080/day31/userServlet?method=active

  <dependencies>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.1.0</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>

 <build>
       <plugins>
           <plugin>
               <groupId>org.apache.tomcat.maven</groupId>
               <artifactId>tomcat7-maven-plugin</artifactId>
               <version>2.2</version>
               <configuration>
                   <!-- 指定端口 -->
                   <port>82</port>
                   <!-- 请求路径 -->
                   <path>/</path>
               </configuration>
           </plugin>
       </plugins>

package com.itheima.servlet01;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;

/*
    用于处理一切与用户有关的请求!
 */
@WebServlet("/user")
public class UserServlet extends HttpServlet {

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

        try {
            //1. 获取请求参数,必须要携带一个参数: method
            String methodName = req.getParameter("method");

            //2. 找方法
            Method m = this.getClass().getMethod(methodName , HttpServletRequest.class , HttpServletResponse.class);

            //3. 调用方法
            m.invoke(this ,req , resp );
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

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

    public void register(HttpServletRequest req, HttpServletResponse resp){
        System.out.println("调用了UserServlet的register方法~!~");
    }

    public void login(HttpServletRequest req, HttpServletResponse resp){
        System.out.println("调用了UserServlet的login方法~!~");
    }
}

4.小结

  1. 一个模块就创建一个Servlet, 每个请求都携带请求参数method=xx

知识点-BaseServlet

1.目标

2.路径

  1. BaseServlet分析
  2. BaseServlet编写

3.讲解

3.1BaseServlet分析

以模块为单元创建Servlet

-- 用户模块
	注册:http://localhost:8080/day31/userServlet?method=regist
	登录:http://localhost:8080/day31/userServlet?method=login
	激活:http://localhost:8080/day31/userServlet?method=active

class UserServlet extend HttpServlet{
  
    //找到对应的方法 执行
  	... doGet(HttpServletRequest request, HttpServletResponse response){
      	//1. 获得method请求参数的值【说白了就是方法名】
      	String methodStr =  request.getParameter("method");
        //2.获得字节码对象
        Class clazz  = this.getClass();
        //3.根据方法名反射获得Method
      Method method =   clazz.getMethod(methodStr,HttpServletRequest.class,HttpServletResponse.class);
        
        //4.调用
        method.invoke(this,request,response)
          
      
  	}

  	public void regist(HttpServletRequest request, HttpServletResponse response){
      	//1. 接受请求参数
      	//2. 调用业务
      	//3. 响应
  	}
  	
  	 public void login(HttpServletRequest request, HttpServletResponse response){
      	//1. 接受请求参数
      	//2. 调用业务
    	//3. 响应
  	}
  	public void active(HttpServletRequest request, HttpServletResponse response){
      	//1. 接受请求参数
      	//2. 调用业务
     	//3. 响应
  	}
 
}


-- 订单模块
	

package com.itheima.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;

/*
    专门处理一切有关订单的请求

 */
@WebServlet("/order")
public class OrderServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {
            //1. 获取参数method:
            String methodName = req.getParameter("method");

            //2. 找方法,让方法执行!
            Method method = this.getClass().getMethod(methodName, HttpServletRequest.class, HttpServletResponse.class);

            //3. 调用
            //以前的调用:  对象.方法(参数)
            //this.register(req, resp);

            //反射: 方法对象.invoke(对象,参数1,参数2...)
            method.invoke(this,req, resp);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

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

    public void add(HttpServletRequest req, HttpServletResponse resp){
        System.out.println("执行了OrderServlet的add...");
    }

    public void update(HttpServletRequest req, HttpServletResponse resp){
        System.out.println("执行了OrderServlet的update...");
    }
}

 
}

3.2 BaseServlet编写

  1. 定义一个类,名字一般叫做BaseXXX, BaseServlet。
  2. 这个类也是一个Servlet, 把刚才的UserServlet和OrderServlet的doGet方法和dw让UserServlet和OrderServlet继承BaseServlet即可
package com.itheima.servlet02;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;

/*
    1. 这个类是所有模块化Servlet的父亲类
    2. 它用来存放共性的代码 doGet和doPost方法
    3. 要想存放doGet和doPost代码,那么这个类必须是一个Servlet
    4. 所有的模块化的Servlet都来继承这个BaseServlet即可,不需要去继承HttpServlet了。
    5. BaseServlet只是用来做判断请求,调用方法的,不管是Get请求过来还是post请求过来
        都会执行子类的方法,所以只需要重写service方法即可,不需要重写doGet和doPost方法了!

    6. 有几个疑问:
        6.1 这里的this 是谁? 子类对象!
        6.2 这个BaseServlet也是一个Servlet,请问,要不要注册这个Servlet?
            不用!,因为这个类这是用来判断请求参数,调用方法的。它没有处理请求的能力,所以不用写映射!
 */

public class BaseServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {
            //1. 获取请求参数,必须要携带一个参数: method
            String methodName = req.getParameter("method");

            //2. 找方法
            Method m = this.getClass().getMethod(methodName , HttpServletRequest.class , HttpServletResponse.class);

            //3. 调用方法
            m.invoke(this ,req , resp );
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
package com.itheima.servlet02;

import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;

/*
    用于处理一切与用户有关的请求!
 */
@WebServlet("/user02")
public class UserServlet extends BaseServlet {



    public void register(HttpServletRequest req, HttpServletResponse resp){
        System.out.println("调用了UserServlet的register方法~!~");
    }

    public void login(HttpServletRequest req, HttpServletResponse resp){
        System.out.println("调用了UserServlet的login方法~!~");
    }
}

package com.itheima.servlet02;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;

/*
    用于处理一切与订单有关的请求!
 */
@WebServlet("/order02")
public class OrderServlet extends BaseServlet {

    public void add(HttpServletRequest req, HttpServletResponse resp){
        System.out.println("调用了OrderServlet的add方法~!~");
    }

    public void update(HttpServletRequest req, HttpServletResponse resp){
        System.out.println("调用了OrderServlet的update方法~!~");
    }
}

4.小结

  1. 把公共的代码抽取到父类BaseServlet, 模块的Servlet继承BaseServlet就可以了
  2. BaseServlet里面的异常打印不要删除

image-20191229121956029

  1. 在BaseServlet里面反射的API是getMethod 也就意味着模块里面的Servlet的方法应该是public的

image-20191229122047029

知识点-自定义MVC框架

1.目标

2.路径

  1. BaseServlet问题分析
  2. 自定义MVC框架初级版本
  3. 自定义MVC框架终极版本

3.讲解

3.1BaseServlet问题分析

​ 这种实现要求客户端的每个请求都必须传递一个method参数,否则无法找到对应的web方法,另外这个控制器(Servlet)也必须==要继承父类BaseServlet,因为是在父类的service()中完成请求解析与调用。这种方式不是很便捷并且耦合度比较高。
​ 接下来想进一步重构BaseServlet,让其用起来更方便更便捷,让它们的耦合关系更松散,比如写一个总控制器类 (类似BaseServlet),这个总控制器继承HttpServlet类,其他的控制器(子控制器 【类似UserServlet...】)是普通Java类,谁都不用继承(不用继承总的控制器类,也不用继承HTTPServlet类),然后由总控制器动态的去调用子控制器的业务方法,这种动态调用是采用在参数中不加入method的方式来实现的。

3.2自定义SpringMVC框架初级版本

3.2.1思路
  1. 创建@RequestMapping注解
    1. 注解的作用主要是为了给servlet里面的方法做映射的。
    2. 注解一般是作用于方法上,并且保留到运行的时候还要存在于字节码
  2. 创建UserServlet , 定义方法,
  3. 在这个方法上面添加@RequestMapping注解
  4. 创建BaseServlet 继承HttpServlet, 映射的路径配置 *.do
  5. 在BaseServlet的 重写的service()方法里面
//1.获得请求的URI和项目部署路径, 截取获得映路径 eg: /user/login
//2.扫描某个包里面的所有类的字节码对象集合List
//3.遍历字节码对象集合List
//4.获得类里面的所有的Method
//5.遍历所有的Method
//6.获得method上面的RequestMapping注解 获得注解的value属性值
//7.判断value属性值是否和获得映路径一致, 一致 就调用method
3.2.2代码实现
package com.itheima.servlet03;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/*
    这个注解打在方法身上,用于表示什么样的请求地址,能够执行这个方法。
    @RequestMapping("/user/register")
    public void register(){

    }

    步骤:
        1. 定义注解,里面给出一个value属性,用于设置映射地址
        2. 这个注解只能打在方法上,所以要表示出来它的位置
        3. 这个注解要保留到运行阶段还存在,因为当程序运行的时候,我们需要去解析类的
            字节码,取到方法身上的注解,所以这个注解不能在前面的阶段就丢失掉了
                源码 --- 编译 --- 运行

 */
@Target(ElementType.METHOD) // 表示这个@RequestMapping注解,只能打在方法上
@Retention(RetentionPolicy.RUNTIME) // 表示这个注解一直到运行阶段都还存在!
public @interface RequestMapping {
    String value();
}

package com.itheima.servlet03;

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

/*
    这时用于处理一切有关用户请求的类,它不是一个Servlet!
    1. 在里面定义方法
    2. 给方法打上注解
 */
public class UserServlet {

    //以后浏览器只要输入了: localhost:82/user/reigster.do
    @RequestMapping("/user/register")
    public void register(HttpServletRequest req , HttpServletResponse resp){
        System.out.println("调用了UserServlet的register方法!~!~");
    }


    //以后浏览器只要输入了: localhost:82/user/login.do
    @RequestMapping("/user/login")
    public void login(HttpServletRequest req , HttpServletResponse resp){
        System.out.println("调用了UserServlet的login方法!~!~");
    }
}
package com.itheima.servlet03;

import com.itheima.utils.ClassScannerUtils;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;

/*
    这是唯一的一个Servlet,它能抓请求,它只抓住尾巴带 .do的请求
        1. 它的映射地址是: *.do
        2. 重写service方法,在service方法里面就要做决定,判断去执行哪个类的哪个方法!
 */
@WebServlet("*.do")
public class BaseServlet extends HttpServlet {

    //来一次请求,service方法就会被执行一次!
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        try {
            //1. 获取请求地址  localhost:82/user/register.do
            //System.out.println(req.getRequestURL());  //  http://localhost:82/user/register.do
            //System.out.println(req.getRequestURI()); //  /user/register.do
            String uri = req.getRequestURI();

            //2. 截取请求地址  /user/register
            String path = uri.substring(0 , uri.lastIndexOf('.')); //    /user/register

            //3. 扫描com.itheima.servlet03 包,得到包下所有类的字节码class
            List<Class<?>> classList = ClassScannerUtils.getClasssFromPackage("com.itheima.servlet03");

            //4. 遍历每一份字节码,并且取出这份字节码(类)的所有方法
            for (Class<?> clazz : classList) {

                Method[] methods = clazz.getMethods();

                //5. 遍历每一方法,得到方法身上的注解 @RequestMapping
                for (Method method : methods) {
                    //6. 取出注解里面的value值  /user/register
                    //6.1 得到注解对象
                    RequestMapping annotation = method.getAnnotation(RequestMapping.class);

                    //6.2 得到注解里面的值
                    if(annotation != null){
                        String value = annotation.value(); //  /user/register

                        //7. 用上面截取到的字符串地址和value值比较,如果一样,就让方法执行!
                        if(value .equals(path)){
                            //正向调用:
                            //XXX  x = new XXX();
                            //x.xxx();
                            //UserServlet us = new UserServlet();
                            //us.register(req, resp);

                            //反射调用:
                            method.invoke(clazz.newInstance(), req, resp);
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

3.3自定义SpringMVC框架终极版本

存在问题:

  1. 在总的控制器里面扫描指定包的时候,扫描的类有点多了。
  2. 等来了请求再去解析,再去反射创建对象调用 ---> 影响处理请求的速度的
  3. 扫描的包名写死了
3.1.1思路
  1. 在项目部署的时候,就马上启动扫描的工作了。

    a. 把扫描的工作提前放到servlet的init方法去做: init --- service - destroy

    b. 让这个init方法调用的时机再提前一些,提前到项目发布的时候就执行。

    c. 设置 1

  2. DispatcherServlet注册的时候,不要使用注解来注册了,而是使用xml来注册, 在xml里面注册的时候,就可以配置servlet的初始化参数,用户指定扫描具体那个包。

  3. 扫描得到包下的所有类了之后,不是每一个类我们都要查看它的所有方法有没有requestMapping这个注解

    1. 只看类上有没有一个注解@Controller , 这个注解是自定义的注解。
    2. 谁身上有这个注解,我们就解析这个类里面的所有方法。
  4. 在init方法里面完成扫描的工作之后,需要使用一个Map集合来进行映射关系,也就是完成扫描工作之后,使用一个map集合来包装 映射的地址和controller的对象。 map集合里面就包装了请求地址和调用方法的一个关系。 KEY : 请求地址 , value : 包装的javaBean。

    class MethodBean{

    ​ private Method method; //具体的方法对象

    ​ private Object obj; //调用这个方法要用的实例对象

    }

    Map<String , MethodBean> map ;

    MethodBean mb = new MethodBean(方法对象 , 类的实例对象);

    map.put("/user/register" , mb);

  5. 在请求来的时候,在service里面获取请求的地址

  6. 截获请求的地址了之后,就可以直接问map要方法来调用。

    MethodBean mb = map.get("/user/register");

    Method m = mb.getMethod();

    m.invoke(mb.getObj() , req , resp);

注册: 
    1. Servlet注册时候要用xml 注册, 不要使用注解测试
    2. 需要让这个Servlet初始化的时机更提前一些,所以需要写上 <load-on-startup>1</load-on-startup>
    3. 需要在注册的时候,提供一个初始化参数,这个初始化参数就是用来设置扫描哪个包!
        <init-param>
            <param-name>packageName</param-name>
            <param-value>com.itheima.servlet</param-value>
        </init-param>
    4. 也只抓尾巴带 .do的请求

init : 
	1. 扫描包,找类,找方法,把注解的内容给分解出来。	
	2. 就得到了什么样的地址会执行什么类的什么方法,可以使用一个JavaBean和Map集合来封装他们
        class MethodBean{
            private Method m;  //这个方法
            private Object o;  // 调用这个方法要用的对象
        }
	
		map.put(映射地址 ,MethodBean对象 );

service :
	1. 截取地址,  /user/register
	2. 拿着这个地址,去问map要东西:  
3.1.2代码实现

打在类上

package com.itheima.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/*
    1. 这是专门打在类身上的注解,做进一步的筛选|过滤工作,
        避免我们扫描太多类。
    2. 这个注解仅仅是一个标记而已,并不需要写任何内容,所以这是一个空注解
    3. 这个注解是打在类身上,所以要打上元注解 @Target(ElementType.TYPE)
    4. 也要保留到运行阶段: @Retention(RetentionPolicy.RUNTIME)
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {
}

打在方法上

package com.itheima.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/*
    这个注解打在方法身上,用于表示什么样的请求地址,能够执行这个方法。
    @RequestMapping("/user/register")
    public void register(){

    }

    步骤:
        1. 定义注解,里面给出一个value属性,用于设置映射地址
        2. 这个注解只能打在方法上,所以要表示出来它的位置
        3. 这个注解要保留到运行阶段还存在,因为当程序运行的时候,我们需要去解析类的
            字节码,取到方法身上的注解,所以这个注解不能在前面的阶段就丢失掉了
                源码 --- 编译 --- 运行

 */
@Target(ElementType.METHOD) // 表示这个@RequestMapping注解,只能打在方法上
@Retention(RetentionPolicy.RUNTIME) // 表示这个注解一直到运行阶段都还存在!
public @interface RequestMapping {
    String value();
}
package com.itheima.controller;

import com.itheima.annotation.Controller;
import com.itheima.annotation.RequestMapping;

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

/*
    这是用来处理一切与用户有关请求的类
    1. 类身上打注解 @Controller
    2. 方法上打注解 @RequestMapping
 */

@Controller
public class UserController {

    @RequestMapping("/user/register")
    public void register(HttpServletRequest req, HttpServletResponse resp){
        System.out.println("调用了UserController的register方法~!");
    }

    @RequestMapping("/user/login")
    public void login(HttpServletRequest req, HttpServletResponse resp){
        System.out.println("调用了UserController的login方法~!");
    }
}

主要是用来封装 被调用的方法和 调用这个方法用到的实例对象

package com.itheima.bean;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.lang.reflect.Method;

/*
    这是用来封装具体的controller对象和它的方法。
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class MethodBean {
    private Method method ; //真正要调用的方法!
    private Object object ; //调用这个方法要用到的对象!
}

package com.itheima.servlet;

import com.itheima.annotation.Controller;
import com.itheima.annotation.RequestMapping;
import com.itheima.bean.MethodBean;
import com.itheima.utils.ClassScannerUtils;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/*
    1. 这是唯一的一个Servlet,它能抓请求,抓住的是尾巴带 .do 的请求
    2. 在这个类里面重写两个方法:  init  和 service
        init : 在init方法里面做初始化工作,请求还没到来之前,我们先去扫描包,准备好调用的方法和对象
        service : 当请求来的时候,在service里面去执行方法即可
    注册:
        1. 这个servlet不能使用注解注册,必须使用xml注册
        2. 在xml里面的注册的时候,可以让servlet初始化的时机提前,提前到项目发布:
            <load-on-startup>1</load-on-startup>
        3. 在注册的时候,使用初始化参数,来配置扫描的包名,以便在init方法能读取到扫描哪个包!
        4. 映射的地址路径还是 *.do


    init:
        1. 读取注册时候指定的初始化的包名
        2. 扫描这个包下的所有类
        3. 遍历每一个类,如果哪个类身上有注解 @Controller,那么就获取它的所有方法
        4. 遍历每一个方法,获取方法身上的注解 @RequestMapping
            4.1 要考虑有的方法没有写上@RequestMapping的情况,得到的对象为空的情况
        5. 获取注解里面的value值  @RequestMapping("/user/register")  ====  /user/register
        6. 创建MethodBean,封装(方法, 对象)
        7. 在外面创建一个Map集合,KEY :  注解的映射地址, Value : methodBean对象
            map.put("/user/register" , methodBean);


    service:
        1. 获取请求地址
        2. 截取字符串  /user/register
        3. 拿着这个字符串去map集合里面找出来MethodBean对象
        4. 调用方法即可。
 */
public class DispatcherServlet  extends HttpServlet {

    Map<String , MethodBean > map = new HashMap<>();

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

        try {
            //1. 获取初始化包名
            String packageName = config.getInitParameter("packageName");

            //2. 扫描包下的所有类
            List<Class<?>> classList = ClassScannerUtils.getClasssFromPackage(packageName);

            //3. 遍历每一个类,
            for (Class<?> clazz : classList) {

                //4. 看一下这个类身上有没有注解@Controller
                if (clazz.isAnnotationPresent(Controller.class)){

                    //5. 获取类中的所有方法
                    Method[] methods = clazz.getMethods();

                    //6. 遍历每一个方法
                    for (Method method : methods) {

                        //7. 获取方法身上的注解@RequestMapping里面的value值
                        RequestMapping annotation = method.getAnnotation(RequestMapping.class);

                        //8. 如果有这个注解,就取它的value值
                        if(annotation != null){

                            String mapping = annotation.value(); // /user/register

                            //封装MethodBean
                            MethodBean methodBean = new MethodBean(method ,clazz.newInstance() );

                            map.put(mapping , methodBean);
                        }

                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        try {
            //1. 获取请求地址:
            String uri = req.getRequestURI(); //   /user/register.do
            String path = uri.substring(0 , uri.lastIndexOf('.')); // /user/register

            //2. 拿着路径去map集合里面找对象执行
            MethodBean methodBean = map.get(path);

            //3. 要考虑没有找到可以执行方法的情况
            if(methodBean != null){
                Method method = methodBean.getMethod();
                Object object = methodBean.getObject();

                //调用方法
                method.invoke(object , req , resp);
            }else{
                System.out.println("没有找到可执行的方法:" + path);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

<?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/javaee"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	version="2.5">
	
	<!--注册DispatcherServlet-->
	<servlet>
		<servlet-name>dispatcher</servlet-name>
		<servlet-class>com.itheima.servlet.DispatcherServlet</servlet-class>

		<!--告诉DispatcherServlet要扫描的包是哪个-->
		<init-param>
			<param-name>packageName</param-name>
			<param-value>com.itheima.controller</param-value>
		</init-param>


		<!--让DispatcherServlet在项目发布的时候,就进行初始化,执行init方法-->
		<load-on-startup>1</load-on-startup>
	</servlet>
	
	<servlet-mapping>
		<servlet-name>dispatcher</servlet-name>
		<url-pattern>*.do</url-pattern>
	</servlet-mapping>

</web-app>

4.小结

  1. 我们自定义MVC框架的目的: 锻炼大家的能力. 但是我们后面的大项目, 工作里面的开发是使用别人写好的框架 SpringMVC

标签:SVN,01,面面,方法,37,注解,import,servlet,javax
来源: https://www.cnblogs.com/ofanimon/p/16182660.html