Java 面试 整理

一、京东面试题

1、@Autowired的实现原理

1、注入方式:

        1、基于构造函数的依赖注入

public class UserServiceImpl implents UserService{
    private UserDao userDao;
    
    @Autowire
    public UserServiceImpl(UserDao userDao){
        this.userDao = userDao;
    }
}

        2、基于setter的依赖注入

public class UserServiceImpl implents UserService{
     private UserDao userDao;
     
     @Autowire
     public serUserDao(UserDao userDao){
         this.userDao = userDao;
     }
 }

        3、基于字段的依赖注入

public class UserServiceImpl implents UserService{
     @Autowire
     private UserDao userDao;
 }

2、@Autowired注解是如何实现自动装配的:


@Autowired注解之所以可以实现自动装配,主要是依赖Spring提供的处理器AutowiredAnnotationBeanPostProcessor,该处理器在初始化的时候便加入了对@Autowired、@Inject、@Value三个注解的处理;

该处理器实现了接口InstantiationAwareBeanPostProcessor,因此可以在bean对象实例化的时候,对其使用了@Autowired的成员进行自动装配。

public AutowiredAnnotationBeanPostProcessor() {
   // 加入了对@Autowired、@Inject、@Value三个注解的处理
   this.autowiredAnnotationTypes.add(Autowired.class);
   this.autowiredAnnotationTypes.add(Value.class);
   try {
      this.autowiredAnnotationTypes.add((Class<? extends Annotation>)
            ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
      logger.info("JSR-330 'javax.inject.Inject' annotation found and supported for autowiring");
   }
   catch (ClassNotFoundException ex) {
      // JSR-330 API not available - simply skip.
   }
}

第一、该处理器何时被加入的 ?:
当构造Spring容器的时候,Spring会向容器注册几个内置的处理器对象,其中就包括了AutowiredAnnotationBeanPostProcessor。

源码可以直接看AnnotationConfigUtils.registerAnnotationConfigProcessors方法。
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
      BeanDefinitionRegistry registry, @Nullable Object source) {

   // 省略代码...

   if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
      RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
      def.setSource(source);
      beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
   }

    // 省略代码...

   return beanDefs;
}

第二、该处理器是什么时候被调用的:

Spring在创建bean的时候会调用doCreateBean方法,在doCreateBean方法中会调用populateBean方法,该方法的作用便是先判断是否该bean对象需要进行自动装配,如果是的话再逐个遍历调用Spring容器已经注入的处理器。

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
	if (hasInstAwareBpps || needsDepCheck) {
		if (pvs == null) {
			pvs = mbd.getPropertyValues();
		}
		PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
		if (hasInstAwareBpps) {
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				if (bp instanceof InstantiationAwareBeanPostProcessor) {
					InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
					// 执行后置处理器,填充属性,完成自动装配
					pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
					if (pvs == null) {
						return;
					}
				}
			}
		}
	}
}

自动装配有几种方式:

没有使用过xml配置进行注入的可能都无法回答这个问题,但是但凡看过Spring实战类书籍的都应该知道这个知识点。

Spring目前支持三种方式,分别是

AUTOWIRE_BY_NAME 按名称自动装配bean属性

AUTOWIRE_BY_TYPE 按类型自动装配bean属性

AUTOWIRE_CONSTRUCTOR 按构造器自动装配

在xml配置注入的时候指定下类型即可进行切换,比如:
(但是xml 配置方式指定装配方式不推荐。)

<bean id="userService" class="com.xxx.UserService" autowire="byName"></bean>

拓展:我司这边不推荐使用在xml里边指定自动装配类型,因为开发者无法对Spring应用中的所有Bean的情况都了如指掌,而通过这种方式指定会导致注入的对象也存在不确定性。

那么使用@Autowired指定的是哪种自动装配
答案是AUTOWIRE_NO,也就是没有指定。

但是实际上看源码的实现其实率先通过类型来装配,如果匹配到的实现是多个的,才会额外采用其他策略。

如果@Autowried注解的接口有多个实现,Spring是如何处理的
大部分人遇见这种情况都是直接回答报错,其实不是的。

如果接口有多个实现,Spring有自己的一套策略:

        1、会看看有没有使用了@Primary注解的bean
        2、根据@Priority注解优先级选择优先级高的。

        3、根据属性的名称和Spring中beanName来进行判断。
        4、再找不到才会报错,也就是NoUniqueBeanDefinitionException异常。
具体的源码实现:

Java 面试 整理

@Autowired注入支持哪几种类型:
码龄较短的或者没有看过源码的都无法回答这个问题,基本上都只知道@Autowired注入一个对象。

而实际上@Autowired除了注册单个对象外,还额外支持注入三种类型,分别是数组、集合以及map类型。

具体参考DefaultListableBeanFactory.resolveMultipleBeans方法

@Autowired 和 @Resource 
Java 面试 整理


2、Bean的默认作用范围是什么?其他的作用范围?


3、索引是什么概念有什么作用?

4、MySQL里主要有哪些索引结构?哈希索引和B+树索引比较?


5、说说Java线程池的原理?线程池有哪些?线程池工厂有哪些线程池类型,及其线程池参数是什么?


6、hashmap原理,处理哈希冲突用的哪种方法?


BIO、NIO、AIO的区别和联系?


jvm内存模型jmm 知道的全讲讲


讲讲Java GC机制?


Java怎么进行垃圾回收的?什么对象会进老年代?


垃圾回收算法有哪些?为什么新生代使用复制算法?


HashMap的时间复杂度?HashMap中Hash冲突是怎么解决的?Java8中的1. HashMap有什么变化?


红黑树需要比较大小才能进行插入,是依据什么进行比较的?其他Hash冲突解决方式?


hash和B+树的区别?分别应用于什么场景?哪个比较好?
 

上一篇:IOC理论推导


下一篇:MyBatis 中使用 Mapper 简化代码