spring十三问

85 阅读8分钟

spring13问

1.什么是控制反转?什么是依赖注入?

控制反转就是对象的创建,初始化和销毁的控制权都交给spring管理,而不是程序员本身,在传统的编程下,业务代码中的关联对象早已被设定,控制反转将对象的关系抽象化。 依赖注入是控制反转的手段,将其他对象所依赖的对象实例化。

依赖注入的四种方式:

1.构造器注入
2.set注入
3.接口注入
4.注解注入

2.BeanFactory 和 ApplicationContext 有什么区别?

BeanFactory和ApplicationContext 是spring的两中容器

BeanFactory是spring IOC中最顶层的接口,所有的容器都要从它继承是实现。启动的时候不会去实例化Bean,中有从容器中拿Bean的时候才会去实例化;

ApplicationContext 是应用上下文,继承BeanFactory接口,在启动的时候就把所有的Bean全部实例化了。它还可以为Bean配置lazy-init=true来让Bean延迟实例化;它是Spring的一各更高级的容器,提供了更多的有用的功能:

  1. 国际化
  2. 访问资源,如URL和文件
  3. 载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层
  4. 消息发送、响应机制
  5. AOP(拦截器)

源码:

public interface BeanFactory {
    //使用转义符&来得到FactoryBean本身,用来区分通过容器获得FactoryBean本身和其产生的对象
    String FACTORY_BEAN_PREFIX = "&";

    //通过name来获取指定的bean
    Object getBean(String name) throws BeansException;
    
    //根据名字和类型来得到bean实例,增加了类型安全验证机制
    <T> T getBean(String name, Class<T> requiredType) throws BeansException;

    //通过类型获取bean实例
    <T> T getBean(Class<T> requiredType) throws BeansException;

    //增加更多获取的条件,同上方法
    Object getBean(String name, Object... args) throws BeansException;

    <T> T getBean(Class<T> requiredType, Object... args) throws BeansException;

    //判断容器是否含有指定名字的bean
    boolean containsBean(String name);

    //查询指定名字的Bean是不是单例的Bean
    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;

    //判断Bean是不是prototype类型的bean
    boolean isPrototype(String name) throws NoSuchBeanDefinitionException;

    //查询指定了名字的Bean的Class类型是否与指定类型匹配
    boolean isTypeMatch(String name, Class<?> targetType) throws NoSuchBeanDefinitionException;
   
    //获取指定名字bean的Class类型
    Class<?> getType(String name) throws NoSuchBeanDefinitionException;

    //查询指定了名字的bean的所有别名,这些别名都是在BeanDefinition中定义的
    String[] getAliases(String name);
}

3、Spring 有几种配置方式?

1.基于xml配置
2.基于注解配置
3.基于java配置

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

5.什么是 Spring inner beans?

内部bean就是当bean只被另一个bean的属性使用时,可以使用内部bean来实现。

<!-- 内部bean -->
	<bean id="personInnerBean" class="com.learn.spring.beans.Person">
		<property name="name" value="白展堂"></property>
		<property name="age" value="25"></property>
		<property name="car">
			<!-- 内部bean,只能在内部使用, -->
			<bean class="com.learn.spring.beans.Car" >
				<property name="brand" value="32"></property>
				<property name="crop" value="lt"></property>
				<property name="price" value="300000"></property>
				<property name="maxSpeed" value="280"></property>
			</bean>
		</property>
	</bean>	

6.Spring 框架中的单例 Beans 是线程安全的么?

有状态bean :每个用户有自己特有的一个实例,在用户的生存期内,bean保持了用户的信息,即“有状态”;也就是说类中至少有一个属性标识目前的状态

//有状态bean
public class B {
       private String name;
       public B(String arg) {
       this.name = arg;
       }
       public String hello() {
          return "Hello" + this.name;
       }
    }
    public class Client {
       public Client() {
          B a = new B("中国");
          System.out.println(a.hello());
          B b = new B("世界");
          System.out.println(b.hello());
       }
    }

无状态bean :bean一旦实例化就被加进会话池中,各个用户都可以共用。即使用户已经消亡,bean 的生命期也不一定结束,它可能依然存在于会话池中,供其他用户调用。由于没有特定的用户,那么也就不能保持某一用户的状态,所以叫无状态bean。也就是没有属性能标识其目前的状态

public class A {
       public A() {}
       public String hello() {
          return "Hello 谁?";
       }
    }
public class Client {
       public Client() {
          A a = new A();
          System.out.println(a.hello());
          A b = new A();
          System.out.println(b.hello());
       }
    }

单例模式存在资源竞争,必然不是线程安全的。如果是单例无状态的bean,在对bean不去执行查询以外的操作的话,就是线程安全的,比如controller,service,dao等等,只专注于方法本身.但是如果Bean是有状态的 那就需要开发人员自己来进行线程安全的保证,最简单的办法就是改变bean的作用域 把 "singleton"改为“protopyte” 这样每次请求Bean就相当于是 new Bean() 这样就可以保证线程的安全了。 在springboot做出以下建议:

1、在@Controller/@Service等容器中,默认情况下,scope值是单例-singleton的,也是线程不安全的。
2、尽量不要在@Controller/@Service等容器中定义静态变量,不论是单例(singleton)还是多实例(prototype)他都是线程不安全的。
3、默认注入的Bean对象,在不设置scope的时候他也是线程不安全的。
4、一定要定义变量的话,用ThreadLocal来封装,这个是线程安全的

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

Spring 提供了以下四种集合类的配置元素: list:该标签用来装配可重复的 list 值。 set: 该标签用来装配没有重复的 set 值。 map :该标签可用来注入键和值可以为任何类型的键值对。 props: 该标签支持注入键和值都是字符串类型的键值对。

8.请解释自动装配模式的区别?

no:这是 Spring 框架的默认设置,在该设置下自动装配是关闭的,开发者需要自行在 bean 定义 中用标签明确的设置依赖关系。

byName:该选项可以根据 bean 名称设置依赖关系。当向一个 bean 中自动装配一个属性时,容 器将根据 bean 的名称自动在在配置文件中查询一个匹配的 bean。如果找到的话,就装配这个属 性,如果没找到的话就报错。

byType:该选项可以根据 bean 类型设置依赖关系。当向一个 bean 中自动装配一个属性时,容器 将根据 bean 的类型自动在在配置文件中查询一个匹配的 bean。如果找到的话,就装配这个属性, 如果没找到的话就报错。

constructor:构造器的自动装配和 byType 模式类似,但是仅仅适用于与有构造器相同参数的 bean,如果在容器中没有找到与构造器参数类型一致的 bean,那么将会抛出异常。

autodetect:该模式自动探测使用构造器自动装配或者 byType 自动装配。首先,首先会尝试找合 适的带参数的构造器,如果找到的话就是用构造器自动装配,如果在 bean 内部没有找到相应的构 造器或者是无参构造器,容器就会自动选择 byTpe 的自动装配方式。

9.如何开启基于注解的自动装配?

两种方式:

1、引入配置文件中的<bean>下引入 <context:annotation-config>
<beans> 
 <context:annotation-config /> 
</beans>
2、在 bean 配置文件中直接引入 AutowiredAnnotationBeanPostProcessor
<beans> 
 <bean
class="org.springframework.beans.factory.annotation.AutowiredAnnotati
onBeanPostProcessor"/> 
</beans>

9.请解释@Required 注解?

@Required 能检查某一属性是否被注入,如果没有被注入将会抛出异常

10.请说明@Qualifier 注解?

@Qualifie注解表示指定bean名称来注入。

11.FileSystemResource 和 ClassPathResource 有何区别?

在 FileSystemResource 中需要给出 spring-config.xml 文件在你项目中的相对路径或者 绝对路径。在 ClassPathResource 中 spring 会在 ClassPath 中自动搜寻配置文件,所以要把 ClassPathResource 文件放在 ClassPath 下。如果将 spring-config.xml 保存在了 src 文件夹下的话,只需给出配置文件的名称即可,因为src 文件夹是默认。 简而言之,ClassPathResource 在环境变量中读取配置文件,FileSystemResource 在配置文件 中读取配置文件

12.IOC 容器对 Bean 的生命周期

1.通过构造器或工厂方法创建 Bean 实例
2.为 Bean 的属性设置值和对其他 Bean 的引用
3.将 Bean 实 例 传 递 给 Bean 后 置 处 理 器 的 postProcessBeforeInitialization 方 法④. 调用 Bean 的初始化方法(init-method)
4.将 Bean 实 例 传 递 给 Bean 后 置 处 理 器 的 postProcessAfterInitialization 方法
5. Bean 可以使用了
6.当容器关闭时, 调用 Bean 的销毁方法(destroy-method)

13.在 Spring 中如何配置 Bean ? 静态工厂方法配置 调用静态工厂方法创建 Bean是将对象创建的过程封装到静态方法中. 当客户端需要对象时, 只需要简单地调用静态方法, 而不用关心创建对象的细节.

public class StaticCarFactory {
    private static Map<String, Car> cars = new HashMap<String, Car>();
    static {
       cars.put("audi", new Car("audi", 300000));
       cars.put("ford", new Car("audi", 400000));
   }
   //静态工厂方法,根据名字获取car对象
    public static Car getCar(String name) {
        return cars.get(name);
    }
 }
-----------------------------------------------------------------------
xml文件
 <!--通过静态工厂方法来配置bean,注意不是配置静态工厂方法实例,而是配置bean实例-->
 <!--
     class属性:指向静态工厂的全类名
    fatory-method:指向静态工厂方法的名字
     constructor-arg:如果工厂方法需要传入参数,则使用constructor-arg来配置参数
  -->
 <bean id="car1" class="com.wzy.factory.StaticCarFactory" factory-method="getCar">
     <constructor-arg name="name" value="audi"/>
 </bean>

实例工厂方法配置bean 使用实例工厂方法配置bean首先需要创建一个实例工厂的bean,通过调用实例工厂bean的非静态方法创建目标的bean对象,同时在factory-bean属性里指定工厂bean的id,最后使用元素为该方法传递方法参数。

 public class InstanceCarFactory {
     private Map<String, Car> cars = null;
 
   public InstanceCarFactory() {
         cars = new HashMap<String, Car>();
        cars.put("audi", new Car("audi", 300000));
        cars.put("ford", new Car("ford", 400000));
    }

     public Car getCar(String name){
        return cars.get(name);
     }

 }
------------------------------------------------------------------------------
xml文件
<!--实例工厂的实例-->
 <!--
    factory-bean:指向实例工厂方法的bean
    fatory-method:指向实例工厂方法的名字
    constructor-arg:如果工厂方法需要传入参数,则使用constructor-arg来配置参数
 -->
<bean id="instanceCarFactory" class="com.wzy.factory.InstanceCarFactory"/>
 <!--通过实例工厂来配置bean-->
 <bean id="car2" factory-bean="instanceCarFactory" factory-method="getCar">
    <constructor-arg name="name" value="ford"/>
 </bean>

FactoryBean配置bean FactoryBean的源码:

 public interface FactoryBean<T> {
   @Nullable
     T getObject() throws Exception;

   @Nullable
    Class<?> getObjectType();
 
     default boolean isSingleton() {
         return true;
     }
 }

创建一个Car的FactoryBean去实现FactoryBean接口:

public class CarFactoryBean implements FactoryBean {
     private String brand;
 
     public void setBrand(String brand) {
         this.brand = brand;
     }

     @Nullable
    public Object getObject() throws Exception {
        return new Car("BMW", 5000000);
     }

     @Nullable
     public Class<?> getObjectType() {
        return Car.class;
    }

   public boolean isSingleton() {
        return true;
    }
}

--------------------------------------------------------------------------------
xml文件
 <!--通过factorybean来配置bean的实例,通过class来指向factorybean的全类名,property指的factory的属性-->
 <!--,但实际返回的的是,getObject方法返回的实例。-->
 <!--我们在配置bean的时候经常需要用的ioc容器里的其他bean,在factorybean中处理你传入的其他bean以达到目的-->
 <bean id="car1" class="com.wzy.factorybean.CarFactoryBean">
     <property name="brand" value="audi"/>
 </bean>