spring
一、Spring的介绍及核心
1、spring的介绍
More ActionsSpring简介:轻量级框架,是一个容器 作用:Bean工厂,用来管理Bean的生命周期和框架集成。 Spring详细介绍:Spring框架是一个轻量级的开源框架,是核心容器、数据访问与集成、AOP、Web、消息、测试六个模块的集成,主要是为了简化企业级应用的后台开发,降低耦合性。平时接触到最多的还是IoC和AOP两个特性。IoC指的是控制反转,把对象的创建和依赖关系的维护交给Spring容器去管理。Spring通过工厂模式、反射机制等技术管理对象的作用域和生命周期。AoP一般称为面向切面编程,是面向对象的一种补充,将程序中独立于其他功能的方法抽取出来,使Java开发模块化,仅需专注于主业务即可 |
---|
2、spring的核心
①. IOC/DI(控制反转/依赖注入) :把dao层依赖注入到service层,service层反转给controller层,Spring顶层容器为BeanFactory。 ②. AOP:面向切面编程 |
---|
3、Spring由哪些模块组成?
①、spring core:提供了框架的基本组成部分,包括控制反转(Inversion of Control,IOC)和依赖注入(Dependency Injection,DI)功能。 ②、spring beans:提供了BeanFactory,是工厂模式的一个经典实现,Spring将管理对象称为Bean。 ③、spring context:构建于 core 封装包基础上的 context 封装包,提供了一种框架式的对象访问方法。 ④、spring jdbc:提供了一个JDBC的抽象层,消除了烦琐的JDBC编码和数据库厂商特有的错误代码解析, 用于简化JDBC。 ⑤、spring aop:提供了面向切面的编程实现,让你可以自定义拦截器、切点等。 ⑥、spring Web:提供了针对 Web 开发的集成特性,例如文件上传,利用 servlet listeners 进行 ioc 容器初始化和针对 Web 的 ApplicationContext。 ⑦、spring test:主要为测试提供支持的,支持使用JUnit或TestNG对Spring组件进行单元测试和集成测试。 |
---|
4、Spring 框架中都用到了哪些设计模式?
①、工厂模式:BeanFactory就是简单工厂模式的体现,用来创建对象的实例; ②、单例模式:Bean默认为单例模式。 ③、代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术; ④、模板方法:用来解决代码重复的问题。比如. RestTemplate, JmsTemplate, JpaTemplate。 ⑤、观察者模式:定义对象键中一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知被制动更新,如Spring中listener的实现–ApplicationListener。 |
---|
5、Spring的优缺点是什么?
优点: |
---|
方便解耦,简化开发 |
可以将对象的创建和依赖关系的维护交给Spring管理。 |
提供面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等功能。 |
声明式事务的支持 |
只需要通过配置就可以完成对事务的管理,而无需手动编程。 |
可以通过注解方便的测试程序。 |
内部提供了对各种优秀框架的直接支持,方便集成各种优秀框架。 |
降低 JavaEE API 的使用难度 |
缺点: |
Spring明明一个很轻量级的框架,却给人感觉大而全 |
Spring依赖反射,反射影响性能 |
使用门槛过高,入门Spring需要较长时间 |
二、Spring控制反转、依赖注入(IOC、DI)
1、什么是IOC,什么又是DI,他们有什么区别?
IOC(控制反转)介绍: |
---|
创建对象实例的控制权从代码控制剥离到IOC容器控制(之前的写法,由程序代码直接操控使用new关键字),实际就是你在xml文件控制,控制权的转移是所谓反转,侧重于原理。 |
DI介绍(依赖注入): |
创建对象实例时,为这个对象注入属性值或其它对象实例,侧重于实现。 |
区别: |
1.它们是spring核心思想的不同方面的描述。 |
2.依赖注入和控制反转是对同一件事情的不同描述,从某个方面讲,就是它们描述的角度不同。 |
2、IOC的作用与原理?
IoC 的实现原理,就是工厂模式加反射机制。 |
---|
可以把IOC容器看作是一个工厂,这个工厂里要生产的对象都在配置文件中给出定义,然后利用编程语言的的反射编程,根据配置文件中给出的类名生成相应的对象。从实现来看,IOC是把以前在工厂方法里写死的对象生成代码,改变为由配置文件来定义,也就是把工厂和对象生成这两者独立分隔开来,目的就是提高灵活性和可维护性 |
IoC 的作用 |
①、管理对象的创建和依赖关系的维护。对象的创建并不是一件简单的事,在对象关系比较复杂时,如果依赖关系需要程序猿来维护的话,那是相当头疼的 ②、解耦,由容器去维护具体的对象 ③、托管了类的产生过程,比如我们需要在类的产生过程中做一些处理,最直接的例子就是代理,如果有容器程序可以把这部分处理交给容器,应用程序则无需去关心类是如何完成代理的 |
3、IOC的优点是什么?
①、IOC 或 依赖注入把应用的代码量降到最低。 ②、它使应用容易测试,单元测试不再需要单例和JNDI查找机制。 ③、 最小的代价和最小的侵入性使松散耦合得以实现。 ④、IOC容器支持加载服务时的饿汉式初始化和懒加载。 |
---|
4、BeanFactory 和 ApplicationContext有什么区别?
BeanFactory和ApplicationContext是Spring的两大核心接口,都可以当做Spring的容器。其中ApplicationContext是BeanFactory的子接口 |
---|
依赖关系: |
BeanFactory:是Spring里面最底层的接口,包含了各种Bean的定义,读取bean配置文档,管理bean的加载、实例化,控制bean的生命周期,维护bean之间的依赖关系 |
ApplicationContext接口作为BeanFactory的派生,除了提供BeanFactory所具有的功能外,还提供了更完整的框架功能: |
①、继承MessageSource,因此支持国际化。 ②、统一的资源文件访问方式。 ③、提供在监听器中注册bean的事件。 ④、同时加载多个配置文件。 ⑤、载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层。 |
加载方式: |
**BeanFactroy:**采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化。这样,我们就不能发现一些存在的Spring的配置问题。如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次使用调用getBean方法才会抛出异常。 |
**ApplicationContext:**它是在容器启动时,一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误,这样有利于检查所依赖属性是否注入。 ApplicationContext启动后预载入所有的单实例Bean,通过预载入单实例bean ,确保当你需要的时候,你就不用等待,因为它们已经创建好了 相对于基本的BeanFactory,ApplicationContext 唯一的不足是占用内存空间。当应用程序配置Bean较多时,程序启动较慢。 |
创建方式: |
BeanFactory通常以编程的方式被创建,ApplicationContext还能以声明的方式创建,如使用ContextLoader。 |
注册方式: |
BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册。 |
5、Spring 如何设计容器的,BeanFactory和ApplicationContext的关系详解
BeanFactory容器和ApplicationContext容器: |
---|
BeanFactory 简单粗暴,可以理解为就是个 HashMap,Key 是 BeanName,Value 是 Bean 实例。通常只提供注册(put),获取(get)这两个功能。我们可以称之为 “低级容器”。 |
ApplicationContext 可以称之为 “高级容器”。因为他比 BeanFactory 多了更多的功能。他继承了多个接口。因此具备了更多的功能。例如资源的获取,支持多种消息(例如 JSP tag 的支持),对 BeanFactory 多了工具级别的支持等待。所以你看他的名字,已经不是 BeanFactory 之类的工厂了,而是 “应用上下文”, 代表着整个大容器的所有功能。该接口定义了一个 refresh 方法,此方法是所有阅读 Spring 源码的人的最熟悉的方法,用于刷新整个容器,即重新加载/刷新所有的 bean。 |
6、 IoC 的实现步骤
基于BeanFactory实现: |
---|
①、加载配置文件,解析成 BeanDefinition 放在 Map 里。 ②、调用 getBean 的时候,从 BeanDefinition 所属的 Map 里,拿出 Class 对象进行实例化,同时,如果有依赖关系,将递归调用 getBean 方法 —— 完成依赖注入。 |
基于ApplicationContext实现: |
至于高级容器 ApplicationContext,他包含了低级容器的功能,当他执行 refresh 模板方法的时候,将刷新整个容器的 Bean。同时其作为高级容器,包含了太多的功能。一句话,他不仅仅是 IoC。他支持不同信息源头,支持 BeanFactory 工具类,支持层级容器,支持访问文件资源,支持事件发布通知,支持接口回调等等。 |
7、DI依赖注入的基本原则
依赖注入的基本原则是:应用组件不应该负责查找资源或者其他依赖的协作对象。配置对象的工作应该由IoC容器负责,“查找资源”的逻辑应该从应用组件的代码中抽取出来,交给IoC容器负责。容器全权负责组件的装配,它会把符合依赖关系的对象通过属性(JavaBean中的setter)或者是构造器传递给需要的对象。 |
---|
8、有哪些不同类型的依赖注入实现方式?
依赖注入是时下最流行的IoC实现方式,依赖注入分为接口注入(Interface Injection),Setter方法注入(Setter Injection)和构造器注入(Constructor Injection)三种方式。其中接口注入由于在灵活性和易用性比较差,现在从Spring4开始已被废弃。 构造器依赖注入:构造器依赖注入通过容器触发一个类的构造器来实现的,该类有一系列参数,每个参数代表一个对其他类的依赖。 Setter方法注入:Setter方法注入是容器通过调用无参构造器或无参static工厂 方法实例化bean之后,调用该bean的setter方法,即实现了基于setter的依赖注入。 |
---|
9、构造器依赖注入和 Setter方法注入的区别
构造函数注入 | setter 注入 |
---|---|
没有部分注入 | 有部分注入 |
不会覆盖 setter 属性 | 会覆盖 setter 属性 |
任意修改都会创建一个新实例 | 任意修改不会创建一个新实例 |
适用于设置很多属性 | 适用于设置少量属性 |
两种依赖方式都可以使用,构造器注入和Setter方法注入。最好的解决方案是用构造器参数实现强制依赖,setter方法实现可选依赖。 |
---|
三、Spring面向切面编程(AOP)
1、什么是AOP?
OOP(Object-Oriented Programming)面向对象编程,允许开发者定义纵向的关系,但并适用于定义横向的关系,导致了大量代码的重复,而不利于各个模块的重用。 |
---|
AOP(Aspect-Oriented Programming),一般称为面向切面编程,作为面向对象的一种补充,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为“切面”(Aspect),减少系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。可用于权限认证、日志、事务处理等。 |
2、静态代理和动态代理区别?
AOP实现的关键在于 代理模式,AOP代理主要分为静态代理和动态代理。静态代理的代表为AspectJ;动态代理则以Spring AOP为代表。 |
---|
(1)AspectJ是静态代理的增强,所谓静态代理,就是AOP框架会在编译阶段生成AOP代理类,因此也称为编译时增强,他会在编译阶段将AspectJ(切面)织入到Java字节码中,运行的时候就是增强之后的AOP对象。 |
(2)Spring AOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改字节码,而是每次运行时在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。 |
3、JDK动态代理和CGLIB动态代理的区别
Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理: |
---|
JDK动态代理只提供接口的代理,不支持类的代理。核心InvocationHandler接口和Proxy类,InvocationHandler 通过invoke()方法反射来调用目标类中的代码,动态地将横切逻辑和业务编织在一起;接着,Proxy利用 InvocationHandler动态创建一个符合某一接口的的实例, 生成目标类的代理对象。 如果代理类没有实现 InvocationHandler 接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。 |
CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成指定类的一个子类对象,并覆盖其中特定方法并添加增强代码,从而实现AOP。CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。 |
静态代理与动态代理区别在于生成AOP代理对象的时机不同,相对来说AspectJ的静态代理方式具有更好的性能,但是AspectJ需要特定的编译器进行处理,而Spring AOP则无需特定的编译器处理。 |
4、解释一下Spring AOP里面的几个名词
(1)切面(Aspect):切面是通知和切点的结合。通知和切点共同定义了切面的全部内容。 在Spring AOP中,切面可以使用通用类(基于模式的风格) 或者在普通类中以 @AspectJ 注解来实现。 |
---|
(2)连接点(Join point):指方法,在Spring AOP中,一个连接点 总是 代表一个方法的执行。 应用可能有数以千计的时机应用通知。这些时机被称为连接点。连接点是在应用执行过程中能够插入切面的一个点。这个点可以是调用方法时、抛出异常时、甚至修改一个字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为。 |
(3)通知(Advice):在AOP术语中,切面的工作被称为通知。 |
(4)切入点(Pointcut):切点的定义会匹配通知所要织入的一个或多个连接点。我们通常使用明确的类和方法名称,或是利用正则表达式定义所匹配的类和方法名称来指定这些切点。 |
(5)引入(Introduction):引入允许我们向现有类添加新方法或属性。 |
(6)目标对象(Target Object): 被一个或者多个切面(aspect)所通知(advise)的对象。它通常是一个代理对象。也有人把它叫做 被通知(adviced) 对象。 既然Spring AOP是通过运行时代理实现的,这个对象永远是一个 被代理(proxied) 对象。 |
(7)织入(Weaving):织入是把切面应用到目标对象并创建新的代理对象的过程。在目标对象的生命周期里有多少个点可以进行织入: 编译期: 切面在目标类编译时被织入。AspectJ的织入编译器是以这种方式织入切面的。 类加载期:切面在目标类加载到JVM时被织入。需要特殊的类加载器,它可以在目标类被引入应用之前增强该目标类的字节码。AspectJ5的加载时织入就支持以这种方式织入切面。 运行期: 切面在应用运行的某个时刻被织入。一般情况下,在织入切面时,AOP容器会为目标对象动态地创建一个代理对象。SpringAOP就是以这种方式织入切面。 |
6、Spring在运行时如何通知对象
通过在代理类中包裹切面,Spring在运行期把切面织入到Spring管理的bean中。代理封装了目标类,并拦截被通知方法的调用,再把调用转发给真正的目标bean。当代理拦截到方法调用时,在调用目标bean方法之前,会执行切面逻辑。 |
---|
直到应用需要被代理的bean时,Spring才创建代理对象。如果使用的是ApplicationContext的话,在ApplicationContext从BeanFactory中加载所有bean的时候,Spring才会创建被代理的对象。因为Spring运行时才创建代理对象,所以我们不需要特殊的编译器来织入SpringAOP的切面。 |
7、Spring(通知)advice有哪些类型?
在AOP术语中,切面的工作被称为通知,实际上是程序执行时要通过SpringAOP框架触发的代码段。 |
---|
Spring切面可以应用5种类型的通知: |
前置通知(Before):在目标方法被调用之前调用通知功能; |
后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么; |
返回通知(After-returning ):在目标方法成功执行之后调用通知; |
异常通知(After-throwing):在目标方法抛出异常后调用通知; |
环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。 同一个(切面)aspect,不同(通知)advice的执行顺序: ①没有异常情况下的执行顺序: around before advice before advice target method 执行 around after advice after advice afterReturning ②有异常情况下的执行顺序: around before advice before advice target method 执行 around after advice after advice afterThrowing:异常发生 java.lang.RuntimeException: 异常发生 |
8、什么是切面 Aspect?
aspect 由 pointcount 和 advice 组成,切面是通知和切点的结合。 它既包含了横切逻辑的定义, 也包括了连接点的定义. Spring AOP 就是负责实施切面的框架, 它将切面所定义的横切逻辑编织到切面所指定的连接点中。 |
---|
AOP 的工作重心在于如何将增强编织目标对象的连接点上, 这里包含两个工作: 如何通过 pointcut 和 advice 定位到特定的 joinpoint 上 如何在 advice 中编写切面代码. 可以简单地认为, 使用 @Aspect 注解的类就是切面。 |
四、Spring的事务
1、事物的定义
事务是指多个操作单元组成的合集,多个单元操作是整体不可分割的,要么都操作不成功,要么都成功。其必须遵循四个原则(ACID)。 |
---|
原子性(Atomicity):即事务是不可分割的最小工作单元,事务内的操作要么全做,要么全不做; |
一致性(Consistency):在事务执行前数据库的数据处于正确的状态,而事务执行完成后数据库的数据还是应该处于正确的状态,即数据完整性约束没有被破坏;如银行转帐,A转帐给B,必须保证A的钱一定转给B,一定不会出现A的钱转了但B没收到,否则数据库的数据就处于不一致(不正确)的状态。 |
隔离性(Isolation):并发事务执行之间互不影响,在一个事务内部的操作对其他事务是不产生影响,这需要事务隔离级别来指定隔离性; |
持久性(Durability):事务一旦执行成功,它对数据库的数据的改变必须是永久的,不会因比如遇到系统故障或断电造成数据不一致或丢失。 |
2、Spring支持的事务管理类型, spring 事务实现方式有哪些?
Spring支持两种类型的事务管理: |
---|
编程式事务管理:这意味你通过编程的方式管理事务,给你带来极大的灵活性,但是难维护。 |
声明式事务管理:这意味着你可以将业务代码和事务管理分离,你只需用注解和XML配置来管理事务。 |
3、Spring事务的实现方式和实现原理
Spring事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring是无法提供事务功能的。真正的数据库层的事务提交和回滚是通过binlog或者redo log实现的。 |
---|
(1)划分处理单元——IOC:由于spring解决的问题是对单个数据库进行局部事务处理的,具体的实现首先用spring中的IOC划分了事务处理单元。并且将对事务的各种配置放到了ioc容器中(设置事务管理器,设置事务的传播特性及隔离机制。 |
(2)AOP拦截需要进行事务处理的类:Spring事务处理模块是通过AOP功能来实现声明式事务处理的,具体操作(比如事务实行的配置和读取,事务对象的抽象),用TransactionProxyFactoryBean接口来使用AOP功能,生成proxy代理对象,通过TransactionInterceptor完成对代理方法的拦截,将事务处理的功能编织到拦截的方法中。读取ioc容器事务配置属性,转化为spring事务处理需要的内部数据结构(TransactionAttributeSourceAdvisor),转化为TransactionAttribute表示的数据对象。 |
(3)对事物处理实现(事务的生成、提交、回滚、挂起): spring委托给具体的事务处理器实现。实现了一个抽象和适配。适配的具体事务处理器:DataSource数据源支持、hibernate数据源事务处理支持、JDO数据源事务处理支持,JPA、JTA数据源事务处理支持。这些支持都是通过设计PlatformTransactionManager、AbstractPlatforTransaction一系列事务处理的支持。为常用数据源支持提供了一系列的TransactionManager。 |
(4)结合:PlatformTransactionManager实现了TransactionInterception接口,让其与TransactionProxyFactoryBean结合起来,形成一个Spring声明式事务处理的设计体系。 |
4、spring 的事务隔离
spring 有五大隔离级别,默认值为 ISOLATION_DEFAULT(使用数据库的设置),其他四个隔离级别和数据库的隔离级别一致: |
---|
1. ISOLATION_DEFAULT(默认):用底层数据库的默认隔离级别,数据库管理员设置什么就是什么 |
2. ISOLATION_READ_UNCOMMITTED(未提交读):最低隔离级别、事务未提交前,就可被其他事务读取(会出现幻读、脏读、不可重复读) |
3. ISOLATION_READ_COMMITTED(提交读):一个事务提交后才能被其他事务读取到(该隔离级别禁止其他事务读取到未提交事务的数据、所以还是会造成幻读、不可重复读)、Oracle、sql server默认级别 |
4. ISOLATION_REPEATABLE_READ(可重复读):可重复读,保证多次读取同一个数据时,其值都和事务开始时候的内容是一致,禁止读取到别的事务未提交的数据(该隔离基本可防止脏读,不可重复读(重点在修改),但会出现幻读(重点在增加与删除))(MySql默认级别,更改可通过set transaction isolation level 级别) |
5. ISOLATION_SERIALIZABLE(序列化):代价最高最可靠的隔离级别(该隔离级别能防止脏读、不可重复读、幻读) |
丢失更新:两个事务同时更新一行数据,最后一个事务的更新会覆盖掉第一个事务的更新,从而导致第一个事务更新的数据丢失,这是由于没有加锁造成的; |
幻读:同样的事务操作过程中,不同时间段多次(不同事务)读取同一数据,读取到的内容不一致(一般是行数变多或变少)。 |
脏读:一个事务读取到另外一个未提及事务的内容,即为脏读。 |
不可重复读:同一事务中,多次读取内容不一致(一般行数不变,而内容变了)。 |
5、Spring的事务传播行为
spring事务的传播行为说的是当多个事务同时存在的时候,一个方法调用另一个方法时,事务该如何操作。 |
---|
① PROPAGATION_REQUIRED:如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置。 |
② PROPAGATION_SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。 |
③ PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。 |
④ PROPAGATION_REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建新事务。 |
⑤ PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。<b |
⑥ PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。 |
⑦ PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则按REQUIRED属性执行。 |
6、Spring框架的事务管理有哪些优点?
它为不同的事务API 如 JTA,JDBC,Hibernate,JPA 和JDO,提供一个不变的编程模式。 |
---|
它为编程式事务管理提供了一套简单的API而不是一些复杂的事务API如 |
它支持声明式事务管理。 |
它和Spring各种数据访问抽象层很好得集成。 |
7、如何利用注解来实现声明式事务?
环境搭建: |
---|
1、导入相关依赖:数据源、数据库驱动、Spring-jdbc模块 |
2、配置数据源、JdbcTemplate(Spring提供的简化数据库操作的工具)操作数据 |
3、给方法上标注 @Transactional 表示当前方法是一个事务方法 |
4、 @EnableTransactionManagement 开启基于注解的事务管理功能(@EnableXXX 开启某一个功能) |
5、配置事务管理器来控制事务; |
事务原理: |
@EnableTransactionManagement会生成两个组件;这两个组件会利用后置处理器机制在对象创建以后,包装对象,返回一个代理对象(增强器),代理对象执行方法利用拦截器链进行调用。 |
8、你更倾向用那种事务管理类型?
大多数Spring框架的用户选择声明式事务管理,因为它对应用代码的影响最小,因此更符合一个无侵入的轻量级容器的思想。声明式事务管理要优于编程式事务管理,虽然比编程式事务管理(这种方式允许你通过代码控制事务)少了一点灵活性。唯一不足地方是,最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。
五、Spring的注解
1、什么是基于Java的Spring注解配置?
基于Java的配置,允许你在少量的Java注解的帮助下,进行你的大部分Spring配置而非通过XML文件。 以@Configuration 注解为例,它用来标记类可以当做一个bean的定义,被Spring IOC容器使用。@Bean注解,它表示此方法将要返回一个对象,作为一个bean注册进Spring应用上下文。
2、 怎样开启注解装配?
注解装配在默认情况下是不开启的,为了使用注解装配,我们必须在Spring配置文件中配置 context:annotation-config/元素。
3、@ComponentScans、@Scop、@Lazy、@Conditional注解
@ComponentScans:ComponentScans指定扫描的包,然后获取出来的就是这个包下,所有标注有@component、@controller、@service、@repository等注解的类。 |
---|
@Scope:Scope注解 调整作用域(prototype:多实例;singleton:单实例,request:同一次请求创建一个实例,session:同一个session创建一个实例) |
@Lazy懒加载:单实例bean:默认在容器启动的时候创建对象;懒加载:容器启动不创建对象。第一次使用(获取)Bean创建对象,并初始化; |
@Conditional注解:按照一定的条件进行判断,满足条件给容器中注册bean。首先要自己写一个类,继承Condition,并实现match方法。ImportSelector可以自己定义导入哪些类,返回一个数组,返回要导入的类的全类名,需要实现ImportSelector接口。 |
4、@Component, @Controller, @Repository, @Service 有何区别?
@controller、@service、@repository都是@component的派生类 |
---|
@Component:这将 java 类标记为 bean。它是任何 Spring 管理组件的通用构造型。spring 的组件扫描机制现在可以将其拾取并将其拉入应用程序环境中。 |
@Controller:这将一个类标记为 Spring Web MVC 控制器。标有它的 Bean 会自动导入到 IoC 容器中。 |
@Service:此注解是组件注解的特化。它不会对 @Component 注解提供任何其他行为。您可以在服务层类中使用 @Service 而不是 @Component,因为它以更好的方式指定了意图。 |
@Repository:这个注解是具有类似用途和功能的 @Component 注解的特化。它为 DAO 提供了额外的好处。它将 DAO 导入 IoC 容器,并使未经检查的异常有资格转换为 Spring DataAccessException。 |
5、@Required 注解有什么作用
这个注解表明bean的属性必须在配置的时候设置,通过一个bean定义的显式的属性值或通过自动装配,若@Required注解的bean属性未被设置,容器将抛出BeanInitializationException 。
6、@Autowired 注解有什么作用
@Autowired默认是按照类型装配注入的,默认情况下它要求依赖对象必须存在(可以设置它required属性为false)。@Autowired 注解提供了更细粒度的控制,包括在何处以及如何完成自动装配。它的用法和@Required一样,修饰setter方法、构造器、属性或者具有任意名称和/或多个参数的PN方法。
7、@Autowired自动注入的原理
1)、默认优先按照类型去容器中找对应的组件:applicationContext.getBean(BookDao.class);找到就赋值 |
---|
2)、如果找到多个相同类型的组件,再将属性的名称作为组件的id去容器中查找applicationContext.getBean(“bookDao”) |
3)、@Qualifier(“bookDao”):使用@Qualifier指定需要装配的组件的id,而不是使用属性名,优先级最高 |
4)、自动装配默认一定要将属性赋值好,没有就会报错;可以使用@Autowired(required=false),这样有的话就会进行赋值,没有的话就会忽略 |
5)、@Primary:让Spring进行自动装配的时候,默认使用首选的bean;也可以继续使用@Qualifier指定需要装配的bean的名字;@Primary与@Qualifier同时存在的时候,优先会取@Qualifier |
8、@Autowired和@Resource之间的区别
1、共同点:两者都可以写在字段和setter方法上。两者如果都写在字段上,那么就不需要再写setter方法。 |
---|
2、不同点: |
(1)@Autowired |
@Autowired为Spring提供的注解,需要导入包org.springframework.beans.factory.annotation.Autowired;只按照byType注入。 |
(2)@Resource |
@Resource默认按照ByName自动注入,由J2EE提供,需要导入包javax.annotation.Resource。@Resource有两个重要的属性:name和type,而Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以,如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不制定name也不制定type属性,这时将通过反射机制使用byName自动注入策略。 |
9、@Qualifier 注解作用
当您创建多个相同类型的 bean 并希望仅使用属性装配其中一个 bean 时,您可以使用@Qualifier 注解和 @Autowired 通过指定应该装配哪个确切的 bean 来消除歧义。
10、@RequestMapping 注解作用
@RequestMapping 注解用于将特定 HTTP 请求方法映射到将处理相应请求的控制器中的特定类/方法。此注释可应用于两个级别:
类级别:映射请求的 URL
方法级别:映射 URL 以及 HTTP 请求方法
六、Spring Beans
1、什么是Spring beans?
Spring beans 是那些形成Spring应用的主干的java对象。它们被Spring IOC容器初始化,装配,和管理。这些beans通过容器中配置的元数据创建。比如,以XML文件中 的形式定义。
2、一个 Spring Bean 定义 包含什么?
一个Spring Bean 的定义包含容器必知的所有配置元数据,包括如何创建一个bean,它的生命周期详情及它的依赖。
3、Spring的三种配置方式
这里有三种重要的方法给Spring 容器提供配置元数据。 |
---|
XML配置文件。 |
基于注解的配置。 |
基于java的配置。 |
4、Spring配置文件包含了哪些信息
Spring配置文件是个XML 文件,这个文件包含了类信息,描述了如何配置它们,以及如何相互调用。
5、Spring基于xml注入bean的几种方式
Set方法注入; |
---|
构造器注入:①通过index设置参数的位置;②通过type设置参数类型; |
静态工厂注入; |
实例工厂; |
6、你怎样定义类的作用域?
当定义一个 在Spring里,我们还能给这个bean声明一个作用域。它可以通过bean 定义中的scope属性来定义。如,当Spring要在需要的时候每次生产一个新的bean实例,bean的scope属性被指定为prototype。另一方面,一个bean每次使用的时候必须返回同一个实例,这个bean的scope 属性 必须设为 singleton。
7、Spring的5种bean的作用域
singleton (默认): bean在每个Spring ioc 容器中只有一个实例。 |
---|
prototype:一个bean的定义可以有多个实例。 |
request:每次http请求都会创建一个bean,该作用域仅在基于web的Spring ApplicationContext情形下有效。 |
session:在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。 |
global-session:在一个全局的HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。 |
注意: 缺省的Spring bean 的作用域是Singleton。使用 prototype 作用域需要慎重的思考,因为频繁创建和销毁 bean 会带来很大的性能开销。 |
8、Spring框架中的单例Beans是线程安全的么?
Spring框架并没有对单例bean进行任何多线程的封装处理。关于单例bean的线程安全和并发问题需要开发者自行去搞定。但实际上,大部分的Spring bean并没有可变的状态(比如Servie类和DAO类),所以在某种程度上说Spring的单例bean是线程安全的。如果你的bean有多种状态的话(比如 View Model 对象),就需要自行保证线程安全。(最简单的就是改变 bean 的作用域,把“singleton”变更为“prototype”,这样请求 bean 相当于 new Bean()了,所以就可以保证线程安全了)。
9、Spring如何处理线程并发问题?
Spring使用ThreadLocal解决线程安全问题。 |
---|
我们知道在一般情况下,只有有状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域。就是因为Spring对一些Bean(如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等)中非线程安全状态采用ThreadLocal进行处理,让它们也成为线程安全的状态,因为有状态的Bean就可以在多线程中共享了。 |
ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。 |
(1)在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。这时该变量是多个线程共享的,使用同步机制要求程序慎密地分析什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放对象锁等繁杂的问题,程序设计和编写难度相对较大。 |
(2)而ThreadLocal则从另一个角度来解决多线程的并发访问。ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。 |
(3)概括起来说,对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。 |
10、Spring框架中bean的生命周期
创建-初始化-调用-销毁 |
---|
\1.Spring 容器根据配置中的 bean 定义中实例化 bean。 |
\2. Spring 使用依赖注入填充所有属性,如 bean 中所定义的配置。 |
\3. 如果 bean 实现 BeanNameAware 接口,则工厂通过传递 bean 的 ID 来调用 setBeanName()。 |
\4. 如果 bean 实现 BeanFactoryAware 接口,工厂通过传递自身的实例来调用 setBeanFactory()。 |
\5. 如果存在与 bean 关联的任何 BeanPostProcessors,则调用 postProcessBeforeInitialization() 方法。 |
\6. 如果为 bean 指定了 init 方法( 的 init-method 属性),那么将调用它。 |
\7. 最后,如果存在与 bean 关联的任何 BeanPostProcessors,则将调用 postProcessAfterInitialization() 方法。 |
\8. 如果 bean 实现 DisposableBean 接口,当 spring 容器关闭时,会调用 destory()。 |
\9. 如果为 bean 指定了 destroy 方法( 的 destroy-method 属性),那么将调用它。 |
11、什么是bean装配?
装配,或bean 装配是指在Spring 容器中把bean组装到一起,前提是容器需要知道bean的依赖关系,如何通过依赖注入来把它们装配到一起。
12、什么是bean的自动装配?
在Spring框架中,在配置文件中设定bean的依赖关系是一个很好的机制,Spring 容器能够自动装配相互合作的bean,这意味着容器不需要和配置,能通过Bean工厂自动处理bean之间的协作。这意味着 Spring可以通过向Bean Factory中注入的方式自动搞定bean之间的依赖关系。自动装配可以设置在每个bean上,也可以设定在特定的bean上。
13、解释不同方式的自动装配,spring 自动装配 bean 有哪些方式?
在spring中,对象无需自己查找或创建与其关联的其他对象,由容器负责把需要相互协作的对象引用赋予各个对象,使用autowire来配置自动装载模式。 |
---|
no:默认的方式是不进行自动装配的,通过手工设置ref属性来进行装配bean。 |
byName:通过bean的名称进行自动装配,如果一个bean的 property 与另一bean 的name 相同,就进行自动装配。 |
byType:通过参数的数据类型进行自动装配。 |
constructor:利用构造函数进行装配,并且构造函数的参数通过byType进行装配。 |
autodetect:自动探测,如果有构造方法,通过 construct的方式自动装配,否则使用 byType的方式自动装配。 |
14、使用@Autowired注解自动装配的过程是怎样的?
使用@Autowired注解来自动装配指定的bean。在使用@Autowired注解之前需要在Spring配置文件进行配置,<context:annotation-config />。 |
---|
①、在启动spring IoC时,容器自动装载了一个AutowiredAnnotationBeanPostProcessor后置处理器, ②、当容器扫描到@Autowied、@Resource或@Inject时,就会在IoC容器自动查找需要的bean,并装配给该对象的属性。 ③、在使用@Autowired时,首先在容器中查询对应类型的bean: 如果查询结果刚好为一个,就将该bean装配给@Autowired指定的数据; 如果查询的结果不止一个,那么@Autowired会根据名称来查找; 如果上述查找的结果为空,那么会抛出异常。解决方法时,使用required=false。 |
15、自动装配有哪些局限性?
重写:你仍需用 和 配置来定义依赖,意味着总要重写自动装配。 |
---|
基本数据类型:你不能自动装配简单的属性,如基本数据类型,String字符串,和类。 |
模糊特性:自动装配不如显式装配精确,如果有可能,建议使用显式装配。 |
七、SpringBoot
1、什么是springboot?
Springboot是spring的子项目,称为一站式解决方案,集成了外部很多的优秀的框架,如常用的mysql、jdbc。约定大于配置 |
---|
主要优点:通过maven导入各种jar包,可以减少jar包的冲突;屏息了繁琐的xml配置文件;集成的开发框架,可以做到开箱即用 |
2、Spring Boot 提供了哪些核心功能?
1、独立运行 Spring 项目 |
---|
2、内嵌 Servlet 容器:Spring Boot 可以选择内嵌 Tomcat、Jetty 或者 Undertow,这样我们无须以 war 包形式部署项目。 |
3、提供 Starter 简化 Maven 配置:例如,当你使用了 spring-boot-starter-web ,会自动加入如下依赖:spring-boot-starter-web 的 pom 文件 |
4、自动配置 Spring Bean:Spring Boot 检测到特定类的存在,就会针对这个应用做一定的配置,进行自动配置 Bean ,这样会极大地减少我们要使用的配置。 |
5、准生产的应用监控:Spring Boot 提供基于 HTTP、JMX、SSH 对运行时的项目进行监控。 |
6、无代码生成和 XML 配置:Spring Boot 没有引入任何形式的代码生成,它是使用的 Spring 4.0 的条件 @Condition 注解以实现根据条件进行配置。同时使用了 Maven /Gradle 的依赖传递解析机制来实现 Spring 应用里面的自动配置。 |
3、SpringBoot中如何配置bean
配置bean有下面几种方式,(2)和(3)是SpringBoot配置bean的主要方式: |
---|
(1)基于xml |
(2)基于java代码:用于代替xml,适用那种从传统xml配置项目迁移到SpringBoot项目(如MyBatisAutoConfiguration) |
(3)基于注解 ①传统注解(@Autowired) ②条件依赖注解(starter就是以这种方式) 条件依赖注解(SpringBoot独有): @ConditionalOnClass/@ConditionalOnBean:有某个class/bean存在时才生效 @AutoConfigureAfter指定顺序等 |
4、SpringBoot的核心配置文件和核心注解
1.SpringBoot的核心配置文件是application.properties/application.yml和bootstrap 配置文件 |
---|
①、application 配置文件: 主要用于 Spring Boot 项目的自动化配置。 |
②、bootstrap 配置文件有以下几个应用场景: 使用 Spring Cloud Config 配置中心时,这时需要在 bootstrap 配置文件中增加连接到配置中心的配置属性来加载外部配置中心的配置信息; 少量固定的不能被覆盖的属性; 少量加密/解密的场景; |
YAML配置文件:YAML是一种人类可读的数据序列化语言。它通常用于配置文件。与属性文件相比,如果我们想要在配置文件中添加复杂的属性,YAML文件就更加结构化,而且更少混淆。可以看出YAML具有分层配置数据。 |
2.SpringBoot的核心注解: |
(1)@SpringBootApplication底层由下面三个注解组成: @SpringBootConfiguration+@EnableAutoConfiguration+@ComponentScan |
(2)@SpringBootConfiguration: 底层是@Configuration,用来代替xml配置如applicationContext.xml。(传统Spring项目中applicationContext.xml主要用于配置Bean/SqlSessionFactory/Aop事务这些。) |
(3)@EnableAutoConfiguration: 自动配置注解,开启后SpringBoot就能根据当前路径下的包(如.Jar)或者类来配置Spring Bean。 |
(4)@ComponentScan: 开启组件扫描,扫描@Compement下的bean实例,将@Controller 、@Service、@Component、@Repository加载到容器中。 |
5、SpringBoot自动配置原理
SpringBoot的自动配置使用@EnableAutoConfiguration注释实现,是通过找到spring.factories配置文件中的所有XXXAutoConfiguration的自动配置类,并将autoconfigure-metadata.properties中含@ConditionalOnClass@AutoConfigureAfter的配置类过滤掉,然后通过@bean注册,@Configuration加载到容器,实现这些类的自动配置。
6、SpringBoot Stater
①.实现原理: |
---|
SpringBoot Stater是一个集成接合器,用于引入模块所需的相关jar包和自动配置各自模块所需的属性。spring-boot启动的时候会找到starter jar包中的resources/META-INF/spring.factories文件,根据spring.factories文件中的配置,找到需要自动配置的类。通过@EnableAutoConfiguration、@ConfigurationProperties实现,使用ConfigurationProperties保存配置,且每个配置都有一个默认值,所有配置属性聚集到application.properties。 |
(2).实现: |
启动器是一套方便的依赖描述符,它可以放在自己的程序中。你可以一站式的获取你所需要的 Spring 和相关技术,而不需要依赖描述符的通过示例代码搜索和复制黏贴的负载。例如,如果你想使用 Sping 和 JPA 访问数据库,只需要你的项目包含 spring-boot-starter-data-jpa 依赖项,你就可以完美进行。 |
springboot常用的starter有哪些? |
spring-boot-starter-web 嵌入tomcat和web开发需要servlet与jsp支持 spring-boot-starter-data-jpa 数据库支持 spring-boot-starter-data-redis redis数据库支持 spring-boot-starter-data-solr solr支持 mybatis-spring-boot-starter 第三方的mybatis集成starter |
7、SpringBoot Actuator(监视器)
Spring boot actuator是spring启动框架中的重要功能之一,对程序进行监控管理,如实时查看服务器的CPU/IO,核心是端点EndPoint,内置了health/info/beans/httptrace/showdown等端点,也可以自定义端点。
8、Spring Boot 如何定义多套不同环境配置?
①、提供多套配置文件,如: |
---|
applcation.properties、application-dev.properties、application-test.properties、application-prod.properties |
②、然后在applcation.properties文件中指定当前的环境spring.profiles.active=test,这时候读取的就是application-test.properties文件。 |
9、Spring Boot 打成的 jar 和普通的 jar 有什么区别
区别: |
---|
①、 Spring Boot 项目最终打包成的 jar 是可执行 jar ,这种 jar 可以直接通过 java -jar xxx.jar 命令来运行,这种 jar 不可以作为普通的 jar 被其他项目依赖,即使依赖了也无法使用其中的类。 |
②、 Spring Boot 的 jar 无法被其他项目依赖,主要还是他和普通 jar 的结构不同。普通的 jar 包,解压后直接就是包名,包里就是我们的代码,而 Spring Boot 打包成的可执行 jar 解压后,在 \BOOT-INF\classes 目录下才是我们的代码,因此无法被直接引用。如果非要引用,可以在 pom.xml 文件中增加配置,将 Spring Boot 项目打包成两个 jar ,一个可执行,一个可引用。 |
10、JavaConfig
Spring JavaConfig是Spring社区的产品,它提供了配置Spring IoC容器的纯Java方法。因此它有助于避免使用XML配置。使用JavaConfig的优点在于: |
---|
①、面向对象的配置。由于配置被定义为JavaConfig中的类,因此用户可以充分利用Java中的面向对象功能。一个配置类可以继承另一个,重写它的@Bean方法等。 |
②、减少或消除XML配置。基于依赖注入原则的外化配置的好处已被证明。但是,许多开发人员不希望在XML和Java之间来回切换。 |
③、JavaConfig为开发人员提供了一种纯Java方法来配置与XML配置概念相似的Spring容器。 |
④、从技术角度来讲,只使用JavaConfig配置类来配置容器是可行的,但实际上很多人认为将JavaConfig与XML混合匹配是理想的。 |
⑤、类型安全和重构友好。JavaConfig提供了一种类型安全的方法来配置Spring容器。由于Java 5.0对泛型的支持,现在可以按类型而不是按名称检索bean,不需要任何强制转换或基于字符串的查找 |
11、spring boot 有哪些方式可以实现热部署?
①、使用 devtools 启动热部署,添加 devtools 库,在配置文件中把 spring. devtools. restart. enabled 设置为 true;
②、使用 Intellij Idea 编辑器,勾上自动编译或手动重新编译。
八、SpringCloud分布式框架
1、什么是微服务?
微服务架构是一种架构模式,或者说是一种架构风格,它提倡将单一的应用程序划分成一组小的服务,每个服务运行在其独立的自己的进程内,服务之间互相协调,互相配置,为用户提供最终价值,服务之间采用轻量级的通信机制(HTTP)互相沟通,每个服务都围绕着具体的业务进行构建,并且能狗被独立的部署到生产环境中,另外,应尽量避免统一的,集中式的服务管理机制,对具体的一个服务而言,应该根据业务上下文,选择合适的语言,工具(Maven)对其进行构建,可以有一个非常轻量级的集中式管理来协调这些服务,可以使用不同的语言来编写服务,也可以使用不同的数据存储。(总结:就是把之前一个大服务拆分成n多个小的服务,服务之间再使用http协议沟通)
2、微服务之间是如何独立通讯的?
同步(REST HTTP协议,RPC TCP 协议) 异步(消息中间件,例如Kafka、ActiveMQ、RabbitMQ、RocketMQ) |
---|
REST 请求在微服务中是最为常用的一种通讯方式,它依赖于 HTTP\HTTPS 协议。 |
RPC TCP 协议(客户端代理序列化方法和参数传入服务器,服务器代理解码方法和参数并执行方法,将结果再序列化传回去,客户端代理再解码结果得到结果) |
RESTFUL 的特点是: |
①、每一个 URI 代表 1 种资源 |
②、客户端使用 GET、POST、PUT、DELETE 4 个表示操作方式的动词对服务端资源进行操作:GET 用来获取资源,POST 用来新建资源(也可以用于更新资源),PUT 用来更新资源,DELETE 用来删除资源 |
③、通过操作资源的表现形式来操作资源 |
④、资源的表现形式是 XML 或者 HTML |
⑤、客户端与服务端之间的交互在请求之间是无状态的,从客户端到服务端的每个请求都必须包含理解请求所必需的信息 |
举个例子,有一个服务方提供了如下接口: |
另外一个服务需要去调用该接口,调用方只需要根据 API 文档发送请求即可获取返回结果。通过这样的方式可以实现服务之间的通讯。 |
3、什么是Spring Cloud?
Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、智能路由、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。
Spring Cloud并没有重复制造轮子,它只是将各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。
4、为什么需要学习Spring Cloud?
不论是商业应用还是用户应用,在业务初期都很简单,我们通常会把它实现为单体结构的应用。但是,随着业务逐渐发展,产品思想会变得越来越复杂,单体结构的应用也会越来越复杂。这就会给应用带来如下的几个问题: |
---|
①、代码结构混乱:业务复杂,导致代码量很大,管理会越来越困难。同时,这也会给业务的快速迭代带来巨大挑战; |
②、开发效率变低:开发人员同时开发一套代码,很难避免代码冲突。开发过程会伴随着不断解决冲突的过程,这会严重的影响开发效率; |
③、排查解决问题成本高:线上业务发现 bug,修复 bug 的过程可能很简单。但是,由于只有一套代码,需要重新编译、打包、上线,成本很高。 |
由于单体结构的应用随着系统复杂度的增高,会暴露出各种各样的问题。近些年来,微服务架构逐渐取代了单体架构,且这种趋势将会越来越流行。Spring Cloud是目前最常用的微服务开发框架,已经在企业级开发中大量的应用。 |
5、设计目标与优缺点?
设计目标:协调各个微服务,简化分布式系统开发。 |
---|
优点: |
①、产出于Spring大家族,Spring在企业级开发框架中无人能敌,来头很大,可以保证后续的更新、完善 |
②、组件丰富,功能齐全。Spring Cloud 为微服务架构提供了非常完整的支持。例如、配置管理、服务发现、断路器、微服务网关等; |
③、Spring Cloud 社区活跃度很高,教程很丰富,遇到问题很容易找到解决方案 |
④、服务拆分粒度更细,耦合度比较低,有利于资源重复利用,有利于提高开发效率 |
⑤、可以更精准的制定优化服务方案,提高系统的可维护性 |
⑥、减轻团队的成本,可以并行开发,不用关注其他人怎么开发,先关注自己的开发 |
⑦、微服务可以是跨平台的,可以用任何一种语言开发 |
⑧、适于互联网时代,产品迭代周期更短 |
缺点: |
①、微服务过多,治理成本高,不利于维护系统 |
②、分布式系统开发的成本高(容错,分布式事务等)对团队挑战大 |
总的来说优点大过于缺点,目前看来Spring Cloud是一套非常完善的分布式框架,目前很多企业开始用微服务、Spring Cloud的优势是显而易见的。因此对于想研究微服务架构的同学来说,学习Spring Cloud是一个不错的选择。 |
6、Spring Cloud包含子项目?
Spring Cloud的子项目,大致可分成两类,一类是对现有成熟框架"Spring Boot化"的封装和抽象,也是数量最多的项目;第二类是开发了一部分分布式系统的基础设施的实现,如Spring Cloud Stream扮演的就是kafka, ActiveMQ这样的角色。 |
---|
Spring Cloud Config: |
集中配置管理工具,分布式系统中统一的外部配置管理,默认使用Git来存储配置,可以支持客户端配置的刷新及加密、解密操作。 |
Spring Cloud Netflix:(Netflix OSS 开源组件集成,包括Eureka、Hystrix、Ribbon、Feign、Zuul等核心组件。) |
Eureka:服务治理组件,包括服务端的注册中心和客户端的服务发现机制; |
Ribbon:负载均衡的服务调用组件,具有多种负载均衡调用策略; |
Hystrix:服务容错组件,实现了断路器模式,为依赖服务的出错和延迟提供了容错能力; |
Feign:基于Ribbon和Hystrix的声明式服务调用组件; |
Zuul:API网关组件,对请求提供路由及过滤功能。 |
Spring Cloud Bus: |
用于传播集群状态变化的消息总线,使用轻量级消息代理链接分布式系统中的节点,可以用来动态刷新集群中的服务配置。 |
Spring Cloud Consul: |
基于Hashicorp Consul的服务治理组件。 |
Spring Cloud Security: |
安全工具包,对Zuul代理中的负载均衡OAuth2客户端及登录认证进行支持。 |
Spring Cloud Sleuth: |
Spring Cloud应用程序的分布式请求链路跟踪,支持使用Zipkin、HTrace和基于日志(例如ELK)的跟踪。 |
Spring Cloud Stream: |
轻量级事件驱动微服务框架,可以使用简单的声明式模型来发送及接收消息,主要实现为Apache Kafka及RabbitMQ。 |
Spring Cloud Task: |
用于快速构建短暂、有限数据处理任务的微服务框架,用于向应用中添加功能性和非功能性的特性。 |
Spring Cloud Zookeeper: |
基于Apache Zookeeper的服务治理组件。 |
Spring Cloud Gateway: |
API网关组件,对请求提供路由及过滤功能。 |
Spring Cloud OpenFeign: |
基于Ribbon和Hystrix的声明式服务调用组件,可以动态创建基于Spring MVC注解的接口实现用于服务调用,在Spring Cloud 2.0中已经取代Feign成为了一等公民。 |
7、SpringBoot和SpringCloud的关系?
SpringBoot专注于快速方便的开发单个个体微服务。 |
---|
SpringCloud是关注全局的微服务协调整理治理框架,它将SpringBoot开发的一个个单体微服务整合并管理起来,为各个微服务之间提供,配置管理、服务发现、断路器、路由、微代理、事件总线、全局锁、决策竞选、分布式会话等等集成服务。 |
SpringBoot可以离开SpringCloud独立使用开发项目, 但是SpringCloud离不开SpringBoot ,属于依赖的关系 |
SpringBoot专注于快速、方便的开发单个微服务个体,SpringCloud关注全局的服务治理框架。 |
8、Spring Cloud 和dubbo区别?
区别: |
---|
(1)服务调用方式 dubbo是RPC springcloud Rest Api |
(2)注册中心,dubbo 是zookeeper springcloud是eureka,也可以是zookeeper |
(3)服务网关,dubbo本身没有实现,只能通过其他第三方技术整合,springcloud有Zuul路由网关,作为路由服务器,进行消费者的请求分发,springcloud支持断路器,与git完美集成配置文件支持版本控制,事物总线实现配置文件的更新与服务自动装配等等一系列的微服务架构要素。 |
9、什么是Spring Cloud Gateway?
Spring Cloud Gateway是Spring Cloud官方推出的第二代网关框架,取代Zuul网关。网关作为流量的,在微服务系统中有着非常作用,网关常见的功能有路由转发、权限校验、限流控制等作用。
使用了一个RouteLocatorBuilder的bean去创建路由,除了创建路由RouteLocatorBuilder可以让你添加各种predicates和filters,predicates断言的意思,顾名思义就是根据具体的请求的规则,由具体的route去处理,filters是各种过滤器,用来对请求做各种判断和修改。
10、Eureka和Zookeeper区别?
区别: |
---|
Eureka:AP架构设计(高可用、分区容错性),Zookeeper:CP架构设计(一致性、分区容错性) |
CAP的三进二: |
①、因为在实现分布式时,必须考虑分区容忍性(P),因为节点随时会崩,需要集群实现,每个节点都拷贝相同的内容以保证内容一样; |
②、不等待所有节点拷贝完成就使用,一致性(C)不能保证,因为一致性需要所有节点在任何时间访问的内容都必须一样; |
③、等待所有节点拷贝完成才使用,可用性(A)不能保证,因为必须等待完成,在那个时间段不能使用; |
④、著名的CAP理论指出,一个分布式系统不可能同时满足C (一致性) 、A (可用性) 、P (容错性),由于分区容错性P再分布式系统中是必须要保证的,因此我们只能再A和C之间进行权衡。 |
Zookeeper保证的是CP(网络问题导致master节点宕机,选举时间长,集群无法使用,无法保证可用性) |
当向注册中心查询服务列表时,我们可以容忍注册中心返回的是几分钟以前的注册信息,但不能接收服务直接down掉不可用。也就是说,服务注册功能对可用性的要求要高于一致性。但zookeeper会出现这样一种情况,当master节点因为网络故障与其他节点失去联系时,剩余节点会重新进行leader选举。问题在于,选举leader的时间太长,30-120s,且选举期间整个zookeeper集群是不可用的,这就导致在选举期间注册服务瘫痪。在云部署的环境下,因为网络问题使得zookeeper集群失去master节点是较大概率发生的事件,虽然服务最终能够恢复,但是,漫长的选举时间导致注册长期不可用,是不可容忍的。 |
Eureka保证的是AP(网络有问题时触发自我保护机制,不会移除过期服务,同时能接受新服务,保证可用性,而不会同步到其他节点,无法保证一致性) |
Eureka看明白了这一点,因此在设计时就优先保证可用性。Eureka各个节点都是平等的,几个节点挂掉不会影响正常节点的工作,剩余的节点依然可以提供注册和查询服务。而Eureka的客户端在向某个Eureka注册时,如果发现连接失败,则会自动切换至其他节点,只要有一台Eureka还在,就能保住注册服务的可用性,只不过查到的信息可能不是最新的,除此之外,Eureka还有之中自我保护机制,如果在15分钟内超过85%的节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,此时会出现以下几种情况:①、①、Eureka不在从注册列表中移除因为长时间没收到心跳而应该过期的服务 ②、Eureka仍然能够接受新服务的注册和查询请求,但是不会被同步到其他节点上 (即保证当前节点依然可用) ③、当网络稳定时,当前实例新的注册信息会被同步到其他节点中。 |
因此,Eureka可以很好的应对因网络故障导致部分节点失去联系的情况,而不会像zookeeper那样使整个注册服务瘫痪 |
11、服务熔断,服务降级?
服务熔断(提供者) |
---|
Hystrix解决服务雪崩的方案(服务熔断):在不可用的服务中服务端给调用方返回备用响应,就可以继续运行调用之后的服务,就可以避免长时间的等待或抛出无法解决的异常,无法释放调用线程,导致服务雪崩 |
使用步骤: |
1、导入maven依赖:spring-cloud-starter-hystrix |
2、在调用方法@HystrixCommand注解 |
3、启动类上加@EnableCircuitBreaker注解,开启服务熔断机制 |
4、运行页面 |
5、向调用方返回一个符合预期的、可处理的备选响应的 |
**注意:**spring强调业务逻辑,处理异常信息,要分离,上面的方法会发生方法膨胀,耦合太严重,最终业务主逻辑和熔断的处理方式,在接口绑定,实现与主逻辑解耦。 |
服务降级(客户端、消费者) |
当某个时间段访问压力大,需要停掉不重要的某些功能(例如:广告。。),释放占用资源以保证主要核心重要业务能够顺利完成,而消费者调用这些不重要功能时,客户端会返回备用响应 |
使用步骤: |
1、在api层创建一个一个降级服务,并实现FallbackFactory接口,且泛型为Feign的调用接口;切记,必须在类上方加上@Component注解。 |
2、在api的接口中(此处我的分布式项目中使用的是Feign的调用方法) |
3、在消费者模块的配置文件中(开启服务熔断服务降级 feign: hystrix: enabled: true) |
4、开启eureka的集群,提供者,Feign的消费者,然后进行测试 |
12、什么是 Hystrix,它如何实现容错?
Hystrix 是一个延迟和容错库,旨在隔离远程系统,服务和第三方库的访问点,当出现故障是不可避免的故障时,停止级联故障并在复杂的分布式系统中实现弹性。 |
---|
通常对于使用微服务架构开发的系统,涉及到许多微服务。这些微服务彼此协作。 |
随着微服务数量的增加,这个问题变得更加复杂。微服务的数量可以高达 1000.这是 hystrix 出现的地方 我们将使用 Hystrix 在这种情况下的 Fallback 方法功能。我们有两个服务 employee-consumer 使用由 employee-consumer 公开的服务。 |
现在假设由于某种原因,employee-producer 公开的服务会抛出异常。我们在这种情况下使用 Hystrix 定义了一个回退方法。这种后备方法应该具有与公开服务相同的返回类型。如果暴露服务中出现异常,则回退方法将返回一些值。 |
13、什么是 Hystrix 断路器?我们需要它吗?
由于某些原因,employee-consumer 公开服务会引发异常。在这种情况下使用Hystrix 我们定义了一个回退方法。如果在公开服务中发生异常,则回退方法返回一些默认值。 |
---|
如果 firstPage method() 中的异常继续发生,则 Hystrix 电路将中断,并且员工使用者将一起跳过 firtsPage 方法,并直接调用回退方法。断路器的目的是给第一页方法或第一页方法可能调用的其他方法留出时间,并导致异常恢复。可能发生的情况是,在负载较小的情况下,导致异常的问题有更好的恢复机会 。 |
情景: |
当一个服务调用另一个服务由于网络原因或自身原因出现问题,调用者就会等待被调用者的响应 当更多的服务请求到这些资源导致更多的请求等待,发生连锁效应(雪崩效应) |
解决方案: |
断路器有完全打开状态:一段时间内 达到一定的次数无法调用 并且多次监测没有恢复的迹象 断路器完全打开 那么下次请求就不会请求到该服务 |
半开:短时间内 有恢复迹象 断路器会将部分请求发给该服务,正常调用时 断路器关闭 |
关闭:当服务一直处于正常状态 能正常调用 |
14、什么是 Netflix Feign?它的优点是什么?
Feign 是受到 Retrofit,JAXRS-2.0 和 WebSocket 启发的 java 客户端联编程序。 |
---|
Feign 的第一个目标是将约束分母的复杂性统一到 http apis,而不考虑其稳定性。 |
在 employee-consumer 的例子中,我们使用了 employee-producer 使用 REST模板公开的 REST 服务。 |
但是我们必须编写大量代码才能执行以下步骤 |
(1)使用功能区进行负载平衡。 |
(2)获取服务实例,然后获取基本 URL。 |
(3)利用 REST 模板来使用服务。前面的代码如下 |
@Controller
public class ConsumerControllerClient {
@Autowired
private LoadBalancerClient loadBalancer;
public void getEmployee() throws RestClientException, IOException {
ServiceInstance serviceInstance=loadBalancer.choose("employee-producer");
System.out.println(serviceInstance.getUri());
String baseUrl=serviceInstance.getUri().toString();
baseUrl=baseUrl+"/employee";
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response=null;
try{
response=restTemplate.exchange(baseUrl,
HttpMethod.GET, getHeaders(),String.class);
}
catch (Exception ex)
{
System.out.println(ex);
}
System.out.println(response.getBody());
}
之前的代码,有像 NullPointer 这样的例外的机会,并不是最优的。我们将看到如何使用 Netflix Feign 使呼叫变得更加轻松和清洁。如果 Netflix Ribbon 依赖关系也在类路径中,那么 Feign 默认也会负责负载平衡。
15、什么是 Spring Cloud Bus?
Spring Cloud Bus 提供了跨多个实例刷新配置的功能。 |
---|
考虑以下情况:我们有多个应用程序使用 Spring Cloud Config 读取属性,而Spring Cloud Config 从 GIT 读取这些属性。 |
下面的例子中多个员工生产者模块从 Employee Config Module 获取 Eureka 注册的财产。 |
如果假设 GIT 中的 Eureka 注册属性更改为指向另一台 Eureka 服务器,会发生什么情况。在这种情况下,我们将不得不重新启动服务以获取更新的属性。 |
还有另一种使用执行器端点/刷新的方式。但是我们将不得不为每个模块单独调用这个 url。例如,如果 Employee Producer1 部署在端口 8080 上,则调用 http:// localhost:8080 / refresh。同样对于 Employee Producer2 http://localhost:8081 / refresh 等等。这又很麻烦。这就是 Spring Cloud Bus 发挥作用的地方。 |
Spring Cloud Bus 提供了跨多个实例刷新配置的功能。因此,在上面的示例中,如果我们刷新 Employee Producer1,则会自动刷新所有其他必需的模块。如果我们有多个微服务启动并运行,这特别有用。这是通过将所有微服务连接到单个消息代理来实现的。无论何时刷新实例,此事件都会订阅到侦听此代理的所有微服务,并且它们也会刷新。可以通过使用端点/总线/刷新来实现对任何单个实例的刷新。 |
16、什么是Spring Cloud Config?
在分布式系统中,由于服务数量巨多,为了方便服务配置文件统一管理,实时更新,所以需要分布式配置中心组件。在Spring Cloud中,有分布式配置中心组件spring cloud config ,它支持配置服务放在配置服务的内存中(即本地),也支持放在远程Git仓库中。在spring cloud config 组件中,分两个角色,一是config server,二是config client。 |
---|
使用: |
(1)添加pom依赖 |
(2)配置文件添加相关配置 |
(3)启动类添加注解@EnableConfigServer |
九、SpringMVC
1、什么是Spring的MVC框架?
Spring MVC是一个基于Java的实现了MVC设计模式的请求驱动类型的轻量级Web框架,通过把Model,View,Controller分离,将web层进行职责解耦,把复杂的web应用分成逻辑清晰的几部分,简化开发,减少出错,方便组内开发人员之间的配合。
2、 什么是MVC模式?
MVC的全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,是一种软件设计典范。它是用一种业务逻辑、数据与界面显示分离的方法来组织代码,将众多的业务逻辑聚集到一个部件里面,在需要改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑,达到减少编码的时间。 |
---|
M即model模型是指模型表示业务规则。在MVC的三个部件中,模型拥有最多的处理任务。被模型返回的数据是中立的,模型与数据格式无关,这样一个模型能为多个视图提供数据,由于应用于模型的代码只需写一次就可以被多个视图重用,所以减少了代码的重复性。 |
V即View视图是指用户看到并与之交互的界面。比如由html元素组成的网页界面,或者软件的客户端界面。MVC的好处之一在于它能为应用程序处理很多不同的视图。在视图中其实没有真正的处理发生,它只是作为一种输出数据并允许用户操纵的方式。 |
C即controller控制器是指控制器接受用户的输入并调用模型和视图去完成用户的需求,控制器本身不输出任何东西和做任何处理。它只是接收请求并决定调用哪个模型构件去处理请求,然后再确定用哪个视图来显示返回的数据。 |
3、SpringMVC的执行流程?
(1)用户发送请求至前端控制器DispatcherServlet; |
---|
(2)DispatcherServlet收到请求后,调用HandlerMapping处理器映射器,请求获取Handler; |
(3)处理器映射器根据请求url找到具体的处理器Handler,生成处理器对象及处理器拦截器(如果有则生成),一并返回给DispatcherServlet; |
(4)DispatcherServlet 调用 HandlerAdapter处理器适配器,请求执行Handler; |
(5)HandlerAdapter 经过适配调用 具体处理器进行处理业务逻辑; |
(6)Handler执行完成返回ModelAndView; |
(7)HandlerAdapter将Handler执行结果ModelAndView返回给DispatcherServlet; |
(8)DispatcherServlet将ModelAndView传给ViewResolver视图解析器进行解析; |
(9)ViewResolver解析后返回具体View; |
(10)DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中) |
(11)DispatcherServlet响应用户。 |
4、Spring MVC的主要组件?
前端控制器:其作用是接收用户请求,然后给用户反馈结果。它的作用相当于一个转发器或中央处理器,控制整个流程的执行,对各个组件进行统一调度,以降低组件之间的耦合性,有利于组件之间的拓展。 |
---|
处理器映射器:其作用是根据请求的URL路径,通过注解或者XML配置,寻找匹配的处理器信息。 |
处理器适配器:其作用是根据映射器处理器找到的处理器信息,按照特定规则执行相关的处理器(Handler)。 |
处理器:其作用是执行相关的请求处理逻辑,并返回相应的数据和视图信息,将其封装至ModelAndView对象中。 |
视图解析器:其作用是进行解析操作,通过ModelAndView对象中的View信息将逻辑视图名解析成真正的视图View(如通过一个JSP路径返回一个真正的JSP页面)。 |
视图:View是一个接口,实现类支持不同的View类型(JSP、FreeMarker、Excel等)。 |
5、Springmvc的优点?
①、SpringMVC本身是与Spring框架结合而成的,它同时拥有Spring的优点(例如依赖注入DI和切面编程AOP等)。 |
---|
②、SpringMVc提供强大的约定大于配置的契约式编程支持,即提供一种软件设计范式,减少软件开发人员做决定的次数,开发人员仅需规定应用中不符合约定的部分。 |
③、支持灵活的URL到页面控制器的映射。 |
④、可以方便地与其他视图技术(JSP、FreeMarker等)进行整合。由于SpringMVC的模型数据往往是放置在Map数据结构中的,因此其可以很方便地被其他框架引用。 |
⑤、拥有十分简洁的异常处理机制。 |
⑥、可以十分灵活地实现数据验证、格式化和数据绑定机制,可以使用任意对象进行数据绑定操作。 |
⑦、支持RestFul风格。 |
6、SpringMVC和Struts2的区别有哪些?
①、SpringMVC的入口是一个Servlet,也就是前端控制器(DispatcherServlet),而Struts2的入口是一个Filter (StrutsPrepareAndExecuteFilter)。 |
---|
②、SpringMVC是基于方法开发(一个url对应一个方法),请求参数传递到方法的形参,可以设计为单例或多例(建议单例)。struts2是基于类开发,请求参数传递到类的成员属性,只能设计为多例。 |
③、SpringMVC通过参数解析器将request请求内容解析,并给方法形参赋值,将数据和视图封装成ModelAndView对象,最后又将ModelAndView中的模型数据通过reques域传输到页面。Jsp视图解析器默认使用JSTL。Struts2采用值栈存储请求和响应的数据,通过OGNL存取数据。 |
6、SpringMVC怎么样设定重定向和请求转发?
请求转发与重定向的区别: |
---|
①、请求转发在服务器端完成的;重定向是在客户端完成的。 |
②、请求转发的速度快;重定向速度慢。 |
③、请求转发的是同一次请求;重定向是两次不同请求。 |
④、请求转发不会执行转发后的代码;重定向会执行重定向之后的代码。 |
⑤、请求转发地址栏没有变化;重定向地址栏有变化。 |
⑥、请求转发必须是在同一台服务器下完成;重定向可以在不同的服务器下完成。 |
SpringMVC设定请求转发: 在返回值前面加"forward:"。
@RequestParam("login")
public String redirect(User user){
if{
//登录成功
}else{
//登录失败,跳转登录页面
return "forward:tologin";
}
}
SpringMVC设定重定向:在返回值前面加"redirect:"。例如我们在登录的时候,登录失败会重定向到登录页面。
@RequestParam("login")
public String redirect(User user){
if{
//登录成功
}else{
//登录失败,跳转登录页面
return "redirect:tologin";
}
}
7、当一个方法向AJAX返回特殊对象,譬如Object,List等,需要做什么处理?
①、在方法上加@ResponseBody注解,表示该方法的返回值不管是什么类型,都会返回JSON格式的数据。 |
---|
②、把原来Controller类上的@Controller注解替换为@RestController注解。@RestController = @Controller + @ResponseBody,表明该Controller类所有的方法都返回JSON格式的数据(没有加@RequestMapping注解的方法除外)。 |
③、加入@ResponseBody注解就能返回JSON格式数据的原因是:SpringMVC提供的HttpMessageConverter自动转为JSON ,如果使用了Jackson或者Gson,不需要额外配置就可以自动返回JSON了,因为框架帮我们提供了对应的HttpMessageConverter ,如果使用了Alibaba的Fastjson的话,则需要自己手动提供一个相应的 HttpMessageConverter的实例。 |
8、如何解决POST请求中文乱码问题,GET的又如何处理呢?
JavaWeb乱码问题一般是客户端(浏览器)与服务器端字符集不一致产生的,如果两者字符集一致就不会出现乱码问题。 |
---|
解决post请求乱码 |
SpringMVC默认提供一个解决post请求乱码的过滤器,在web.xml中配置即可 |
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
解决get请求乱码 |
---|
修改tomcat配置文件添加编码与工程编码一致。 |
<ConnectorURIEncoding=“utf-8” connectionTimeout=“20000” port=“8080” protocol=“HTTP/1.1” redirectPort=“8443”/> |
对请求参数进行重新编码,ISO8859-1是tomcat默认编码,需要将tomcat编码后的内容按utf-8编码。 |
String userName = new String(request.getParamter(“userName”).getBytes(“ISO8859-1”),“utf-8”); |
9、SpringMVC常用的注解有哪些?
@Controller |
---|
@Controller用于标记在一个类上,使用它标记的类就是一个SpringMVC Controller对象。处理器适配器将会扫描使用了该注解的类的方法,并检测该方法是否使用了@RequestMapping注解。@Controller 只是定义了一个控制器类,而使用@RequestMapping 注解的方法才是真正处理请求的处理器。 |
@RequsestMapping |
---|
@RequestMapping是一个用来处理请求地址映射的注解,可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。返回值会通过视图解析器解析为实际的物理视图,对于 InternalResourceViewResolver 视图解析器,通过 prefix + returnValue + suffix 这样的方式得到实际的物理视图,然后做转发操作。 |
@RequsestMapping有如下6个属性: |
①、value:指定请求的实际地址。 |
②、method:指定请求的method类型, GET、POST、PUT、DELETE等。 |
③、consumes:指定处理请求的提交内容类型(Content-Type),例如application/json, text/html。 |
④、produces:指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回; |
⑤、params:指定request中必须包含某些参数值是,才让该方法处理。 |
⑥、headers:指定request中必须包含某些指定的header值,才能让该方法处理请求。 |
@ResponseBody |
---|
@ResponseBody把Java对象转化为json对象,这种方式用于Ajax异步请求,返回的不是一个页面而是JSON格式的数据。 |
@Valid |
---|
标志参数被Hibernate-Validator校验框架校验。 |
@PathVariable |
---|
①、@PathVariable用于接收uri地址传过来的参数,Url中可以通过一个或多个{Xxx}占位符映射,通过@PathVariable可以绑定占位符参数到方法参数中,在RestFul接口风格中经常使用。 |
②、例如:请求URL:http://localhost/user/21/张三/query |
@RequestParam |
---|
@RequestParam用于将请求参数映射到控制器方法的形参上,有如下三个属性: |
①、value:参数名。 |
②、required:是否必需,默认为true,表示请求参数中必须包含该参数,如果不包含抛出异常。 |
③、defaultValue:默认参数值,如果设置了该值自动将required设置为false,如果参数中没有包含该参数则使用默认值。 |
@ControllerAdvice |
---|
@ControllerAdvice标识一个类是全局异常处理类。 |
@ExceptionHandler |
---|
@ExceptionHandler标识一个方法为全局异常处理的方法。 |
10、SpingMvc中的控制器的注解一般用那个,有没有别的注解可以替代?
一般使用@Controller注解标识控制器。 |
---|
也可以使用@RestController注解替代@Controller注解,@RestController相当于@ResponseBody+@Controller,表示控制器中所有的方法都返回JSON格式数据,一般不使用其他注解标识控制器。 |
11、如果在拦截请求中,想拦截get方式提交的方法,怎么配置?
①、可以在@RequestMapping注解里面加上method=RequestMethod.GET。 |
---|
②、可以使用@GetMapping注解。 |
12、怎样在控制器方法里面得到request或者session?
直接在控制器方法的形参中声明request,session,SpringMvc就会自动把它们注入。
13、SpringMVC用什么对象从后台向前台传递数据的?
使用Map、Model和ModelMap的方式,这种方式存储的数据是在request域中
@RequestMapping("/getUser")
public String getUser(Map<String,Object> map,Model model,ModelMap modelMap){
//1.放在map里
map.put("name", "xq");
//2.放在model里,一般是使用这个
model.addAttribute("habbit", "Play");
//3.放在modelMap中
modelMap.addAttribute("city", "gd");
modelMap.put("gender", "male");
return "userDetail";
}
使用request的方式
@RequestMapping("/getUser")
public String getUser(Map<String,Object> map,Model model,ModelMap modelMap,HttpServletRequest request){
//放在request里
request.setAttribute("user", userService.getUser());
return "userDetail";
}
使用ModelAndView
@RequestMapping("/getUser")
public ModelAndView getUser(ModelAndView modelAndView) {
mav.addObject("user", userService.getUser());
mav.setViewName("userDetail");
return modelAndView;
}
14、怎么样把ModelMap里面的数据放入session里面?
在类上添加@SessionAttributes注解将指定的Model数据存储到session中。 |
---|
@SessionAttributes |
默认情况下Spring MVC将模型中的数据存储到request域中。当一个请求结束后,数据就失效了。如果要跨页面使用。那么需要使用到session。而@SessionAttributes注解就可以使得模型中的数据存储一份到session域中。 |
@SessionAttributes只能定义在Class,interface enum上,作用是将指定的Model中的键值对添加至session中,方便在一个会话中使用。 |
@SessionAttributes参数 |
names:这是一个字符串数组。里面应写需要存储到session中数据的名称。 |
types:根据指定参数的类型,将模型中对应类型的参数存储到session中。 |
value:其实和上面的names是一样的。 |
@SessionAttributes(value={"names"},types={Integer.class})
@Controller
public class session{
@RequestMapping("/session")
public String session(Model model){
model.addAttributes("names", Arrays.asList("caoyc","zhh","cjx"));
model.addAttributes("age", 22);
return "/session";
}
}
在上面代码中,在类上添加@SessionAttributes注解,并指定将names名称的Model数据存储到session域中,以及将Integer类型的Model数据存储到session域中。
15、SpringMMV中有个类把视图和数据都合并的一起的,叫什么?
①、使用ModelAndView类存储处理完后的结果数据,以及显示该数据的视图。从名字上看ModelAndView中的Model代表模型,View代表视图,从名字看就很好地解释了该类的作用。Controller处理器调用模型层处理完用户请求后,把结果数据存储在该类的model属性中,把要返回的视图信息存储在该类的view属性中,然后把ModelAndView返回给前端控制器。前端控制器通过调用配置文件中定义的视图解析器,对该对象进行解析,最后把结果数据显示在指定的页面上。 |
---|
②、返回指定页面 |
③、ModelAndView构造方法可以指定返回的页面名称。 |
16、SpringMVC拦截器实现原理
拦截器作用: |
---|
①、SpringMVC提供的拦截器类似于JavaWeb中的过滤器,只不过SpringMVC拦截器只拦截被前端控制器拦截的请求,而过滤器拦截从前端发送的任意请求。 |
②、熟练掌握SpringMVC拦截器对于我们开发非常有帮助,在没使用权限框架(shiro,spring security)之前,一般使用拦截器进行认证和授权操作。 |
③、SpringMVC拦截器有许多应用场景,比如:登录认证拦截器,字符过滤拦截器,日志操作拦截器等等。 |
自定义拦截器 |
---|
SpringMVC拦截器的实现一般有两种方式: |
①、自定义的Interceptor类要实现了Spring的HandlerInterceptor接口。 |
②、继承实现了HandlerInterceptor接口的类,比如Spring已经提供的实现了HandlerInterceptor接口的抽象类HandlerInterceptorAdapter。 |
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {}
}
HandlerInterceptor接口中定义了三个方法,我们就是通过这三个方法来对用户的请求进行拦截处理的: |
---|
①、preHandle(): 这个方法在Controller处理请求之前被调用,SpringMVC中的Interceptor是链式的调用的,在一个应用中或者说是在一个请求中可以同时存在多个Interceptor 。每个Interceptor的调用会依据它的声明顺序依次执行,而且最先执行的都是Interceptor 中的preHandle方法,所以可以在这个方法中进行一些前置初始化操作或者是对当前请求的一个预处理,也可以在这个方法中进行一些判断来决定请求是否要继续进行下去。该方法的返回值是布尔值Boolean 类型的,当它返回为false 时,表示请求结束,后续的Interceptor和Controller都不会再执行;当返回值为true时就会继续调用下一个Interceptor的preHandle 方法,如果已经是最后一个Interceptor的时候就会是调用当前请求的Controller方法。 |
②、postHandle():这个方法在Controller方法处理当前请求之后执行,但是它会在DispatcherServlet进行视图返回渲染之前被调用,所以我们可以在这个方法中对Controller处理之后的ModelAndView对象进行操作。postHandle方法被调用的方向跟preHandle 是相反的,也就是说先声明的Interceptor的postHandle方法反而会后执行。 |
③、afterCompletion():这个方法也是需要当前对应的Interceptor的preHandle方法的返回值为true时才会执行。顾名思义,该方法将在整个请求结束之后,也就是在DispatcherServlet渲染了对应的视图之后执行。这个方法的主要作用是用于进行资源清理工作的。 |
拦截器规则 |
---|
我们可以配置多个拦截器,每个拦截器中都有三个方法。下面将总结多个拦截器中的方法执行规律。 |
preHandle:Controller方法处理请求前执行,根据拦截器定义的顺序,正向执行。 |
postHandle:Controller方法处理请求后执行,根据拦截器定义的顺序,逆向执行。需要所有的preHandle方法都返回true时才会调用。 |
afterCompletion:View视图渲染后处理方法:根据拦截器定义的顺序,逆向执行。preHandle返回true就会调用。 |
登录拦截器 |
---|
接下来编写一个登录拦截器,这个拦截器可以实现认证操作。就是当我们还没有登录的时候,如果发送请求访问我们系统资源时,拦截器不放行,请求失败。只有登录成功后,拦截器放行,请求成功。登录拦截器只要在preHandle()方法中编写认证逻辑即可,因为是在请求执行前拦截。代码实现如下: |
/**
* 登录拦截器
*/
public class LoginInterceptor implements HandlerInterceptor {
/**
在执行Controller方法前拦截,判断用户是否已经登录,
登录了就放行,还没登录就重定向到登录页面
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
HttpSession session = request.getSession();
User user = session.getAttribute("user");
if (user == null){
//还没登录,重定向到登录页面
response.sendRedirect("/toLogin");
}else {
//已经登录,放行
return true;
}
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {}
}
编写完SpringMVC拦截器,我们还需要在springmvc.xml配置文件中,配置我们编写的拦截器,配置代码如下: |
---|
配置需要拦截的路径 |
配置不需要拦截的路径 |
配置我们自定义的拦截器类 |
<!--配置拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<!--
mvc:mapping:拦截的路径
/**:是指所有文件夹及其子孙文件夹
/*:是指所有文件夹,但不包含子孙文件夹
/:Web项目的根目录
-->
<mvc:mapping path="/**"/>
<!--
mvc:exclude-mapping:不拦截的路径,不拦截登录路径
/toLogin:跳转到登录页面
/login:登录操作
-->
<mvc:exclude-mapping path="/toLogin"/>
<mvc:exclude-mapping path="/login"/>
<!--class属性就是我们自定义的拦截器-->
<bean id="loginInterceptor" class="cn.zwq.springmvc.interceptor.LoginInterceptor"/>
</mvc:interceptor>
<!--如果还有其他拦截器,那么还是按照上面的拦截器配置-->
</mvc:interceptors>