IoC会怎么面试
Spring面试中,我们会经常被问到一个问题,那就是 什么是IoC?,说实话,我一直认为这个问题很容易回答,但又很难答的好。因为每个使用过的人对IOC都会有不同的看法,你同样的回答可以让这个面试官满意,但不一定就能让另一个面试官满意。
简单回答
IoC是一种控制反转的思想,主要有依赖查找和依赖注入实现。他们之间的关系就类似于好莱坞原则“你不要给我打电话,我们需要的时候会给你打电话”,即低层只需要管理好自己的具体实现,而高层有自己的一套做事逻辑,需要你的时候会去找你,不需要的时候就不会去调用你。
进一步回答
但是进一步也可以这么细讲,按照IoC的定义,那么有很多方面都可以是IoC,我们经常用的JavaBeans就是Ioc的一个实现,在基于Spring框架的项目开发中,Bean的存在是极为重要的,也因此很多面试都会专门挑Bean的生命周期来问。Servlet也是IoC的一种实现,因为他可以去依赖或者反向地通过JNDI(Java Naming and Directory Interface)的形式得到一些外部的资源,比如DataSource或者EJB的组件。这些都是比较常见的IoC实现。
然后讲讲反转控制,这里简单举个例子,比如消息机制,它就是其中的一种,使用过消息事件的程序员都知道,相比我们传统的调用方式,消息机制是一种被动的、推送的方式。这其实也属于Ioc中的一种实现。
拓展回答
再往深处说,就涉及到另一个我司经常问的一个问题:依赖查找和依赖注入的区别?
一般我会这样回答,依赖查找是手动或者主动的依赖查找方式,一般通过名称、通过类型、通过路径的方式来查找。是需要依赖于容器标准的API来实现,比如Servlet的API,这是一种显现的调用API的方式去获取你想要的资源。Spring-beans的BeanFactory里的getBean方法。
而依赖注入则是手动或者自动的绑定的方式,它不需要依赖特定的容器和API。最为常见的一种使用就是@Autowired的使用了。
一般如果只是面试初级程序员,那么面试官问完依赖查找和依赖注入后一般都会停止,如果你工作过2-3年,那么当你回答的问题涉及到Bean时,面试官可能会往更深层去提问你,考察你关于源码的了解情况。因为Bean本身就是IoC的一种实现,很多面试官问完IoC就会让你讲讲Bean,小面曾经就遇过“你知道几种方法去注册一个Spring Bean?”
一般回答 通过BeanDefinition和外部单体对象来注册 即可得分。
运气比较差的就会被接着细问接下来的实现。
没错!那个运气比较差的就是我!!
BeanDefinition方式做法有很多,比如
1、XML配置元信息<bean name="..." .../>
2、使用注解来配置元信息@Bean,@Component,@Import,以及使用Java API来配置元信息
看看下面怎么使用JAVA 注解和标准API来怎么注册Bean
JAVA 注解和BeanDefinition对象:
//3.通过@Import方式注册 @Import(BeanRegistrationDemo.Config.class) public class BeanRegistrationDemo { public static void main(String[] args) { //创建一个BeanFactory容器 AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); //注册Configuration Class(配置类) applicationContext.register(BeanRegistrationDemo.class); applicationContext.refresh(); User user = applicationContext.getBean("user", User.class); User user1 = applicationContext.getBean("user1", User.class); System.out.println("user对象是否一致:"+(user==user1)); //关闭Spring应用上下文 applicationContext.close(); } //2.通过@Component方式注册 @Component public static class Config { //1.通过@Bean方式注册 @Bean(name = {"user","user1"}) public User user() { User user = new User(); return user; } } } //通过BeanDefinition注册 public static void main(String[] args) { //创建一个BeanFactory容器 AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); //通过BeanDefinition注册API实现 registerBeanDefinition(applicationContext,"user"); //启动上下文 applicationContext.refresh(); User user = applicationContext.getBean("user", User.class); System.out.println("user对象:" + user); //关闭Spring应用上下文 applicationContext.close(); } public static void registerBeanDefinition(AnnotationConfigApplicationContext applicationContext,String beanName) { BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(User.class); beanDefinitionBuilder.addPropertyValue("id", 1L); beanDefinitionBuilder.addPropertyValue("nickName", "xxxx"); //注册bean applicationContext.registerBeanDefinition(beanName, beanDefinitionBuilder.getBeanDefinition()); }
@Bean,@Component,@Import都可以注册Bean,且一起用也不会出现Bean注册重复的问题。不过一般项目开发中,使用@Bean就足够了。
BeanDefinition则是把一个类手动的设置成一个Bean,不想注解那般方便,但是也是一种实现方式。
外部单体对象注册:
public static void main(String[] args) { //创建一个BeanFactory容器 AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); //将当前类作为配置类(随便一个具体的实现类来替换即可) EchartServiceImpl echartServic = new EchartServiceImpl(); //注册方法一 //注册Configuration Class(配置类) ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory(); beanFactory.registerSingleton("echartService", echartServic); //注册方法二 //注册Configuration Class(配置类) -> Spring Bean applicationContext.registerBean("echartService", EchartServicImpl.class, () -> echartServic); //注册方法一和方法二只能二选一,否则会报bean名称相同的异常 //启动Spring应用上下文 applicationContext.refresh(); //获取bean方法一 EchartServicImpl service1 = applicationContext.getBean("echartService",EchartServicImpl.class); System.out.println("是否相等:"+(service1 == echartServic)); //获取bean方法二 EchartServicImpl servic2 = beanFactory.getBean("echartService", EchartServicImpl.class); System.out.println("是否相等:"+(servic2 == echartServic)); //关闭Spring应用上下文 applicationContext.close(); }
简单讲解一下外部单体对象
1.我们需要创建一个BeanFactory工厂,new AnnotationConfigApplicationContext(),用它来存放我们的业务bean,存放可以直接使用applicationContext对象的registerBean()方法,也可以使用applicationContext.getBeanFactory()皆可。
2.注册完bean后使用applicationContext.refresh()启动Spring上下文后就可以使用依赖查找去获取bean了,然后判断完是否是同一个bean后,调用close()方法关闭掉Spring上下文销毁bean。
3.一个简单的Bean注册与依赖查找就完成了。
结束
这次的介绍到此为止,IOC的运用实在多种多样,因为在Spring框架的开发中,与它有关的东西实在太多太多,接下来的内容,我会在后续文章中对IOC进行介绍。关注我,不要让自己错过任何一点知识点。