目录
1 写在前面的话
1、小七不提供任何可以直接运行的源代码,好记性不如烂笔头,希望大家可以亲自动手跟着文章中的代码敲一下。
2、本系列文章说的 Spring,未做特殊说明皆指代 SpringFramework。
3、为了更好的阅读本文,读者至少要学习了解过JavaSE和JavaEE。
2 为什么要学 Spring
2.1 从面包来说
SpringFramework 实际上已经成为了 JavaEE 的标准,作为一个后台 JavaCoder 是肯定离不开它的。说直白点就是,企业需要,学习 Spring 能让你升职加薪。
2.2 从精神食粮来说
Spring 无论是设计还是代码规范都很好(如果你有幸拜读过 eureka 的源码,小七相信你一定不会反对我的),对开发人员技术的提升很有帮助。说直白点,多看大佬写的代码,你会感觉自己也是个大佬~
3 如果没有 Spring,我们是怎么写代码
首先我们来看下面这个例子。
假设我们有两个类
类 A
public class A {
public void a(){
System.out.println("类 A 的 a()方法被调用了...");
}
}
类 B
public class B {
public void b(){
System.out.println("类 B 的 b()方法被调用了...");
}
}
如果我们要在 B 类的 b 方法中调用 A 类的 a 方法(这个时候,我们说B是依赖于A的),我们能怎么做呢?
3.1 方案一
直接在 b 方法中,创建 A 对象,通过 A 对象调用 a 方法
public class B {
public void b(){
System.out.println("类 B 的 b()方法被调用了...");
A a = new A();
a.a();
}
}
此方案缺点很明显,A和B是强耦合在一起的,并且还是直接写死在b()方法中的。
3.2 方案二
我们通过组合的方式,将A作为B的属性,并且构造B的时候就实例化一个A类。
public class B {
private A a;
public B(){
this.a = new A();
}
public void b(){
System.out.println("类 B 的 b()方法被调用了...");
this.a.a();
}
}
以上方案也有缺点。B中A对象的创建是写死的,也就是我们无法控制A对象的生成,举个例子,如果我们想在创建B对象的时候,使用不同的A对象,以上方法就是无能为力的。
3.3 方案三
为了不把B中A对象的创建写死,我们可以改变B的构造方法,外部传A进去。
public class B {
private A a;
public B(A a){
this.a = a;
}
public void b(){
System.out.println("类 B 的 b()方法被调用了...");
this.a.a();
}
}
这个时候,当前需求下我们的问题基本上都解决了。但是如果B中不仅仅依赖了A,还有CDF等等复杂的依赖关系,且不说写起来复杂,修改、拓展也很困难。
3.4 方案四
以上三种方案都在生成对象的时候存在问题,站在前人的肩膀上,生成对象我们可以使用工厂模式,如下:
public class BeanFactory {
public static A getBean(String className) {
try {
return (A) Class.forName(className).newInstance();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("初始化失败, 失败原因: " + e.getMessage());
}
}
}
这里我们通过反射,传入全类名去获取我们需要的对象,那我们通过BeanFactory.getBean(className)方法,就能很简单的获取到我们想要的那个对象了。
3.5 方案五
针对方案四,我们还可以做一丢丢优化,从外部配置文件获取。
首先在resources下面写一个spring.properties,内容如下
a=com.sheep.ioc.ioc001.A
然后初始化的时候加载这个文件,最后修改一下反射的代码
public class BeanFactory {
// 直接使用java的类库
private static Properties properties;
// 加载spring.properties文件
static {
properties = new Properties();
try {
properties.load(BeanFactory.class.getClassLoader().getResourceAsStream("spring.properties"));
} catch (IOException e) {
throw new ExceptionInInitializerError("BeanFactory初始化失败, 失败原因: " + e.getMessage());
}
}
public static Object getBean(String beanName) {
try {
Class<?> beanClazz = Class.forName(properties.getProperty(beanName));
return beanClazz.newInstance();
} catch (ClassNotFoundException e) {
throw new RuntimeException("未获取到[" + beanName + "]的bean!", e);
} catch (IllegalAccessException | InstantiationException e) {
throw new RuntimeException("[" + beanName + "]初始化失败!", e);
}
}
}
3.6 小结
方案四、五,我们将获取对象的方式交给了 BeanFactory 。这种将控制权交给别人的思想,就叫做:控制反转(IOC)。而 BeanFactory 根据指定的 beanName 去获取和创建对象的过程,就叫做:依赖查找( DL )。 说直白点,就是你去洗脚,平常你随便点一个小姐姐,那么控制权在你;但是有一天你对老板说,你安排,那么控制权就不在你了,你就把点小姐姐的权利给了老板,那么就是控制反转了。