分享一道面试题:模拟Spring IOC 控制反转实现原理,建议收藏!
Posted 前程有光
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了分享一道面试题:模拟Spring IOC 控制反转实现原理,建议收藏!相关的知识,希望对你有一定的参考价值。
前言
Spring IOC控制反转是通过反射机制与工厂模式的结合,下面给大家模拟一下
生活案例引入
2000 年,你们家开了一家叫 “笑笑” 的包子铺 ------爸
2002 年,你们城东新开了一家分店 “笑笑” 包子铺------妈
2004 年,你们城西也新开了一家分店"笑笑" 包子铺-----你
…
多年后,包子铺越开越多,口味难以保证
…
“笑笑” 包子铺老板:以后我们几个核心班子只在一家做包子,所有分店不在做包子,直接来我们这里提货即可
带来的好处:
1、所有包子铺的口味一致
2、所有包子铺不在关注做包子,只卖,做包子的过程(控制权限)–某一个工厂里面—IOC(Spring的控制反转)------>工厂模式
代码案例
构建纯净类
public class Book {
private String name;
private String author;
private double price;
public Book (){}
@Override
public String toString() {
return "Book{" +
"name=\'" + name + \'\\\'\' +
", author=\'" + author + \'\\\'\' +
", price=" + price +
\'}\';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public Book(String name, String author, double price) {
this.name = name;
this.author = author;
this.price = price;
}
}
class AB extends Book{}
class AC extends Book{}
传统方式构建实例
//1、传统方式
//弊端:需要自己构建对象(new),若项目中有几百个类,通过该方式构建实例不可取
Book b1 = new AB();
Book b2 = new AC();
纯净类的工厂类BookFactory
public class BookFactory {
//使用简单反射升级
public static Book getBook1() throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//1、先使用反射获取Book的Class对象
Properties pro = new Properties();
//2、获取配置文件信息,加载配置文件
pro.load(new FileInputStream("D://JavaServletProject//ServletDemo//ZhenAiDemoVersionTwo//src//org//soffteem//sources//book//book.properties"));
//3、得到配置文件里面的key值,根据key值反射得到class对象
Class<Book> bookClass = (Class<Book>)Class.forName(pro.getProperty("className"));
//4、获取有参构造器
Constructor<Book> cons = bookClass.getDeclaredConstructor(String.class,String.class,double.class);
//5、通过反射构建对象实例
return cons.newInstance(pro.getProperty("name"),pro.getProperty("author"),Double.parseDouble(pro.getProperty("price")));
}
//如果用户过来需要书,会根据类名去构建书对象,原始方法
public static Book getBook(String name){
Book book = null;
if (name.equals("AB")){
b = new AB();
}else if (name.equals("AC")){
b = new AC();
}else {
//....如果有很多的书籍多次判定.....
}
return book;
}
}
传统方式上升级
//2、现在有了笑笑包子铺(纯净类工厂)
//弊端:此时不需要构建对象,但是依然需要手动输入对象名称
System.out.println("请您输入需要的书名");
//....此处省略部分代码......
Book book = BookFactory.getBook("你输入的名字");
通过简单反射升级
className=org.soffteem.myspring.Book
author=王大锤
name=倚天屠龙记
price=38.9
//3、不用输入就能构建对象
//弊端:如果不是Book对象而是其他对象呐?调用该工厂的这个方法只能够返回Book对象
System.out.println("获取书籍:" + BookFactory.getBook1());
类似Spring IOC容器的工厂构建
XML文件
<?xml version="1.0" encoding="utf-8" ?>
<beans>
<!--书籍类-->
<bean id="book" class="org.soffteem.myspring.Book">
<property name="name" value="倚天屠龙记"></property>
<property name="author" value="金庸"></property>
<property name="price" value="25.3"></property>
</bean>
<!--各种其他类-->
<!--此处的student类没有展示-->
<bean id="student" class="org.soffteem.test.Student"></bean>
</beans>
类似"Spring IOC容器"类
public class ApplicationContextUtils {
//文档对象
Document document;
//1、初始化文档对象
public ApplicationContextUtils() throws DocumentException {
//1.1 通过dom4j的jar读取xml文件,获取文档对象
SAXReader reader = new SAXReader();
this.document = reader.read("D://JavaServletProject//ServletDemo//ZhenAiDemoVersionTwo//src//org//soffteem//sources//book//books.xml");
}
//2、反射 + 集合 + xml解析 获取Bean实例
public <T>T getBean(String id,Class<T> tc) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
//2.1 解析xml文件,获取根节点 <beans><beans>
Element element = document.getRootElement();
//2.2 获取根节点(<beans>)所有的子节点 <bean></bean>
List<Element> list = element.elements();
//3、迭代所有的<bean>节点
for (Element e:list){
//3.1 得到<bean>子节点中id的值
String myId = e.attributeValue("id");
//3.2 得到<bean>子节点中class的数值
String className = e.attributeValue("class");
//3.3 判定:根据用户输入的id进行指定的实例化
//因为此处可能不只只有一个纯净类的节点配置项,要根据用户输入的id值实例化执行的配置节点
if (myId.equals(id)){
//3.3.1 反射当前id对应的className的class对象
Class c = Class.forName(className);
//3.3.2 再次判定:若是父类呐?此处判断父类我是通过class对象判定的
if (c == tc){
//3.3.3 通过反射构建对象
T obj = tc.newInstance();
//3.3.4 得到当前<bean>节点中的所有<properties>子节点
List<Element> props = e.elements();
//3.3.5 得到所有的孙子节点对象<properties>
for (Element prop:props){
//3.3.5.1 得到孙子节点的每一个属性内容name与value对应的值
String name = prop.attributeValue("name");
String value = prop.attributeValue("value");
//3.3.5.2 将内容赋值到对象属性中
setAttribute(tc.getDeclaredFields(),obj,name,value);
}
//返回对象
return obj;
}
}
}
return null;
}
//封装给属性赋值的方法
//① 需要得到所有的属性 ② 给哪个对象赋值 ③ 给哪个属性赋值 ④赋值的内容
public <T>void setAttribute(Field[] fields,T obj,String key,String value) throws IllegalAccessException {
//1、循环属性,得到具体的属性值
for (Field field:fields){
//2、要想给属性赋值,由于属性是私有的,需要设置属性的访问权限
field.setAccessible(true);
//3、考虑一下属性的类型:基本属性类型、对象类型
Class type = field.getType();//获取当前属性字段field的类型
//此处我只写了几个基本数据类型,其他类型可自行添加......
if (key.equals(field.getName())){
if (type == int.class){
field.set(obj,Integer.parseInt(value));
}else if (type == double.class){
field.set(obj,Double.parseDouble(value));
}else if (type == String.class){
field.set(obj,value);
}else {
//若是对象类型
field.set(obj,value);
}
}
}
}
}
模拟"Spring容器"构建实例
//1、构建 "Spring容器" 最先实例化可通过on-load-startup属性,后期交给tomcat容器
ApplicationContextUtils application = new ApplicationContextUtils();
//2、通过"Spring容器"结合配置文件的Id属性值获取Book对象
Book book = application.getBean("book",Book.class);
//3、格式化输出对象,进行内容展示
System.out.println(book.toString());
模拟"Spring容器"构建实例步骤总结
- 构建纯净类,也就是JavaBean类
- 构建工厂类,便于JavaBean通过工厂创建实例
- 实例化工厂,调用工厂类的方法获取实例对象
- Spring IOC获取实例步骤总结
- 构建纯净类
- 构建Spring框架的配置文件
- 将纯净类注入到配置文件中
- 实例化Spring容器,通过getBean()方法根据Id属性值来获取对应的实例
最后
欢迎关注公众号:前程有光,领取一线大厂Java面试题总结+各知识点学习思维导+一份300页pdf文档的Java核心知识点总结! 这些资料的内容都是面试时面试官必问的知识点,篇章包括了很多知识点,其中包括了有基础知识、Java集合、JVM、多线程并发、spring原理、微服务、Netty 与RPC 、Kafka、日记、设计模式、Java算法、数据库、Zookeeper、分布式缓存、数据结构等等。
以上是关于分享一道面试题:模拟Spring IOC 控制反转实现原理,建议收藏!的主要内容,如果未能解决你的问题,请参考以下文章