天天看点

一篇文章讲透spring ioc (概念、原理、实例、应用场景、面试题)

    • ioc概念
    • 优势
    • 原理
    • 常见api
    • 实例
    • 应用场景
    • 常见面试题
        ioc概念、优势、原理、常见使用、实例、应用场景 ioc概念与定义:ioc翻译成中文为控制反转。意思可以理解为类与类之间的依赖通过容器来控制、配置实现;换一种方式可以这么理解 原来类与类之间在编译时即产生了依赖,即new 对象时,已建立好两者之间的依赖关系;ioc机制将两者之间的依赖关系通过容器进行控制,通过注解的方式,在运行时阶段,动态的将某种依赖注入到类当中。 优势:
    • 减少了代码之间的耦合度
    • 增强了类的扩展性
    • 简化配置,提高代码复用性
      原理:ioc主要是通过反射原理实现 ,设计流程如下:
    一篇文章讲透spring ioc (概念、原理、实例、应用场景、面试题)
                                                      ioc反射原理实现 流程解析: 
    1. 根据路径、资源名称等方式,将xml文件、注解类加载到容器中
    2. 通过BeanDefinitionReader将对象解析成BeanDefinition实例
    3. 创建BeanFactory工厂(注册前后需要添加bean前置、后置处理器)
    4. 通过BeanFactory工厂将对象实例化、对象初始化(初始化前后执行前置、后置处理器)
      核心类解析:
    • BeanDefinition:  
    bean定义类,定义了bean内部的组成方式(例如class、properties、autoware等)  配置的xml文件,annotation类的 注解都基于此定义类实现。 容器中的每一个 bean 都会有一个对应的 BeanDefinition 实例。该实例负责保存 bean 对象的所有必要信息,包括 bean 对象的 class 类型、是否是抽象类、构造方法和参数、其他属性等等。
    • BeanDefinitionReader:   加载、解析资源
    bean定义类读取接口:定义上文BeanDefinition实例的读取方式。通过获取类加载器、文件路径资源等方式读取实例所在的xml文件或者java类,将其加载到虚拟机中。最终将其解析成为BeanDefinition实例对象
    一篇文章讲透spring ioc (概念、原理、实例、应用场景、面试题)
    • BeanFactory
    spring Bean处理的主入口,负责Bean的创建与管理。 常见使用例如  ApplicationContext、 ClassPathXmlApplicationContext 即是其中的实例 public void refresh() throws BeansException, IllegalStateException {    synchronized (this.startupShutdownMonitor) {       // Prepare this context for refreshing.       prepareRefresh();           // 创建bean工厂,读取xml文件,加载beanDefinitions到缓存中       ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();           // bean工厂前置处理       prepareBeanFactory(beanFactory);           try {        注册bean工厂处理器          postProcessBeanFactory(beanFactory);             //执行bean工厂          invokeBeanFactoryPostProcessors(beanFactory);              // 注册bean处理器          registerBeanPostProcessors(beanFactory);              // Initialize message source for this context.          initMessageSource();              // 初始化容器事件广播          initApplicationEventMulticaster();              // Initialize other special beans in specific context subclasses.          onRefresh();              // 注册事件监听器          registerListeners();              // Instantiate all remaining (non-lazy-init) singletons.          finishBeanFactoryInitialization(beanFactory);              // Last step: publish corresponding event.          finishRefresh();       }
    •  BeanFactoryPostProcessor
    BeanFactory级别的处理,是针对整个Bean的工厂进行处理,在bean工厂初始化前后进行处理。
    • BeanPostProcessor

    bean级别的处理,针对某个具体的bean进行处理。在bean初始化操作的前后进行处理   常见api : @Controller 用于标注控制器层组件 @Service 用于标注业务层组件 @Component 用于标注这是一个受 Spring 管理的组件,组件引用名称是类名,第一个字母小写。可以使用@Component(“beanID”) 指定组件的名称 @Repository 用于标注数据访问组件,即DAO组件 @Auwared 方法级别的注解,主要用在@Configuration和@Component注解的类里,@Bean注解的方法会产生一个Bean对象,该对象由Spring管理并放到IoC容器中。引用名称是方法名,也可以用@Bean(name = "beanID")指定组件名 @Resource 默认按名称进行自动装配,当找不到与名称匹配的Bean时会按类型装配。 @Scope("protoype") 将组件的范围设置为原型的(即多例)。保证每一个请求有一个单独的action来处理,避免action的线程问题。   常见面试题 : 1、Spring是什么? Spring是一个轻量级的IoC和AOP容器框架。目的是解决企业应用开发的复杂性,使用基本的JavaBean来完成以前只可能由EJB完成的事情,并提供了更多的企业应用功能,Spring的用途不仅限于服务器端的开发,从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。 2、Spring 的优点? (1)spring属于低侵入式设计,代码的污染极低; (2)spring的DI机制降低了业务对象替换的复杂性; (3)容器提供了AOP技术,利用它很容易实现如权限拦截,运行期监控等功能; (4)降低了组件之间的耦合性 ,实现了软件各层之间的解耦; (5)容器提供单例模式支持; (6)可以使用容器提供的众多服务,如事务管理,消息服务等; (7)容器提供了众多的辅助类,能加快应用的开发; (8)spring对于主流的应用框架提供了集成支持,如hibernate,JPA,Struts等 (9)独立于各种应用服务器 (10)Spring的高度开放性,并不强制应用完全依赖于Spring,开发者可以自由选择spring的部分或全部。 4、Spring的IoC理解: (1)IOC就是控制反转。就是对象的创建权反转交给Spring,由容器控制程序之间的依赖关系,作用是实现了程序的解耦合,而非传统实现中,由程序代码直接操控。(依赖)控制权由应用代码本身转到了外部容器,由容器根据配置文件去创建实例并管理各个实例之间的依赖关系,控制权的转移,是所谓反转,并且由容器动态的将某种依赖关系注入到组件之中。BeanFactory 是Spring IoC容器的具体实现与核心接口,提供了一个先进的配置机制,使得任何类型的对象的配置成为可能,用来包装和管理各种bean。 (2)最直观的表达就是,IOC让对象的创建不用去new了,可以由spring自动生产,这里用的就是java的反射机制,通过反射在运行时动态的去创建、调用对象。spring就是根据配置文件在运行时动态的去创建对象,并调用对象的方法的。

    (3)Spring的IOC有三种注入方式 : 第一是根据属性注入,也叫set方法注入; 第二种是根据构造方法进行注入; 第三种是根据注解进行注入。 详细的说: (4)IoC,控制反转:将对象交给容器管理,你只需要在spring配置文件总配置相应的bean,以及设置相关的属性,让spring容器生成类的实例对象以及管理对象。在spring容器启动的时候,spring会把你在配置文件中配置的bean都初始化以及装配好,然后在你需要调用的时候,就把它已经初始化好的那些bean分配给你需要调用这些bean的类。就是将对象的控制权反转给spring容器管理。 (5)DI机制(Dependency Injection,依赖注入):可以说是IoC的其中一个内容,在容器实例化对象的时候主动的将被调用者(或者说它的依赖对象)注入给调用对象。比如对象A需要操作数据库,以前我们总是要在A中自己编写代码来获得一个Connection对象,有了 spring我们就只需要告诉spring,A中需要一个Connection,至于这个Connection怎么构造,何时构造,A不需要知道。在系统运行时,spring会在适当的时候制造一个Connection,然后像打针一样,注射到A当中,这样就完成了对各个对象之间关系的控制。 IoC让相互协作的组件保持松散的耦合,而AOP编程允许你把遍布于应用各层的功能分离出来形成可重用的功能组件。 5、BeanFactory和ApplicationContext有什么区别? BeanFactory和ApplicationContext是Spring的两大核心接口,而其中ApplicationContext是BeanFactory的子接口。它们都可以当做Spring的容器,生成Bean实例的,并管理容器中的Bean。 (1)BeanFactory:是Spring里面最底层的接口,提供了最简单的容器的功能,负责读取bean配置文档,管理bean的加载与实例化,维护bean之间的依赖关系,负责bean的生命周期,但是无法支持spring的aop功能和web应用。 (2)ApplicationContext接口作为BeanFactory的派生,因而具有BeanFactory所有的功能。而且ApplicationContext还在功能上做了扩展,以一种更面向框架的方式工作以及对上下文进行分层和实现继承,相较于BeanFactorty,ApplicationContext还提供了以下的功能: ①默认初始化所有的Singleton,也可以通过配置取消预初始化。 ②继承MessageSource,因此支持国际化。 ③资源访问,比如访问URL和文件。 ④事件机制。 ⑤同时加载多个配置文件。 ⑥以声明式方式启动并创建Spring容器。 ⑦载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层。 (3)①BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化,这样,我们就不能发现一些存在的Spring的配置问题。如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次使用调用getBean方法才会抛出异常。 ②而ApplicationContext则相反,它是在容器启动时,一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误,这样有利于检查所依赖属性是否注入。 ApplicationContext启动后预载入所有的单实例Bean,通过预载入单实例bean ,确保当你需要的时候,你就不用等待,因为它们已经创建好了。   ③相对于基本的BeanFactory,ApplicationContext 唯一的不足是占用内存空间。当应用程序配置Bean较多时,程序启动较慢。 (4)BeanFactory通常以编程的方式被创建,ApplicationContext还能以声明的方式创建,如使用ContextLoader。 (5)BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册。 6、 解释Spring支持的几种bean的作用域。 Spring容器中的bean可以分为5个范围:

    (1)singleton:这种bean范围是默认的,这种范围确保不管接受到多少个请求,每个容器中只有一个bean的实例,单例的模式由bean factory自身来维护。 (2)prototype:原形范围与单例范围相反,为每一个bean请求提供一个实例。 (3)request:在请求bean范围内会每一个来自客户端的网络请求创建一个实例,在请求完成以后,bean会失效并被垃圾回收器回收。 (4)Session:与请求范围类似,确保每个session中有一个bean的实例,在session过期后,bean会随之失效。 (5)global-session:global-session和Portlet应用相关。当你的应用部署在Portlet容器中工作时,它包含很多portlet。如果你想要声明让所有的portlet共用全局的存储变量的话,那么这全局变量需要存储在global-session中。全局作用域与Servlet中的session作用域效果相同。 7、请解释Spring Bean的生命周期? 首先说一下Servlet的生命周期:实例化,初始init,接收请求service,销毁destroy; Spring上下文中的Bean生命周期也类似,如下: (1)实例化一个Bean--也就是我们常说的new; (2)按照Spring上下文对实例化的Bean进行配置--也就是IOC注入; (3)如果这个Bean已经实现了BeanNameAware接口,会调用它实现的setBeanName(String)方法,此处传递的就是Spring配置文件中Bean的id值; (4)如果这个Bean已经实现了BeanFactoryAware接口,会调用它实现的setBeanFactory(setBeanFactory(BeanFactory)传递的是Spring工厂自身(可以用这个方式来获取其它Bean,只需在Spring配置文件中配置一个普通的Bean就可以); (5)如果这个Bean已经实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文(同样这个方式也可以实现步骤4的内容,但比4更好,因为ApplicationContext是BeanFactory的子接口,有更多的实现方法); (6)如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessBeforeInitialization(Object obj, String s)方法,BeanPostProcessor经常被用作是Bean内容的更改,并且由于这个是在Bean初始化结束时调用那个的方法,也可以被应用于内存或缓存技术; (7)如果Bean在Spring配置文件中配置了init-method属性会自动调用其配置的初始化方法。 (8)如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法、; 注:以上工作完成以后就可以应用这个Bean了,那这个Bean是一个Singleton的,所以一般情况下我们调用同一个id的Bean会是在内容地址相同的实例,当然在Spring配置文件中也可以配置非Singleton。 (9)当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用那个其实现的destroy()方法; (10)最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法。 另外我们这里描述的是应用Spring上下文Bean的生命周期,如果应用Spring的工厂也就是BeanFactory的话去掉第5步就Ok了。 8、Spring中bean的加载过程: (1)获取配置文件资源; (2)对获取的xml资源进行一定的处理检验; (3)处理包装资源; (4)解析处理包装过后的资源; (5)加载提取bean并注册(添加到beanDefinitionMap中)。 9、Spring框架中的单例Beans是线程安全的么? Spring框架并没有对单例bean进行任何多线程的封装处理。关于单例bean的线程安全和并发问题需要开发者自行去搞定。但实际上,大部分的Spring bean并没有可变的状态(比如Serview类和DAO类),所以在某种程度上说Spring的单例bean是线程安全的。如果你的bean有多种状态的话(比如 View Model 对象),就需要自行保证线程安全。 最浅显的解决办法就是将多态bean的作用域由“singleton”变更为“prototype”。 10、Spring如何处理线程并发问题? Spring使用ThreadLocal解决线程安全问题。 我们知道在一般情况下,只有有状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域。就是因为Spring对一些Bean(如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等)中非线程安全状态采用ThreadLocal进行处理,让它们也成为线程安全的状态,因为有状态的Bean就可以在多线程中共享了。 ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。 (1)在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。这时该变量是多个线程共享的,使用同步机制要求程序慎密地分析什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放对象锁等繁杂的问题,程序设计和编写难度相对较大。 (2)而ThreadLocal则从另一个角度来解决多线程的并发访问。ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。 (3)概括起来说,对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。 11、请解释Spring自动装配模式的区别? 在Spring框架中共有5种自动装配: (1)no:这是Spring框架的默认设置,在该设置下自动装配是关闭的,开发者需要自行在bean定义中用标签明确的设置依赖关系。 (2)byName:该选项可以根据bean名称设置依赖关系。当向一个bean中自动装配一个属性时,容器将根据bean的名称自动在配置文件中查询一个匹配的bean。如果找到的话,就装配这个属性,如果没找到的话就报错。 (3)byType:该选项可以根据bean类型设置依赖关系。当向一个bean中自动装配一个属性时,容器将根据bean的类型自动在在配置文件中查询一个匹配的bean。如果找到的话,就装配这个属性,如果没找到的话就报错。 (4)constructor:构造器的自动装配和byType模式类似,但是仅仅适用于与有构造器相同参数的bean,如果在容器中没有找到与构造器参数类型一致的bean,那么将会抛出异常。 (5)autodetect:该模式自动探测使用构造器自动装配或者byType自动装配。首先,首先会尝试找合适的带参数的构造器,如果找到的话就是用构造器自动装配,如果在bean内部没有找到相应的构造器或者是无参构造器,容器就会自动选择byTpe的自动装配方式。 12、Spring 控制器的加载过程:(XML版) (1)Web容器创建; (2)上下文创建,但未初始化; (3)监听器创建,并注册到Context上; (4)上下文初始化; (5)通知到监听者,Spring配置文件/@Configuration加载; (6)Load-on-startup>0的ServletConfig创建,springMVC的DispatcherServlet此时创建。 PS:Spring容器时SpringMVC的父容器。Spring的AOP在Spring的上下文创建时就会创建;如果想要代理SpringMVC的控制层,需要将配置写到SpringMVC的配置文件下。 13、Spring 框架中都用到了哪些设计模式? (1)代理模式—在AOP和remoting中被用的比较多。 (2)单例模式—在spring配置文件中定义的bean默认为单例模式。 (3)工厂模式—BeanFactory用来创建对象的实例。 (4)模板方法—用来解决代码重复的问题。比如. RestTemplate, JmsTemplate, JpaTemplate。 (5)前端控制器—Spring提供了DispatcherServlet来对请求进行分发。 (6)视图帮助(View Helper )—Spring提供了一系列的JSP标签,高效宏来辅助将分散的代码整合在视图里。 (7)依赖注入—贯穿于BeanFactory / ApplicationContext接口的核心理念。 14、Spring事务的种类和各自的区别: spring支持编程式事务管理和声明式事务管理两种方式: (1)编程式事务管理使用TransactionTemplate或者直接使用底层的PlatformTransactionManager。对于编程式事务管理,spring推荐使用TransactionTemplate。 (2)声明式事务管理建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。声明式事务最大的优点就是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明(或通过基于@Transactional注解的方式),便可以将事务规则应用到业务逻辑中。 (3)显然声明式事务管理要优于编程式事务管理,这正是spring倡导的非侵入式的开发方式。声明式事务管理使业务代码不受污染,一个普通的POJO对象,只要加上注解就可以获得完全的事务支持。和编程式事务相比,声明式事务唯一不足地方是,后者的最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。

    15、spring的事务传播行为: spring事务的传播行为说的是当一个方法调用另一个方法时,事务该如何操作。 (1)PROPAGATION_REQUIRED:如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置。 (2)PROPAGATION_SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。‘ (3)PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。 (4)PROPAGATION_REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建新事务。 (5)PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 (6)PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。 (7)PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。 16、Spring事务的实现方式和实现原理: (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声明式事务处理的设计体系。                    

继续阅读