Spring简单剖析

24 篇文章 0 订阅
16 篇文章 0 订阅

1、Spring概述

Spring是什么

Spring是一个 开源的 轻量级的 Java SE或Java EE 开发应用框架,其目的是简化企业级应用程序的开发。

Spring框架提供日志记录性能统计安全控制异常处理等面向切面的功能,还提供数据库事务管理:本身有一套简单的JDBC访问实现,同时还提供与第三方数据访问框架的集成,还提供一套web层的框架SpringMVC。

所以Spring框架是超级粘合平台,不仅自身提供基础功能,还提供与其他技术或其他框架整合的能力,使得Java SE和Java EE开发都很简单。

牢记三个特点:

1、基于AOP的日志记录、性能统计、安全控制等功能

2、数据层,提供JDBC访问和集成第三方ORM框架的能力

3、Web层,提供Spring MVC框架和集成第三方Web框架的能力

Spring能做什么

Spring框架能带给我们最多的就是简化开发

  • 简化依赖关系。Spring会根据配置文件来创建和组装对象间依赖关系,只需要修改配置文件即可。
  • 低耦合地实现日志记录、性能统计、安全控制。Spring面向切面编程能够帮助开发者实现低耦合的日志记录、性能统计、安全控制。
  • 管理数据库事务。Spring能够帮助开发者提供基础的数据库事务管理。
  • 与第三方数据访问层框架无缝集成。能够与Hibernate、JPA等第三方框架无缝集成,同时自己也提供一套JDBC访问模板。
  • 与第三方Web框架无缝集成。能够与Struts、JSF等第三方框架无缝集成,同时自己也提供一套Spring MVC框架,方便web层搭建。
  • 方便整合技术。能够与Java Mail、缓存等技术无缝整合。

Spring的相关概念

  • 应用程序(Application):即完成所需功能或需求的成品,如购物网站、OA系统、xx小程序。
  • 框架(Framework):是能够完成一定功能的半成品,如我们可以在框架的基础上进一步开发购物网站,但是框架本身不一定就是最终成品。大多数时候是框架实现一部分功能,自己本身再实现一部分功能,最终形成应用程序。但是框架大多数时候会规定或者约定了开发应用程序的整体架构,提供了基础功能,简化开发人员的后续开发,使得开发人员更加专注于业务逻辑。
  • 非侵入式设计:无需继承框架提供的类,这样的设计就可以看作是非侵入式设计,如果继承了这些框架类,就是侵入式设计。非侵入式设计假如在后续想要更换框架则之前写的代码可以复用;侵入式设计则无法复用。
  • POJO:即Plain Old Java Objects,简单的Java对象,它可以包含业务逻辑或持久化逻辑,但不担当任何特殊角色且不继承或不实现任何其它Java框架的类或接口。
  • 容器:日常生活中来理解容器就是盛放东西的器具,从程序设计角度看就是装对象的对象,因为存在放入、拿出的“行为”,所以容器还要管理对象的生命周期。
  • 控制反转:即Inversion of Control,缩写为IoC,它还有一个别名叫依赖注入(Dependency Injection),就是由容器控制程序之间的关系,而非传统开发中由程序代码控制。
  • Bean:一般指容器管理的对象,在Spring中指IoC容器管理的对象。

Spring的优点

  • 非常轻量级的容器。以集中的、自动化的方式进行应用程序对象创建和装配,负责对象的创建和装配,管理对象的生命周期,能够组合成复杂的应用程序。IoC容器是非侵入式的,不需要依赖任何特定类。且核心JAR包非常小,Spring3.0.5不到1M,而且不需要依赖任何应用服务器,可以部署在任何环境(Java SE或Java EE)。
  • 面向切面编程(AOP)。AOP是Aspect Oriented Programming的缩写。提供从另一个角度来考虑程序结构以完善面向对象编程,即可以通过在编译期间、装载期间或运行期间在不修改源代码的情况下为程序动态添加功能的一种技术。
  • 简单的数据库事务管理。Spring支持简单的可插入的事务管理支持。
  • JDBC抽象及ORM框架支持。Spring本身提供JDBC访问支持,并且还非常方便集成ORM框架,如Hibernate等,还支持使用Spring事务和Spring提供的异常。
  • 灵活的Web层支持。Spring本身提供一套强大的MVC框架,Spring MVC,同时还方便与其他Web层框架集成,比如Struts等。
  • 简化各种技术并且简化集成。简化并提供对Java Mail、任务调度、JMX、JNDI、EJB、Web Service等的集成。

Spring框架为开发者简化应用程序的开发,并且还对JDBC、ORM、Web提供良好的支持,并且还对其他很多技术提供良好的整合,使系统结构更优良,性能更好。

如何学好Spring

Spring的核心是IoC容器,他的所有技术基本都是基于容器来实现的。所以先要透彻理解IoC容器,如何配置和使用他。

理解完IoC容器后再理解AOP,明确概念->基本配置->如何使用->实现原理。接下来就是简单的Spring事务管理了,其实Spring事务管理是通过AOP实现的,所以需要先理解AOP。

2、Spring IoC核心流程简述

初始化

初始化的过程主要是读取XML资源,然后解析,最终注册到Bean Factory中:

在这里插入图片描述

完成初始化后,Bean对象就在BeanFactory中等待调用了。

注入依赖

当初始化完IoC容器后,如果Bean没有设置lazy-init(懒加载)属性的话,那么bean实例的初始化就会在IoC初始化完成后:

在这里插入图片描述

3、Java代理模式的实现

在聊代理之前需要搞懂为什么需要代理这个东西,它的目的是什么。代理模式是为了在不修改已有代码的前提下,对原来的功能进行增强(做更多处理),比如打印日志,安全控制等。其实不使用代理模式的话也可以实现,但是对代码的入侵性极高,所以使用代理模式几乎可以做到对原有代码没有入侵,并且能够实现想要的功能。

Java代理模式主要有三种:JDK静态代理、JDK动态代理和CGLIB动态代理。其中Spring的AOP的实现中就是利用了JDK动态代理和CGLIB动态代理。

静态代理

Java静态代理是代理类和被代理类共同实现同一接口,代理类对被代理类的操作进行增强。

比如:我创建一个学生类,有一个跑方法,我需要代理类对跑进行增强:跑之前操作,跑之后操作。

共同接口StudentInterface.java:

public interface StudentInterface {
    void run();
}

普通学生类(被代理类)Student.java:(有name属性)

public class Student implements StudentInterface{

    private String name;

    public Student(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        System.out.println(name+" is running ");
    }
}

增强学生类(代理类)StudentProxy.java:

public class StudentProxy implements StudentInterface {

    private StudentInterface target;

    public StudentProxy(StudentInterface target){
        // 在代理类初始化时传入目标类
        this.target = target;
    }

    @Override
    public void run() {
        System.out.println(" before running ");//增强
        this.target.run();//目标类本身操作
        System.out.println(" after running ");//增强
    }
}

测试执行Main.java:

public class Main {
    public static void main(String[] args) {
        //被代理类对象
        Student student = new Student();
        //代理类对象
        StudentProxy proxy = new StudentProxy(student);
        //代理类执行方法
        proxy.run();
    }
}

可以看到,普通学生类(被代理类)和增强学生类(代理类)都实现了同一接口,并且都实现了run方法,普通类就是很普通的执行了,但是代理类里不仅执行了run方法,还在run方法前后进行了更多的处理(也就是增强)。所以这就是静态代理,在不改变原代码的前提下,使用另一个类对原类进行进一步的增强处理(比如打印日志,安全控制等)。在普通学生类Student的代码中是完全感知不到的,对原代码没有入侵。

  1. 统一接口
  2. 普通类(被代理类)
  3. 增强类(代理类)以参数的形式获得目标类,执行普通类中方法再进行增强。

动态代理

Java动态代理仍然需要指定一个接口,但是代理类并不需要实现它。主要是利用Java反射机制来获取到被代理类执行的方法。

接口StudentInterface.java:

public interface StudentInterface {
    void run();
}

被代理类Student.java:

public class Student implements StudentInterface {

    private String name;

    public Student(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        System.out.println(name+" is running");
    }
}

代理类StudentProxyHandler.java:

和静态代理的类似,都需要获得被代理类对象。不过动态代理需要实现InvocationHandler接口,然后重新invoke()方法。

public class StudentProxyHandler implements InvocationHandler {

    private Object subject;

    public StudentProxyHandler(Object subject) {
        this.subject = subject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        /*
          proxy    代理类对象
          method   被代理类执行的方法
          args     被代理类执行的参数
         */
        System.out.println("before invoke "+method.getName());
        method.invoke(subject,args);//执行被代理类的方法
        System.out.println("after invoke "+method.getName());
        return null;
    }
}

测试执行Main.java:

public class Main {
    public static void main(String[] args) {
        // 被代理类
        Student student = new Student("Jack");
        // 代理类
        InvocationHandler handler = new StudentProxyHandler(student);

        StudentInterface studentInterface = (StudentInterface) Proxy.newProxyInstance(
                student.getClass().getClassLoader(),
                student.getClass().getInterfaces(),
                handler
        );
        studentInterface.run();
        /*
        print:
            before invoke run
            Jack is running
            after invoke run
         */
    }
}

可以看到StudentProxyHandler是实现了InvocationHandler,重写了invoke()方法,invoke方法中的第一个参数proxy是代理类,第二个参数method是被代理类执行的方法,第三个参数args是被代理类执行的参数。动态代理原理就是利用Java反射机制来获得被代理类执行的方法,执行的参数,执行的方法的返回值等。

  1. 接口
  2. 被代理类实现接口
  3. 代理类实现InvocationHandler接口,重写invoke方法,并利用好proxy、method、args参数。

区别

动态代理相较于静态代理的区别在于动态代理能够利用反射获取到执行方法各个属性,比如方法名、方法参数、方法返回值等。

CGLIB动态代理

Cglib是一个强大的、高性能的代码生成包,它广泛被许多AOP框架使用,为他们提供方法的拦截。下,下图是我网上找到的一张Cglib与一些框架和语言的关系:

在这里插入图片描述

  • 最底层的是字节码Bytecode,字节码是Java为了保证“一次编译、到处运行”而产生的一种虚拟指令格式,例如iload_0、iconst_1、if_icmpne、dup等
  • 位于字节码之上的是ASM,这是一种直接操作字节码的框架,应用ASM需要对Java字节码、Class结构比较熟悉
  • 位于ASM之上的是CGLIB、Groovy、BeanShell,后两种并不是Java体系中的内容而是脚本语言,它们通过ASM框架生成字节码变相执行Java代码,这说明在JVM中执行程序并不一定非要写Java代码----只要你能生成Java字节码,JVM并不关心字节码的来源,当然通过Java代码生成的JVM字节码是通过编译器直接生成的,算是最“正统”的JVM字节码
  • 位于CGLIB、Groovy、BeanShell之上的就是Hibernate、Spring AOP这些框架了,这一层大家都比较熟悉
  • 最上层的是Applications,即具体应用,一般都是一个Web项目或者本地跑一个程序

三种代理方式的区别

代理方式实现优点缺点特点
JDK静态代理代理类与委托类实现同一接口,并且在代理类中需要硬编码接口实现简单,容易理解代理类需要硬编码接口,在实际应用中可能会导致重复编码,浪费存储空间并且效率很低好像没啥特点
JDK动态代理代理类与委托类实现同一接口,主要是通过代理类实现InvocationHandler并重写invoke方法来进行动态代理的,在invoke方法中将对方法进行增强处理不需要硬编码接口,代码复用率高只能够代理实现了接口的委托类底层使用反射机制进行方法的调用
CGLIB动态代理代理类将委托类作为自己的父类并为其中的非final委托方法创建两个方法,一个是与委托方法签名相同的方法,它在方法中会通过super调用委托方法;另一个是代理类独有的方法。在代理方法中,它会判断是否存在实现了MethodInterceptor接口的对象,若存在则将调用intercept方法对委托方法进行代理可以在运行时对类或者是接口进行增强操作,且委托类无需实现接口不能对final类以及final方法进行代理底层将方法全部存入一个数组中,通过数组索引直接进行方法调用

4、Spring事务概述

数据库事务概述

事务:是一系列操作组成的工作单元。该工作单元内的操作是不可分割的,即要么所有的操作都做,要么所有的操作都不做

事务必须满足ACID(原子性、一致性、隔离性、持久性)原则,缺一不可:

  • 原子性(Atomicity):即事务是不可分割的最小单元,事务内的操作要么全做,要么全不做。
  • 一致性(Consistency):即事务完成前后数据库状态是一致的。
  • 隔离性(Isolation):并发执行的事务之间是不会相互影响的,一个事务执行的内部不会对其他事务产生影响。(无法避免,所以才会产生事务隔离级别)
  • 持久性(Durability):事务一旦执行成功,它对数据库的改变是永久的,不会因为断电或者系统故障导致数据不一致或者丢失。

实际并发操作可能会出现以下问题:

  • 脏读:(A事务读取了B事务还未确定提交的数据,但是B事务回滚了)。A事务读取到了B事务还没提交的数据,最终B事务回滚了,那么A事务就读到了不应该读到的数据。
  • 不可重复读:(读取到了提交的新事务,指更新操作)A事务多次读取数据时不一致,原因是B事务在此期间修改了数据。
  • 幻读:(读取到了提交的新事务,指增删操作)A事务多次读取数据时不一致,原因是B事务在此期间增删了记录。
  • 第一类事务丢失:(回滚丢失)指A事务和B事务同时操作一条数据(A事务比B事务先或同时开始),B事务提交了修改,但是A事务最后回滚了,那么数据最后就会回滚到A事务前,则B事务提交的修改就会丢失。(事务的特性:执行事务前会将数据当前状态“快照”,以便回滚)。
  • 第二类事务丢失:(提交覆盖丢失)指A事务和B事务同时操作一条数据,B事务中途提交了修改,最后提交事务,可是在B事务完成的过程中A事务也进行了修改,最后的结果确实以B事务提交的结果为准,所以A事务的修改就被覆盖了。

为了解决以上问题,事务隔离级别由低到高分为Read Uncommited、Read commited、Repeatable read、Serializable。分别是读未提交、读已提交、可重复读、串行化

  • Read Uncommited(读未提交):A事务能够读到B事务还未提交的数据,可能会出现脏读
  • Read Commited(读已提交):A事务只能在B事务提交后才能读,可能会出现不可重复读,但是解决了脏读。原因是A事务只能在B事务完成前后才能读,如果B事务发生了修改,那么B事务完成前后的数据肯定不相同,所以数据才会不重复。
  • Repeatable Read(可重复读):B事务开启时,A事务不能再修改数据。可能会出现幻读,但是解决了不可重复读。原因是B事务开启后,此时A事务即使读到但是无法修改,但是A事务可以读到修改中途的数据。
  • Serializable(串行化):所有事务按顺序执行,必须等前一个事务执行完毕后再执行下一个事务,效率较低,但是可以解决以上所有问题。

大多数据库默认的隔离级别是Read Commited(读已提交),如Sql Server、Oracle。

MySQL的默认隔离级别是Repeated Read(可重复读)。

为什么会有事务隔离?

虽然依据事务的隔离性原则来说,事务与事务之间不会相互影响。可是项目实际运行过程中可能会出现很多人一起访问资源的并发情况,所以这个时候必定会出现一些事务与事务之间相互影响的情况,如:脏读、幻读等,针对这些可能出现的情况,就出现了事务的隔离级别,隔离级别越低对两个事务之间就越不干涉,这样效率最高,但是互相影响就越大。

所以需要考虑项目实际运行过程中事务与事务之间能接受的影响程度以及项目运行效率,结合两者来确定隔离级别,以达到效率最大化、事务之间影响最小化。

事务类型

Spring支持编程式事务管理和声明式事务管理两种。其中比较推荐使用声明式事务管理的方式。

Spring编程式事务(了解)

编程式事务管理对代码有侵入性(所以不推荐),使用TransactionTemplate或者使用PlatformTransactionManager,对于编程式事务管理,Spring更推荐使用TransactionTemplate。

需要导入TransactionTemplate然后调用excute()方法,里面包含的就是一整条事务了。

@SpringBootTest
@RunWith(SpringRunner.class)
class UserServiceTest {

    @Autowired
    private TransactionTemplate transactionTemplate;

    @Autowired
    private UserService userService;

    @Test
    void transactionOne(){
        transactionTemplate.execute(transactionStatus -> {
            userService.selectAllUser();
            userService.deleteOneUser(1);
            return null;
        });
    }
    
}

transactionTemplate.execute()的参数是一个TransactionCallback接口,这个接口只有一个抽象方法doInTransaction()。所以可以使用lambda表达式(当参数是一个接口,并且接口中只有一个抽象方法时,可以使用匿名内部类+lambda表达式)。

其中transactionOne()就是一个事务,transactionTemplate.execute()就是开启事务,并且事务里包含两条操作。(其中一个操作出错了整条事务就会回滚)。

Spring声明式事务(熟悉)

声明式事务管理器是基于AOP的,所以对代码的入侵性较小,这也是Spring所倡导的非侵入式编程方式。

声明式事务管理器有两种开启方式:

  1. 在XML文件里配置
  2. 以注解的方式配置(@Transactional

这里重点讲解以注解的方式,@Transactional注解可以加在类或者方法上,如果加在类上表示该类下的所有方法都交给事务管理器管理,加在方法上则表示该类交给事务管理器管理。

加在类上:

@Transactional
class UserServiceTest {

    @Test
    void transactionOne(){

    }

    @Test
    void transactionTwo(){

    }

}

加在方法上:

class UserServiceTest {

    @Test
    void transactionOne(){

    }

    @Test
    @Transactional
    void transactionTwo(){

    }

}

同时该注解也可以为事务配置隔离级别、事务传播特性、回滚规则、是否只读、事务超时时间等:

public @interface Transactional {
    @AliasFor("transactionManager")
    String value() default "";

    @AliasFor("value")
    String transactionManager() default "";

    String[] label() default {};

    Propagation propagation() default Propagation.REQUIRED;

    Isolation isolation() default Isolation.DEFAULT;

    int timeout() default -1;

    String timeoutString() default "";

    boolean readOnly() default false;

    Class<? extends Throwable>[] rollbackFor() default {};

    String[] rollbackForClassName() default {};

    Class<? extends Throwable>[] noRollbackFor() default {};

    String[] noRollbackForClassName() default {};
}

可以看到Spring的默认隔离级别是Isolation.DEFAULT,这个值为-1,代表的是数据库的默认隔离级别,也就是说Spring的默认隔离级别是数据库的默认隔离级别。同样的,Spring有以下隔离级别:

public enum Isolation {
    DEFAULT(-1),
    READ_UNCOMMITTED(1), //读未提交
    READ_COMMITTED(2),   //读已提交
    REPEATABLE_READ(4),	 //可重复读
    SERIALIZABLE(8);     //串行化
    
    ......
}

编程式事务和声明式事务的区别

编程式事务每次都需要单独实现,如果遇到业务量大且复杂的时候,使用编程式事务则会是一场灾难。而声明式事务属于非侵入式,不会影响业务的逻辑实现,只需要在需要的方法或者类上加上注解,或者使用XML的方式配置。

唯一不足的地方就是声明式事务管理的粒度是方法级别,而编程式事务管理是可以到代码块的,但是可以通过提取方法的方式完成声明式事务管理的配置。

Spring事务的传播行为

外围方法和内部方法

Spring讲事务传播行为前需要了解两个概念,外围方法和内部方法。如以下代码:

public class Service{
    
    Boolean insertOne(){
        ...
    }
    
}
public class Transaction{
    
    public void transactionOne(){
        Service.insertOne();
    }
    
}

在Transaction类中,transactionOne()对于Service.insertOne()来说,就是外围方法,而Service.insertOne()里面就是内部方法。内部开启事务管理则是在insertOne()加注解,外围开启事务则是在transactionOne()加注解。

Spring中七种事务传播行为

参考Spring事务传播行为详解@JerryTse

事务传播行为类型说明
PROPAGATION_REQUIRED如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。
PROPAGATION_SUPPORTS支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY使用当前的事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。
PROPAGATION_REQUIRED(Spring默认事务传播行为)

外围方法未开启事务的情况下PROPAGATION_REQUIRED修饰的内部方法会新开启自己的事务,且开启的事务相互独立,互不干扰。外围方法没有开启事务,则内部方法分别开启自己的事务,相互独立互不干扰。

外围方法开启事务的情况下PROPAGATION_REQUIRED修饰的内部方法会加入到外围方法的事务中,所有PROPAGATION_REQUIRED修饰的内部方法和外围方法均属于同一事务,只要一个方法回滚,整个事务均回滚。如果外围方法开启事务,则内部方法也加入进该事务,统一为同一事务。

PROPAGATION_REQUIRES_NEW

外围方法未开启事务的情况下Propagation.REQUIRES_NEW修饰的内部方法会新开启自己的事务,且开启的事务相互独立,互不干扰。外围方法没有开启事务,则内部方法分别开启自己的事务,相互独立互不干扰。

外围方法开启事务的情况下Propagation.REQUIRES_NEW修饰的内部方法依然会单独开启独立事务,且与外部方法事务也独立,内部方法之间、内部方法和外部方法事务均相互独立,互不干扰。外围方法开启事务时,内部方法仍旧会开启独立的事务,与外部事务相互独立,互不干扰。

PROPAGATION_NESTED

外围方法未开启事务的情况下Propagation.NESTEDPropagation.REQUIRED作用相同,修饰的内部方法都会新开启自己的事务,且开启的事务相互独立,互不干扰。外围方法没有开启事务,内部方法分别开启自己的事务,相互独立互不干扰。

外围方法开启事务的情况下Propagation.NESTED修饰的内部方法属于外部事务的子事务,外围主事务回滚,子事务一定回滚,而内部子事务可以单独回滚而不影响外围主事务和其他子事务。外围方法开启事务时,内部方法的事务为外围方法的子事务,外围事务回滚内部事务同时回滚,但是内部事务可以单独捕获,并且外围事务不回滚。

5、SpringMVC概述

SpringMVC是什么

Spring Web MVC是一种基于Java的,实现了Web MVC设计模式的请求驱动类型的轻量级Web框架。即,使用了MVC架构的模式的思想,简化开发Web开发。

前端控制器是DispatcherServlet

应用控制器拆分为处理器映射器HandlerMapping进行处理器管理和视图解析器View Resolver进行视图管理。

页面控制器是Controller接口(仅包含ModelAndView handleRequest(request, response)方法)的实现。

提供了非常灵活的数据验证、格式化和数据绑定机制;提供了强大的配置(约定大于配置)编程支持。

Spring MVC架构

Spring Web MVC框架也是一个基于请求驱动的Web框架,并且也是用了前端控制器模式来进行设计,再根据请求映射规则分发给响应的页面控制器进行处理。

Spring MVC 处理请求的流程

在这里插入图片描述

图片来自:https://github.com/h2pl/Java-Tutoria

具体执行流程如下:

  1. 用户发起请求——>前端控制器(DispatcherServlet)接收,并根据请求的信息(如URL)来决定哪个页面控制器进行处理然后把该请求委托给它。
  2. 页面控制器(Controller)收到请求后对请求进行具体的功能处理(调用业务层Service对象);处理完毕后返回一个ModelAndView对象给前端控制器。
  3. 前端控制器根据返回的视图名View选择对应的页面进行渲染,并把模型数据一并传入视图供视图进行数据渲染。
  4. 最后前端控制器将响应返回给用户。

Spring MVC 架构图

在这里插入图片描述

图片来自:https://github.com/h2pl/Java-Tutoria

Spring MVC 优势

  1. 清晰的角色划分:前端控制器(DispatcherServlet)、处理器映射器(HandlerMapping)、处理器适配器(HandlerAdapter)、视图解析器(ViewResolver)、页面控制器(Controller)、验证器(Validator)等。
  2. 分工明确,扩展灵活,很容易扩展。
  3. 和其他Spring框架无缝集成,是其他Web框架所不具备的。
  4. 可定制性,HandlerMapping、ViewResolver能够很容易定制。
  5. 支持RESTful风格。
  6. 约定大于配置的契约式编程。
  7. 基于注解的零配置支持。

DispatcherServlet作用

DispatcherServlet是前端控制器设计模式的实现,提供Spring Web MVC的集中访问点,负责职责的分派,与Spring IoC容器无缝集成。主要职责如下:

  1. 文件上传解析,如果请求类型是multipart将通过MultipartResolver进行文件上传解析。
  2. 通过HandlerMapping,将请求映射到处理器。
  3. 通过ViewResolver解析逻辑视图名到具体视图实现。

DispatcherServlet主要负责流程控制。

本文部分内容参考自:https://github.com/h2pl/Java-Tutorial/

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值