Java面试经验个人总结(十)——Spring

本文深入探讨了Spring框架的动态代理机制,包括JDK和CGLIB两种实现方式,并分析了它们的优缺点。此外,详细阐述了Spring的AOP(面向切面编程)概念,解释了切点、通知、切面等核心概念,以及如何通过注解和配置实现AOP。最后,简要回顾了IoC(控制反转)和事务管理,讨论了SpringMVC的工作流程及其重要组件。
摘要由CSDN通过智能技术生成

Spring

一、动态代理

1. 概念

动态代理主要用于拦截对真实业务对象的访问;一般来说,真实业务对象具有什么方法,那么代理对象就会具备相应的方法。(利用反射机制获得被代理对象属性,在运行时动态创建代理对象;而静态代理的代理对象则是在编译时被创建,耦合度高)。静态代理通过实现接口创建代理类,动态代理通过反射机制创建代理类。

2. 动态代理分类

(1)JDK动态代理

(2)CGLib动态代理

3. JDK动态代理

Java提供了一个Proxy类,调用它的newInstance方法可以生成某个对象的代理对象,使用该方法生成代理对象时,需要三个参数:

(1)被代理对象使用的类装载器,利用反射获得;

(2)被代理对象实现的接口,因为需要根据接口动态生成对象;

(3)InvocationHandler:事件处理器,即对目标对象方法的执行

需要注意的是:

(1)Proxy类负责创建代理对象时,如果指定了事件处理器,那么不管用户调用代理对象的什么方法,该方法都是调用处理器的invoke()方法。

(2)由于invoke()方法被调用需要三个参数:代理对象、方法、方法的参数,因此不管代理对象哪个方法调用处理器的invoke()方法,都必须把自己所在的对象、自己(调用invoke()方法的方法)、方法的参数传递进来。

4. CGlib动态代理

利用ASM(开源的Java字节码编辑库,操作字节码)开源包,将代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

5. JDK动态代理和CGlib动态代理的区别

(1)JDK代理使用的是反射机制实现aop的动态代理,CGLIB代理使用字节码处理框架asm,通过修改字节码生成子类。所以jdk动态代理的方式创建代理对象效率较高,执行效率较低,cglib创建效率较低,执行效率高;

(2)JDK动态代理机制是委托机制,具体说动态实现接口类,在动态生成的实现类里面委托hanlder去调用原始实现类方法,CGLIB则使用的继承机制,具体说被代理类和代理类是继承关系,所以代理类是可以赋值给被代理类的,如果被代理类有接口,那么代理类也可以赋值给接口。

 

二、AOP

1. 概念

AOP,即Aspect Oriented Programming,意味面向切面编程。AOP思想的实现一般都是基于代理模式,在JAVA中一般采用JDK动态代理模式,但是我们都知道,JDK动态代理模式只能代理接口而不能代理类。因此,Spring AOP 会这样子来进行切换,因为Spring AOP 同时支持 CGLIB、ASPECTJ、JDK动态代理。

(1)如果目标对象的实现类实现了接口,Spring AOP 将会采用 JDK 动态代理来生成 AOP 代理类;

(2)如果目标对象的实现类没有实现接口,Spring AOP 将会采用 CGLIB 来生成 AOP 代理类

2. AOP的作用

AOP可以分离系统的业务逻辑和系统服务(日志,安全等),这个功能我想是不难明白(原理是使用了代理模式)。AOP将业务逻辑模块和非业务逻辑的公共模块分割,让这些公共模块在连接点进行动态插入运行。

3. AOP的基本概念

(1)通知:通知是个在方法执行前或执行后要做的动作,实际上是程序执行时要通过SpringAOP框架触发的代码段,告诉目标对象何时使用切面。Spring切面可以应用五种类型的通知:

a. before:前置通知,在一个方法执行前被调用。

b. after: 在方法执行之后调用的通知,无论方法执行是否成功。

c. after-returning: 仅当方法成功完成后执行的通知。

d. after-throwing: 在方法抛出异常退出时执行的通知。

e. around: 在方法执行之前和之后调用的通知。

(2)切点:切点在Spring AOP中确实是对应系统中的方法。但是这个方法是定义在切面中的方法,一般和通知一起使用,一起组成了切面。

(3)切面:AOP核心就是切面,它将多个类的通用行为封装成可重用的模块,一般单独作为一个类。该模块含有一组API提供横切功能。比如,一个日志模块可以被称作日志的AOP切面。根据需求的不同,一个应用程序可以有若干切面。在Spring AOP中,切面通过带有@Aspect注解的类实现。

(4)连接点:连接点代表一个应用程序的某个位置,在这个位置我们可以插入一个AOP切面,它实际上是个应用程序执行Spring AOP的位置。这个连接点可以是接口中的方法。

(5)关注点:增加的某个业务,如日志、安全、缓存、事务等;

(6)引入:引入允许我们在已存在的类中增加新的方法和属性。

(7)目标对象:被一个或者多个切面所通知的对象。它通常是一个代理对象。也指被通知(advised)对象。

(8)织入:织入是将切面和到其他应用类型或对象连接或创建一个被通知对象的过程。织入可以在编译时,加载时,或运行时完成。

 

4. 相关注解

(1)类上注解:@Aspect:作用是把当前类标识为一个切面供容器读取。

(2)切面方法上注解:

a. @Pointcut:Pointcut是植入Advice的触发条件。每个Pointcut的定义包括2部分,一是表达式,二是方法签名。方法签名必须是 public及void型。可以将Pointcut中的方法看作是一个被Advice引用的助记符,因为表达式不直观,因此我们可以通过方法签名的方式为 此表达式命名。因此Pointcut中的方法只需要方法签名,而不需要在方法体内编写实际代码。

b. @Around:环绕增强,相当于MethodInterceptor

c. @AfterReturning:后置增强,相当于AfterReturningAdvice,方法正常退出时执行

d. @Before:标识一个前置增强方法,相当于BeforeAdvice的功能,相似功能的还有

e. @AfterThrowing:异常抛出增强,相当于ThrowsAdvice

f. @After: final增强,不管是抛出异常或者正常退出都会执行

5. 配置:<aop:aspectj-autoproxy/>

 

6. 动态AOP自定义标签

(1)AOP标签的定义解析是从NamespaceHandlerSupport的实现类开始解析的,这个实现类就是AopNamespaceHandler;

(2)要启用AOP,我们一般会在Spring里面配置<aop:aspectj-autoproxy/>  ,所以在配置文件中在遇到aspectj-autoproxy标签的时候我们会采用AspectJAutoProxyBeanDefinitionParser解析器;

(3)进入AspectJAutoProxyBeanDefinitionParser解析器后,调用AspectJAutoProxyBeanDefinitionParser已覆盖BeanDefinitionParser的parser方法,然后parser方法把请求转交给了AopNamespaceUtils的registerAspectJAnnotationAutoProxyCreatorIfNecessary去处理;

(4)在已经进入了AopNamespaceUtils(AOP命名空间单元)的registerAspectJAnnotationAutoProxyCreatorIfNecessary方法后,会先调用AopConfigUtils的registerAspectJAnnotationAutoProxyCreatorIfNecessary方法,里面在转发调用给registerOrEscalateApcAsRequired,注册或者升级AnnotationAwareAspectJAutoProxyCreator类。对于AOP的实现,基本是靠AnnotationAwareAspectJAutoProxyCreator去完成的,它可以根据@point注解定义的切点来代理相匹配的bean;

(5)AopConfigUtils的registerAspectJAnnotationAutoProxyCreatorIfNecessary方法处理完成之后,接下来会调用useClassProxyingIfNecessary() 处理proxy-target-class以及expose-proxy属性。如果将proxy-target-class设置为true的话,那么会强制使用CGLIB代理,否则使用jdk动态代理,expose-proxy属性是为了解决有时候目标对象内部的自我调用无法实现切面增强;

(6)最后的调用registerComponentIfNecessary 方法,注册组建并且通知便于监听器做进一步处理。

 

7. AOP实现

AOP的核心逻辑是在AnnotationAwareAspectJAutoProxyCreator类里面实现

(1)spring 容器启动,每个bean的实例化之前都会先经过AbstractAutoProxyCreator类的postProcessAfterInitialization()这个方法,然后接下来是调用wrapIfNecessary方法;

(2)进入wrapIfNecessary方法后,我们直接看重点实现逻辑的方法getAdvicesAndAdvisorsForBean,这个方法会提取当前bean 的所有增强方法,然后获取到适合的当前bean 的增强方法,然后对增强方法进行排序,最后返回;

(3)获取到当前bean的增强方法后,便调用createProxy方法,创建代理。先创建代理工厂proxyFactory,然后获取当前bean 的增强器advisors,把当前获取到的增强器添加到代理工厂proxyFactory,然后设置当前的代理工的代理目标对象为当前bean,最后根据配置创建JDK的动态代理工厂,或者CGLIB的动态代理工厂,然后返回proxyFactory

 

三、IoC

1. 目的:IoC出现的目的或者说是作用是为了解决对象之间的耦合度过高的问题。

2. IOC理论提出的观点大体是这样的:借助于“第三方”实现具有依赖关系的对象之间的解耦,如下图:

 

中间位置的“第三方”,也就是IOC容器,使得A、B、C、D这4个对象没有了耦合关系,对象之间的通信依赖第三方容器,全部对象的控制权全部上缴给“第三方”IOC容器,所以,IOC容器成了整个系统的关键核心,它起到了一种类似“粘合剂”的作用,把系统中的所有对象粘合在一起发挥作用,如果没有这个“粘合剂”,对象与对象之间会彼此失去联系,这就是有人把IOC容器比喻成“粘合剂”的由来。

控制反转的含义在于:若对象A依赖对象B,则对象A在初始化或者运行到某个方法时会自己创建对象B;若使用IoC容器,则对象B的创建由容器完成,并注入到A需要的地方,对象B的控制权由A转移到了IoC容器,这就是控制反转。获得依赖对象的过程被反转了。

3. IoC容器的作用

Spring IOC负责创建对象,管理对象(通过依赖注入(DI),装配对象,配置对象,并且管理这些对象的整个生命周期。

4. 依赖注入:DI是实现IoC思想的具体方法。运行时动态创建依赖对象。

对象A依赖于对象B,当对象 A需要用到对象B的时候,IOC容器就会立即创建一个对象B送给对象A。IOC容器就是一个对象制造工厂,你需要什么,它会给你送去,你直接使用就行了,而再也不用去关心你所用的东西是如何制成的,也不用关心最后是怎么被销毁的,这一切全部由IOC容器包办。

5. 使用IoC容器的好处

(1)解耦;

(2)专注于业务逻辑,提高开发效率;

(3)提高了模块的可复用性;

(4)IOC生成对象的方式转为外置方式,也就是把对象生成放在配置文件里进行定义,这样,当我们更换一个实现子类将会变得很简单,只要修改配置文件就可以了,完全具有热插拨的特性

6. IoC容器技术解析

IoC中最基本的技术就是“反射(Reflection)”编程,通俗来讲就是根据给出的类名(字符串方式)来动态地生成对象。这种编程方式可以让对象在生成时才决定到底是哪一种对象,因为输入了类名不同,输出的对象也不同。

7. bean初始化过程

读取XML资源,并解析,最终注册到Bean Factory中。

 

完成后,便可以理由getBean()来获取对象了。

8.  BeanFactory、Applicationcontext和FactoryBean的区别

(1)BeanFactory是spring简单工厂模式的接口类,spring IOC特性核心类,提供从工厂类中获取bean的各种方法,是所有bean的容器。

(2)FactoryBean仍然是一个bean,但不同于普通bean,它的实现类最终也需要注册到BeanFactory中。它也是一种简单工厂模式的接口类,但是生产的是单一类型的对象,与BeanFactory生产多种类型对象不同。FactoryBean是一个接口,实现了这个接口的类,在注册到spring BeanFactory后,并不像其它类注册后暴露的是自己,它暴露的是FactoryBean。

(3)Applicationcontext如同beanfactory一样具有bean定义、bean关联关系的设置,根据请求分发bean的功能。但applicationcontext在此基础上还提供了其他的功能,比如:提供了支持国际化的文本消息;统一的资源文件读取方式;已在监听器中注册的bean的事件。

9. @Autowired的实现

AutowiredAnnotationBeanPostProcessor筛选@Autowired标识的构造函数,筛选出这些构造函数之后,Spring使用ConstructorResolver这个类来择取合适的构造函数,流程如下:

(1)首先对这些构造函数按修饰符优先public排序,修饰符相同再按参数的长短排序,最先解析参数最长的public修饰的构造函数,其优先级最高.

(2)对构造函数的每一个参数解析,如果每一个参数均能从Spring容器中找到合适的Bean,则此将此构造函数作为最优解,如果容器内Bean不能满足所有参数,则解析下一个构造函数。

(3)如果存在两个参数长度相同的构造函数,且容器内Bean均能满足参数解析,则按参数类型和Bean类型的差异性求取参数的差异权重,比如参数是接口,Bean是实现类,则差异加2,参数是集合,Bean是单个类,则转换成集合,差异加2等等,比较两个构造函数的差异权重大小,差异小的那个作为最优解。如果两个差异权重相等,则抛出含有模棱两可的构造函数的BeanCreationException。

(4)当有了最优解的构造函数后,如果下一个构造函数的参数长度等于最优解,则解析此构造函数,如果参数长度小于最优解,则不再解析,直接忽略之后的所有构造函数。

(5)当得到了构造函数最优解之后,将此构造函数存入此Bean的BeanDefinition中,以备下次复用,就是说只对构造函数候选者集合解析一次,下次实例化Bean的时候可以直接得到这个最优解。

10. @Autowired与@Resource的区别

(1)@Autowired与@Resource都可以用来装配bean都可以写在字段上或写在setter方法上。

(2) @Autowired默认按类型装配(这个注解是属业spring的),默认情况下必须要求依赖对象必须存在,如果要允许null值,可以设置它的required属性为false,如:@Autowired(required=false) ,如果我们想使用名称装配可以结合@Qualifier注解进行使用,如下:

 

(3)@Resource(这个注解属于J2EE的),默认按照名称进行装配,名称可以通过name属性进行指定,如果没有指定name属性,当注解写在字段上时,默认取字段名进行安装名称查找,如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。

 

四、Spring事务管理

1. 按照给定的事务规则来执行提交或者回滚操作

(1)PlatformTransactionManager: (平台)事务管理器

(2)TransactionDefinition: 事务定义信息(事务隔离级别、传播行为、超时、只读、回滚规则)

(3)TransactionStatus: 事务运行状态

2. @Transaction

底层通过代理实现,一个方法若想开启一个事务,那么必须是public的,且注解在本方法外。方法外没有事务注解,即使方法内调用了事务方法,也只按普通方法来用。

3. TransactionDefinition接口属性介绍

(1)隔离级别

(2)事务传播行为(为了解决业务层方法之间互相调用的事务问题):

a. PROPAGATION_SUPPORTS: 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。(支持当前事务)

b. PROPAGATION_REQUIRED: 如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。(支持当前事务)

c. PROPAGATION_MANDATORY: 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。(支持当前事务)

d. PROPAGATION_REQUIRES_NEW: 创建一个新的事务,如果当前存在事务,则把当前事务挂起。(不支持当前事务)

e. PROPAGATION_NOT_SUPPORTED: 以非事务方式运行,如果当前存在事务,则把当前事务挂起。(不支持当前事务)

f. PROPAGATION_NEVER: 以非事务方式运行,如果当前存在事务,则抛出异常。(不支持当前事务)

g. PROPAGATION_NESTED: 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。

(3)事务超时属性(一个事务允许执行的最长时间)

所谓事务超时,就是指一个事务所允许执行的最长时间,如果超过该时间限制但事务还没有完成,则自动回滚事务。在 TransactionDefinition 中以 int 的值来表示超时时间,其单位是秒。

(4)事务只读属性(对事物资源是否执行只读操作)

事务的只读属性是指,对事务性资源进行只读操作或者是读写操作。所谓事务性资源就是指那些被事务管理的资源,比如数据源、 JMS 资源,以及自定义的事务性资源等等。如果确定只对事务性资源进行只读操作,那么我们可以将事务标志为只读的,以提高事务处理的性能。在 TransactionDefinition 中以 boolean 类型来表示该事务是否只读。

(5)回滚规则(定义事务回滚规则)

这些规则定义了哪些异常会导致事务回滚而哪些不会。默认情况下,事务只有遇到运行期异常时才会回滚,而在遇到检查型异常时不会回滚(这一行为与EJB的回滚行为是一致的)。

但是你可以声明事务在遇到特定的检查型异常时像遇到运行期异常那样回滚。同样,你还可以声明事务遇到特定的异常不回滚,即使这些异常是运行期异常。

4. 声明式事务管理——@Transactional注解

声明式事务管理。该注解底层通过代理模式实现。添加该注解,会在业务方法调用时打开一个事务,对数据库进行读写操作。@Transactional 注解应该只被应用到 public 方法上。

 

五、bean的作用域

1. bean:在 Spring 中,那些组成应用程序的主体及由 Spring IOC 容器所管理的对象,被称之为 bean。简单地讲,bean 就是由 IOC 容器初始化、装配及管理的对象,除此之外,bean 就与应用程序中的其他对象没有什么区别了。而 bean 的定义以及 bean 相互间的依赖关系将通过配置元数据来描述。

2. 作用域

 

五种作用域中,request、session 和 global session 三种作用域仅在基于web的应用中使用(不必关心你所采用的是什么web应用框架),只能用在基于 web 的 Spring ApplicationContext 环境。为了保证线程的安全性,需要对bean定义作用域scope。

(1)singleton作用域——默认作用域

当一个 bean 的作用域为 singleton,那么Spring IoC容器中只会存在一个共享的 bean 实例,并且所有对 bean 的请求,只要 id 与该 bean 定义相匹配,则只会返回bean的同一实例。

(2)prototype——每次请求都会创建一个新的 bean 实例

当一个bean的作用域为 prototype,表示一个 bean 定义对应多个对象实例。 prototype 作用域的 bean 会导致在每次对该 bean 请求(将其注入到另一个 bean 中,或者以程序的方式调用容器的 getBean() 方法)时都会创建一个新的 bean 实例。prototype 是原型类型,它在我们创建容器的时候并没有实例化,而是当我们获取bean的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象。根据经验,对有状态的 bean 应该使用 prototype 作用域,而对无状态的 bean 则应该使用 singleton 作用域。如果对有状态bean设置singleton作用域,则后面请求的状态会覆盖前面请求的状态。

(3)request——每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP request内有效

request只适用于Web程序,每一次 HTTP 请求都会产生一个新的bean,同时该bean仅在当前HTTP request内有效,当请求结束后,该对象的生命周期即告结束。

(4)session——每一次HTTP请求都会产生一个新的 bean,该bean仅在当前 HTTP session 内有效

session只适用于Web程序,session 作用域表示该针对每一次 HTTP 请求都会产生一个新的 bean,同时该 bean 仅在当前 HTTP session 内有效.与request作用域一样,可以根据需要放心的更改所创建实例的内部状态,而别的 HTTP session 中根据 userPreferences 创建的实例,将不会看到这些特定于某个 HTTP session 的状态变化。当HTTP session最终被废弃的时候,在该HTTP session作用域内的bean也会被废弃掉

(5)globalSession

global session 作用域类似于标准的 HTTP session 作用域,不过仅仅在基于 portlet 的 web 应用中才有意义。Portlet 规范定义了全局 Session 的概念,它被所有构成某个 portlet web 应用的各种不同的 portle t所共享。在global session 作用域中定义的 bean 被限定于全局portlet Session的生命周期范围内。

 

六、bean的生命周期和spring的启动流程

1. 生命周期分析

(1)Bean容器找到配置文件中 Spring Bean 的定义。

(2)Bean容器利用Java Reflection API创建一个Bean的实例。

(3)如果涉及到一些属性值 利用set方法设置一些属性值。

(4)如果Bean实现了BeanNameAware接口,调用setBeanName()方法,传入Bean的名字。

(5)如果Bean实现了BeanClassLoaderAware接口,调用setBeanClassLoader()方法,传入ClassLoader对象的实例。

(6)如果Bean实现了BeanFactoryAware接口,调用setBeanClassLoader()方法,传入ClassLoader对象的实例。

(7)与上面的类似,如果实现了其他*Aware接口,就调用相应的方法。

(8)如果有和加载这个Bean的Spring容器相关的BeanPostProcessor对象,执行postProcessBeforeInitialization()方法

(9)如果Bean实现了InitializingBean接口,执行afterPropertiesSet()方法。

(10)如果Bean在配置文件中的定义包含init-method属性,执行指定的方法。

(11)如果有和加载这个Bean的Spring容器相关的BeanPostProcessor对象,执行postProcessAfterInitialization()方法

(12)当要销毁Bean的时候,如果Bean实现了DisposableBean接口,执行destroy()方法。

(13)当要销毁Bean的时候,如果Bean在配置文件中的定义包含destroy-method属性,执行指定的方法。

 

2. 生命周期

(1)通过构造器或工厂方法创建Bean实例;

(2)为Bean的属性设置值和其他Bean的引用,将Bean实例传递给Bean后置处理器;

(3)调用Bean的初始化方法,将Bean实例传递给Bean后置处理器;

(4)Bean可以使用;

(5)当容器关闭时候,调用Bean的销毁方法

3. 什么时候创建Bean

(1)使用BeanFactory作为Spring Bean的工厂类,则所有的bean都是在第一次使用该Bean的时候实例化 ;

(2)使用ApplicationContext作为Spring Bean的工厂类:

a. 如果bean的作用域是singleton的,并且lazy-init为false(默认是false,所以可以不用设置),则ApplicationContext启动的时候就实例化该Bean,并且将实例化的Bean放在一个map结构的缓存中,下次再使用该Bean的时候,直接从这个缓存中取;

b. 如果bean的作用域是singleton的,并且lazy-init为true,则该Bean的实例化是在第一次使用该Bean的时候进行实例化;

c. 如果bean的作用域是prototype的,则该Bean的实例化是在第一次使用该Bean的时候进行实例化

4. 不同作用域的bean的生命周期

(1)singleton

单例模式会在启动容器时(即实例化容器时)时实例化。但我们可以指定Bean节点的lazy-init=”true”来延迟初始化bean,这时候,只有在第一次获取bean时才会初始化bean,即第一次请求该bean时才初始化。

默认情况下,Spring 在读取 xml 文件的时候,就会创建对象。在创建对象的时候先调用构造器,然后调用 init-method 属性值中所指定的方法。对象在被销毁的时候,会调用 destroy-method 属性值中所指定的方法(例如调用Container.destroy()方法的时候)。

(2)prototype

当bean的作用域为prototype时,容器也会延迟初始化 bean,Spring 读取xml 文件的时候,并不会立刻创建对象,而是在第一次请求该 bean 时才初始化(如调用getBean方法时)。在第一次请求每一个prototype的bean 时,Spring容器都会调用其构造器创建这个对象,然后调用init-method属性值中所指定的方法。对象销毁的时候,Spring 容器不会帮我们调用任何方法,因为是非单例,这个类型的对象有很多个,Spring容器一旦把这个对象交给你之后,就不再管理这个对象了。 Spring不能对一个 prototype bean 的整个生命周期负责:容器在初始化、配置、装饰或者是装配完一个prototype实例后,将它交给客户端,随后就对该prototype实例不闻不问了,清除prototype作用域的对象并释放任何prototype bean所持有的昂贵资源,都是客户端代码的职责。

5. Spring的启动流程

(1)SpringIOC启动过程

a. 在web.xml中配置<context-param>,初始化上下文;

b. 加载ContextLoaderListener上下文监听器;

c. 初始化WebApplicationContext;

d. 创建WebApplicationContext;

e. 加载bean;

f.  将WebApplicationContext放入ServletContext(Java Web的全局变量)中

(2)SpringMVC启动过程

a. 在web.xml中配置DispatcherServlet,并初始化;

b. 初始化SpringMVC使用的web上下文,指定父容器为WebApplicationContext;

c. 初始化DispatcherServlet使用的策略,如HandlerMapping、HandlerAdapter等

 

七、SpringMVC工作原理

1. 工作流程

 

(1)客户端(浏览器)发送请求,直接请求到 DispatcherServlet;

(2)DispatcherServlet 根据请求信息调用 HandlerMapping,解析请求对应的 Handler;

(3)解析到对应的 Handler后,开始由 HandlerAdapter 适配器处理;

(4)HandlerAdapter 会根据 Handler 来调用真正的处理器开处理请求,并处理相应的业务逻辑;

(5)处理器处理完业务后,会返回一个 ModelAndView 对象,Model 是返回的数据对象,View 是个逻辑上的 View;

(6)ViewResolver 会根据逻辑 View 查找实际的 View;

(7)DispaterServlet 把返回的 Model 传给 View(视图渲染);

(8)把 View 返回给请求者(浏览器)。

2. 重要组件

(1)前端控制器DispatcherServlet(不需要工程师开发,由框架提供)

Spring MVC 的入口函数。接收请求,响应结果,相当于转发器,中央处理器。有了 DispatcherServlet 减少了其它组件之间的耦合度。用户请求到达前端控制器,它就相当于mvc模式中的Controller,DispatcherServlet是整个流程控制的中心,由它调用其它组件处理用户的请求,DispatcherServlet的存在降低了组件之间的耦合性。

(2)处理器映射器HandlerMapping(不需要工程师开发,由框架提供)

根据请求的url查找Handler。HandlerMapping负责根据用户请求找到Handler处理器,SpringMVC提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。

(3)处理器适配器HandlerAdapter

按照特定规则(HandlerAdapter要求的规则)去执行Handler 通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。

(4)处理器Handler(需要工程师开发)

Handler是继DispatcherServlet前端控制器的后端控制器,在DispatcherServlet的控制下Handler对具体的用户请求进行处理。 由于Handler涉及到具体的用户业务请求,所以一般情况需要工程师根据业务需求开发Handler。这是业务

(5)视图解析器View resolver(不需要工程师开发,由框架提供)

进行视图解析,根据逻辑视图名解析成真正的视图(view) View Resolver负责将处理结果生成View视图,View Resolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。 springmvc框架提供了很多的View视图类型,包括:jstlView、freemarkerView、pdfView等。 一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由工程师根据业务需求开发具体的页面。

(6)视图View(需要工程师开发)

View是一个接口,实现类支持不同的View类型(jsp、freemarker、pdf...)。

3. 常用注解

(1)@RequestMapping:用于处理请求 url 映射的注解,可用于类或方法上。用于类上,则表示类中的所有响应请求的方法都是以该地址作为父路径。

(2)@RequestBody:注解实现接收http请求的json数据,将json转换为java对象。

(3)@ResponseBody:注解实现将conreoller方法返回对象转化为json对象响应给客户。

(4)@Conntroller注解,表示是控制层

(5)@sessionattributes注解应用到Controller上面,可以将Model中的属性同步到session当中。

 

七、在Springboot中配置拦截器和数据源

1. 拦截器配置

(1)新建拦截类,实现HandlerInterceptor所有的方法,比如preHandler(),postHandler(),afterCompletion(),告知controller之前还是之后执行拦截,以及需要告知前端,拦截的错误信息;

(2)新建配置类,实现WebMvcConfigurer接口,实现addInterceptors(),将拦截器以bean的形式注册到Spring中,并添加需要检测的接口路径;

2. 拦截器和过滤器

(1)过滤器:过滤器是对所有请求信息的过滤,并且一个过滤器只能在容器初始化时调用一次;

(2)拦截器:拦截器是对某些特定请求的拦截,一个拦截器实例在一个controller生命周期之内可以多次调用;

3. Druid数据源配置

(1)在pom.xml中添加依赖;

(2)在application.properties中添加配置信息,比如数据库访问配置(数据可库驱动,数据库URL等),连接池设置(poolSize,连接存活时间等)和其他配置;

(3)新建配置类,实现WebMvcConfigurer接口,实现addResourceHandlers()的方法,扫描配置信息,创建数据库连接。主从,可以额外用一个@DependeOn注解。

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值