RedCloud Help

Spring高分面试题

  1. 什么是Spring框架?Spring框架有那些主要模块? Spring框架是一个为java应用程序的开发提供了综合、广泛的基础支持的java平台。Spring帮助开发解决了开发中基础性的问题,使得开发人员可以专注于应用程序的开发。Spring框架本身亦是按照设计模式精心打造,这时的我们可以在开发环境中安心的集成Spring 框架,不必担心Spring是如何在后台工作的。 Spring框架至今已集成了20多个模块。这些模块主要被分如下图所示的核心容器、数据访问/集成、Web、AOP(面向切面编程)、工具、消息和测试模块。

  2. 使用Spring框架能带来什么好处? 下面列举了一些使用Spring框架带来的主要好处:

    • Dependency injection(DI)方法使得构造器和JavaBean properties文件中的依赖关系一目了然。

    • 与EJB容器相比较,IOC容器更加趋向于轻量级。这样一来IOC容器在有限的内存和cpu资源的情况下进行应用程序的开发和发布就变得十分有利。

    • Spring并没有闭门造车,Spring利用了已有的技术比如ORM框架、logging框架、J2EE、Quartz和JDK Timer,以及其他视图技术。

    • Spring框架是按照模块的形式来组织的。由包和类的编号就可以看出其所属的模块,开发者仅仅需要选用它们需要的模块即可。

    • 要测试一项用Spring开发的应用程序十分简单,因为测试相关的环境代码都已经囊括在框架中了。更加简单的是,利用JavaBean形式的POJO类,可以很方便的利用依赖注入来写测试数据。

    • Spring的Web框架亦是一个精心设计的WebMVC框架,为开发者们在web框架的选择上提供了一个除了主流框架比如Struts、过渡设计的、不流行的web框架的以外的有利选项。

    • Spring提供了一个便捷的事物管理接口,适用于小型的本第事务处理(比如单DB的环境下)和复杂的共同事物处理(比如利用JTA的复杂DB环境)

  3. 什么是控制反转(IOC)?什么是依赖注入?

    • 控制反转是应用于软件工程领域中,在运行时被装配器对象来绑定耦合对象的一种编程技巧,对象之间耦合关系在编译时通常是未知的。在传统的编程方式中,业务逻辑的流程是由程序中的早已被设定好关联关系的对象来决定的。在使用控制反转的情况下,业务逻辑的流程是有对象关系图来决定的,该对象关系图由装配器负责实例化,这中实现方式还可以将对象之间的关联关系的定义抽象化。而绑定的过程是通过“依赖注入“实现的。

    • 控制反转是一种以给予应用程序中目标组件更多控制为目的设计范式,并在我们的实际工作中起到了有效的作用。

    • 依赖注入是在编译阶段尚未知所有的功能是来自那个类的情况下,将其他对象所依赖的功能对象实例化的模式。这需要一种机制用来集火相应的组件以提供特定的功能,所以依赖注入是控制反转的基础。否则如果在组件不收框架控制的情况下,框架又怎么知道要创建那个组件?

  4. 在java中依赖注入的方式有?

    • 构造器注入

    • setter方法注入

    • 接口注入

  5. BeanFactory和ApplicationContext有什么区别? BeanFactory可以理解为含有bean集合的工厂类。BeanFactory包含了bean的定义,以便在接收到客户端请求时将对应的bean实例化。 BeanFactory还能在实例化对象时生成写作类之间的关系。此举将bean自身与bean自身与bean客户端的配置中释放出来。BeanFactory还包含了bean的生命周期的控制,调用客户端的初始化方法(initializationMethods)和销毁方法(destruction Methods)。 从表面上看,ApplicationContext如同BeanFactory一样具有bean定义,bean关联关系的设置,根据请求分发bean的功能。

  • 但ApplicationContext在此基础上还提供了其他的功能。

    • 提供了支持国际化的文本信息。

    • 提供统一的资源文件读取方式。

    • 已在监听器中注册的bean的事件。

  • 以下是三种比较常见的ApplicationContext实现方式:

    • ClassPathXmlApplicationContext:从classpath的XMl配置文件中读取上下文,并生成上下文定义。应用程序上下文从程序环境变量中取得。

      ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
    • FileSystemXmlApplicationContext:由文件系统的XMl配置文件读取上下文。

      ApplicationContext context = new FileSystemXmlApplicationContext("application.xml");
    • XmlWebApplicationContext:由Web应用的XML文件读取上下文。

  1. 如何使用XML配置的方式配置Spring?

在Spring框架中,依赖和服务需要在专门的配置文件中实现,我常用的xml格式的配置文件。这些配置文件的格式通常用开头,然后一系列的bean定义和专门的应用配置选项组成。 SpringXMl配置的主要目是使所有的Spring组件都可以用xml方法的形式来进行配置。这意味着不会出现其他的Spring配置类型(比如声明的方式或基于java Class的配置方式) Spring的XMl配置方式是使用被Spring命名空间的所支持的一系列的xml标签来实现的,Spring有以上主要命名空间:context、bean、jdbc、tx、aop、mvc和aso。

<beans> <bean name="viewResolver" class="org.springframework.web.servlet.view.BeanNameViewResolver"/> <bean name="jsonTemplate" class="org.springframework.web.servlet.view.json.MappingJackson2JsonView"/> <bean id="restTemplate" class="org.springframework.web.client.RestTemplate"/> </beans>

下面这个web.xml仅仅配置了DispatcherServlet,这件最简单的配置便满足应用程序配置运行时组件的需求。

<web-app> <display-name>Archetype Created Web Application</display-name> <servlet> <sevlet-name>spring</sevlet-name> <serlet-class> org.springframework.web.sevlet.DispatcheServlet </serlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>spring</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
  1. Spring提供那些配置形式?

Spring对java配置的支持是@Configuration注解和@Bean注解来实现的。由@Bean注解的方法将会实例化、配置和初始化一个新对象,这个对象由Spring的IOC容器来管理。@Bean声明所起到的作用与元素类似。被@Configuration所注解的类则表示了这个类的主要目的是作为bean定义的资源。被@Configuration声明的类可以通过在同一个类的内部调用@Bean方法来设置嵌入bean的依赖关系。

最简单的@Configuration声明类请参考下面的代码:

@Configuration public class AppConfig{ @Bean public MyService myService(){ return new MyServivce(); } }

对于上面的@Beans配置文件相同的XMl配置文件如下:

<beans> <bean id="myService" class="cn.jihongyun.services.MyServiceImpl"/> </beans>

上述配置方式的实例化方式如下:利用AnnotationConfigApplicationContext类进行实例化

public static void main(String[] args){ ApplicationContext ctx = new ApplicationConfigApplicationContext(AppConfig.class); MyService myService = ctx.getBean(MyService.class); myService.doStuff(); }

要使用组件扫描,仅需要@Configuration进行注解即可:

@Configuration @ComponentScan(basePackages="cn.jihongyun") public class AppConfig{}

在上面的例子中,cn.jihongyun爆首先被扫描到,然后在容器内查找被@Component声明的类,找到后将这些类按照Spring bean定义进行注册。

如果你要在你的web应用开发中选用上述的配置的方式的话,需要用AnnotationConfigWebApplicationContext类来读取配置文件,可以用来配置Spring的Servlet监听器ContextLoaderListeneer或者Spring MVC的DispatcherServlet。

<web-app> <context-param> <param-name>contextClass</param-name> <param-value> org.springframework.web.context.support.AnnotationConfigWebApplicationContext </param-value> </context-param> <context-param> <param-name>contextConfigLocation</param-name> <param-value>cn.jihongyun.AppConfig</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextClass</param-name> <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value> </init-param> <init-param> <param-name>contextConfigLocation</param-name> <param-value>cn.jihongyun.web.MvcConfig</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>/web/*</url-pattern> </servlet-mapping> </web-app>
  1. 怎样用注解方式配置Spring? Spring 在2.5版本以后开始支持用注解的方式来配置依赖注入。可以用注解的方式来替代XML方式的bean描述,可以将bean描述转移到组件类的内部,只需要在相关类上、方法上或者字段声明上使用注解即可。注解注入将会被容器在XML注入之前被处理,所以后者会覆盖掉前者对于同一属性的处理结果。

注解装配在Spring中是默认关闭的。所以需要在spring文件中配置使用基于注解的装配模式。如果你想要在你的应用程序中使用关于注解的方法的话,请参考如下的配置。

<beans> <context:annotation-config/> </beans>

在标签配置完成以后,就可以用注解的方式在Spring中向属性、方法和构造方法中自动装配变量。

下面几种比较重要的注解类型:

  1. @Required:该注解应用于设值方法。

  2. @Autowired:该注解应用于有值设值方法、非设值方法、构造方法和变量。

  3. @Qualifier:该注解和@Autowired注解搭配使用,用于消除特定bean自动装配的歧异。

  4. JSR-250 Annotations:Spring支持基于JSR-250注解以下注解,@Resource、@PostConstruc和@PreDestroy。

  5. 请解释Spring Bean 的生命周期?

SpringBean的生命周期简单易懂。在一个bean实力被初始化时,需要执行一系列的初始化操作以达到可用状态。同样的,当一个bean在被调用时需要进行相关的析构操作,并从bean容器中一处。Spring bean factory负责管理在spring容器中被创建的bean的生命周期。bean的生命周期由两组回调(call back)方法组成。

  1. 初始化之后调用的回调方法。

  2. 销毁之前调用的回调方法。

Spring 框架提供一下四种方式来管理bean的声明周期事件:

  1. InitializingBean和DisposableBean回调接口。

  2. 针对特殊行为的其他Aware接口

  3. Bean配置文件中的Custom init()方法和destroy()方法。

  4. @PostConstruct和@PreDestroy注解方式。

使用 custom init()和customDestroy()方法管理bean生命周期的代码样例如下:

<beans> <bean id="demoBean" class="cn.jihongyun" init-Method="customInit" destroy-Method="customDestroy"/> </beans>
  1. 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作用域效果相同。

  1. 什么是Spring inner beans?

在Spring 框架中,无论何时bean被使用时,当仅被调用了一个属性。一个明智的做法是将这个bean声明为内部bean。内部bean可以用setter注入“属性”和构造方法注入“构造参数”的方式来实现。比如,我们在应用程序中,一个customer类应用了一个person类,我们要做的是创建一个person的实例,然后在customer内部使用。

public class Customer{ private Person person; } public class Person{ private String name; private String address; private int age; }

内部bean的声明方式如下:

<bean id="CustomerBean" class="cn.jihongyun.common.Customer"> <porperty name="person"> <bean class="cn.jihongyun.common.Person"> <porperty name="name" value="lokesh"/> <porperty name="address" value="India"/> <porperty name="age" value="34"/> </bean> </porperty> </bean>
  1. Spring框架中的单利Beans是线程安全的么?

Spring框架并没有对单例bean进行任何线程的封装处理。关于单例bean的线程安全和并发问题需要开发者自行搞定。但实际情况下,大部分的Spring bean并没有可变的状态(比如Serview类和DAO类),所以在某种程度上说Spring 的单利bean是线程安全的。如果你的bean有多种状态的话(比如view model对象),就需要自行保证线程安全。

最浅显的解决办法就是将多态bean的作用域由“singleton”变更为“prototype”

  1. 请举例说明如何在Spring中注入一个java集合?

Spring提供了一下四种集合类的配置元素:

  1. 该标签用来装配可重复的list值。

  2. 该标签用来装配没有重复的set值。

  3. 该标签可用来注入键和值可以为任何类型的键值对。

  4. 该标签支持注入键和值都是字符串类型的键值对。

下面看一下具体的例子:

<beans> <bean id="javaCollection" class="cn.jihongyun.JavaCollection"> <property name="customList"> <list> <value>INDIA</value> <value>Pakistan</value> <value>USA</value> <value>UK</value> </list> </property> <property name="customSet"> <set> <value>INDIA</value> <value>Pakistan</value> <value>USA</value> <value>UK</value> </set> </property> <property name="customMap"> <map> <entry key="1" value="INDIA"/> <entry key="2" value="Pakistan"/> <entry key="3" value="USA"/> <entry key="4" value="UK"/> </map> </property> <property name="customProperties"> <props> <prop key="admin">admin@jihongyun.cn</prop> <prop key="support">support@jihongyun.cn</prop> </props> </property> </bean> </beans>
  1. 如何向Spring Bean中注入java.util.Properties?

第一种方法使用如下代码所示的标签:

<bean id="adminUser" class="cn.jihongyun.common.Customer"> <property name="emails"> <prop key="admin">admin@jihongyun.cn</prop> <prop key="support">support@jihongyun.cn</prop> </property> </bean>

也可用“util”:命名空间来从properties文件中创建一个propertiesbean,然后利用setter方法注入到bean的应用。

  1. 请解释spring bean的自动装配?

在spring 框架中,在配置文件中设定bean的依赖关系是一个很好的机制,spring容器还可以自动装配合作关系bean之间的关联关系。这意味着spring 可以通过向bean factory中注入的方式自动搞定bean之间的依赖关系。自动装配可以设置在每个bean上,也可以设置在特定的bean上。

下面xml配置文件表明了如何根据名称将一个bean设置为自动装配:

<bean id="employeeDAO" class="cn.jihongyun.EmployeeDAOImpl" autowire="byName"/>

除了bean配置文件中提供的自动装配模式,还可以使用@Autowired注解来自动装配指定的bean。在使用@Autowired注解之前需要在按照如下的配置方式在Spring配置文件进行配置才可以使用。

<context:annotation-config/>

也可以通过配置文件中配置AutowiredAnnotationBeanPostprocessor达到相同的效果。

<bean class="org.springframewok.beans.factory.annotation.AutowiredAnnotationBeanPostprocessor"/>

配置好以后可以使用@Autowired来标注了。

@Autowired public EmployeeDAOImpl(EmployeeManager manager){ this.manager= manager; }
  1. 自动装配有那些局限性?

自动装配有如下属性:

  • 重写:你任然需要使用和 设置执行依赖,这意味着总要重写自动装配。

  • 原生数据类型:你不能自动装配简单的属性,如原生类型、字符串和类。

  • 模糊特性:自动装配总是没有自定义装配精准,因此,如果可能进量使用自定义装配。

  1. 清洁是各种自动装配模式的区别?

在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内部没有找到相应的构造器或者是无参构造器,容器就会自动选择byType的自动装配方式。

  6. 请举例解释@Required Annotation?

在产品级别的应用中,IOC容器可能声明了数十万个bean,bean与bean之间有着复杂的依赖关系。设值注解方法的短板之一就是验证所有的属性是否被注解是一项十分困难的操作。可以通过在中设置“dependency-check”来的解决这个问题。

在应用程序的生命周期中,你可能不愿意花时间在验证所有bean的属性是否按照上下文文件正确配置。或者你宁可验证某个bean的特定属性是否被正确的设置。即使是用“dependency-check”属性也不能很好的解决这个问题,在这种情况下,你需要使用@Required注解。

需要用如下的方式使用来表明bean的设值方法。

public class EmployeeFactoryBean extends AbstractFactoryBean<Object>{ private String designation; public String getDesignation(){ return designation; } @Required public void setDesignation(String designation){ this.designation = designation; } }

RequiredAnnotationBeanPostProcessor是Spring中的后置处理用来验证@Required注解的bean属性是否被正确的设值了。在使用RequiredAnnotationBeanPostProcessor来验证bean属性之前,首先在IOC容器中对其进行注册:

<bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostprocessor"/>

但是如果没有属性被用@Required注解过后的话,后置处理器会抛出一个BeanInitializationException异常。

  1. 请举例说明@Qualifier注解?

@Qualifier注解意味着可以被标记bean的字段上可以自动装配。Qualifier注解可以用来取消Spring不能取消的bean的应用。

  1. 构造方法注入和设值注入有什么区别?

请注意一下明显的区别:

  1. 在设值注入方法支持大部分的依赖注入,如果我们仅需要注入int、string和long类型的变量,我们不要用设值的方法注入。对于基本类型,如果我们没有注入的话,可以为基本类型设置默认值。在构造方法注入不支持大部分的依赖注入,因为在调用构造方法必须穿入正确的构造参数,否则的话为报错。

  2. 设置注入不会重写构造方法的值。如果我们对同一个变量同时使用了构造方法注入又使用了设置方法的注入的话,那么构造方法将不能覆盖由设值方法注入的值。很明显,因为构造方法尽可能在对象被创建时调用。

  3. 在使用设值注入时有可能还不能保证某种依赖是否已经被注入,也就是说这时对象的关系有可能不是完整的。而在另外一种情况下,构造器注入则不允许生成依赖关系不完整的对象。

  4. 在设值注入时如果对象a和对象b相互依赖,在创建对象a时spring会抛出sObjectCurrentlyInCreationException异常,因为在B对象被创建之前A对象是不能被创建的,反之亦然。所以Spring用设值注入的方法解决了循环依赖的问题,因对象的设值方法是在对象创建之前被调用的。

  5. Spring框架中有那些不同类型的事件?

spring的ApplicationContext提供了支持事件和代码中监听器的功能。

我们可以创建bean用来监听在applicationcontext中发布的事件。ApplicationEvent类和在ApplicationContext接口中处理的事件,如果一个bean实现了ApplicationListener接口,当一个ApplicationEvent被发布以后,bean会自动被通知。

public class AllApplicationEventListener implements ApplicationListener<ApplicationEvent>{ @Override public void onApplicationEvent(ApplicationEvent applicationEvent){ // process event } }

Spring提供一下5种标准的事件:

  1. 上下文更新事件(ContextRefreshEvent):该事件会在ApplicationContext被初始化或者更新时发布。也可以在调用COnfigurableApplicationContext接口中refresh()方法时被触发。

  2. 上下文开始事件(ContextStartedEvent):当容器调用ConfigurableApplicationContext的Start()方法开始/重新开始容器时触发该事件。

  3. 上下文停止事件(ContextStoppedEvent):当容器调用ConfigurableApplicationContext的Stop()方法停止容器时触发该事件。

  4. 上下文关闭事件(ContextClosedEvent):当ApplicationContext被关闭时触发该事件。容器被关闭时,其管理的所有单例bean都被销毁。

  5. 请求处理事件(RequestHandledEvent):在web应用中,当一个http请求(request)结束触发该事件。

除了上面介绍的事件意外,还可以扩展ApplicationEvent类来开发自定义的事件。

public class CustomApplicationEvent extends ApplicationEvent{ public customApplicationEvent(Object source,final String msg){ super(source); System.out.println("Created a Custom event") } }

为了监听这个事件,还需要创建一个监听器:

public class CustomEventListener implements ApplicationListener<CustomApplicationEvent>{ @Override public void onApplicationEvent(CustomApplicationEvent applicationEvent){ } }

之后通过applicationContext接口的publishEvent()方法来发布自定义事件。

CustomApplicationEvent customEvent = new CustomApplicationEvent(applicationContext,"Test message"); applicationContext.publishEvent(customEvent);

  1. FileSystemResource和ClassPathResource有何区别?

在FileSystemResource中需要给出spring-config。xml文件在你项目中的相对路径或者绝对路径。在ClassPathResource中spring会在classPath中自动搜寻配置文件,所以要把ClassPathResource文件放在ClassPathResource中spring会在ClassPath中自动搜寻配置文件,所以要把ClassPathResource文件放在ClassPath下。

如果将spring-config.xml保存在了src文件夹下的话,只需要给出配置文件的名称即可,因为src文件夹是默认的。

简而言之,ClassPathResource在环境变量中读取配置文件,FileSystemResource在配置文件中读取配置文件。

  1. spring 框架中都用到了那些设计模式?

Spring框架中使用了大量的设计模式,下面列举了比较有代表性的:

  1. 单例模式:在spring 配置文件中定义的bean默认是单例模式。

  2. 代理模式:在AOP和remoting中被用的比较多。

  3. 模板模式:用来解决代码重复的问题。

    1. 比如RestTemplate,JmsTemplate,JpaTemplate.

  4. 委派模式:Spring提供了DispatcherServlet来对请求进行分派。

  5. 工厂模式:BeanFactory用来创建对象的实例,贯穿于BeanFactory/ApplicationContext接口的核心理念。

  6. 代理模式:Aop思想的底层技术,Spring中采用JDK Proxy和Cglib类库。

  7. 策略模式:XmlBeanDefinitionReader,PropertiesBeanDefinitionReader

  8. 观察者模式:listener,event,multicast

  9. 适配器:Adapter

  10. 装饰者模式:BeanWrapper

  11. 责任链模式:使用aop时候先生成一个拦截器。

  12. 委托者模式:delegate

  1. 在Spring框架中如何有效的使用JDBC?

使用Spring JDBC框架,资源管理以及错误处理的代价都会减轻。开发人员只需要通过statements和queries语句从数据库中存取数据,Spring框架中通过使用模板类能更有效的使用JDBC,也就是所谓的JdbcTemplate。

  1. 请解释一下Spring框架中的IOC容器?

Spring 中org.springframework.beans包和org.springframework.context包构成了spring框架ioc容器的基础。

BeanFactory接口提供了一个先进的配置机制,使得任何类型的对象成为可能。ApplicationContext接口对BeanFactory(是一个字接口)进行了扩展,在BeanFactory的基础上添加了其他功能,比如与Spring的Aop更容易集成,也提供了处理message resource的机制(用于国际化)、时间传播以及应用层的特别配置,比如针对web应用的webApplicationContext。

  1. 在Spring中可以注入null或字符串吗? 完全可以

  2. 谈谈SpringIOC的理解,原理与实现?

  1. 其中有两个比较重要的概念:控制反转和依赖注入。

  2. ioc容器:存储对象的,使用的是map结构,在spring中有存在三级缓存,singletonObjects存放了完整的bean对象,整个bean的生命周期,从创建使用到销毁的过程全部是由容器进行管理的也就是bean的生命周期。

  3. 控制反转:传统的对象是使用者来进行控制的,有了spring,可以经整个对象交给spring容器ioc来进行管理。

  4. 依赖注入:是把对应的属性注入到具体的对象中去,配合注解@Autowired,方法populateBean来完成属性值的注入。

  5. 容器的创建过程是由BeanFactory,DefaultListableBeanFactory来管理的,此过程会设置一些参数关于BeanPostProcessor和Aware接口的子类等等属性。

  6. 通过xml或注解加载解析bean的对象,生成要创建的BeanDefinition元信息

  7. 将元信息交给BeanPostProcessor来处理,此处是一个扩展点,例如扩展了PlaceHoderConfurationSupoort等。

  8. BeanPostProcessor的注册功能,方便后续对bean对象完成具体的扩展功能

  9. 通过反射的方法,将BeanDefinition对象实例化成具体的对象。

  10. Bean对象的初始化过程,先填充属性,调用Aware子类的方法,调用BeanPostProcessor前置处理方式,调用init-method方法,调用BeanPostProcessor的后置处理方法。

  11. 生成完成的Bean对象,通过getBean方法可以直接获取。

  12. 销毁过程。 面试官,这是我对ioc的整体理解,包含了一些详细的处理过程,你看一下有什么问个体,可以指点我一下。 具体的细节我记不太清楚了,但是spring中的bean都是通过反射的方式生成的,同事其中包含了很多的扩展点,比如最常用的对BeanFactory的扩展,对bean的扩展,我们在公司对这方面的时候还是比较多的,除此之外,ioc中最核心的也是填充具体bean的属性和生命周期。

  1. 谈一下spring ioc的底层实现

底层实现:工作原理,过程,数据结构,流程,设计模式,设计思想。

  1. 先通过createBeanFactory创建一bean工厂(DefaultListableBeanFactory)

  2. 开始循环创建对象,因为容器中的bean模式是单例的,所以首先通过getBean,doGetBean从容器中查找,找不到话。

  3. 通过createBean,doCreateBean方法,以反射的方式创建对象,一般情况下使用的是无参的构造方法(getDeclaredConstructor,newInstance)

  4. 进行对象的属性填充populateBean

  5. 进行其他的初始化操作(initializingBean)

  6. 描述一下bean的生命周期?

  1. 实例化bean:通过反射的方法生成对象。

  2. 填充bean的属性:populatebean,循环依赖问题(三级缓存)

  3. 调用aware接口相关的方法:invokeAwareMethod(完成BeanName,BeanFactory,BeanClassLoader对象的属性设置)

  4. 调用BeanPostProcessor中的前置处理方法:使用比较多的有(ApplicationContextPostProcessor,设置ApplicationContext,Environment,ResourceLoader,EmbeddValueResolver等对象)

  5. 调用initMethod方法:invokeInitMethod(),判断是否实现了initializingBean接口,如果有,调用afterPropertiesSet方法,没有就不调用。

  6. 调用BeanPostProcessor的后置方法:spring的aop就是在此处实现的,AbstractAutoProxyCreator注册Destruction相关的回调接口:钩子函数。

  7. 获取到完整的对象,可以通过getBean的方式来进行对象的获取

  8. 销毁过程

    1. 判断是否实现了DispoableBean接口

    2. 调用destroyMethod方法

  1. Spring是如何解决循环依赖的问题的? 此问题涉及到两个概念,什么是循环依赖,什么是三级缓存。

  1. 循环依赖:a对象与a对象相互依赖,a对象与b对象相互依赖,abc三个对象相互依赖形成闭环。

  2. spring内部使用了三级缓存帮我们解决了循环依赖的问题。分别是

    1. singletonObjects一级缓存:保存实例化完成,初始化完成的bean实例。

    2. earlySingletonObjects二级缓存:保存实例化完成,的bean实例。

    3. singletonFactories三级缓存:保存bean创建工厂,以便于后面扩展有机会创建代理对象。

  3. 以aba为例,a对象存在,不过a对象不是一个完整的实例,只完成了实例化未完成初始化,如果在程序调用的过程中,拥有了b对象的引用,在后期再给他进行复制操作,就是说可以优先给非完整对象a进行复制,等待后续操作完成之后初始化,spring解决依赖注入的核心其实就是将容器初始化和实例化操作分开。

  1. 为什么需要三级缓存?

三级缓存的value是一个ObjectFactory,是一个函数式接口,存在的意义是保证在整个容器的运行过程中同名的bean对象只能有一个。 普通对象和代理对象是不能同时出现在容器中的,因此当一个对象需要被代理的时候,就要使用代理对象覆盖掉之前的普通对象,在实际的调用过程中,是没有办法确定什么时候对象被使用。所以就要求当某个对象被调用的时候,优先判断此对象是否需要被代理,类似一种回调机制的实现,因此传入lambda表达式的时候,可以通过lambda表达式来执行对象的覆盖过程,getEarlyBeanReference() 因此,所有的bean对象在创建的时候都要优先放到三级缓存中,在后续的使用过程中,如果需要被代理则返回代理对象,如果不需要被代理,则直接返回普通对象。

  1. 缓存的放置时间和删除时间

三级缓存:createBeanInstance之后:addSingletonFactory 二级缓存:第一次从三级缓存确定对象是代理对象还是普通对象的时候放,同时删除三级缓存getSingleton 一级缓存:生成完整对象之后放到一级缓存,删除二三级缓存:addSingleton

  1. BeanFactory与FactoryBean有什么区别?

相同点:都是用来创建bean对象的。 不同点:使用beanFactory创建对象的时候,必须要遵循严格的生命流程,太复杂了,如果想要简单的自定义某个对象的创建,同时创建完成的对象想交给spring来管理,哪么就需要实现FactroyBean接口 isSingleton:是否是单例对象 getObjectType:获取返回对象的类型 getObject:自定义创建对象的过程(new,反射,动态代理)

  1. Spring的AOP的底层实现原理?

aop是ioc的一个扩展功能,现有的ioc,才有的aop,aop只是在ioc整个流程上面的一个扩展点。涉及到的类BeanPostProcessor 在整个bean的创建过程中有一个步骤可以对bean进行扩展实现,aop本省就是一个扩展的功能,所以在BeanPostProssor的后置方法中进行实现的。整个过程是:

  1. 代理对象的创建过程(有通知,切面,切点的信息)

  2. 通过jdk或者cglib的方式来生成代理对象

  3. 在执行方法调用的时候,会调用生成的字节码文件,直接会调用DynamicAdvisoredInterceptor类中的intercept方法,从此方法开始执行

  4. 根据之前的配置信息生成拦截器链

  5. 从拦截器链路中一次执行每一个通知,在执行过程中,为了方便找到下一个通知是哪个,会有一个CglibMethodInvocation的对象,从-1的位置一次开始执行查找。

  1. Spring的事务是如何回滚的? spring的事务管理是如何实现的?

  1. spring分为声明式事务和编程式事务

  2. Spring的事务是aop来实现的,首先要生成具体的代码里,然后按照aop的整套流程来执行具体的操作逻辑,正常情况下要通过通知来完成核心功能,但是事务不是通过通知来实现的,而是通过transactionInterceptor来实现的,然后调用invoke来实现具体的逻辑。

  3. 先做准备工作,解析各种方法上事务相关的属性,根据具体的属性来判断是否开启新事物。

  4. 当需要开启的时候,获取数据库连接,关闭自动提交功能,开启事务。

  5. 执行具体的sql逻辑操作

  6. 在操作过程中,如果执行失败了,哪么会通过completeTransactionAfterThrowing看来完成事务的回滚操作,回滚的具体逻辑是通过doRollBack方法来实现的,实现的时候也要先获取连接对象,通过连接对象回滚。

  7. 如果执行过程中,没有任何意外情况的发生,哪么通过commitTransactionAfterReturning来完成事务的提交操作,提交的具体逻辑是通过doCommit方法来实现的,实现的时候也要获取连接,通过连接对象来提交。

  8. 当事务执行完毕之后需要清除相关的事务信息clearUpTransactionInfo

  1. 谈一下Spring事务的传播特性? 事务的传播特性有几种?7中

  1. required

  2. required_new

  3. support

  4. not_support

  5. never

  6. mandatory

  7. nested

  1. 某一个事务嵌套另外一个事务的时候怎么办?

A方法调用B方法,AB方法都有事务,并且传播特性不同,呢么a如果有异常,B怎么办,B如果有异常,A怎么办?

  • 总 :事务的传播特性指的是不同方法的嵌套调用过程中,事务应该如何进行处理,是用同一个事务还是不同的事务,当出现异常的时候会回滚还是提交,两个方法之间的相关影响,在日常工作中,用的比较多的是required,requireds_new,nested

  • 分:

  1. 先说事务的不同分离,可以分为三类:支持当前事务,不支持当前事务,嵌套事务

  2. 如果外层方法是required,内层方法是,required,requireds_new,nested

  3. 如果外层方法是requireds_new,内层方法是required,requreds_new,nested

  4. 如果外层方法是nested,内层方法是required,requireds_new,nested

核心处理逻辑非常简单:

  1. 判断内层方法是否是同一个事务

    1. 是:异常统一在外层方法处理

    2. 不是:内存方法有可能影响到外层方法,但是外层方法是不是影响内存方法(大致可以这么理解,

03 May 2025