Spring

发布于:2024-08-08 ⋅ 阅读:(103) ⋅ 点赞:(0)

        两大核心 IOC  AOP 

一、在maven项目中 搭建Springboot

1、创建maven项目

                 在setting中配置maven

2、 搭建Springboot

        在pom.xml文件中添加Springboot依赖并加载   

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-parent</artifactId>
        <version>2.6.6</version>
    </parent>

    <groupId>org.example</groupId><!--组织名(公司域名)-->
    <artifactId>EasySpringA</artifactId><!--项目名-->
    <version>1.0-SNAPSHOT</version><!--项目版本-->

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
    </dependencies>

</project>

 约定大于配置  

        内嵌了Tomcat服务器,只需要在Maven配置文件(Pom.xml)里面导入SpringMVC所需要的依赖就可以了。
  这就是SpringBoot的优势,在传统所需要配置的地方,SpringBoot都进行了约定(配置好了),开发人员能配置得更少,更直接地开发项目,写业务逻辑代码。

        加载成功后 就可以在External Libraries中看到一系列maven包

如此就可以开始写springboot项目了 

3、项目启动类

                运行起该类就运行了整个项目

@SpringBootApplication
public class EasySpringAApplication {
    //项目启动类  运行起该类就运行了整个项目
    public static void main(String[] args) {
        SpringApplication.run(EasySpringAApplication.class,args);
    }
}

        其中SpringApplication.run函数的两个参数,第一个是项目启动类的类对象,第二个是main函数的形参args

二、Spring bean

1、bean 

        在 Spring 中,构成应用程序主干并由Spring IoC容器管理的对象称为bean。bean是一个由Spring IoC容器实例化、组装和管理的对象。

        即bean是对象,一个或者多个不限定
            bean由Spring中一个叫IoC的东西管理
            应用程序由一个个bean构成

2、Spring Bean的作用域   5种

singleton     单例模式         只有一个对象         默认

prototype    原型模式         每次需要都来一个新的对象

request       web项目中请求作用域         每次请求都会创建一个新的对象

session       会话        每次会话都会创建一个新的对象

GlobalSession         全局的

3、Spring bean的生命周期 

(1)实例化 Bean

        根据配置情况调用 Bean 构造方法或工厂方法实例化 Bean。

(2)Bean 属性值的配置注入

        利用依赖注入完成 Bean 中所有属性值的配置注入。

(3) 传入当前 Bean 的 id 值。

        如果 Bean 实现了 BeanNameAware 接口,则 Spring 调用 Bean 的 setBeanName() 方法传入当前 Bean 的 id 值。

(4)传入当前工厂实例

        如果 Bean 实现了 BeanFactoryAware 接口,则 Spring 调用 setBeanFactory() 方法传入当前工厂实例的引用。

(5)传入当前ApplicationContext

        当一个 Bean 实现了 ApplicationContextAware 接口并在 Spring 容器中被实例化时,Spring 容器会自动调用该 Bean 的 setApplicationContext 方法,并将应用程序上下文ApplicationContext作为参数传递进来。

(6)在 bean 实例化之后、属性设置之前的处理

        BeanPostProcessor 是 Spring 框架中的一个重要接口,它允许开发者在 Spring 容器创建和初始化 bean 的过程中,对 bean 进行自定义处理。这包括在 bean 实例化之后、属性设置之前(postProcessBeforeInitialization 方法),以及在 bean 初始化之后(postProcessAfterInitialization 方法)执行自定义逻辑

        Spring 容器在创建和初始化每一个 bean 时,都会调用 CustomBeanPostProcessor 中的这两个方法,允许你进行自定义处理

         在这一步执行的是postProcessBeforeInitialization方法

(7)当 Spring 容器创建 bean 的所有属性都被设置完成后,调用afterPropertiesSet 方法

        InitializingBean 是 Spring 框架中的一个接口,它定义了一个 afterPropertiesSet 方法。当 Spring 容器创建 bean 的所有属性都被设置完成后,会调用这个方法。

(8)如果在配置文件中通过 init-method 属性指定了初始化方法,则调用该初始化方法。

        initMethod指定的方法必须是void类型,并且不接受任何参数 

(9)调用BeanPostProcessor接口的初始化方法 postProcessAfterInitialization()。

         如果 BeanPostProcessor 和 Bean 关联,则 Spring 将调用该接口的初始化方法 postProcessAfterInitialization()。此时,Bean 已经可以被应用系统使用了。

(10)指定该 Bean 的作用范围

        如果在 <bean> 中指定了该 Bean 的作用范围为 scope="singleton",则将该 Bean 放入 Spring IoC 的缓存池中,将触发 Spring 对该 Bean 的生命周期管理;如果在 <bean> 中指定了该 Bean 的作用范围为 scope="prototype",则将该 Bean 交给调用者,调用者管理该 Bean 的生命周期,Spring 不再管理该 Bean。

@Scope(scopeName =ConfigurableBeanFactory.SCOPE_PROTOTYPE )

@Scope(scopeName =ConfigurableBeanFactory.SCOPE_SINGLETON)
(11)销毁

        如果 Bean 实现了 DisposableBean 接口,则 Spring 会调用 destory() 方法将 Spring 中的 Bean 销毁;如果在配置文件中通过 destory-method 属性指定了 Bean 的销毁方法,则 Spring 将调用该方法对 Bean 进行销毁。

        在Spring项目正常结束时,自动调用销毁方法 

三、IOC  反转控制 

 1、Spring IOC

                        管理项目中java bean的生命周期

在项目运行阶段,程序中需要很多的对象来完成整体业务
让Spring管理这些类的对象的生命周期, 而Spring提供了IOC的方式方便获取该对象
容器和代码之间的控制权反转,代码中不需要明文调用方法来得到对象,
只需要声明该类需要什么类型的对象即可

 为什么叫控制反转?

        传统开发中,需要调用对象的时候,需要调用者手动来创建被调用者的实例,即对象是由调用者new出来的,但是在Spring框架中,创建对象的工作不再由调用者来完成,而是交给IOC容器来创建,再推送给调用者,整个流程完成反转,所以是控制反转

        使用

@RestController
public class EasyAController {
    @Autowired
    TestA testa;
    @RequestMapping("testa")
    public String testA(){
        testa.test();
        return "EasyA method";
    }

}

2、DI  依赖注入

        若该类中需要EasyService类的对象
        在这个类中只需要声明我们依赖EasyService这个类就可以
        而不需要通过代码主动获取EasyService类的对象

    //依赖注入DI  通过识别依赖关系注入对应的对象
    @Autowired
    EasyService easyS;

四、常用注解

Springboot提供了很多注解标注类,

@Controller(控制器 访问层)      @RestController(控制器 访问层)       @Service(业务类) @Repository(Dao数据访问 )     @Component(其它功能类)     @Configuration       @Bean

注:

        @Service是不能代替@Controller的

五、代理模式

        代理模式是常见的设计模式之一,顾名思义,代理模式就是代理对象具备业务对象的功能,并代替业务对象完成相应操作,并能够在操作执行的前后,对操作进行增强处理。(为业务对象提供代理,然后供其他对象通过代理访问业务对象)

1、静态代理

        以租房为例,租客找房东租房,然后中间经过房屋中介,以此为背景,它的UML图如下:

        可以看出房东类(RentHouse)和代理类(IntermediaryProxy)都实现了租房接口,这就是一个静态代理的前提,那就是业务类和代理类要实现同一个接口,在代理类中实现业务类的方法同时可以进行业务类方法的增强处理,在一个代理类中就可以完成对多个业务对象的注入工作。

代码如下:

        接口

public interface IRentHouse {
    void rentHouse();
}

        业务类 

public class RentHouse implements IRentHouse {

    @Override
    public void rentHouse() {
        System.out.println("实现租房");
    }
}

        代理类 

public class IntermediaryProxy implements IRentHouse {
    private IRentHouse iRent;
    public IntermediaryProxy(IRentHouse iRentHouse) {
        iRent=iRentHouse;
    }
    @Override
    public void rentHouse() {
        System.out.println("交中介费");
        iRent.rentHouse();
        System.out.println("中介负责维修管理");
    }
}

         测试类

//client测试类
public class TestStaticProxy {
    public static void main(String[] args) {
        //定义租房
        IRentHouse iRentHouse = new RentHouse();
        //定义中介
        IRentHouse intermediaryProxy = new IntermediaryProxy(iRentHouse);
        //中介租房
        intermediaryProxy.rentHouse();
    }
}

优点
    好理解

缺点  
    1.代码冗余:当多个被代理类,多个需增强方法的增强内容一样时,代码重复冗余
    2.不易维护:每个被代理类需要专门维护一个代理类,类成倍增加,需增强方法增加时,需要同时维护代理类和被代理类

 2、动态代理

        运行时动态地生成类字节码,加载进JVM

        动态代理允许使用一种方法的单个类(代理类)为具有任意数量方法的任意类(真实类)的多个方法调用提供服务,看到这句话,可以容易的联想到动态代理的实现与反射密不可分。

        JAVA 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为 java 语言的反射机制。 

(1)jdk动态代理(接口代理)

        Jdk代理涉及到java.lang.reflect包中的InvocationHandler接口和Proxy类,核心方法是 

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 

        jdk动态代理过程中实际上代理的是接口,是因为在创建代理实例的时候,依赖的是java.lang.reflect包中Proxy类的newProxyInstance方法,该方法的生效就恰恰需要这个参数; 

        public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException{
        ……
        }

代码如下:

        接口

public interface EasyInterface {
    void easy();
}

        业务类

ublic class EasyObj implements EasyInterface{
    public void easy() {
        System.out.println("正常业务逻辑");
    }
}

         代理类

public class EasyInvocationHander implements InvocationHandler {
    private Object proxyedObj;//被代理对象
    EasyInvocationHander(Object proxyedObj){
        this.proxyedObj=proxyedObj;
    }
    public Object invoke(Object proxy, Method method, Object[] args) 
        throws Throwable 
    {
        Object result=null;//定义方法的返回对象
        System.out.println("---------执行方法之前添加的业务");
        //正常执行业务逻辑
        result=method.invoke(proxyedObj,args);

        System.out.println("-----------执行方法之后的处理-----");

        return result;
    }
}

        测试类

public class Factory {
    public static Object getProxy(Object obj){
        //jdk代理只能实现接口中的方法
        return Proxy.newProxyInstance(
                obj.getClass().getClassLoader(), //类加载器
                obj.getClass().getInterfaces(),//实现的接口
                new EasyInvocationHander(obj)
        );
    }

    public static void main(String[] args) {
        EasyObj easy=new EasyObj();
        Object obj=getProxy(easy);//动态生成的一个代理类的对象

        if(obj instanceof EasyInterface){
            System.out.println("obj是代理对象 是EasyInterface的实例");
            EasyInterface e=(EasyInterface) obj;
            e.easy();
        }
        Class c=obj.getClass();
        System.out.println(c);

        EasyA easyA=new EasyA();
        obj=getProxy(easyA);
        System.out.println(obj.getClass());
    }
}

 (2)cglib动态代理

        从上面可以看出,jdk动态代理的前提条件是要有接口存在,那还有许多场景是没有接口的,这个时候就需要cglib动态代理了,CGLIB(Code Generation Library)是一个基于ASM的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成。CGLIB通过继承方式实现代理。        被代理类不能被final修饰

         代码如下:

        业务类

        代理类

        测试类

3、总结对比

  • 静态代理中,代理类和真实类实现的是同一个接口,重写同样的方法;jdk动态代理中,代理类和真实类关系不大,代理类实现无侵入式的代码扩展。
  • 静态代理中当接口中方法增加的时候,在代理类代码量也会增加,显然是不妥的;jdk动态代理解决了这个问题,当业务增加,代理类的代码不会增加。
  • jdk动态代理实现的是jdk自带InvocationHandler接口,实现了这个接口的类也叫拦截器类,也叫代理类。
  • cglib动态代理和jdk动态代理的区别显而易见,但是实现逻辑差不多,cglib代理类是通过实现MethodInterceptor,重写intercept方法,通过生成被代理类的子类来达到代理增强代码的目的;而Jdk代理是通过实现InvocationHandler,重写invoke方法,通过生成接口的代理类来达到代码增强的目的,所以jdk动态代理的实现需要接口,cglib则不需要,spring5.0以上以及springboot2.0以上默认采用cglib动态来实现AOP。

六、AOP

        AOP,一般称为面向切面,作为面向对象OOP的一种补充,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为“切面”(Aspect),减少系统中的重复代码,降低了模块之间的耦合度,提高了系统的可维护性。可用于权限认证,日志和事务处理,以及进程的监测。

        AOP可以拦截指定的方法并且对方法增强,而且无需侵入到业务代码中,使业务与非业务处理逻辑分离,比如Spring的事务,通过事务的注解配置,Spring会自动在业务方法中开启、提交事务,并且在事务处理失败时,执行相应的回滚策略。

1、为什么使用AOP

        Java是一个面向对象(OOP)的编程语言,但它有个弊端就是当需要为多个不具有继承关系的对象引入一个公共行为时,例如日志记录、权限校验、事务管理、统计等功能,只能在每个对象里都引用公共行为,这样做不便于维护,而且有大量重复代码,AOP的出现弥补了OOP的这点不足。 

                       

         有多少个业务操作,就要写多少重复的校验和日志记录代码,这显然是无法接受的。当然用面向对象的思想,可以把这些重复的代码抽离出来,写成公共方法,就是上面右图

        代码冗余和可维护性的问题得到了解决,但每个业务方法中依然要依次手动调用这些公共方法,也是略显繁琐。 有没有更好的方式呢?有的,那就是AOP,AOP将权限校验、日志记录等非业务代码完全提取出来,与业务代码分离,并寻找节点切入业务代码中 。         

 2、AOP的作用

        AOP 采取横向抽取机制(动态代理),取代了传统纵向继承机制的重复性代码,其应用主要体现在事务处理、日志管理、权限控制、异常处理等方面。

        主要作用是分离功能性需求和非功能性需求,使开发人员可以集中处理某一个关注点或者横切逻辑,减少对业务代码的侵入,增强代码的可读性和可维护性。

        简单的说,AOP 的作用就是保证开发者在不修改源代码的前提下,为系统中的业务组件添加某种通用功能。

 3、Spring AOP的术语

  • 连接点:JoinPoint,可以被AOP控制的方法(暗含方法执行时的相关信息)
  • 通知:Advice,指对切入点增强的内容
  • 切入点:PointCut,指被拦截的连接点
  • 切面:Aspect,描述通知与切入点的对应关系(通知+切入点)
  • 目标对象:Target,通知所应用的对象
  • 织入:Weaving,就是通过动态代理,把增强代码应用到目标上,生成代理对象的过程
  • 代理:Proxy,指生成的代理对象

 4、Spring AOP通知的分类

  • @Around:环绕通知,此注解标注的通知方法在目标方法前、后都被执行
  • @Before:前置通知,此注解标注的通知方法在目标方法前被执行
  • @After :后置通知,此注解标注的通知方法在目标方法后被执行,无论是否有异常都会执行
  • @AfterReturning : 返回后通知,此注解标注的通知方法在目标方法后被执行,有异常不会执行
  • @AfterThrowing : 异常后通知,此注解标注的通知方法发生异常后执行 

5、Spring AOP 织入时期

 6、代码

        定义切点

        后置通知

         前置、异常、返回

         环绕

 七、Spring MVC

        Spring MVC 使用 MVC 架构模式的思想,将 Web 应用进行职责解构,把一个复杂的 Web 应用划分成模型(Model)、控制器(Contorller)以及视图(View)三层,有效地简化了 Web 应用的开发,降低了出错风险,同时也方便了开发人员之间的分工配合

1、Spring MVC @Controller和@RestController注解

(1)@Controller 注解

        @Controller 是 Spring MVC 中的核心注解,用于标记一个类作为处理器控制器。当 Spring 容器扫描到此类时,会将其纳入到 Spring MVC 处理流程中,使其能够处理相应的 HTTP 请求。标注了 @Controller 的类中的方法可以通过 @RequestMapping 等注解映射到具体的 URL 路径,进而执行业务逻辑并返回视图名或者 ModelAndView 对象,通常配合视图解析器来渲染并返回 HTML 页面或其他类型的视图

(2)@RestController注解

        @RestController 是 Spring 4 引入的一个组合注解,它是 @Controller 和 @ResponseBody 的结合体,专门为创建 RESTful Web 服务而设计。当一个类使用 @RestController 进行标注时,意味着其处理的所有方法都将自动添加 @ResponseBody 效果,即方法的返回值不再被解释为视图名,而是直接序列化成 JSON、XML 或其他媒体类型的数据,然后以 HTTP 响应体的形式返回给客户端。

2、 @RequestMapping注解

        @RequestMapping 注解是 Spring MVC 中最常被用到的注解之一。它通常被标注在控制器方法上,负责将请求与处理请求的控制器方法关联起来,建立映射关系。

        Spring MVC 的前端控制器(DispatcherServlet)拦截到用户发来的请求后,会通过 @RequestMapping 注解提供的映射信息找到对应的控制器方法,对这个请求进行处理。

@RequestMapping 注解的使用方式

  •     修饰方法
    当 @RequestMapping  注解被标注在方法上时,value 属性值就表示访问该方法的 URL 地址。当用户发送过来的请求想要访问该 Controller 下的控制器方法时,请求路径就必须与这个 value 值相同
  •     修饰类
    当 @RequestMapping 注解标注在控制器类上时,value 属性的取值就是这个控制器类中的所有控制器方法 URL 地址的父路径。也就是说,访问这个 Controller 下的任意控制器方法都需要带上这个父路径。

@RequestMapping 注解的属性

  •     value 属性
            在 @RequestMapping 注解中,value 属性用来设置控制器方法的请求映射地址。所有能够匹配到该请求映射地址的请求,都可以被该控制器方法处理
            value 属性是 @RequestMapping 注解的默认属性,如果我们在 @RequestMapping 注解中只设置了一个 value 属性,则该属性名可以被省略
            value 属性的取值是一个字符串类型的数组,表示该控制器方法可以匹配多个请求地址。
  •     name 属性
            name 属性相当于方法的注释,用于解释这个方法是用来干什么的,使方法更易理解。@RequestMapping(value = "toUser",name = "获取用户信息")
  •     method 属性
            method 属性用来设置控制器方法支持的请求方式。如果一个控制器方法没有设置 @RequestMapping 注解的 method 属性,则说明该控制器方法支持全部请求类型,可以处理所有类型的请求。
            method 属性的取值是一个 RequestMethod 类型的数组,表示一个控制器方法支持多种方式的请求,常用的请求方式有 GET、POST、DELETE、PUT 等。
                @RequestMapping(value = "/toUser",method = RequestMethod.GET)
            我们也可以为同一个控制器方法指定支持多种类型的请求。例如,一个方法既支持 GET 方式的请求,也支持 POST 方式的请求
                @RequestMapping(value = "/toUser",method = {RequestMethod.GET,RequestMethod.POST})
  •     params 属性
            params 属性用于指定请求中的参数,只有当请求中携带了符合条件的参数时,控制器方法才会对该请求进行处理。
            使用表达式

            params 属性的取值是一个字符串类型的数组,表示只有请求中同时携带了 params 属性指定的全部参数时,控制器方法才会对该请求进行处理。
                @RequestMapping(value = "/testParam", params = {"name=javaeasy", "url=www.easy.com"})
  •     headers 属性
            headers 属性用于设置请求中请求头信息,只有当请求中携带指定的请求头信息时,控制器方法才会处理该请求。
            使用表达式

            header 属性是一个字符换类型的数组,表示只有当请求同时携带数组中规定的所有头信息时,控制器方法才会对该请求进行处理。
                @RequestMapping(value = "toUser",headers = "Referer=http://c.biancheng.net")

映射地址支持 Ant 风格的路径
    Spring MVC 还提供了对 Ant 风格路径的支持,我们可以在 @RequestMapping 注解的 value 属性中使用 Ant 风格的统配符,来设置请求映射地址。
 

Get与Post请求的区别

1) Get请求参数会使用?和&拼接到url上。Post请求参数放在请求体的Form Data数据域中。

2) Get请求参数必须是字符串。Post请求参数除了可以传递字符串类型的,也可以传递二进制。

3) Get请求参数的长度会受到url长度的限制。Post请求参数长度不会受到客户端浏览器的限制,只要服务器允许。

4) Get安全性低。Post相对安全。

 

3、Spring MVC获取请求参数

(1) 通过形参获取请求参数

        在 Controller 的控制器方法中设置与请求参数同名的形参,以获取请求中携带的参数。当浏览器发送的请求匹配到这个控制器方法时,Spring MVC 会自动将请求参数赋值给相应的方法形参。

 注意

  •     必须保证参数名一致
    我们必须保证控制器方法的形参名称与请求中携带参数的名称完全一致(区分大小写),否则控制器方法接收到的请求参数值会是 null。
            如果由于一些特殊原因,实在无法保证参数名严格一致,可以通过 @RequestParam 注解来解决。
  •     无视数据类型
    在控制器方法中使用 String 字符串类型的形参接收所有的请求参数,也可以根据实际情况在控制器方法中使用对应数据类型的参数来接收请求参数,而无须自行进行数据类型转换。
            public String test(String a, String b) {
            public String test(String a, Integer b) {
  •     不适用于请求参数过多的请求
  •     同名请求参数的处理方式

//接受前端的参数   方法的参数名和前台传递的参数名一样
    @RequestMapping("parama")
    public String paramA(String name){
        return "SpringMVC接收到的参数是"+name;
    }

    //获取地址上的参数
    @RequestMapping("paramd/{id}")
    public String paramd(@PathVariable Integer id, HttpServletRequest request){
        String username=request.getParameter("username");
        return "接收到的参数是"+id+"-----username"+username;
    }
(2) @RequestParam

        可以在控制器方法中通过 @RequestParam 注解,在请求参数与控制器方法的形参之间建立起映射关系,将它们绑定起来。这样即使请求参数与控制器方法中的形参名称不一致,也能获取到对应的请求参数值。

         @RequestParam 注解中共包含 4 个属性

//同时添加多个参数  Map 接收   非常灵活  只要前台传的都会接收   不严谨  有安全问题
    @RequestMapping("paramb")
    public Map paramB(@RequestParam Map params){
        return params;
    }
(3) 通过实体类对象获取(推荐)

        在 Controller 控制器方法的形参中设置一个实体类形参,如果请求参数的参数名与实体类中的属性名一致,那么 Spring MVC 会自动将请求参数封装到该实体类对象中。此时我们就可以通过该实体类对象获取所需的请求参数了。

        能够有效地解决“控制器方法形参不适用于请求参数过多的请求”问题。

//使用封装对象接收参数    程序中只接收需要的数据
    @RequestMapping("paramc")
    public Staff paramc(Staff staff){
        return staff;
    }
 (4)使用HashMap获取
//同时添加多个参数  Map 接收   非常灵活  只要前台传的都会接收   不严谨  有安全问题
    @RequestMapping("paramb")
    public Map paramB(@RequestParam Map params){
        return params;
    }

4、Spring MVC域对象

  • pageContext:只有在当前 JSP 页面有效,几乎不用,可用来获取项目上下文路径 (${pageContext.request.contextPath})
  • request:一次请求,多个资源共享数据
  • session:一次会话,多次请求,多个资源共享数据
  • servletContext:一个应用,多次会话,多次请求,多个资源共享数据

 5、Spring MVC请求转发与重定向

(1)请求转发
        通过 String 类型的返回值实现转发

        控制器方法指定逻辑视图名(View Name)时,使用“forward:”关键字进行请求转发操作。
    当控制器方法中所设置的逻辑视图名称以“forward:”为前缀时,该逻辑视图名称不会被 Spring MVC 配置的视图解析器解析,而是会将前缀“forward:”去掉,以剩余部分作为最终路径通过转发的方式实现跳转。

         通过 ModelAndView 实现转发

(2)重定向
        通过 String 类型的返回值实现重定向 

        通过 ModelAndView 实现重定向

(3)请求转发与重定向的区别
  • 发生位置:请求转发发生在服务器内部,是服务器行为;重定向是服务器指挥浏览器,发生在浏览器中。
  • 请求次数:请求转发只有一次请求;重定向要发送两次请求。
  • 浏览器地址:请求转发浏览器地址不变;重定向浏览器会指向重定向后的地址。
  • 请求对象:请求转发多页面共享一对request和response,可以使用request共享数据;重定向每次请求创建不同的request和response,不能使用request共享数据。
  • 范围:请求转发只能在当前项目内进行跳转;重定向可以跨站跳转

 6、JSON 数据转换

        为了实现浏览器与控制器类之间的 JSON 数据交互,Spring MVC 提供了一个默认的 MappingJackson2HttpMessageConverter 类,来处理 JSON 格式请求和响应。通过它,我们既可以将 Java 对象转换为 JSON 数据,也可以将 JSON 数据转换为 Java 对象。

        Spring MVC 为我们提供了两个十分重要的与 JSON 格式转换相关的注解,它们分别是 @RequestBody 和 @ResponseBody。

@RequestBody 

        前台代码

        Controller代码

7、Spring MVC 拦截器

        拦截器就是用来拦截指定的请求,在请求前、请求处理后做一些响应的业务逻辑处理,或者在请求完成之后做一些资源释放。

        拦截器最常用的使用场景就是认证,在请求开始之前,对当前请求进行权限校验,如果当前请求用户具备操作当前请求的权限,就对当前请求放行,允许执行业务逻辑;否则拦截当前请求,直接返回。

 (1)自定义拦截器,实现拦截器接口HandlerInterceptor

        要在SpringBoot中实现拦截器,首先需要创建一个类并实现HandlerInterceptor接口。HandlerInterceptor接口包含以下三个方法:

  • preHandle:在请求到达处理器之前执行,可以用于权限验证、数据校验等操作。如果返回true,则继续执行后续操作;如果返回false,则中断请求处理。
  • postHandle:在处理器处理请求之后执行,可以用于日志记录、缓存处理等操作。
  • afterCompletion:在视图渲染之后执行,可以用于资源清理等操作。
/**
 * 登录检查
 * 1.配置到拦截器要拦截哪些请求
 * 2.把这些配置放在容器中
 *
 * 实现HandlerInterceptor接口
 */
public class LoginInterceptor implements HandlerInterceptor {
    /**
     * 目标方法执行之前
     * 登录检查写在这里,如果没有登录,就不执行目标方法
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, 
HttpServletResponse response, Object handler) throws Exception {
        // 获取进过拦截器的路径
        String requestURI = request.getRequestURI();
 
        // 登录检查逻辑
        HttpSession session = request.getSession();
        Object loginUser = session.getAttribute("loginUser");
        if(loginUser !=null){
            // 放行
            return true;
        }
        // 拦截   就是未登录,自动跳转到登录页面,然后写拦截住的逻辑
        return false;
    }
 
    /**
     * 目标方法执行完成以后
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request,HttpServletResponse response, 
Object handler, ModelAndView modelAndView) throws Exception {
      HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }
 
    /**
     * 页面渲染以后
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, 
HttpServletResponse response, Object handler, Exception ex) throws Exception {
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
}
(2)将拦截器添加到容器当中并配置拦截器的拦截规则

        要让拦截器生效,需要将其注册到InterceptorRegistry中。这可以通过实现WebMvcConfigurer接口并重写addInterceptors方法来实现。以下是一个简单的注册示例:

@Configuration
//定制SpringMVC的一些功能都使用WebMvcConfigurer
public class AdminWebConfig implements WebMvcConfigurer {
 
    /**
     * 配置拦截器
     * @param registry 相当于拦截器的注册中心
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
				//下面这句代码相当于添加一个拦截器   添加的拦截器就是我们刚刚创建的
         registry.addInterceptor(new LoginInterceptor())
	        //addPathPatterns()配置我们要拦截哪些路径 
            //addPathPatterns("/**")表示拦截所有请求,包括我们的静态资源
                 .addPathPatterns("/**")
					//excludePathPatterns()表示我们要放行哪些(表示不用经过拦截器)
					//excludePathPatterns("/","/login")表示放行“/”与“/login”请求
					//如果有静态资源的时候可以在这个地方放行
                 .excludePathPatterns("/","/login");
    }
}
(3)拦截流程 

8、Spring MVC异常处理

(1)@ExceptionHandler 注解(局部处理)

     @GetMapping("ex")
    public CommonResult ex(){
        int a=12/0;
        return CommonResult.success();
    }

    //只能处理本类的异常  当本类被 @ControllerAdvice标识时 此方法可处理整个项目的异常
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public CommonResult exh(){
        return CommonResult.success(200,"稍有问题");
    }

        Spring MVC 允许我们在控制器类(Controller 类)中通过 @ExceptionHandler 注解来定义一个处理异常的方法,以实现对控制器类内发生异常的处理。

        @ExceptionHandler 注解中包含了一个 value 属性,我们可以通过该属性来声明一个指定的异常。如果在程序运行过程中,这个 Controller 类中的方法发生了这个指定的异常,那么 ExceptionHandlerExceptionResolver 就会调用这个 @ExceptionHandler 方法对异常进行处理。

        定义在某个控制器类中的 @ExceptionHandler 方法只在当前的控制器中有效,它只能处理其所在控制器类中发生的异常。

        @ExceptionHandler 方法的优先级--指定异常类型最精确的

(2)全局异常处理

        @ExceptionHandler 方法定义在一个使用了 @ControllerAdvice 注解的类中。使用 @ControllerAdvice 注解的类可以包含多个不同的带有 @ExceptionHandler 注解的方法

 9、Spring MVC 工作流程

  • 用户通过浏览器发起一个 HTTP 请求,该请求会被 DispatcherServlet(前端控制器)拦截;
  • DispatcherServlet 调用 HandlerMapping(处理器映射器)找到具体的处理器(Handler)及拦截器,
  • HandlerMapping将Handler以 HandlerExecutionChain 执行链的形式返回给 DispatcherServlet。
  • DispatcherServlet 将执行链返回的 Handler 信息发送给 HandlerAdapter(处理器适配器);
  • HandlerAdapter 根据 Handler 信息找到并执行相应的 Handler(即 Controller 控制器)对请求进行处理;
  • Handler 执行完毕后会返回给 HandlerAdapter 一个 ModelAndView 对象(Spring MVC 的底层对象,包括 Model 数据模型和 View 视图信息);
  • HandlerAdapter 接收到 ModelAndView 对象后,将其返回给 DispatcherServlet ;
  • DispatcherServlet 接收到 ModelAndView 对象后,会请求 ViewResolver(视图解析器)对视图进行解析;
  • ViewResolver 解析完成后,会将 View 视图并返回给 DispatcherServlet;
  • DispatcherServlet 接收到具体的 View 视图后,进行视图渲染,将 Model 中的模型数据填充到 View 视图中的 request 域,生成最终的 View(视图);
  • 视图负责将结果显示到浏览器(客户端)。

 10、Spring MVC 常用(核心)组件

        Spring MVC 的常用组件共有 5 个,它们分别是: DispatcherServlet(前端控制器)、HandlerMapping(处理器映射器)、HandlerAdapter(处理器适配器)、Handler(处理器)、ViewResolver(视图解析器)
 


网站公告

今日签到

点亮在社区的每一天
去签到