1、Spring是什么?
Spring是一个开源的轻量级的Java开发框架。
目的:解决企业应用开发的复杂性 功能:使用基本的JavaBean代替EJB,并提供了更多的企业应用功能 范围:任何Java应用 简单来说,Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。
2、Spring的特点有哪些?或者优点有哪些?
- IOC(控制反转) 和 DI(依赖注入) 的支持
Spring 的核心就是一个大的工厂容器,可以维护所有对象的创建和依赖关系,Spring 工厂用于生成 Bean,并且管理 Bean 的生命周期,实现高内聚低耦合的设计理念。 - AOP (面向切面编程)编程的支持
Spring 提供了面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等切面功能。 - 声明式事务的支持
支持通过配置就来完成对事务的管理,而不需要通过硬编码的方式,以前重复的一些事务提交、回滚的JDBC代码,都可以不用自己写了。 - 快捷测试的支持
Spring 对 Junit 提供支持,可以通过注解快捷地测试 Spring 程序。 - 快速集成功能
方便集成各种优秀框架,Spring 不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如:Struts、Hibernate、MyBatis、Quartz 等)的直接支持。 - 复杂API模板封装
Spring 对 JavaEE 开发中非常难用的一些 API(JDBC、JavaMail、远程调用等)都提供了模板化的封装,这些封装 API 的提供使得应用难度大大降低。
3、详细介绍Spring的IOC和AOP(重点、核心!!!!)
我们是在使用Spring框架的过程中,其实就是为了使用依赖注入(IOC),和面向切面编程(AOP),这两个是Spring的灵魂。 主要使用的设计模式是工厂模式和代理模式。 IOC就是典型的工厂模式,通过sessionfactory去注入实例。 AOP就是典型的代理模式的体现。 代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。(了解)
spring的IoC容器是spring的核心,spring AOP是spring框架的重要组成部分。
在传统的程序设计中,当调用者需要被调用者的协助时,通常由调用者来创建被调用者的实例。但在spring里创建被调用者的工作不再由调用者来完成,因此控制反转(IoC);创建被调用者实例的工作通 常由spring容器来完成,然后注入调用者,因此也被称为依赖注入(DI),依赖注入和控制反转是同一 个概念。
IOC:控制反转 也叫依赖注入,IOC利用java反射机制,AOP利用代理模式。所谓控制反转是指,本来被调用者的实例是由调用者来创建的,这样的缺点是耦合性太强,IOC则是统一交给spring来管理创建,将对象交给容器管理,你只需要在spring配置文件中配置相应的bean,以及设置相关的属性,让spring容器来生成类的实例对象以及管理对象。在spring容器启动的时候,spring会把你在配置文件中配置的bean都初始化好,然后在你需要调用的时候,就把它已经初始化好的那些bean分配给你需要调用这些bean的类。
AOP:面向切面编程。(Aspect-Oriented Programming) AOP可以说是对OOP的补充和完善。OOP引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。实现AOP的技术,主要分为两大类:一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;二是采用静态织入的方式,引入特定的语法创建“方面”,从而使得编译器可以在编译期间织入有关“方面”的代码,属于静态代理。
4、 Spring有哪些模块?
Spring 框架是分模块存在,除了最核心的Spring Core Container是必要模块之外,其他模块都是可选,大约有 20 多个模块。 最主要的七大模块:
- Spring Core:Spring 核心,它是框架最基础的部分,提供 IOC 和依赖注入 DI 特性。
- Spring Context:Spring 上下文容器,它是 BeanFactory 功能加强的一个子接口。
- Spring Web:它提供 Web 应用开发的支持。
- Spring MVC:它针对 Web 应用中 MVC 思想的实现。
- Spring DAO:提供对 JDBC 抽象层,简化了 JDBC 编码,同时,编码更具有健壮性。
- Spring ORM:它支持用于流行的 ORM 框架的整合,比如:Spring + Hibernate、Spring + iBatis、Spring + JDO 的整合等。对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。
- Spring AOP:即面向切面编程,它提供了与 AOP 联盟兼容的编程实现。
5、说一说你对Spring容器的了解
Spring主要提供了两种类型的容器:BeanFactory和ApplicationContext。
- BeanFactory:是基础类型的IoC容器,是Spring内部的接口,不提供开发人员使用,加载配置文件的时候不会创建对象,在获取对象的时候才回去创建对象。提供完整的IoC服务支持。如果没有特殊指定,默认采用延迟初始化策略。只有当客户端对象需要访问容器中的某个受管对象的时候,才对该受管对象进行初始化以及依赖注入操作。所以,相对来说,容器启动初期速度较快,所需要的资源有限。对于资源有限,并且功能要求不是很严格的场景,BeanFactory是比较合适的IoC容器选择。
- ApplicationContext:它是在BeanFactory的基础上构建的,是BeanFactory接口的子接口,是相对比较高级的容器实现,提供更多更强大的功能,一般由开发人员进行使用。除了拥有BeanFactory的所有支持,ApplicationContext还提供了其他高级特性,比如事件发布、国际化信息支持等。ApplicationContext所管理的对象,在该类型容器启动之后,默认全部初始化并绑定完成。所以,相对于BeanFactory来说,ApplicationContext要求更多的系统资源,同时,因为在启动时就完成所有初始化,容器启动时间较之BeanFactory也会长一些。在那些系统资源充足,并且要求更多功能的场景中,ApplicationContext类型的容器是比较合适的选择。
6、说一说你对BeanFactory的了解
BeanFactory是一个类工厂,与传统类工厂不同的是,BeanFactory是类的通用工厂,它可以创建并管理各种类的对象。这些可被创建和管理的对象本身没有什么特别之处,仅是一个POJO,Spring称这些被创建和管理的Java对象为Bean。并且,Spring中所说的Bean比JavaBean更为宽泛一些,所有可以被Spring容器实例化并管理的Java类都可以成为Bean。
BeanFactory是Spring容器的顶层接口,Spring为BeanFactory提供了多种实现,最常用的是XmlBeanFactory。但它在Spring 3.2中已被废弃,建议使用XmlBeanDefinitionReader、DefaultListableBeanFactory替代。BeanFactory最主要的方法就是 getBean(String beanName),该方法从容器中返回特定名称的Bean。
7、说一说你对Spring IOC的理解
所谓的IOC(控制反转):就是由容器来负责控制对象的生命周期和对象间的关系。以前是我们想要什么,就自己创建什么,现在是我们需要什么,容器就给我们送来什么。
IoC(Inversion of Control)是控制反转的意思,这是一种面向对象编程的设计思想。在不采用这种思想的情况下,我们需要自己维护对象与对象之间的依赖关系,很容易造成对象之间的耦合度过高,在一个大型的项目中这十分的不利于代码的维护。IoC则可以解决这种问题,它可以帮我们维护对象与对象之间的依赖关系,降低对象之间的耦合度。
DI(Dependency Injection),DI是依赖注入的意思,它是IoC实现的实现方式,就是说IoC是通过DI来实现的。由于IoC这个词汇比较抽象而DI却更直观,所以很多时候我们就用DI来代替它,在很多时候我们简单地将IoC和DI划等号,这是一种习惯。而实现依赖注入的关键是IoC容器,它的本质就是一个工厂。
DI(依赖注入):指的是容器在实例化对象的时候把它依赖的类注入给它。有的说法IOC和DI是一回事,有的说法是IOC是思想,DI是IOC的实现。
使用IOC最主要的原因即使解耦,硬编码会造成对象间的过度耦合,使用IOC之后,我们可以不用关心对象间的依赖,专心开发应用就行。
在具体的实现中,主要由三种注入方式:
- 构造方法注入:就是被注入对象可以在它的构造方法中声明依赖对象的参数列表,让外部知道它需要哪些依赖对象。然后,IoC Service Provider会检查被注入的对象的构造方法,取得它所需要的依赖对象列表,进而为其注入相应的对象。构造方法注入方式比较直观,对象被构造完成后,即进入就绪状态,可以马上使用。
- setter方法注入:通过setter方法,可以更改相应的对象属性。所以,当前对象只要为其依赖对象所对应的属性添加setter方法,就可以通过setter方法将相应的依赖对象设置到被注入对象中。setter方法注入虽不像构造方法注入那样,让对象构造完成后即可使用,但相对来说更宽松一些, 可以在对象构造完成后再注入。
- 接口注入:相对于前两种注入方式来说,接口注入没有那么简单明了。被注入对象如果想要IoC Service Provider为其注入依赖对象,就必须实现某个接口。这个接口提供一个方法,用来为其注入依赖对象。IoC Service Provider最终通过这些接口来了解应该为被注入对象注入什么依赖对象。相对于前两种依赖注入方式,接口注入比较死板和烦琐。
总体来说,构造方法注入和setter方法注入因为其侵入性较弱,且易于理解和使用,所以是现在使用最多的注入方式。而接口注入因为侵入性较强,近年来已经不流行了。
8、Spring是如何管理Bean的?
什么是Bean管理?
- Spring创建对象
- Spring注入属性
Bean管理操作的两种方式
- 基于xml配置文件方式实现
- 基于注解方式实现
Spring通过IoC容器来管理Bean,我们可以通过XML配置或者注解配置,来指导IoC容器对Bean的管理。因为注解配置比XML配置方便很多,所以现在大多时候会使用注解配置的方式。
使用set方法注入属性: 使用有参构造注入属性
以下是管理Bean时常用的一些注解:
- @ComponentScan用于声明扫描策略,通过它的声明,容器就知道要扫描哪些包下带有声明的类,也可以知道哪些特定的类是被排除在外的。
- @Component、@Repository、@Service、@Controller用于声明Bean,它们的作用一样,但是语义不同。@Component用于声明通用的Bean,@Repository用于声明DAO层的Bean,@Service用于声明业务层的Bean,@Controller用于声明视图层的控制器Bean,被这些注解声明的类就可以被容器扫描并创建。
- @Autowired、@Qualifier用于注入Bean,即告诉容器应该为当前属性注入哪个Bean。其中,@Autowired是按照Bean的类型进行匹配的,如果这个属性的类型具有多个Bean,就可以通过@Qualifier指定Bean的名称,以消除歧义。
- @Scope用于声明Bean的作用域,默认情况下Bean是单例的,即在整个容器中这个类型只有一个实例。可以通过@Scope注解指定prototype值将其声明为多例的,也可以将Bean声明为session级作用域、request级作用域等等,但最常用的还是默认的单例模式。
- @PostConstruct、@PreDestroy用于声明Bean的生命周期。其中,被@PostConstruct修饰的方法将在Bean实例化后被调用,@PreDestroy修饰的方法将在容器销毁前被调用。
9、介绍Bean的作用域有哪些?
默认情况下,Bean在Spring容器中是单例的,我们可以通过@Scope注解修改Bean的作用域。该注解有如下5个取值,它们代表了Bean的5种不同类型的作用域:
- singleton : 在Spring容器仅存在一个Bean实例,Bean以单实例的方式存在,是Bean默认的作用域。
- prototype : 每次从容器重调用Bean时,都会返回一个新的实例。
以下三个作用域于只在Web应用中适用:
- request : 每一次HTTP请求都会产生一个新的Bean,该Bean仅在当前HTTP Request内有效。
- session : 同一个HTTP Session共享一个Bean,不同的HTTP Session使用不同的Bean。
- globalSession:同一个全局Session共享一个Bean,只用于基于Protlet的Web应用,Spring5中已经不存在了。
10、说一说Bean的生命周期
生命周期: 从对象创建到对象销毁的过程 Bean的生命周期:
- 通过构造器创建Bean实例(无参数构造)
- 为Bean的属性设置值和对其他bean引用(调用set方法)
- 把Bean实例传递Bean后置处理器的方法postProcessBeforeInitialization
- 调用Bean的初始化方法(需要进行配置)
- 把Bean实例传递Bean后置处理器的方法postProcessAfterInitialization
- 获取到了Bean对象,Bean就可以使用了
- 当容器关闭的时候,调用Bean的销毁的方法(需要进行配置销毁的方法)
Spring容器管理Bean,涉及对Bean的创建、初始化、调用、销毁等一系列的流程,这个流程就是Bean的生命周期。整个流程参考下图: 这个过程是由Spring容器自动管理的,其中有两个环节我们可以进行干预。
- 我们可以自定义初始化方法,并在该方法前增加@PostConstruct注解,届时Spring容器将在调用SetBeanFactory方法之后调用该方法。
- 我们可以自定义销毁方法,并在该方法前增加@PreDestroy注解,届时Spring容器将在自身销毁前,调用这个方法。
11、Spring是怎么解决循环依赖的?
什么是循环依赖? 简单说就是自己依赖自己,或者和别的Bean相互依赖。
只有单例的Bean才存在循环依赖的情况,原型(Prototype)情况下,Spring会直接抛出异常。原因很简单,AB循环依赖,A实例化的时候,发现依赖B,创建B实例,创建B的时候发现需要A,创建A实例……无限套娃,系统就会崩溃。
假如我们有两个bean,A 和 B。他们的代码简单如下:
@Bean
public class A {
@Autowire
private B b;
}
@Bean
public class B {
@Autowire
private A a;
}
也就是需要在A中注入B,在B中注入A,那么Spring在创建A的时候会出现这种现象:创建A实例后,在依赖注入时需要B,然后就去创建B,这时候发现又需要依赖注入 A ,这样就导致了循环依赖。(类似于死锁) Spring对循环依赖的解决方法可以概括为 用三级缓存方式达到Bean提前曝光的目的。 Spring创建的过程简单的可以简单概括为实例化——>依赖注入(属性赋值)——>初始化。而Spring解决循环依赖的方法就是在实例化之后,依赖注入之前,将实例化的对象放到缓存中进行提前曝光,后边的对象则在实例化前,先到缓存中查找有无对应的实例化对象即可。
具体分析: Spring创建的过程简单的可以简单概括为实例化——>依赖注入(属性赋值)——>初始化。
注入就发生在第二步,属性赋值,结合这个过程,Spring 通过三级缓存解决了循环依赖:
- 一级缓存 : Map<String,Object> singletonObjects,单例池,用于保存实例化、属性赋值(注入)、初始化完成的 bean 实例。
- 二级缓存 : Map<String,Object> earlySingletonObjects,早期曝光对象,用于保存实例化完成的 bean 实例。
- 三级缓存 : Map<String,ObjectFactory<?>> singletonFactories,早期曝光对象工厂,用于保存 bean 创建工厂,以便于后面扩展有机会创建代理对象。
三级缓存解决循环依赖的过程:
A实例的初始化过程:
- 创建A实例,实例化的时候把A对象??放?三级缓存,表示A开始实例化了,虽然我这个对象还不完整,但是先曝光出来让大家知道。
- A注?属性时,发现依赖B,此时B还没有被创建出来,所以去实例化B。
- 同样,B注?属性时发现依赖A,它就会从缓存里找A对象。依次从?级到三级缓存查询A,从三级缓存通过对象??拿到A,发现A虽然不太完善,但是存在,把A放??级缓存,同时删除三级缓存中的A,此时,B已经实例化并且初始化完成,把B放入?级缓存。
- 接着A继续属性赋值,顺利从?级缓存拿到实例化且初始化完成的B对象,A对象创建也完成,删除?级缓存中的A,同时把A放??级缓存。
- 最后,?级缓存中保存着实例化、初始化都完成的A、B对象。
为什么需要三级缓存,二级不行吗?
不行,主要是为了?成代理对象。如果是没有代理的情况下,使用二级缓存解决循环依赖也是OK的。但是如果存在代理,三级没有问题,二级就不行了。
因为三级缓存中放的是?成具体对象的匿名内部类,获取Object的时候,它可以?成代理对象,也可以返回普通对象。使?三级缓存主要是为了保证不管什么时候使?的都是?个对象。
假设只有?级缓存的情况,往?级缓存中放的显示?个普通的Bean对象,Bean初始化过程中,通过 BeanPostProcessor 去?成代理对象之后,覆盖掉?级缓存中的普通Bean对象,那么可能就导致取到的Bean对象不一致了。
12、@Autowired和@Resource注解有什么区别?
- @Autowired是Spring提供的注解,@Resource是JDK提供的注解。
- @Autowired是只能按类型注入,@Resource默认按名称注入,也支持按类型注入。
- @Autowired按类型装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它required属性为false,如果我们想使用按名称装配,可以结合@Qualifier注解一起使用。@Resource有两个中重要的属性:name和type。name属性指定byName,如果没有指定name属性,当注解标注在字段上,即默认取字段的名称作为bean名称寻找依赖对象,当注解标注在属性的setter方法上,即默认取属性名作为bean名称寻找依赖对象。需要注意的是,@Resource如果没有指定name属性,并且按照默认的名称仍然找不到依赖对象时, @Resource注解会回退到按类型装配。但一旦指定了name属性,就只能按名称装配了。
13、Spring中默认提供的单例是线程安全的吗?
首先结论在这:Spring中的单例Bean不是线程安全的。
因为单例Bean,是全局只有一个Bean,所有线程共享。如果说单例Bean,是一个无状态的,也就是线程中的操作不会对Bean中的成员变量执行查询以外的操作,那么这个单例Bean是线程安全的。比如Spring mvc 的 Controller、Service、Dao等,这些Bean大多是无状态的,只关注于方法本身。
假如这个Bean是有状态的,也就是会对Bean中的成员变量进行写操作,那么可能就存在线程安全的问题。
单例Bean线程安全问题怎么解决呢?
- 将Bean定义为多例,这样每一个线程请求过来都会创建一个新的Bean,但是这样容器就不好管理Bean,不能这么办。
- 在Bean对象中尽量避免定义可变的成员变量,削足适履了属于是,也不能这么干。
- 将Bean中的成员变量保存在ThreadLocal中,我们知道ThredLoca能保证多线程下变量的隔离,可以在类中定义一个ThreadLocal成员变量,将需要的可变成员变量保存在ThreadLocal里,这是推荐的一种方式。(采用这种方式)
14、说一说你对Spring AOP的理解
AOP 可以将遍布应用各处的功能分离出来形成可重用的组件。在编译期间、装载期间或运行期间实现在不修改源代码的情况下给程序动态添加功能。从而实现对业务逻辑的隔离,提高代码的模块化能力。
AOP 的核心其实就是动态代理,如果是实现了接口的话就会使用 JDK 动态代理,否则使用 CGLIB 代理,主要应用于处理一些具有横切性质的系统级服务,如日志收集、事务管理、安全检查、缓存、对象池管理等。 AOP的术语:
-
切面(aspect):是一个可以定义切点、各类通知和引入的内容,SpringAOP将通过它的信息来增强Bean的功能或者将对应的方法织入流程。 -
连接点(join point):对应的是具体被拦截的对象,因为Spring只能支持方法,所以被拦截的对象往往就是指特定的方法,AOP将通过动态代理技术把它织入对应的流程中。 -
切点(point cut):有时候,我们的切面不单单应用于单个方法,也可能是多个类的不同方法,这时,可以通过正则式和指示器的规则去定义,从而适配连接点。切点就是提供这样一个功能的概念。 -
通知(advice):就是按照约定的流程下的方法,分为前置通知、后置通知、环绕通知、事后返回通知和异常通知,它会根据约定织入流程中。 -
目标对象(target):即被代理对象。 -
引入(introduction):是指引入新的类和其方法,增强现有Bean的功能。 -
织入(weaving):它是一个通过动态代理技术,为原有服务对象生成代理对象,然后将与切点定义匹配的连接点拦截,并按约定将各类通知织入约定流程的过程。
AOP可以有多种实现方式,而Spring AOP支持如下两种实现方式。(可以问两者的区别)
- JDK动态代理:这是Java提供的动态代理技术,可以在运行时创建接口的代理实例。Spring AOP默认采用这种方式,在接口的代理实例中织入代码。
- CGLib动态代理:采用底层的字节码技术,在运行时创建子类代理的实例。当目标对象不存在接口时,Spring AOP就会采用这种方式,在子类实例中织入代码。
15、请你说说AOP的应用场景与实现一个AOP场景
Spring AOP为IoC的使用提供了更多的便利,一方面,应用可以直接使用AOP的功能,设计应用的横切关注点,把跨越应用程序多个模块的功能抽象出来,并通过简单的AOP的使用,灵活地编制到模块中,比如可以通过AOP实现应用程序中的日志功能。另一方面,在Spring内部,一些支持模块也是通过Spring AOP来实现的,比如事务处理。从这两个角度就已经可以看到Spring AOP的核心地位了。 视频:狂神说
16、Spring AOP不能对哪些类进行增强?
- Spring AOP只能对IoC容器中的Bean进行增强,对于不受容器管理的对象不能增强。
- 由于CGLib采用动态创建子类的方式生成代理对象,所以不能对final修饰的类进行代理。
17、说说JDK 动态代理和 CGLIB 代理 ?为什么Spring还要使用JDK动态代理?
Spring的AOP是通过动态代理来实现的,动态代理主要有两种方式JDK动态代理和Cglib动态代理,这两种动态代理的使用和原理有些不同。
JDK 动态代理
- Interface:对于 JDK 动态代理,目标类需要实现一个Interface。
- InvocationHandler:InvocationHandler是一个接口,可以通过实现这个接口,定义横切逻辑,再通过反射机制(invoke)调用目标类的代码,在次过程,可能包装逻辑,对目标方法进行前置后置处理。
- Proxy:Proxy利用InvocationHandler动态创建一个符合目标类实现的接口的实例,生成目标类的代理对象。
CgLib 动态代理
- 使用JDK创建代理有一大限制,它只能为接口创建代理实例,而CgLib 动态代理就没有这个限制。
- CgLib 动态代理是使用字节码处理框架 ASM,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。
- CgLib 创建的动态代理对象性能比 JDK 创建的动态代理对象的性能高不少,但是 CGLib 在创建代理对象时所花费的时间却比 JDK 多得多,所以对于单例的对象,因为无需频繁创建对象,用 CGLib 合适,反之,使用 JDK 方式要更为合适一些。同时,由于 CGLib 由于是采用动态创建子类的方法,对于 final 方法,无法进行代理。
原因: 在性能方面,CGLib创建的代理对象比JDK动态代理创建的代理对象高很多。但是,CGLib在创建代理对象时所花费的时间比JDK动态代理多很多。所以,对于单例的对象因为无需频繁创建代理对象,采用CGLib动态代理比较合适。反之,对于多例的对象因为需要频繁的创建代理对象,则JDK动态代理更合适。
18、Spring如何管理事务?
Spring为事务管理提供了一致的编程模板,在高层次上建立了统一的事务抽象。也就是说,不管是选择MyBatis、Hibernate、JPA还是Spring JDBC,Spring都可以让用户以统一的编程模型进行事务管理。
Spring支持两种事务编程模型:
1、编程式事务管理
Spring提供了TransactionTemplate模板,利用该模板我们可以通过编程的方式实现事务管理,而无需关注资源获取、复用、释放、事务同步及异常处理等操作。相对于声明式事务来说,这种方式相对麻烦一些,但是好在更为灵活,我们可以将事务管理的范围控制的更为精确。
2、声明式事务管理
Spring事务管理的亮点在于声明式事务管理,它允许我们通过声明的方式,在IoC配置中指定事务的边界和事务属性,Spring会自动在指定的事务边界上应用事务属性。相对于编程式事务来说,这种方式十分的方便,只需要在需要做事务管理的方法上,增加@Transactional注解,以声明事务特征即可。 1 基于注解方式。2 基于XML方式
19、Spring的事务传播方式有哪些?
当我们调用一个业务方法时,它的内部可能会调用其他的业务方法,以完成一个完整的业务操作。这种业务方法嵌套调用的时候,如果这两个方法都是要保证事务的,那么就要通过Spring的事务传播机制控制当前事务如何传播到被嵌套调用的业务方法中。
Spring在TransactionDefinition接口中规定了7种类型的事务传播行为,它们规定了事务方法和事务方法发生嵌套调用时如何进行传播,如下表:(了解)
20、Spring的事务如何配置,常用注解有哪些?
事务的打开、回滚和提交是由事务管理器来完成的,我们使用不同的数据库访问框架,就要使用与之对应的事务管理器。在Spring Boot中,当你添加了数据库访问框架的起步依赖时,它就会进行自动配置,即自动实例化正确的事务管理器。
对于声明式事务,是使用@Transactional进行标注的。这个注解可以标注在类或者方法上。
- 当它标注在类上时,代表这个类所有公共(public)非静态的方法都将启用事务功能。
- 当它标注在方法上时,代表这个方法将启用事务功能。
另外,在@Transactional注解上,我们可以使用isolation属性声明事务的隔离级别,使用propagation属性声明事务的传播机制。
21、说一说你对声明式事务的理解
Spring事务管理的亮点在于声明式事务管理,它允许我们通过声明的方式,在IoC配置中指定事务的边界和事务属性,Spring会自动在指定的事务边界上应用事务属性。相对于编程式事务来说,这种方式十分的方便,只需要在需要做事务管理的方法上,增加@Transactional注解,以声明事务特征即可。
22、Spring中有哪些常用的注解?
Web:
- @Controller:组合注解(组合了@Component注解),应用在MVC层(控制层)。
- @RestController:该注解为一个组合注解,相当于@Controller和@ResponseBody的组合,注解在类上,意味着,
- Controller的所有方法都默认加上了@ResponseBody。
- @RequestMapping:用于映射Web请求,包括访问路径和参数。如果是Restful风格接口,还可以根据请求类型使用不同的注解:@GetMapping @PostMapping @PutMapping @DeleteMapping
- @ResponseBody:支持将返回值放在response内,而不是一个页面,通常用户返回json数据。
- @RequestBody:允许request的参数在request体中,而不是在直接连接在地址后面。
- @PathVariable:用于接收路径参数,比如@RequestMapping(“/hello/{name}”)申明的路径,将注解放在参数中前,即可获取该值,通常作为Restful的接口实现方法。
- @RestController:该注解为一个组合注解,相当于@Controller和@ResponseBody的组合,注解在类上,意味着,该Controller的所有方法都默认加上了@ResponseBody。
容器:
- @Component:表示一个带注释的类是一个“组件”,成为Spring管理的Bean。当使用基于注解的配置和类路径扫描时,这些类被视为自动检测的候选对象。同时@Component还是一个元注解。
- @Service:组合注解(组合了@Component注解),应用在service层(业务逻辑层)。
- @Repository:组合注解(组合了@Component注解),应用在dao层(数据访问层)。
- @Autowired:Spring提供的工具(由Spring的依赖注入工具(BeanPostProcessor、BeanFactoryPostProcessor)自动注入)。
- @Qualifier:该注解通常跟 @Autowired 一起使用,当想对注入的过程做更多的控制,@Qualifier 可帮助配置,比如两个以上相同类型的 Bean 时 Spring 无法抉择,用到此注解。
- @Configuration:声明当前类是一个配置类(相当于一个Spring配置的xml文件)。
- @Value:可用在字段,构造器参数跟方法参数,指定一个默认值,支持 #{} 跟 ${} 两个方式。一般将 SpringbBoot 中的 application.properties 配置的属性值赋值给变量。
- @Bean:注解在方法上,声明当前方法的返回值为一个Bean。返回的Bean对应的类中可以定义init()方法和destroy()方法,然后在@Bean(initMethod=”init”,destroyMethod=”destroy”)定义,在构造之后执行init,在销毁之前执行destroy。
- @Scope:定义我们采用什么模式去创建Bean(方法上,得有@Bean)其设置类型包括:Singleton 、Prototype、Request 、 Session、GlobalSession。
AOP:
- @Aspect:声明一个切面(类上) 使用@After、@Before、@Around定义建言(advice),可直接将拦截规则(切点)作为参数。@After :在方法执行之后执行(方法上)。@Before: 在方法执行之前执行(方法上)。@Around: 在方法执行之前与之后执行(方法上)。@PointCut: 声明切点 在java配置类中使用@EnableAspectJ***注解开启Spring对AspectJ代理的支持(类上)。
事务:
@Transactional:在要开启事务的方法上使用@Transactional注解,即可声明式开启事务。
23、Spring中使用了什么设计模式?
- 厂模式 : Spring 容器本质是一个大工厂,使用工厂模式通过 BeanFactory、ApplicationContext 创建 bean 对象。
- 代理模式 : Spring AOP 功能功能就是通过代理模式来实现的,分为动态代理和静态代理。
- 单例模式 : Spring 中的 Bean 默认都是单例的,这样有利于容器对Bean的管理。
- 模板模式 : Spring 中 JdbcTemplate、RestTemplate 等以 Template结尾的对数据库、网络等等进行操作的模板类,就使用到模板模式。
- 观察者模式: Spring 事件驱动模型就是观察者模式很经典的一个应用。
- 适配器模式 :Spring AOP 的增强或通知 (Advice) 使用到了适配器模式、Spring MVC 中也是用到了适配器模式适配 Controller。
- 策略模式:Spring中有一个Resource接口,它的不同实现类,会根据不同的策略去访问资源。
24、Spring有哪些自动装配的方式?
什么是自动装配?
Spring IOC容器知道所有Bean的配置信息,此外,通过Java反射机制还可以获知实现类的结构信息,如构造方法的结构、属性等信息。掌握所有Bean的这些信息后,Spring IOC容器就可以按照某种规则对容器中的Bean进行自动装配,而无须通过显式的方式进行依赖配置。
Spring提供的这种方式,可以按照某些规则进行Bean的自动装配,元素提供了一个指定自动装配类型的属性:autowire=“<自动装配类型>”
Spring提供了哪几种自动装配类型?
- byName:根据名称进行自动匹配,假设Boss又一个名为car的属性,如果容器中刚好有一个名为car的bean,Spring就会自动将其装配给Boss的car属性
- byType:根据类型进行自动匹配,假设Boss有一个Car类型的属性,如果容器中刚好有一个Car类型的Bean,Spring就会自动将其装配给Boss这个属性
- constructor:与 byType类似, 只不过它是针对构造函数注入而言的。如果Boss有一个构造函数,构造函数包含一个Car类型的入参,如果容器中有一个Car类型的Bean,则Spring将自动把这个Bean作为Boss构造函数的入参;如果容器中没有找到和构造函数入参匹配类型的Bean,则Spring将抛出异常。
- autodetect:根据Bean的自省机制决定采用byType还是constructor进行自动装配,如果Bean提供了默认的构造函数,则采用byType,否则采用constructor。
25、@Autowired的实现原理是什么?
实现@Autowired的关键是:AutowiredAnnotationBeanPostProcessor
在Bean的初始化阶段,会通过Bean后置处理器来进行一些前置和后置的处理。实现@Autowired的功能,也是通过后置处理器来完成的。这个后置处理器就是AutowiredAnnotationBeanPostProcessor。
Spring在创建bean的过程中,最终会调用到doCreateBean()方法,在doCreateBean()方法中会调用populateBean()方法,来为bean进行属性填充,完成自动装配等工作。
在populateBean()方法中一共调用了两次后置处理器,第一次是为了判断是否需要属性填充,如果不需要进行属性填充,那么就会直接进行return,如果需要进行属性填充,那么方法就会继续向下执行,后面会进行第二次后置处理器的调用,这个时候,就会调用到AutowiredAnnotationBeanPostProcessor的postProcessPropertyValues()方法,在该方法中就会进行@Autowired注解的解析,然后实现自动装配。
26、Spring AOP和AspectJ AOP有什么区别?
Spring AOP是属于运行时增强,而AspectJ是编译时增强。Spring AOP基于代理(Proxying),而AspectJ基于字节码操作(Bytecode Manipulation)。
Spring AOP已经集成了AspectJ,AspectJ应该算得上是Java生态系统中最完整的AOP框架了。AspectJ相比于Spring AOP功能更加强大,但是Spring AOP相对来说更简单。
如果我们的切面比较少,那么两者性能差异不大。但是,当切面太多的话,最好选择AspectJ,它比SpringAOP快很多。
|