一、概述
Spring的三大核心思想:IoC(控制反转),DI(依赖注入),AOP(面向切面编程)。本问讲着重介绍一下控制反转。
何谓控制反转:Spring 通过一种称作控制反转(IoC)的技术促进了松耦合。当应用了IoC,一个对象依赖的其它对象会通过被动的方式传递进来,而不是这个对象自己创建或者查找依赖对象。你可以认为IoC与JNDI相反——不是对象从容器中查找依赖,而是容器在对象初始化时不等对象请求就主动将依赖传递给它。
通俗的讲,控制权由代码中转到了外部容器,控制权的转移,就是所谓反转。也就是说,正常我们都是 new 一个新对象,才可以调用对象。有了控制反转就不需要了,交给容器来管理,我们只需要通过一些配置来完成把实体类交给容器这么个过程。这样可以减少代码量,简化开发的复杂度和耦合度。
二、面向接口编程
在一个面向对象的系统中,系统的各种功能是由许许多多的不同对象协作完成的。在这种情况下,各个对象内部是如何实现自己的,对系统设计人员来讲就不那么重要了;而各个对象之间的协作关系则成为系统设计的关键。小到不同类之间的通信,大到各模块之间的交互,在系统设计之初都是要着重考虑的,这也是系统设计的主要工作内容。面向接口编程就是指按照这种思想来编程。
面向接口编程,具体到某一个接口,对于调用者来说,我们不关心该接口是由哪个实现类来实现的,也不关心具体是怎么实现的,你只需把该接口提供给调用者就可以了。
三、编码实现IoC
下面,我们使用IoC思想来实现一个简单的面向接口编程。
首先定义两个接口 IAccountService 和 IOrderService,如下:
public interface IAccountService { int insertAccount(String msg); }
IAccountService 接口
public interface IOrderService { void insert(String msg); }
IOrderService 接口
然后,定义这两个接口的实现类
public class AccountBigServiceImpl implements IAccountService { @Override public int insertAccount(String msg) { System.out.println("===== AccountBigServiceImpl ====" + msg); return 0; } }
AccountBigServiceImpl
public class AccountSamllServiceImpl implements IAccountService { @Override public int insertAccount(String msg) { System.out.println("smallService"); return 0; } }
AccountSamllServiceImpl
public class OrderServiceImpl implements IOrderService { @Override public void insert(String msg) { System.out.println("===== OrderServiceImpl ====" + msg); } }
OrderServiceImpl
正常我们使用下面这段代码来调用这两个接口
public class Controller { public static void main(String[] args) { IAccountService accountService = new AccountBigServiceImpl(); accountService.insertAccount("Hello!!"); IOrderService orderService = new OrderServiceImpl(); orderService.insert("World!!"); } }
我们在 Controller 这个类中直接 new 了 IAccountService 和 IOrderService 这两个接口的子类:AccountBigServiceImpl 和 OrderServiceImpl。这样就违背上面提到的面向接口编程的思想,我既要处理 Controller 这个类中的业务逻辑,还要关心 IAccountService 和 IOrderService 这两个接口具体的实现类是谁,上面是使用 AccountBigServiceImpl 这个类来实现接口 IAccountService,如果我要使用 AccountSamllServiceImpl这个实现类来实现这个接口,那我还得修改 Controller 中的代码,显然这样做的维护成本就太高了,而且也很容易出错,那么,我们可以想其他的办法。
首先,我们使用一个配置文件来定义这两个接口指向的实现类
IAccountService=com.jack.course.spring.factory.impl.AccountBigServiceImpl IOrderService=com.jack.course.spring.factory.impl.OrderServiceImpl
conf.properties
然后,我们再定义一个工厂类
public class ServiceFactory { private static final String CONF_FILE_NAME = "factory/conf.properties"; private static Properties prop; private static Map<String, Object> beanContainer; /** * 静态代码块,程序执行时只会被加载一次 */ static { try { beanContainer = Maps.newHashMap(); //1.从配置文件中找出关系 prop = new Properties(); prop.load(ServiceFactory.class.getClassLoader().getResourceAsStream(CONF_FILE_NAME)); } catch (IOException e) { throw new IllegalArgumentException(e); } } public static <T> T getService(Class<T> clazz){ if (beanContainer.containsKey(clazz.getSimpleName())){ return (T) beanContainer.get(clazz.getSimpleName()); } try { //2.根据关系,将具体的实现类返回 Object obj = instanceObj(clazz); beanContainer.put(clazz.getSimpleName(),obj); return (T) obj; } catch (Exception e) { throw new IllegalArgumentException(e); } } private static <T> Object instanceObj(Class<T> clazz) throws Exception { String className = prop.getProperty(clazz.getSimpleName()); Class<?> instance = Class.forName(className); return instance.newInstance(); } }
注意:以上代码做了两处优化。第一,把读取配置文件的操作单独抽出来放入静态代码块,只会执行一次不会重复读取。第二,定义了一个 Map 作为 bean容器存放实例化后的对象,如果容器中已经存在实例化后的对象就可以直接返回,不用在重现通过 instanceObj() 方法来实例化对象了,通过这两处优化,能够大大提高性能。
这个工厂类实现的功能是:通过读取配置文件 conf.properties 得到接口的具体实现类,然后通过反射来生成一个对象传给 Controller,这样 Controller 类当中就可以不关心接口的实现类是谁,怎么实现也不用关心。
那我们 Controller 中就可以这么实现
public class Controller { public static void main(String[] args) { IAccountService accountService = ServiceFactory.getService(IAccountService.class); accountService.insertAccount("Hello!"); IOrderService orderService1 = ServiceFactory.getService(IOrderService.class); IOrderService orderService2 = ServiceFactory.getService(IOrderService.class); orderService1.insert("One"); orderService2.insert("Two"); } }
这样,我们如果想修改实现类的话,只需要修改配置文件就行了,不用修改代码。
运行结果如下:
以上,我们就通过 IoC 的思想实现了面向接口编程。但是,我们这样的实现就完全没有问题吗?
我们稍微修改一下 AccountBigServiceImpl 实现类,增加一个有参数的构造方法来覆盖默认的构造方法
public class AccountBigServiceImpl implements IAccountService { private AccountBigServiceImpl(String msg) { System.out.println(msg); } @Override public int insertAccount(String msg) { System.out.println("===== AccountBigServiceImpl ====" + msg); return 0; } }
然后再执行 Controller,就会报错了。原因是因为我们使用 newInstance 实例化对象时使用的是默认的无参构造方法。
那么,我们通过修改工厂类,也可以实现该功能。但是,可能慢慢还会发现有其他问题,那么我们是不是要全部自己来实现呢,我们可以直接来使用 Spring 框架即可。Spring 框架把我们能想到的和想不到的功能都给实现了,我们只需直接使用就可以了。
四、使用XML文件配置IoC
五、使用注解+XML文件配置IoC
六、使用全注解配置IoC
原文地址:https://www.cnblogs.com/L-Test/p/11593641.html