您当前的位置: 首页 >  spring

陈橙橙丶

暂无认证

  • 3浏览

    0关注

    107博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

深入剖析Spring(三):MVC核心思想

陈橙橙丶 发布时间:2020-03-22 20:33:58 ,浏览量:3

Spring框架对于java开发者来说是不可缺少的,如今我们接触到的项目几乎都不开spring,我们整天都在使用spring,例如@Controller、@Autowired、@Autowired、@RequestMapping,我相信大部分都能知道它的作用,同时我也希望能本文能帮助到你更深的了解他们。

前言

记录文章目的及意义在于为了自己知识的巩固,当然也十分希望在此能够得到业界前辈们的指导。 若有不正确的地方,希望能够及时批评指正。首先在此感谢

本篇内容

本篇文章的内容,主要对于springMVC的配置阶段、初始化阶段、运行阶段做些粗浅的学习记录。围绕着@Controller、@Autowired、@Autowired、@RequestMapping来实现功能。 方式:手写一个简单的mvc。

一、准备

介绍一下mvc的基本实现思路,先介绍然后在编码。

1.1、配置阶段

我们都知道SpringMVC的本质就是从Servlet演变而来,它继承自HttpServlet,通常在使用Servlet的时候,我们都会去配置Web.xml文件,其实所有依赖于web容器的项目,都是从读取web.xml文件开始的。 web.xml:



    MVC Web Application

    
        CcMvc
        
        com.ccc.mvcframework.v1.setvlet.CccDispatcherServletTest
        
        
            contextConfigLocation
            classpath:application.properties
        
        
        
            configFile
            application.properties
        
        
        1
    
    
    	
        CcMvc
        /*
    

以上作用: 1.指定DispathcerServlet 2.配置ServletMapping(url路径) 3.如果是与spring一块使用我们还会指定spring.xml配置文件. 当然web.xml的功能远远不止于此,在这里咱就介绍几个关键的。

1.2初始化阶段

此阶段主要分为六个步骤: 1.加载配置文件(web.xml中的param-value). 2.通过配置文件扫描相关的类(.class结尾的文件)。 3.筛选出带有注解的类并放入到IOC容器中.(控制反转,注解的类:@controller@Service等) 4.扫描IOC容器中的实例,给没有复制的属性自动复制(DI依赖注入) 5.初始化HandlerMapping,将url和方法对应起来。 到此初始化过程大致完成。

1.3运行阶段

1.doPost()/doGet():获取到request/response对象。 2.从HandlerMapping匹配到request中的url,找到对应的方法。 3.反射调用该方法。 4.返回数据。 贴一张配置文件的内容:application.properties,只有一行

scanPackage=com.ccc.mvcframework.v1.demo

二、编码

提醒一下,记得web.xml文件哦,由于前文介绍到了这里就不在介绍了。

2.1定义注解

2.1.1 Controller

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

2.1.2 Service

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

2.1.3 Autowired

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

2.1.4 RequestMapping

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

2.1.5 RequestParam

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

解释一下定义注解: 定义一个最基本的注解包含了两个步骤:1、注解的名字;2、注解包含的类型。 如: 1.@Target :限定某个自定义注解能够被应用在哪些Java元素上面的.是一个数组类型。它使用的是一个枚举(ElementType)类型定义. ElementType:

public enum ElementType {
    /** Class, interface (including annotation type), or enum declaration */
    /** 类,接口(包括注解类型)或枚举的声明*/
    TYPE,

    /** Field declaration (includes enum constants) */
    /** 属性的声明 */
    FIELD,

    /** Method declaration */
    /** 方法的声明 */
    METHOD,

    /** Formal parameter declaration */
    /** 方法形式参数声明 */
    PARAMETER,

    /** Constructor declaration */
    /** 构造方法的声明 */
    CONSTRUCTOR,

    /** Local variable declaration */
    /** 局部变量声明 */
    LOCAL_VARIABLE,

    /** Annotation type declaration */
     /** 注解类型声明 */
    ANNOTATION_TYPE,

    /** Package declaration */
    /** 包的声明 */
    PACKAGE,

    /**
     * Type parameter declaration
     * 类型参数声明
     * @since 1.8
     */
    TYPE_PARAMETER,

    /**
     * Use of a type
     *类型的使用
     * @since 1.8
     */
    TYPE_USE
}

2.@Retention声明注解在什么时候生效.同样是以枚举类型来声明。这里就不展示源码了为了节省位置。 主要有3个: 1.SOURCE:源文件阶段(声明为此阶段生效也就是只在源文件中生效,不参与编译与运行类似于注释效果) 2.CLASS:编译到class文件阶段(在编译阶段生效,可以用来处理一些逻辑。不会在运行期生效) 3.RUNTIME:运行期阶段(emmm不用解释了吧,我们用的几乎都是这个。)

2.2定义DispatcherServlet

此处就是mvc的核心步骤了。我们将按照前文中介绍的步骤依次来完成。 1.定义一个DispatcherServlet继承自HttpServlet并重写父类的doGet()、doPost()、init()三个方法。 init()方法相信大家都知道此方法会在加载的时候就调用。所以我们初始化的入口就在此。 请看代码: 2.声明一个Properties变量存储配置文件的内容。 3.在init方法中我们按照前文步骤依次声明方法。

public class CccDispatcherServletTest extends HttpServlet {

    //存储aplication.properties的配置内容
    private Properties contextConfig = new Properties();

    @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);
    }

    @Override
    public void init(ServletConfig config) throws ServletException {
        //ServletConfig 可以获取web.xml文件的属性
        //使用模板模式
        //1、加载配置文件
        doLoadConfig(config.getInitParameter("configFile"));
        //2、扫描相关的类
        doScanner(contextConfig.getProperty("scanPackage"));
        //3、初始化所有相关的类的实例,并且放入到IOC容器之中
        doInstance();
        //4、完成依赖注入
        doAutowired();
        //5、初始化HandlerMapping
        initHandlerMapping();
        System.out.println("XH Spring framework is init end.");
    }
    //5、初始化HandlerMapping
    private void initHandlerMapping() {
    }
    //4、完成依赖注入
    private void doAutowired() {
    }
    //2、扫描相关的类
    private void doScanner(String scanPackage) {
    }
    //3、初始化所有相关的类的实例,并且放入到IOC容器之中
    private void doInstance() {
    }
    //1、加载配置文件
    private void doLoadConfig(String configFile) {
    }
}
2.2.1加载配置文件doLoadConfig()

这里比较简单

private void doLoadConfig(String contextConfigLocation) {
        try(InputStream fis = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation)) {
            //加载配置文件,此时配置文件已存于内存中了
            contextConfig.load(fis);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
2.2.2 扫描相关的类doScanner()

首先声明一个一个集合存储扫描到的类

private List classNames = new ArrayList();

private void doScanner(String scanPackage) {
		//scanPackage=com.ccc.mvcframework.v1.demo
        //包传过来包下面的所有的类全部扫描进来的,并替换成/路径
        URL url = this.getClass().getClassLoader()
                .getResource("/" + scanPackage.replaceAll("\\.", "/"));
        //获取这个文件
        File classPath = new File(url.getFile());
        //遍历文件下的数据
        for (File file : classPath.listFiles()) {
            //如果是一个目录,则递归查找
            if (file.isDirectory()) {
                doScanner(scanPackage + "." + file.getName());
            } else {
                //如果是一个.class文件去掉.class后缀存放到集合中,如果不是则跳过此次循环
                if (!file.getName().endsWith(".class")) {
                    continue;
                }
                String className = (scanPackage + "." + file.getName()).replace(".class", "");
                classNames.add(className);
            }
        }

    }

此方法的作用就是找到路径下所有的类文件并存储起来。 解释一下: 1.为什么要用/来替换.呢? 因为我们scanPackage的值是以命名包名的形式来写的,而使用File的话就得换成/的目录形式了。 2.为什么要去掉.class后缀在保存呢? 这里举个例子吧.假设类名className=Test,如果我们通过名字得到这个类对象怎么获取呢?当然是使用反射Class.fromName(className).结果是不是很明显了,我们可以通过Test来得到这个对象,但是不能通过Test.class来得到这个对象。

2.2.3 控制反转doInstance()

1.首先声明一个IOC容器用来保存所有实例化对象

private Map ioc = new HashMap();

2.声明一个方法用来进行类名首字母小写.

private String toLowerFirstCase(String simpleName) {
		//该方法将字符串转为一个char数组。
        char[] chars = simpleName.toCharArray();
        /**
		* 将首字母往左移32位,1            
关注
打赏
1648473527
查看更多评论
0.0478s