当前位置:   article > 正文

IOC、AOP以及spring框架的面试汇总_spring ioc和aop原理 面试

spring ioc和aop原理 面试

控制反转(IOC)

将创建管理对象的工作交给容器来做。在容器初始化(或在某个时间节点)通过反射机制创建好对象,在使用时直接从容器中获取。

控制反转:将对象的控制权反过来交给容器管理。

官方解释

控制反转即IoC (Inversion of Control),它把传统上由程序代码直接操控的对象的调用权交给容器,通过容器来实现对象组件的装配和管理。所谓的“控制反转”概念就是对组件对象控制权的转移,从程序代码本身转移到了外部容器。

IOC实现原理(流程):(反射+XML技术)
利用(反射+工厂)技术,根据配置文件中给出的类名生成相应的对象。

  1. Tomcat启动Spring容器;
  2. 初始化Spring容器,加载application.xml配置文件;
  3. 获取扫描包下所有class文件;
  4. 解析class中的注解信息;
  5. 通过反射实例化相应bean对象,以<beanId,bean>的形式保存集合,存储在IOC容器中。
  6. 通过ApplicationContext的getBean方法获取Bean。

SpringIOC流程图 

通俗理解IOC是什么?

Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想。如何理解好Ioc呢?理解好Ioc的关键是要明确“谁控制谁,控制什么,为何是反转(有反转就应该有正转了),哪些方面反转了”。

  • 谁控制谁,控制什么:传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对 象的创建;谁控制谁?当然是IoC 容器控制了对象;控制什么?那就是主要控制了外部资源获取(不只是对象包括比如文件等)。
  • 为何是反转,哪些方面反转了:有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了。

IoC很好的体现了面向对象设计法则之一—— 好莱坞法则:“别找我们,我们找你”;即由IoC容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。 

IOC优势

创建Bean及其依赖对象的工作交给IOC容器管理,业务代码只需要getBean就行了。将依赖关系写入配置文件中,有修改时,直接修改配置文件即可,而不用去业务代码中每一个使用Bean的地方修改。 

IOC类型

  1. 构造注入:通过容器触发一个类的构造器来实现的,该类有一系列参数,每个参数代表一个对其他类的依赖。(对象的依赖关系可在构造函数中完成)。
  2. Setter方法注入:容器通过调用无参构造器或无参static方法实例化bean之后,调用bean的setter方法。

IOC的作用

产生对象实例,所以它是基于工厂设计模式的  

Spring IOC的注入

通过属性进行注入;通过构造函数进行注入;注入对象数组;注入List集合;注入Map集合;注入Properties类型

Spring IOC 自动绑定模式

可以设置autowire按以下方式进行绑定:

byType只要类型一致会自动寻找;按byName自动按属性名称进行自动查找匹配

Spring实现IOC控制反转描述

原来需要我们自己进行bean的创建以及注入,而现在交给spring容器去完成bean的创建以及注入。所谓的“控制反转”就是 对象控制权的转移,从程序代码本身转移到了外部容器。控制反转,把创建对象的权利交给spring;

IOC容器的初始化过程
当创建一个ClassPathXmlApplicationContext时,构造方法做了两件事情:①调用父容器的构造方法为容器设置好Bean资源加载器。②调用父类的setConfigLocations方法设置Bean配置信息的定位路径。Spring Ioc容器的作用就是对这些注册的Bean定义信息进行处理和维护,注册的Bean定义信息是控制反转和依赖注入的基础。

基于注解的容器初始化

  • 直接将注解Bean注册到容器中,可以在初始化容器时注册,也可以在容器创建之后手动注册,然后刷新容器使其对注册的注解Bean进行处理。
  • 通过扫描指定的包及其子包的所有类处理,在初始化注解容器时指定要自动扫描的路径。

二、DI(依赖注入)

什么是DI

dependency injection;属性的依赖注入,spring在通过IOC创建对象的时候,如果对象还有属性,就一并给赋值进去DI是在IOC的基础上进行对象的属性注入;在spring框架负责创建Bean对象时,动态将依赖对象注入到Bean组件。

理解DI的关键是:“谁依赖谁,为什么需要依赖,谁注入谁,注入了什么”,那我们来深入分析一下:

  • 谁依赖于谁:当然是应用程序依赖于IoC容器
  • 为什么需要依赖:应用程序需要IoC容器来提供对象需要的外部资源
  • 谁注入谁:很明显是IoC容器注入应用程序某个对象,应用程序依赖的对象
  • 注入了什么:就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)

依赖注入的三种实现方式

构造器注入,Setter方法注入,接口注入。

依赖注入的相关注解
@Autowired :自动按类型注入,如果有多个匹配则按照指定的Bean的id查找,查找不到会报错。
@Qualifier :在自动按照类型注入的基础上再按照Bean的id注入,给变量注入时必须搭配@Autowired,给方法注入时可单独使用。
@Resource:直接按照Bean的id注入,只能注入Bean类型。
@Value:用于注入基本数据类型和String类型

IOC和DI区别
IOC 控制反转,是指对象实例化权利由spring容器来管理
DI 依赖注入 在spring创建对象的过程中,对象所依赖的属性通过配置注入对象中。

IOC和DI的联系 

在平时的java应用开发中,我们要实现某一个功能或者说是完成某个业务逻辑时至少需要两个或以上的对象来协作完成,在没有使用Spring的时候,每个对象在需要使用他的合作对象时,自己均要使用像new object() 这样的语法来将合作对象创建出来,这个合作对象是由自己主动创建出来的,创建合作对象的主动权在自己手上,自己需要哪个合作对象,就主动去创建,创建合作对象的主动权和创建时机是由自己把控的,而这样就会使得对象间的耦合度高了,A对象需要使用合作对象B来共同完成一件事,A要使用B,那么A就对B产生了依赖,也就是A和B之间存在一种耦合关系,并且是紧密耦合在一起,而使用了Spring之后就不一样了,创建合作对象B的工作是由Spring来做的,Spring创建好B对象,然后存储到一个容器里面,当A对象需要使用B对象时,Spring就从存放对象的那个容器里面取出A要使用的那个B对象,然后交给A对象使用,至于Spring是如何创建那个对象,以及什么时候创建好对象的,A对象不需要关心这些细节问题(你是什么时候生的,怎么生出来的我可不关心,能帮我干活就行),A得到Spring给我们的对象之后,两个人一起协作完成要完成的工作即可。

所以控制反转IoC(Inversion of Control)是说创建对象的控制权进行转移,以前创建对象的主动权和创建时机是由自己把控的,而现在这种权力转移到第三方,比如转移交给了IoC容器,它就是一个专门用来创建对象的工厂,你要什么对象,它就给你什么对象,有了 IoC容器,依赖关系就变了,原先的依赖关系就没了,它们都依赖IoC容器了,通过IoC容器来建立它们之间的关系。

AOP(面向切面编程)

AOP 面向方面(切面)编程,AOP是OOP的延续,是Aspect Oriented Programming的缩写,意思是面向方面(切面)编程。注:OOP(Object-Oriented Programming ) 面向对象编程

AOP的应用场景

事务(数据库更新),权限(web请求权限),日志(每一次更新记录)等。

AOP  主要应用于日志记录,性能统计,安全控制,事务处理(项目中使用的)等方面。

AOP基本术语切点、切面、连接点、通知等

  • 通知:定义AOP切面执行的工作,以及切面执行的时间。(Before、After、After-returning、After-throwing、Around)
  • 切点:定义切面执行的地点,满足配置条件的目标方法(在什么地方执行切面)。
  • 连接点:切点的全集。一个程序可能会有多个切面,不同切面映射需要不同切点。
  • 切面:切点+通知。在什么时候、什么地点、执行什么工作。

解释AOP

在软件业,AOP为Aspect   Oriented    Programming的缩写,意味:面向切面编程.通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术,AOP是OOP的延续.将一些共性的内容进行抽取,在需要用到的地方,以动态代理的方式进行插入.在不修改源码的基础上,还能对源码进行前后增强。

AOP原理(动态代理)
JDK(动态)代理(反射)

利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。

动态代理实际上是程序在运行中,根据被代理的接口来动态生成代理类的class文件,并加载class文件运行的过程。而动态生成的代理类已经继承了Proxy类,由于Java的单继承,所以只能靠实现被代理类的接口的形式,而不是靠继承被代理类的形式。

JVM编译完成时并没有实际的字节码文件,而是在运行时动态生成类字节码,并加载到JVM中。

  • 优点:通过反射,可以有效减少代理类的数量,使用更灵活;
  • 缺点:反射代理,比较消耗系统性能。

目标对象实现接口,代理对象可以不实现业务接口。

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

由于被代理类没有实现接口,所以运行时在内存中动态生成一个子类对象从而实现对目标对象的扩展。子类对象会重写目标对象的方法,所以目标对象不能为final类。

代理类和目标类都无需实现业务接口。

Spring默认动态代理

Spring在5.X之前默认的动态代理实现一直是jdk动态代理。但是从5.X开始,Spring就开始默认使用Cglib来作为动态代理实现。

SpringBoot从2.X开始也转向了Cglib动态代理实现。

JDK动态代理要求接口,没有接口的情况下会报错。

而CGLIB作为默认动态代理,效果和JDK差不多,但是不会报错,更方便。

AOP的两种实现方式(注解、xml文件) 

xml配置

 注解配置

Spring中实现AOP技术

在Spring中可以通过代理模式来实现AOP;代理模式分为

  1. 静态代理:一个接口,分别有一个真实实现和一个代理实现。
  2. 动态代理:通过代理类的代理,接口和实现类之间可以不直接发生联系,而可以在运行期(Runtime)实现动态关联。

动态代理有两种实现方式,可以通过jdk的动态代理实现也可以通过cglib来实现而AOP默认是通过jdk的动态代理来实现的。jdk的动态代理必须要有接口的支持,而cglib不需要,它是基于类的。

Spring AOP事务的描述

在spring-common.xml里通过<aop:config>里面先设定一个表达式,设定对service里那些方法  如:对add* ,delete*,update*等开头的方法进行事务拦截。我们需要配置事务的传播(propagation="REQUIRED")特性,通常把增,删,改以外的操作需要配置成只读事务(read-only="true"),只读事务可以提高性能。之后引入tx:advice,在tx:advice引用 transactionManager(事务管理),在事务管理里再引入sessionFactory,sessionFactory注入 dataSource,最后通过<aop:config> 引入txAdvice。

Spring框架

什么是spring框架

spring是一个开放源代码的设计层面框架,它解决的是业务逻辑层和其他各层的松耦合问题,是一个分层的javaEE一站式轻量级开源框架

什么是spring,它能够做什么?
Spring是一个开源框架,它由Rod Johnson创建。它是为了解决企业应用开发的复杂性而创建的。Spring使用基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。

  •    目的:解决企业应用开发的复杂性
  •    功能:使用基本的JavaBean代替EJB,并提供了更多的企业应用功能
  •    范围:任何Java应用

简单来说,Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。

Spring的概述

Spring 是完全面向接口的设计,降低程序耦合性,主要是事务控制并创建bean实例对象。在ssh整合时,充当黏合剂的作用。IOC(Inversion of Control) 控制反转/依赖注入,又称DI(Dependency Injection) (依赖注入) 。

spring的作用

方便解耦,简化开发,AOP编程支持,声明式事务支持,集成Junit更加方便的进行分层测试,方便集成各种优秀框架

如何通过注解创建Bean
@Component 把当前类对象存入Spring容器中,相当于xml中配置了一个bean标签。value属性指定bean的id,默认使用当前类的首字母小写的类名。

@Controller 、@Service、@Repository 三个注解都是@Component的衍生注解,作用及属性都是一模一样的,只是提供了更加明确的语义。

@Controller用于表现层,@Service用于业务层,@Repository用于持久层。如果注解的中只有一个value属性要赋值时可以省略value。如果想将第三方的类变成组件有没有源代码,也就没办法使用@Component进行自动配置,这种时候就要使用@Bean注解,被@Bean注解的方法返回值是一个对象,将会实例化,配置和初始化一个新对象并返回,这个对象由Spring的Ioc容器管理。name属性用于给当前@Bean注解方法创建的对象指定一个名称,即Bean的id,当使用注解配置方法时,如果方法有参数,Spring会去容器查找是否有可用的bean对象,查找方式和@Autowired一样。

解释Spring支持的几种bean的作用域

a、singleton : bean在每个Spring ioc 容器中只有一个实例。

b、prototype:一个bean的定义可以有多个实例。

c、request:每次http请求都会创建一个bean,该作用域仅在基于web的Spring ApplicationContext情形下有效。

d、session:在一个HTTP   Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring    ApplicationContext情形下有效。

e、global-session:在一个全局的HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。缺省的Spring bean 的作用域是Singleton。

Spring bean的生命周期

实例化,初始init,接收请求service,销毁destroy;

1.推断构造方法:

首先一个 Spring bean 对象是对应一个 Java 类的,而一个Java 类中又有很多个构造方法,Spring 在创建 bean 时先需要推断出一个最佳的构造方法,以此构造方法来进行实例化;

2.实例化Java对象:

对于BeanFactory容器,当客户向容器请求一个尚未初始化的 Java 对象时,或初始化这个Java对象的时候需要注入另一个尚未初始化的依赖时,容器就会调用createBean方法进行实例化。对于ApplicationContext容器,当容器启动结束后,通过获取 BeanDefinition 对象中的信息,通过Java 反射机制实例化这个Java 对象(原型 bean)。

3.进行初始化:

看是否需要做一个 Definition 的合并;验证这个 Spring 容器是否支持循环依赖(循环依赖:A类中需要注入B类对应的 bean,B类中也需要注入A类对应的 bean),注意的是单例池都是支持循环依赖的。假如支持循环依赖,那么 Spring 就会把 ObjectFactory工厂对象 进行提前暴露(暴露:就是将这个工厂对象放在一个二级缓存这个 map 中,供循环依赖时创建另一个 bean 时使用)。

那么问题来了为什么不直接先暴露一个 Spring Bean 呢?
答案大概是如果提前就暴露 Spring Bean ,那么就不便于我们对 Bean 进行一个扩展,要是先暴露一个 ObjectFactory 这个工厂对象,后期就会做一个很好的扩展了。

4.设置对象属性(依赖注入):

实例化后的对象被封装在 BeanWrapper对象中,紧接着,Spring根据BeanDefinition中的信息,以及通过BeanWrapper提供的设置属性的接口完成依赖注入。

5.处理Aware接口:
接着,Spring会检测该对象是否实现了xxxAware接口,并将相关的xxxAware实例注入给 bean,还有进行回调。

①如果这个 bean已经实现了BeanNameAware 接口,会调用它实现的setBeanName(String beanId)方法,此处传递的就是Spring配置文件中Bean的 id 值;

②如果这个Bean已经实现了 BeanFactoryAware 接口,会调用它实现的setBeanFactory() 方法,传递的是Spring工厂自身;

③如果这个Bean已经实现了 ApplicationContextAware 接口,会调用setApplicationContext(ApplicationContext) 方法,传入Spring上下文,说白了就是IOC容器;

④ 如果这个Bean已经实现了 ClassLoaderAware 接口,会调用setClassLoader() 方法.

6.BeanPostProcessor:后置处理器

如果想对Bean进行一些自定义的处理,那么可以让Bean实现了BeanPostProcessor接口,那将会调用postProcessBeforeInitialization(Object obj, String s)方法。

7.InitializingBean 与 init-method:
如果Bean在Spring配置文件中配置了 init-method属性或者Bean实现了InitializingBean接口,并重写了里面的afterPropertiesSet方法,则会自动调用,进行对应的初始化方法。

8.如果这个Bean实现了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法;由于这个方法是在Bean初始化结束时调用的,所以可以被应用于内存或缓存技术;

以上几个步骤完成后,Bean就已经被正确创建了,之后就会把这个 bean 放到容器中就可以使用这个 Bean 了,调用方法 getBean()。

9.DisposableBean:
当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用其实现的destroy()方法;

10.destroy-method:
最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法。

Spring支持的事务管理类型

a、编程式事务管理:这意味你通过编程的方式管理事务,给你带来极大的灵活性,但是难维护。

b、声明式事务管理:这意味着你可以将业务代码和事务管理分离,你只需用注解和XML配置来管理事务。

你更倾向用那种事务管理类型?

大多数Spring框架的用户选择声明式事务管理,因为它对应用代码的影响最小,因此更符合一个无侵入的轻量级容器的思想。       声明式事务管理要优于编程式事务管理,虽然比编程式事务管理(这种方式允许你通过代码控制事务)少了一点灵活性。

什么是通知?有哪五种类型的通知?

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

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

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

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

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

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

Spring框架中都用到了哪些设计模式

(1)工厂模式:BeanFactory就是简单工厂模式的体现,用来创建对象的实例;
(2)单例模式:Bean默认为单例模式;
(3)代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术;
(4)模板方法:用来解决代码重复的问题。比如. RestTemplate, JmsTemplate, JpaTemplate;
(5)观察者模式:定义的对象间存在一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知被制动更新,如 ApplicationContext 事件机制是观察者设计模式的实现,通过 ApplicationEvent 类和 ApplicationListener接口,可以实现ApplicationContext事件处理。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/217836
推荐阅读
相关标签
  

闽ICP备14008679号