Spring面试题——持续更新

Q:依赖倒置原则(Dependency Inversion Principle,DIP),依赖注入(Dependency Injection,DI)和控制反转(Inversion of Control,IoC)容器

依赖倒置原则(Dependency Inversion Principle, DIP)。这个设计准则某种程度上和依赖注入模式有些关联。DIP的出发点是:在应用开发中,高层模块不应当直接依赖低层模块。DIP并不意味着依赖注入。这个准则并没有讲到高层模块如何知道调用哪个低层模块。不过这一点通过实现工厂模式接口可以间接获知,或者通过类似Spring框架、Pico容器、Guice或者Apache HiveMind之类的loC容器实现依赖注入从而得知高层模块调用的具体哪个低层模块。

依赖注入模式(Dependency Injection):在运行时将类的依赖注入到代码中。通过将依赖定义为接口,并将实现这个接口的实体类注入到主类的构造器中来实现这个模式。这允许程序员在不同的实现之间转换而不用去修改主类。依赖注入模式可以通过单一责任原则(Single Responsibility Principle)SRP来使得代码高内聚(high cohesion),因为所依赖的通常都是完成独立的功能的对象,例如,(通过DAO进行)数据存取或(通过Service和Delegate类实现)业务服务。

控制反转容器(Inversion of Control Container,IoC),是一个支持依赖注入的容器。这种方式下,可以采用一个中心容器,例如Spring框架,Guice或者HiveMind,来定义哪个依赖应该使用哪个实体类。Ioc的松耦合性可以带来更多的灵活性,并且在程序运行时更容易去切换到正确的依赖对象上。控制反转模式的基本概念是,不去实际生成对象,而是去定义如何生成对象。不用直接在代码中将模块和服务硬编码在一起,而是在配置文件中描述哪个模块需要哪个服务。容器(例如Spring框架这个IoC容器)会负责将这两者绑定起来。应用IoC的时候,某对象所需的依赖会在创建的时候通过外部实体传入,这些外部实体用来协调系统中的不同对象。也就是说,依赖是被注入到对象中去的。因此,IoC就是关于一个对象如何获得其协作对象的引用的一种责任反转机制。

DI和IoC的真正强大之处在于:

  1. 在运行时而非编译时绑定类间关系。例如,在Seam框架中,你可以对一个接口进行两种实现:真正的实现和模拟(mock)的实现,而在运行时根据某个属性、另一个文件存在与否或者某个优先值去决定真正调用哪一个实现。这尤其当你希望程序在不同场景下表现不同的行为时,这是非常好用的。
  2. 使得代码更容易进行单元测试。
  3. 不用使用工厂或者单例模式就可以实现松耦合,其实现方法一致因此适合缺乏经验的程序员。

Q:IoC中支持的依赖注入有哪些类型?

  1. 构造子注入(例如,Spring框架):依赖是通过构造器参数提供的。
  2. 设值方法注入(例如,Spring框架):依赖是通过JavaBeans属性注入的(ex:setter方法)
  3. 接口注入(例如,Avalon):注入通过接口完成。

选择哪种注入方式?
两种依赖方式都可以使用,构造器注入和Setter方法注入。最好的解决方案是用构造器参数实现强制依赖,setter方法实现可选依赖。

Q:Bean的生命周期

BeanFactory中的Bean生命周期

Spring面试题——持续更新_第1张图片
BeanFactory中的Bean生命周期

  1. 在调用getBean方法之前,如果容器注册了InstantiationAwareBeanPostProcessor接口,则在实例化bean之前调用postProcessBeforeInstantiation方法
  2. 根据配置情况调用Bean的构造函数或者工厂方法实例化bean
  3. 如果容器注册了InstantiationAwareBeanPostProcessor接口,则在实例化bean之前调用postProcessAfterInstantiation方法,对实例化后的bean进行装饰
  4. 如果Bean配置了属性值,则在配置属性值之前调用InstantiationAwareBeanPostProcessor接口的postProcessPropertyValues方法
  5. 调用Bean的属性配置方法设置属性值
  6. 如果Bean实现了BeanNameAware接口,则调用setBeanName方法,将配置文件中该Bean对应的名称设置到Bean中
  7. 如果Bean实现了BeanFactoryAware接口,则调用setBeanFactory方法,将BeanFactory实例传到Bean中
  8. 如果容器注册了BeanPostProcessor接口,则调用postProcessBeforeInitialzation(Object bean,String beanName)方法对Bean进行加工操作,返回对象为加工后的Bean。BeanPostProcessor在Spring框架占有有重要地位,Spring提供的AOP以及动态代理均通过此接口完成。
  9. 如果Bean实现了InitializingBean接口,那么调用afterPropertiesSet方法
  10. 如果中通过init-method定义了初始化方法,则执行此方法
  11. 如果容器注册了BeanPostProcessor接口,则调用postProcessAfterInitialzation(Object bean,String beanName)方法,再次提供对Bean的加工机会
  12. 如果在中指定Bean的作用范围为scope="prototype",将Bean返回给调用者,调用者负责Bean后续生命管理。如果scope="singleton",则将Bean放入Spring IoC容器的缓存池中,并将Bean引用返回给调用者,Spring继续对Bean的后续生命周期进行管理
  13. 对于scope="prototype"的Bean,当容器关闭时,将触发Spring对Bean的后续生命管理工作,如果Bean实现了DisposableBean接口,则将调用afterPropertiesSet方法,可再次编写释放资源,记录日志等操作
  14. 对于scope="prototype"的Bean,如果中通过destroy-method定义了销毁方法,则执行此方法

Bean的完整生命周期经历了各种方法调用,这些方法可以划分为以下几类:

  1. Bean自身的方法
    这个包括了Bean本身调用的方法和通过配置文件中的init-method和destroy-method指定的方法
  2. Bean级生命周期接口方法
    这个包括了BeanNameAware、BeanFactoryAware、InitializingBean和DiposableBean这些接口的方法
  3. 容器级生命周期接口方法
    这个包括了InstantiationAwareBeanPostProcessor 和 BeanPostProcessor 这两个接口实现,一般称它们的实现类为“后处理器”。

Q:Bean的作用域

  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中。

Q:BeanFactory和ApplicationContext区别

ApplicationContext 的主要实现类是ClassPathXmlApplicationContext 和FileSystemXmlApplicationContext,前者默认从类路径加载配置文件,后者默认从文件系统中装载配置文件。

  1. 利用MessageSource进行国际化
  2. 事件发布机制
    让容器拥有发布应用上下文事件的功能,包括容器启动事件、关闭事件等。实现了 ApplicationListener 事件监听接口的Bean 可以接收到容器事件, 并对事件进行响应处理。在ApplicationContext 抽象实现类AbstractApplicationContext 中,我们可以发现存在一个ApplicationEventMulticaster,它负责保存所有监听器,以便在容器产生上下文事件时通知这些事件监听 者。
  3. 底层资源的访问
    ApplicationContext扩展了ResourceLoader(资源加载器)接口,从而可以用来加载多个Resource,而BeanFactory是没有扩展ResourceLoader
  4. LifeCircle
    该接口提供start()和stop()方法用于控制异步访问。在具体使用时该接口同时被ApplicationContext以及Bean实现,ApplicationContext会将start/stop中的信息传递给容器中实现了该接口的Bean
  5. BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化,这样,我们就不能发现一些存在的Spring的配置问题。而ApplicationContext则相反,它是在容器启动时,一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误。
  6. BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor以及InstantiationAwareBeanPostProcessor 的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册

Q:IoC容器内部工作机制

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

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

org.springframework.beans.factory.BeanFactory是Spring IoC容器的具体实现,是Spring IoC 容器的核心接口,用来包装和管理前面提到的各种bean。

Spring面试题——持续更新_第2张图片
IoC流水线
  1. ResourceLoader从存储介质中加载Spring配置文件,并使用Resource表示这个配置文件的资源;
  2. BeanDefinitionReader读取Resource所指向的配置文件资源,然后解析配置文件。配置文件中每一个解析成一个BeanDefinition对象,并保存到BeanDefinitionRegistry中;
  3. 容器扫描BeanDefinitionRegistry中的BeanDefinition,使用Java的反射机制自动识别出Bean工厂后处理器(实现BeanFactoryPostProcessor接口)的Bean,然后调用这些Bean工厂后处理器对BeanDefinitionRegistry中的BeanDefinition进行加工处理。主要完成以下两项工作:
    (1)对使用到占位符的元素标签进行解析,得到最终的配置值,这意味对一些半成品式的BeanDefinition对象进行加工处理并得到成品的BeanDefinition对象;
    (2)对BeanDefinitionRegistry中的BeanDefinition进行扫描,通过Java反射机制找出所有属性编辑器的Bean(实现java.beans.PropertyEditor接口的Bean),并自动将它们注册到Spring容器的属性编辑器注册表中(PropertyEditorRegistry);
  4. Spring容器从BeanDefinitionRegistry中取出加工后的BeanDefinition,并调用InstantiationStrategy着手进行Bean实例化的工作;
  5. 在实例化Bean时,Spring容器使用BeanWrapper对Bean进行封装,BeanWrapper提供了很多以Java反射机制操作Bean的方法,它将结合该Bean的BeanDefinition以及容器中属性编辑器,完成Bean属性的设置工作;
  6. 利用容器中注册的Bean后处理器(实现BeanPostProcessor接口的Bean)对已经完成属性设置工作的Bean进行后续加工,直接装配出一个准备就绪的Bean。

Q:请举例说明如何在Spring中注入一个Java Collection?

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

  • : 该标签用来装配可重复的list值
  • : 该标签用来装配没有重复的set值
  • : 该标签可用来注入键和值可以为任何类型的键值对
  • : 该标签支持注入键和值都是字符串类型的键值对

 
   
   
 
      
      
        
           INDIA
           Pakistan
           USA
           UK
        
      
 
     
     
        
           INDIA
           Pakistan
           USA
           UK
        
      
 
     
     
        
           
           
           
           
        
      
 
      
    
        
            [email protected]
            [email protected]
        
    
 
   
 

Q:Spring Bean的自动装配

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

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


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


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


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

Spring面试题——持续更新_第3张图片
自动装配类型,中使用

自动装配局限性

  • 重写: 你仍需用 配置来定义依赖,意味着总要重写自动装配。
  • 基本数据类型:你不能自动装配简单的属性,如基本数据类型,String字符串,和类。
  • 模糊特性:自动装配不如显式装配精确,如果有可能,建议使用显式装配。

Q:Spring三种配置方式

1 基于XML配置的方式配置Spring

在Spring框架中,依赖和服务需要在专门的配置文件来实现,我常用的XML格式的配置文件。这些配置文件的格式通常用开头,然后一系列的bean定义和专门的应用配置选项组成。

SpringXML配置的主要目的时候是使所有的Spring组件都可以用xml文件的形式来进行配置。这意味着不会出现其他的Spring配置类型(比如声明的方式或基于Java Class的配置方式)

Spring的XML配置方式是使用被Spring命名空间的所支持的一系列的XML标签来实现的。Spring有以下主要的命名空间:context、beans、jdbc、tx、aop、mvc和aso。


 
    
    
    
 
    
 

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


  Archetype Created Web Application
 
  
        spring
            
                org.springframework.web.servlet.DispatcherServlet
            
        1
    
 
    
        spring
        /
    
 

2 基于Java配置的方式配置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 MyServiceImpl();
    }
}

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


    

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

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

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

@Configuration
@ComponentScan(basePackages = "com.howtodoinjava")
public class AppConfig  {
    ...
}

在上面的例子中,com.acme包首先会被扫到,然后再容器内查找被@Component 声明的类,找到后将这些类按照Sring bean定义进行注册。

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


    
    
        contextClass
        
            org.springframework.web.context.support.AnnotationConfigWebApplicationContext
        
    
 
    
    
        contextConfigLocation
        com.howtodoinjava.AppConfig
    
 
    
    
        org.springframework.web.context.ContextLoaderListener
    
 
    
    
        dispatcher
        org.springframework.web.servlet.DispatcherServlet
        
        
            contextClass
            
                org.springframework.web.context.support.AnnotationConfigWebApplicationContext
            
        
        
        
            contextConfigLocation
            com.howtodoinjava.web.MvcConfig
        
    
 
    
    
        dispatcher
        /app/*
    

3 用注解的方式配置Spring

Spring在2.5版本以后开始支持用注解的方式来配置依赖注入。可以用注解的方式来替代XML方式的bean描述,可以将bean描述转移到组件类的内部,只需要在相关类上、方法上或者字段声明上使用注解即可。注解注入将会被容器在XML注入之前被处理,所以后者会覆盖掉前者对于同一个属性的处理结果。

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


 
   
   
 

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

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

@Required:该注解应用于设值方法。
@Autowired:该注解应用于有值设值方法、非设值方法、构造方法和变量。
@Qualifier:该注解和@Autowired注解搭配使用,用于消除特定bean自动装配的歧义。
JSR-250 Annotations:Spring支持基于JSR-250 注解的以下注解,@Resource、@PostConstruct 和 @PreDestroy。

Q:Spring中AOP相关基础问题

  • 解释AOP:
    面向切面的编程,或AOP, 是一种编程技术,允许程序模块化横向切割关注点,或横切典型的责任划分,如日志和事务管理。

  • Aspect 切面:
    AOP核心就是切面,它将多个类的通用行为封装成可重用的模块,该模块含有一组API提供横切功能。比如,一个日志模块可以被称作日志的AOP切面。根据需求的不同,一个应用程序可以有若干切面。在Spring AOP中,切面通过带有@Aspect注解的类实现。

  • 关注点和横切关注的区别
    关注点是应用中一个模块的行为,一个关注点可能会被定义成一个我们想实现的一个功能。
    横切关注点是一个关注点,此关注点是整个应用都会使用的功能,并影响整个应用,比如日志,安全和数据传输,几乎应用的每个模块都需要的功能。因此这些都属于横切关注点。

  • 连接点
    连接点代表一个应用程序的某个位置,在这个位置我们可以插入一个AOP切面,它实际上是个应用程序执行Spring AOP的位置。

你可能感兴趣的:(Spring面试题——持续更新)