spring学习--三、IOC容器(一)

一、IOC概念以及包含的设计思想

   IOC的概念我们已经熟知,即控制反转(依赖注入),那么IOC的内涵是什么呢,它又是如何使代码解耦的呢?据一个例子来讲,直接用演员来编排剧本,用java语言来描述此场景,可以在剧本类里面直接调用演员类创建需要出场的演员对象,比如在Mottack剧本类中new一个LiuDeua对象来编排剧本,但是这样的编码会使具体的演员类与该剧本有耦合关系,即只能由这个演员来演该剧本,当我们需要还演员时,就必须改剧本。

   在实际的大型程序设计时,往往会涉及很多类之间的各种依赖关系,如果都像上面那样进行直接调用,必然会造成大量类之间有着很强的耦合,既不利于代码的维护,也不利于代码的阅读,所以有必要用一种方法来降低这一种依赖。一种自然而然想到的办法是,“剧本“类只负责剧情,而不关心具体由谁来演,剧本使用角色名字来编排剧本,不负责为角色指定相应的演员,但是一部戏必须要由演员来拍,才能够成为一部戏,不然就只能成为一部戏的空空的剧本,所以这时需要一个新的类“导演”来为这部戏里面的角色指定演员实体,这边是spring IOC的设计思想,即引入 第三方类,将调用类解耦。

//直接调用实体类的方式

public class MoAttack{
    public void cityGateAsk(){
        LiuDeHua ldh = new LiuDeHua();
        ldh.responseAsk("墨者蛤蜊");    
    }
}

//引入接口通过多态调用的方式
//LiuDeHua实现了GeLi接口
public class MoAttack{
    public void cityGateAsk(){
        GeLi geli = new LiuDeHua();
        ldh.responseAsk("墨者蛤蜊");    
    }
}

//引入第三方导演的方式(属性注入)
public class MoAttack{
    private GeLi geli;
    public void setGeLi(GeLi geli){
         this.geli = geli;
    }
    public void cityGateAsk(){
        geli.responseAsk("墨者蛤蜊");    
    }
}    
public class Director {
    public void direct(){
        MoAttack moAttack = new MoAttack();
        Geli geli = new LiuDeHua();
        moAttack.setGeli(geli);
        moAttack.cityGateAsk();    
}} //实际上是将MoAttack 与 LiuDeHua的耦合关系转移给了Director,如果能提前将Director实现,那么就可以实现代码的解耦,如果能设计出一种重复使用的广式Director,恭喜!你已经设计出了一个springIOC容器

     如果能在代码运行调用接口时,动态的为接口指定实现类,那就完美了!

   以上是打比方的说法,对于软件来讲,所谓控制反转(依赖注入)就是将某一接口具体实现类的选择控制权从调用类中移除,转交给第三方来决定,具体的就是由spring容器借由bean配置来进行控制(让调用类对某一接口实现类的依赖关系由第三方(容器或协作类)注入,以移除调用类对某一接口实现类的依赖),因此,我们只需调用接口,而不必关注具体的实现类(实现类可以多样化)。

 

二、IOC的类型:构造函数注入、属性注入、接口注入,spring支持前两种。接口注入和属性注入本质并无区别。

 

三、通过容器进行依赖注入的原理:spring容器可以根据配置文件,在代码运行时完成对接口的实现类的注入,这要归功于java的反射特性,反射允许以代码的方式来操作类和类的方法和属性,在程序运行时可以动态改变一个类和接口(以代码的方式来操纵代码,可以在运行时动态改变代码,很强大的特性吧!)。

  例子:

<bean id = "geli" class = "LiuDeHua" /> //给“geli”接口装配实现类“LiuDeHua”
<bean id = "moAttack" class="com.smart.ioc.MoAttack"
        p:geli-ref="geli"/>  //给mottack接口装配实现类,并给geli属性建立依赖关系,依赖geli接口

    这样spring就自动帮我们装配好了类与类之间的依赖,可以直接调用之,并且实现了各调用类之间的解耦(接口功不可莫)

四、相关的java基础知识

  能够实现上述IOC功能,涉及了java的类装载机制与反射机制的基础知识,还有class=“xxx”中的对xxx资源访问的资源访问器(资源访问接口),当然也包含了xml的解析等。。。现在还没具体学习只能想到这么多,既然有这么多想法,赶紧来学习以下看看究竟涉及哪些基础的知识吧!

 

  1、类装载器ClassLoader

  类装载器是一个java组件,它负责:1、寻找类的字节码文件;2、构造出类在JVM内部表示对象的组件。在Java中,类装载器把一个类装入JVM中需要以下步骤:

  •     装载:查找和导入Class文件。
  •          链接:执行 校验、准备、解析 (解析是可以选择的)
  •                  校验:检查载入的Class文件数据的正确性;准备:给类的静态变量分配存储空间;解析:将符号引用转化为直接引用。

                        初始化:对类的静态变量和静态代码块执行初始化工作
  

 

     ClassLoader有三种,分别为 根装载器(1)、ExtClassLoader(扩展类装载器)(2)、AppClassLoader(应用类装载器)(3)

     根装载器由C++编写,负责装载JRE的核心类库,ExtClassLoader和AppClassloader都是ClassLoader的子类,前者负责装载JRE的扩展目录ext中的jar类包;AppClassLoader负责装载ClassPath路径下的类包。(1)是(2)的父装载器,(2)是(3)的父装载器

    JVM装载类采用“全盘负责委托装载机制”,全盘负责是指,一个类装载器在装载当前类时,当前类的所有依赖也使用该装载器装载,除非显示得使用另外的装载器;委托机制是指装载时先委托父装载器装载类,当父装载器找不到该类时,才从子装载器开始装载。

    除了默认的三个ClassLoader之外,用户还可以编写自己的类装载器来实现自己的需求。

    类装载器的重要方法在本节示例编写完毕之后再讨论

    2、java反射机制

    Class对象,乘坐类描述对象。类文件在被装载并且被解析后,由JVM通过调用类装载器中的defineClass()方法自动构造(没有public构造方法),每个类都有.Class的引用来指向这个类描述对象,而类描述对象又拥有指向关联ClassLoader的引用.总之,java反射体系保证了可以通过程序化的方式访问目标类的所有元素(只要JVM的安全机制允许,就可以访问private与protected的元素)。

    下面是类反射机制的练习。

 

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class Car {
    private String brand;
    private String color;

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public int getMaxSpeed() {
        return maxSpeed;
    }

    public void setMaxSpeed(int maxSpeed) {
        this.maxSpeed = maxSpeed;
    }

    public void introduce() {
        System.out.println("brand:"+brand+";color:"+color+";maxSpeed:" + maxSpeed);
    }
    private int maxSpeed;
    public static void main(String []args) throws Throwable{
        //通过类装载器获得Car类描述对象
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        Class clazz = loader.loadClass("Car");

        //获取类的默认构造器对象并通过它实例化Car
        Constructor cons = clazz.getDeclaredConstructor((Class[])null);
        Car car = (Car)cons.newInstance();

        //通过反射的方法设置属性(访问public方法)
        Method setBrand = clazz.getMethod("setBrand",String.class);
        setBrand.invoke(car,"红旗");
        Method setColor = clazz.getMethod("setColor",String.class);
        setColor.invoke(car,"黑色");
        Method setMaxSpeed = clazz.getMethod("setMaxSpeed",int.class);
        setMaxSpeed.invoke(car,200);

     //私有成员访问限制
     Field colorFid = class.getDeciaredField("color");
     colorFid.setAccessible(true);
     colorFid.set(car,"红色");
//通过反射的方式实现了对类和对象的访问,检测一下 Method introduce = clazz.getMethod("introduce"); introduce.invoke(car); System.out.println("Hello World!"); } }

    通过run mian方法可以有如下输出

  

    下面我们来介绍ClassLoader类的重要方法,以及java反射机制重要反射类。

  五、ClassLoader类的重要方法

    Class loadClass(String name): 根据name来加载一个类,name使用类的全限定名,返回该类的一个描述对象;该方法有一个重载方法(String name,boolen resolve),resolve参数告诉类装载器是否要解析该类,解析就是把类的符号引用转为直接引用。

    Class defineClass(String name,byte[] b,int off,int len) 将类文件的字节数组转换为JVM内部的java.lang.Class对象。

    Class findSystemClass(String name) 从本地文件系统载入Class,若找不到,则抛出ClassNotFoundException异常,这是JVM默认的类装载方法

    ClassLoader getParent() 获取类装载器的父装载器。

  六、java反射机制的主要反射类及其分别的主要方法

   Constructor ,构造函数的反射类:获取途径通过Class对象get,重要方法为newInstance(),创建一个实体类的对象,相当于new

    Method,类的方法的反射类:通过Class对象的getDeclareDMethods()方法获取,重要方法为invoke,其他的可以在详细学习时再查,

    Field,类的成员变量的反射类:获取方式类似,重要方法就是一系列的set,有set(),和setInt()、setBoolean()。

    对于私有变量和私有方法,利用反射对象的setAccessible(true)方法可以绕过访问限制(只要JVM的安全机制允许)。

posted @ 2017-08-29 16:24  The_shy  阅读(199)  评论(0编辑  收藏  举报