关于Spring框架的理解

目录

一. 关于Spring IoC的理解

1.1 IOC的概念和原理

1.2 Spring中的应用上下文

1.3 基于注解实现IOC的对象注入和管理

二. 关于AOP的理解

2.1 AOP代理的几种方式

2.1.1 AOP怎么实现JDK动态代理

2.2、关于Servlet容器、web容器、spring容器、springmvc容器的区别

三.  SpringMVC的工作原理/执行流程

四. Spring中Bean的作用域

五、Spring Bean的生命周期

1、对于Spring Bean在实例化之前和实例化之后的调用点

2、对于Spring Bean在属性赋值前后的调用点

3、对于Spring Bean在初始化方法前后的调用点

4、Bean对象生成后销毁前的调用点


一. 关于Spring IoC的理解

IoC(Inversion of Control:控制反转)是一种设计思想,指的是将原本程序中手动创建对象的控制权,交给Spring框架来管理。

对于传统的开发模式,如果一个类A依赖B,若是在A中需要使用到B的方法,那就需要在A中new一个B出来,然后再调用B的方法。但是如果使用了IoC的思想,则是是类B的对象注入到容器中,等到A需要B时,直接从IoC容器获取该对象即可。

这样的思想能够使得对象之间的耦合度或者说依赖程度降低。例如当类B更新为一个新的类后,我们不需要在每个类中重new类B,只需要将新的类B注入到容器中即能完美解决。

具体的情况我们可以使用一个例子来解释,如下图所示,我们需要在一个Controller类中引入

service类的功能,如果按照传统的方法,需要在该Contoller中new一个service的实现类,但是当我们的service实现类发生改变时如下所示,那就需要在每个用到service类的类中更新。但当我们使用IoC思想史,只需要更换service实现类的@service注解,即可更换service实现类,这大大降低了代码的耦合性。

1.1 IOC的概念和原理

IOC是把对象创建和对象之间的调用过程交给Spring框架进行管理,可以有效降低耦合度。IOC底层原理主要分为三个部分:

(1)XML解析:XML有三种解析方式,DOM SAX STAX。存储和传输数据经常一起使用,XML数据通常由程序生成的,用程序解析XML(XML一般不加约束)配置文件单独使用(通常会加约束)。现在基本上是基于注解进行开发(基于注解和基于XML所能达到的效果是一样的)。

(2)工厂模式:把对类的创建初始化全都交给一个工厂来执行,而用户不需要去关心创建的过程是什么样的。

(3)反射:反射可以在运行时根据指定的类名获得类的信息

IOC思想是基于IOC容器完成的,IOC容器底层就是对象工厂,Spring框架对IOC容器实现提供了两种方式(即两种接口)。

(1)BeanFactory接口:此接口是IOC容器的基本实现,也是Spring框架内部使用的接口,不提供给开发人员进行使用。此接口是在加载配置文件时不会创建对象,而是在获取对象或使用时才去创建对象

(2)ApplicationContext接口:此接口是BeanFactory接口的子接口,提供了更强大的功能,一般由开发人员进行使用。此接口在加载配置文件时就会创建配置文件中所配置的对象

1.2 Spring中的应用上下文

Spring中的应用上下文是Spring容器的一种抽象化表述,我们常见的ApplicationContext本质上就是一个维护Bean定义以及对象之间协作关系的高级接口。Spring的核心是容器,而容器并不唯一,框架本身就提供了很多个容器的实现,大概分为两种类型:一种是不常用的BeanFactory,这是最简单的容器,只能提供基本的DI功能;还有一种就是继承了BeanFactory后派生而来的应用上下文,其抽象接口也就是上面提到的的ApplicationContext,它能提供更多企业级的服务,例如解析配置文本信息等等,这也是应用上下文实例对象最常见的应用场景。有了上下文对象,我们就能向容器注册需要Spring管理的对象了。对于上下文抽象接口,Spring也为我们提供了多种类型的容器实现,供我们在不同的应用场景选择:

(1)AnnotationConfigApplicationContext:从一个或多个基于java的配置类中加载上下文定义,适用于java注解的方式;

(2)ClassPathXmlApplicationContext:从类路径下的一个或多个xml配置文件中加载上下文定义,适用于xml配置的方式;

(3)FileSystemXmlApplicationContext:从文件系统下的一个或多个xml配置文件中加载上下文定义,也就是说系统盘符中加载xml配置文件;

(4)AnnotationConfigWebApplicationContext:专门为web应用准备的,适用于注解方式;

(5)XmlWebApplicationContext:从web应用下的一个或多个xml配置文件加载上下文定义,适用于xml配置方式。

1.3 基于注解实现IOC的对象注入和管理

IOC的对象管理主要有两种方式实现,第一种是基于XML配置文件方式实现Spring框架种创建对象和注入属性。第二种是基于注解实现IOC操作Bean管理。主要由下面的几步组成:

(1)引入依赖

(2)开启组件扫描:

<!--开启组件扫描 1 如果扫描多个包,多个包使用逗号隔开 2 扫描包上层目录 --> 
<context:component-scan base-package="com.atguigu"></context:component-scan>

如果是SpringBoot项目,该功能在@SpringBootApplication注解种已经实现。

(3)创建类,在类上面添加创建对象注解

@Commponent,@Service:一般使用在Service层,@Controller:一般使用在Web层
@Repository:一般使用在Dao层。

(4)使用注解方式实现属性的注入

@Autowired:根据属性类型进行自动装配。@Qualifier:根据名称进行注入,这个@Qualifier注解的使用,和上面@Autowired一起使用 。@Resource:可以根据类型注入,可以根据名称注入。@Value:注入普通类型属性。

二. 关于AOP的理解

AOP(Aspect oriented programming:面向切面编程)是OOP(面向对象编程)的一种延续。

面向对象编程是一种垂直纵向的继承编程,这也是面向对象编程的特性。对于JAVA的子类继承父类的话,是继承了父类的所有方法,这可以有效降低重复代码的数量。这是一个垂直结构。但是对于多个不同的类想要实现的同一种功能,比如(事务处理,日志管理,权限控制)等这些功能,若是在每个类中都实现一遍,无疑会增加代码量。但是我们可以将这些通用的功能封装起来,将这个功能切入到不同的类中。

具体的实现的过程如下面这个例子所示,我们在项目中定义一个返回类,在返回类中定义返会的

 String信息,还有是否正确返回的代码等。如下图所示,在每个controller类中,即可new这个ReturnObject类,即实现了将一个功能切入到各种不同的类中。

2.1 AOP代理的几种方式

JAVA代理的优势是实现无侵入式的代码拓展,也就是方法的增强,可以在不用修改源码的情况下,增强一些方法,在方法前后做任何想做的事。类如我们在SSM框架开发时,在Controller类中注入service,在执行service中方法的前后,我们都可以编写代码,而不用修改service类中的代码,这就是无侵入式的代码拓展。

因为AOP是针对函数调用进行编程,就需要捕获函数调用,所以AOP思想的实现一半都是基于代理模式,在JAVA中一般采用JDK动态代理模式,但是因为JDK的动态代理模式只能代理接口而不能代理类,所以如果目标对象的实现类实现了接口,Spring AOP将会采用JDK动态代理来实现AOP代理类。如果目标对象的实现类没有实现接口,SpringAOP将会采用CGLIB来生成AOP代理类。此外SpringAOP还支持CGLIB、ASPECTJ、JDK动态代理。

2.1.1 AOP怎么实现JDK动态代理

1、JDK动态代理最核心的一个接口和方法如下所示(该接口在java.lang.reflect下):

public interface InvocationHandler {
 public Object invoke(Object proxy, Method method, Object[] args) throwsThrowable;
}

InvocationHandler接口是proxy代理实例的调用处理程序实现的一个接口,每一个proxy代理实例都有一个关联的调用处理程序;在代理实例调用方法时,方法调用被编码分派到调用处理程序的invoke方法。对于被代理的类的操作都会由该接口宏的invoke方法实现,其中参数的含分别是:

(1)proxy:被代理的类的实例

(2)method:调用被代理的类的方法

(3)该方法需要的参数

使用方法首先就是需要实现该接口,并且我们可以在invoke方法中调用被代理类的方法并获得返回值,自然也可以在调用该方法的前后去做一些额外的事,从而实现动态代理。

这里的invoke方法返回的是代理类的方法的返回值。

2、 java.lang.reflect 包中的 Proxy 类中的 newProxyInstance ⽅法:

该方法的代码如下所示:

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

其中参数的含义如下所示:

 (1)loader:被代理的类的类加载器;

(2)interfaces:被代理类的接口数组;

(3)invocationHandler:调用处理器类的对象实例

该方法会返回一个被修改过的类的实例,从而可以自由的调用该实例的方法。

3、JDK动态代理的测试程序

(1)接口:

package jdktest;
public interface IHello {
    void sayHello();
}

(2)实现类

package jdktest;
public class Hello implements IHello{
    @Override
    public void sayHello() {
        System.out.println("hello world...");
    }
}

(3)InvocationHandler接口实现

package jdktest;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyInvocationHandler implements InvocationHandler {
    private Object target;
    public MyInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {
        System.out.println("before...");
        Object retureval = method.invoke(target, args);
        System.out.println("after...");
        return retureval;
    }
}

(4)测试

package jdktest;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class MyProxyTest {
    public static void main(String...strings) throws Exception {
        // 1、生成$Proxy0的class文件
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        // 2、获取动态代理类
        Class proxyClazz = Proxy.getProxyClass(IHello.class.getClassLoader(),IHello.class);
        // 3、获得代理类的构造函数,并传入参数类型InvocationHandler.class
        Constructor constructor = proxyClazz.getConstructor(InvocationHandler.class);
        // 4、通过构造函数来创建动态代理对象,将自定义的InvocationHandler实例传入
        IHello iHello1 = (IHello) constructor.newInstance(new MyInvocationHandler(new Hello()));
        // 5、通过代理对象调用目标方法
        iHello1.sayHello();
        // ==========================第二种=============================
        /**
         * Proxy类中还有个将2~4步骤封装好的简便方法来创建动态代理对象,
         *其方法签名为:newProxyInstance(ClassLoader loader,Class<?>[] instance, InvocationHandler h)
         */
        IHello  iHello2 = (IHello) Proxy.newProxyInstance(IHello.class.getClassLoader(),         // 加载接口的类加载器
                new Class[]{IHello.class}, // 一组接口
                new MyInvocationHandler(new Hello())); // 自定义的InvocationHandler
        iHello2.sayHello();
    }
}

2.2、关于Servlet容器、web容器、spring容器、springmvc容器的区别

 如下图所示,web容器中有servlet容器,spring项目部署后存在spring容器。其中spring控制service层和dao层的bean对象以及controller层bean对象。servlet容器控制servlet对象。项目启动是,首先 servlet初始化,初始化过程中通过web.xml中spring的配置加载spring配置,初始化spring容器。待容器加载完成。servlet初始化完成,则完成启动。springmvc是viewAndModie的请求传递和结果解析。本身并没有容器管理,都是交给spring管理。
HTTP请求到达web容器后,会到达Servlet容器,容器通过分发器分发到具体的spring的Controller层。执行业务操作后返回结果。

Servlet(Server Applet),全称Java Servlet,未有中文译文。是用Java编写的服务器端程序。其主要功能在于交互式地浏览和修改数据,生成动态Web内容。狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类,一般情况下,人们将Servlet理解为后者。

Servlet运行于支持Java的应用服务器中。从实现上讲,Servlet可以响应任何类型的请求,但绝大多数情况下Servlet只用来扩展基于HTTP协议的Web服务器。

Servlet API 包含以下4个Java包:

1.javax.servlet   其中包含定义servlet和servlet容器之间契约的类和接口。

2.javax.servlet.http   其中包含定义HTTP Servlet 和Servlet容器之间的关系。

3.javax.servlet.annotation   其中包含标注servlet,Filter,Listener的标注。它还为被标注元件定义元数据。

4.javax.servlet.descriptor,其中包含提供程序化登录Web应用程序的配置信息的类型。

三.  SpringMVC的工作原理/执行流程

简单来说:客户端发送请求-> 前端控制器 DispatcherServlet 接受客户端请求 -> 找到处理器映射 HandlerMapping 解析请求对应的 Handler -> HandlerAdapter 会根据 Handler 来调⽤真正的处理器来处理请 求,并处理相应的业务逻辑 -> 处理器返回⼀个模型视图 ModelAndView -> 视图解析器进⾏解析 -> 返回⼀个视图 对象 -> 前端控制器 DispatcherServlet 渲染数据(Model)-> 将得到视图对象返回给⽤户。图形表示如下图所示:

上图⽤于辅助理解,⾯试时可⽤下列 8 步描述 SpringMVC 运⾏流程:

1. ⽤户向服务器发送请求,请求被 Spring 前端控制Servelt DispatcherServlet 捕获;

2. DispatcherServlet 对请求 URL 进⾏解析,得到请求资源标识符(URI)。然后根据该 URI,调⽤HandlerMapping 获得该 Handler 配置的所有相关的对象(包括 Handler 对象以及 Handler 对象对应的拦截 器),最后以 HandlerExecutionChain 对象的形式返回;

3. DispatcherServlet 根据获得的 Handler,选择⼀个合适的HandlerAdapter;(附注:如果成功获得 HandlerAdapter 后,此时将开始执⾏拦截器的 preHandler(...)⽅法)

4.提取 Request 中的模型数据,填充 Handler ⼊参,开始执⾏Handler(Controller)。在填充 Handler 的⼊参 过程中,根据你的配置,Spring 将帮你做⼀些额外的⼯作:

(1)HttpMessageConveter:将请求消息(如:Json、xml 等数据)转换成⼀个对象,将对象转换为指定的响应信息;

(2)数据转换:对请求消息进⾏数据转换。如:String 转换成 Integer、Double 等;

(3)数据格式化:对请求消息进⾏数据格式化。如:将字符串转换成格式化数字或格式化⽇期等;

(4)数据验证:验证数据的有效性(⻓度、格式等),验证结果存储到 BindingResult 或 Error 中;

5. Handler 执⾏完成后,向 DispatcherServlet 返回⼀个 ModelAndView 对象;

6. 根据返回的 ModelAndView,选择⼀个适合的 ViewResolver(必须是已经注册到 Spring 容器中的 ViewResolver)返回给DispatcherServlet;

7. ViewResolver 结合 Model 和 View,来渲染视图;

8. 将渲染结果返回给客户端

四. Spring中Bean的作用域

1、singleton作用域
如果bean的作用域的属性被声明为singleton,那么Spring IOC容器只会创建一个共享的bean实例。对于所有的bean请求,只要id与该bean定义的相匹配,那Spring每次需要时都会返回同一个bean实例。

2、prototype作用域
prototype是原型类型,它在我们创建容器的时候并没有实例化,而是当我们获取bean的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象。根据经验,对有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域。

3、Request作用域
当http请求调用作用域为request的bean的时候,每增加一个HTTP请求,Spring就会创建一个新的bean,在请求处理完成之后便及时销毁这个bean。开发者可以随意改变实例的状态,因为通过Http请求来创建的其他实例根本看不到开发者改变的实例状态,所有创建的Bean实例都是根据独立的请求来的。

4、Session作用域
同一个HTTP Session共享一个Bean,不同Session使用不同的Bean。该作用域仅适用于web的Spring WebApplicationContext环境。

5、application作用域
限定一个Bean的作用域为ServletContext的生命周期。该作用域仅适用于web的Spring WebApplicationContext环境。

五、Spring Bean的生命周期

首先对于传统的java编程来说,一个对象的生命周期只有两步,第一步是实例化,第二步时该对象不再被使用时,通过垃圾回收机制进行回收。

对于Spring Bean的生命周期来说,主要分为接下来四步,分别是实例化,属性赋值,初始化,销毁四步。在Spring Bean的实例化和属性赋值以及初始化前后都会有各种拓展点,我们按照Spring Bean的生命周期依次介绍常用的拓展点。

1、对于Spring Bean在实例化之前和实例化之后的调用点

在SpringBean实例化之前有InstantiationAwareBeanPostProcessor接口的postProcessBeforeInstantiation调用点。在该调用点如果返回了bean实例,则会替代原来正常通过目标bean对象生成的bean的流程。典型的应用场景有aop返回proxy对象,此时的bean的执行流程将会缩短(实例化和初始化都不会执行),只会执行BeanPostProcessor接口中的postProcessAfterInitialization接口完成初始化。

在Spring Bean实例化之后,初始化之前,会调用InstantiationAwareBeanPostProcessor接口的postProcessAfterInstantiation调用点在实例化之后调用。如果此时有指定的bean的时候返回false,那么后续的属性填充和属性依赖注入(populateBean)将不会执行,但是后续的初始化和初始化前后的BeanPostProcessor仍然会执行。

2、对于Spring Bean在属性赋值前后的调用点

在上面的postProcessAfterInstantiation调用点返回为true时,InstantiationAwareBeanPostProcessor接口的postProcessProperties会调用,返回的PropertyValues(后面被改为postProcessProperties代替)将作用于给定bean属性赋值。如果返回的值为null,将不会进行后续的属性填充,如果返回的PropertyValues额外添加了属性,后续将会填充到该类对应的属性中。具体的方法如下所示:

public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName)

3、对于Spring Bean在初始化方法前后的调用点

在对Spring Bean的属性进行填充之后,在Bean初始化之前会执行BeanPostProcessor接口的postProcessBeforeInitialization然后再执行执行InitializingBean的afterPropertiesSet。然后是属性配置的初始化方法。

最后在Bean初始化之后执行BeanPostProcessor接口的postProcessAfterInitialization。

4、Bean对象生成后销毁前的调用点

在Bean对象生成之后,如果该对象的作用域是prototype将该Bean给用户,剩下的生命周期由用户控制,如果Bean对象的作用域是singleton,返回Bean给用户并存入缓存池。

在Bean销毁前可以执行DisposableBean的destory,然后再执行Bean对象的销毁方法。

以上处理方法的图示如下所示:

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值