spring IOC机制 制霸简结

  spring的ioc基于反射机制实现。所以聊ioc之前,不得不提一下反射。

 

  一、反射:

 

  1.一个简单实例

  在创建jdbc数据连接前,需要加载jdbc的驱动类:

   1 Class.forName("com.mysql.jdbc.Driver"); 

  这就是一个简单的反射。这与new 一个类不一样,Class.forName()中间是一个String,即加载类前,jvm会多做一件事 ——  java.lang.String --> java.lang.Class

  注意到这一点,我们在改进jdbc连接代码时就可以这么玩——将驱动的名字写进配置文件,然后读到数据库操作类中。

  jdbc.properties

1 driver=com.mysql.jdbc.Drive

  DataSourceUtil.java

 1 import java.util.Properties;
 2 import java.sql.DriverManager;
 3 import java.io.*;
 4 
 5 public class DataSourceUtil {
 6     private static String driver;
 7     
 8     static {
 9         Properties properties = new Properties();
10         InputStream is = DataSourceUtil.class.getClassLoader()
11                 .getResourceAsStream("jdbc.properties");
12         try {
13             properties.load(is);
14         } catch (IOException e) {
15             throw new RuntimeException(e);
16         }
17         driver = properties.getProperty("driver");
18     }
19     
20     Class.forName(driver);
21 ......

  配置化的好处就不多说了。这里要说的是反射的好处:因为有反射机制,使得我们配置化的资源变多,大大减少硬编码(hard code)。

 

  2.反射是在程序运行时发挥作用的。

  这是反射调用类与我们在代码里写构造函数真正的区别。

  现在我们在代码里new一个类 Haha ha = new Haha();jvm在运行new Haha()前,会先检查类Haha.class是否加载,寻找类对应的class对象 ( 这个对象跟ha代表的对象意义不同——jvm启动后,类加载器会将class文件加载到jvm的方法区中,创建类的class对象到堆中,注意这个是类的类型对象,每个类只有一个class对象,作为方法区类的数据结构的接口 ),若加载好,则为你的对象分配内存,初始化Haha()。

  回到反射的代码 Class.forName("com.mysql.jdbc.Driver");jvm直接根据全类名"com.mysql.jdbc.Driver"找到并加载类。后面创建类的类型对象等等步骤不赘述。

  简单的说,直接构造函数需要jvm事先将类加载到内存中,而反射则是在jvm运行时完成类加载的过程。由此可见反射是一个相当动态的过程,需要什么就加载什么,不要求jvm在启动时把所有资源都加载。而且在实际中,我们需要动态更改或者引用一些类(例如换个驱动类),这些类肯定没有事先加载,反射调用可以满足我们这些需要灵活处理的需求。

  3.反射的弊端

  凡事好坏相对,反射的动态加载过程必然牺牲性能,在某贴看过测试,反射调用相对于new耗费多几百倍的时间。当然就算是几百倍也是毫秒级的事情,通常不会成为制约运行时间的关键。另外,反射除了用在上面实例中的Lib类外,也可以用到直接的业务类(后面的ioc就是这么干的)中,这样除了性能(初次调用),还会减低代码可读性。

 

  二、IOC

  Inverse of Control,控制反转,是spring容器的内核。IOC是一种理念,目的是解除代码中的紧耦合关系。

 

  1.紧耦合的例子

  我们要调用某一个类的动态方法,首先要构造函数:

  Test.java

   Service s=new Service(); 

  这里为了获取Service类的内容,首先new Service(),然后赋给Service类型的对象s。这种调用导致了紧耦合——

  (1)上下级依赖性强。Service由Test创建,生命周期完全由Test控制,即控制权牢牢地在Test手里。

  (2)生产机制效率低下,不可重用。假如在类Testtwo中也要调用Service,不得不自己构造函数。

 

  2.spring的解决方案——IOC

  spring的ioc机制就是为了解决紧耦合的问题。首先,spring在程序运行时帮我我们创建好所有类的实例,放在spring容器中。然后,当我们需要调用类时,让spring将实例注入到调用类中,完成实例化的过程。这样就解决了紧耦合——

  (1)打破上下级的依赖关系。Service不再由调用类Test创建,而是spring创建(通过反射创建),并放到spring容器中。生命周期由spring控制。

  (2)提高效用和重用性。spring默认用单例模式创建对象,比起每次new效率高,哪里需要调用便注入到哪里。

  因为ioc,我们可以把spring看作是一个“管家式”框架,将层层耦合的程序打散成不同的小模块,统一由spring协调管理,完成实例化,调用,结束回收等过程。控制反转,可以这样字面理解,实现类的控制权从调用类中移除,“反转”移交给第三方spring容器

 

  3.spring的解决例子

  在代码层面上,spring就是设法将 Service s = new Service() 两边的Service弄掉。

  前面的好解决,spring面向接口编程,即多创建一个Service的接口类ServiceIfc(),这样代码就变成了 ServiceIfc s = new Service()

  new Service()部分这样处理——

  (1)在spring容器中创建实例,需要在spring配置文件里加入相应的bean。

  applicationContext.xml

1 <?xml version="1.0" encoding="UTF-8"?>
2 <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
3 <beans>    
4     <bean id="Service"    class="demo.Service">        
5     </bean>
6 </beans>

 

  (2)要从spring容器中拿出来,需要用到ApplicationContext。

  BeanUtil.java

 1 import org.springframework.context.*;
 2 
 3 public class BeanUtil {    
 4     //单例创建ApplicationContext
 5     private static ApplicationContext appContext = new ClassPathXmlApplicationContext("applicationContext.xml");
 6     private BeanUtil() {}    
 7     
 8     public static Object getBean(String name){        
 9         return appContext.getBean(name);
10     }
11 }

  (3)注入到调用类Test

   ServiceIfc s=(ServiceIfc)BeanUtil.getBean("Service"); 

  至此,一个创建到调用的过程就完成了。这是一个理解性的过程。在实际中,通常会采用spring的注解方式,扫描创建和自动注入bean,不需要自己写BeanUtil.java,配置文件也不用一个个添加bean,整个过程就像直接new一样优雅。

 

posted @ 2017-10-23 01:53  枫林晚月  阅读(233)  评论(0编辑  收藏  举报