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对象:
代码语言:javascript复制//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,不想注解那般方便,但是也是一种实现方式。
外部单体对象注册:
代码语言:javascript复制
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进行介绍。关注我,不要让自己错过任何一点知识点。