• 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的安全机制允许)。

  • 相关阅读:
    第二周作业
    7-2 求最大值及其下标
    第十一周作业
    第九周编程总结
    第八周作业
    第七周作业
    第六周作业
    第五周作业
    第4周作业
    第三周作业
  • 原文地址:https://www.cnblogs.com/Theshy/p/7446305.html
Copyright © 2020-2023  润新知