zoukankan      html  css  js  c++  java
  • Spring和Hello World----IoC

    <Spring>

    Spring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson 在其著作Expert One-On-One J2EE Development and Design中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 J2EE 应用程序开发提供集成的框架。Spring使用基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。Spring的核心是控制反转(IoC)和面向切面(AOP)。简单来说,Spring是一个分层的JavaSE/EEfull-stack(一站式) 轻量级开源框架。

    <Hello, World!>

    “Hello, World”程序指的是只在计算机屏幕上输出“Hello, World!”(意为“世界,你好!”)这行字符串的计算机程序。hello world作为所有编程语言的起始阶段,占据着无法改变的地位,所有中/英/法/德/美……版本的编程教材中,hello world总是作为第一个TEST记录于书本之中,所有的编程第一步就在于此了!经典之中的经典!hello world!

    ----百度百科

    传统的HelloWorld

    作为程序员,一定写过不少“Hello World!”的例子。现在回顾一下这个经典之中的经典。

    package com.laoqian.demo;
    
    public class OldHelloWorld {
    
        public static void main(String[] args) { 
            System.out.println("Hello World!"); 
        }
    
    }

    运行这段代码,将在控制台上看到熟悉的“Hello World!”。

    这个当然很简单了。不过现在想改变消息内容,不再是只能输出“Hello World!”,而是希望输出“世界,你好!”,或者其想要输出的信息。再简单的OldHelloWorld例子中,可以通过调整代码实现这一需求。可以直接将“Hello World!”修改其他信息,不过这样做的话,每次想输出新的信息都要修改代码、编译,重复多余的工作。显然这样做是不可行的。更好的解决方法是将消息内容移到类代码的外部,并在运行时读入内容。比如通过命令行参数提供消息:

    package com.laoqian.demo;
    
    public class ArgsHelloWorld {
    
        public static void main(String[] args) { 
            if (args.length &gt; 0) { 
                String greetString=args[0]; 
                System.out.println(greetString); 
            }else { 
                throw new IllegalArgumentException("没有提供命令行参数!"); 
            } 
        }
    
    }

    这样在运行时输入命令行参数,就可以输出不同的消息。

    如上所示,ArgsHelloWorld实现了我们的设想,现在无需改变代码就可以更改消息的内容了。不过我们希望将消息输出到一个网页页面中,或者输出到一个文件中,而非简单的打印到控制台上。

    对于这样的需求,ArgsHelloWorld又无能为力了,这是因为ArgsHelloWorld同时承担了两个角色,即负责消息的获取(消息获取器)又负责消息的显示(消息输出器)——说明在类的结构设计上存在缺陷。这意味着,我们需要改变消息输出器时也就等于要改变消息获取器。

    重新设计的HelloWorld

    如果我们要解决以上所述的问题,只得对程序重构,将消息获取器和消息输出器分别用独立的类承担。另外如果我们希望程序变得灵活,最好为消息获取器和消息输出器分别定义接口,这样我们就可以根据实际情况提供不同的实现类。

    负责消息获取的消息获取器通过以下接口描述:

    package com.laoqian.demo;
    
    public interface MsgProvider {
        String getMsg();
    }

    负责消息输出的消息输出器通过以下借口描述:

    package com.laoqian.demo;
    
    public interface MsgPrinter {
        void print();
        void setMsgProvider(MsgProvider provider);
        MsgProvider getMsgProvider();
    }

    任何MsgPrinter的实现类都不关心消息如何获取,这部分的功能已经委托给MsgProvider了,并通过一个setXxx方法设置具体的消息获取器,因此MsgPrinter依赖于MsgProvider。

    创建这两个借口的实现类非常简单,先来看MsgProvider的实现类:

    package com.laoqian.demo;
    
    public class SimpleMsgProvider implements MsgProvider {
        public String getMsg() {
            return "Hello World!";
        }
    }

    SimpleMsgProvider返回一个一成不变的消息,总是 HelloWorld! 。

    再来看看MsgPrinter的实现类:

    package com.laoqian.demo;
    
    public class ConsoleMsgPrinter implements MsgPrinter {
        private MsgProvider provider;
        public void print() {
            if(provider==null){
                throw new IllegalStateException("MsgProvider未提供");
            }
            System.out.println(provider.getMsg());
        }
    
        public void setMsgProvider(MsgProvider provider) {
            this.provider=provider;
        }
    
        public MsgProvider getMsgProvider() {
            return provider;
        }
    
    }

    ConsoleMsgPrinter依然很简单,它将MsgProvider的消息输出到控制台中,至此剩下要做的方法是将消息获取器和消息输出器整合起来,让他们联合工作的类:

    package com.laoqian.demo;
    
    public class DecoupledHelloWorld {
    
        public static void main(String[] args) {
            MsgProvider provider = new SimpleMsgProvider();
            MsgPrinter printer = new ConsoleMsgPrinter();
            printer.setMsgProvider(provider);
            printer.print();
        }
    
    }

    上面的实现非常简单,我们先是创建MsgProvider和MsgPrinter的实例,然后将MsgProvider实例组装到MsgPrinter实例中,再调用MsgPrinter的print()方法将消息输出到控制台中。

    一切如预想的那样,“Hello World!”被输出到控制台中。

    DecoupledHelloWorld虽然比OldHelloWorld复杂了很多,但带来的好处是非常明显的。假如,我们希望将消息输出到一个文件中,只需要对MsgPrinter接口提供一个满足需求的实现类就可以了:

    package com.laoqian.demo;
    
    import java.io.File;
    import java.io.FileWriter;
    import java.io.Writer;
    
    public class FileMsgPrinter implements MsgPrinter {
        private MsgProvider provider;
        public void print() {
            if(provider==null){
                throw new IllegalStateException("MsgProviderδ�ṩ");
            }
            try {
                Writer writer = new FileWriter(new File("D:/output.txt"));
                writer.write(provider.getMsg());
                writer.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        public void setMsgProvider(MsgProvider provider) {
            this.provider=provider;
        }
    
        public MsgProvider getMsgProvider() {
            return provider;
        }
    }

    FileMsgPrinter和ConsoleMsgPrinter相比,不同的是print()方法的实现上,它将消息输出到“D:output.txt”的文件中。

    相应的DecoupledHelloWorld只需要做很小的变动就可以了:

    package com.laoqian.demo;
    
    public class DecoupledHelloWorld {
    
        public static void main(String[] args) {
            MsgProvider provider = new SimpleMsgProvider();
            MsgPrinter printer =new FileMsgPrinter();// new ConsoleMsgPrinter();
            printer.setMsgProvider(provider);
            printer.print();
        }
    
    }

    重新运行DecoupledHelloWorld,你将会在D盘根目录下看到一个新生成的output.txt文件,文件中包含了“HelloWorld!”的消息。

    你可以依此举一反三,将消息输出到一个Socket端口中,或是输出到一个网页的页面中。这说明通过将消息提供者和消息输出者两个角色分解后没我们得到一个比较灵活的代码实现。

    用工厂类创建对象

    至此,我们的 Hello World 看上去已经相当完美了,它可以轻松地应付各种变化的需求,但它并非无懈可击。因为只要我们调整 MsgProvider 或 MsgPrinter 的实现类(如将 MsgPrinter 的实现从 StandardOutputMsgPrinter 调整为 FileMsgPrinter),我们就不得不手工调整 DecoupledHelloWorld 的代码,这意味着我们还是要重新编译和部署程序。

    为了解决这个问题,我们可以创建一个工厂类,由工厂类负责实例化 MsgProvider 和 MsgPrinter 的实现类,具体的实现类我们通过一个属性文件存放。

    首先我们来实现这个工厂类:

    未完。。。

    package com.laoqian.demo;
    
    import java.io.FileInputStream;
    import java.util.Properties;
    
    public class HelloWorldFactory {
        private static HelloWorldFactory instance;
        private Properties init;
        private MsgProvider msgProvider;
        private MsgPrinter msgPrinter;
    
        private HelloWorldFactory(){
            init = new Properties();
            try {
                init.load(new FileInputStream("src/init.properities"));
                String providerClass = init.getProperty("msgProvider");
                String printerClas = init.getProperty("msgPrinter");
                //以下两行根据实现类名实例化实现类
                msgProvider = (MsgProvider)Class.forName(providerClass).newInstance();
                msgPrinter = (MsgPrinter)Class.forName(printerClas).newInstance();
            } catch(Exception e) {
                e.printStackTrace();
            }
        }
        //创建工厂实例
        public static HelloWorldFactory getInstance() {
            if (instance == null) {
                instance = new HelloWorldFactory();
            }
            return instance;
        }
        public MsgPrinter getMsgPrinter(){
            return msgPrinter;
        }
        public MsgProvider getMsgProvider(){
            return msgProvider;
        }
    
    }

    以上代码,我们实现了一个简单的工厂类,它能够从 init.properities 属性文件中读取 MsgProvider 和 MsgPrinter接口的实现类名,并根据实现类的类名分别创建实例化对象。

    init.properities 属性文件需要放在src目录下,具体内容如下所示:

    msgProvider= com.laoqian.demo.SimpleMsgProvider
    
    msgPrinter= com.laoqian.demo.ConsoleMsgPrinter

    相应的你需要修改 DecoupledHelloWorld.java 中的代码,使用工厂方式获取 MsgProvider 和 MsgPrinter。具体代码如下:

    package com.laoqian.demo;
    
    public class DecoupledHelloWorld {
    
        public static void main(String[] args) {
            HelloWorldFactory factory = HelloWorldFactory.getInstance();
            MsgProvider provider = factory.getMsgProvider();
            MsgPrinter printer = factory.getMsgPrinter();
            printer.setMsgProvider(provider);
            printer.print();
        }
    
    }
     

    现在,如果你要调整 MsgProvider 和 MsgPrinter接口的具体实现类,只需要调整 init.properities 属性文件中的内容就可以了,程序代码无需进行任何变化。

    虽然 HelloWorldFactory 的实现显得过于简单,属性文件的地址还是硬编码的,但是对于我们要说明的问题来说,它已经足够了。

    我们对上面的例子进行简短的回顾,从最简单 OldHelloWorld 开始,我们一直在追寻两个目标:

    • 消息内容应当易于调整,而非硬编码在程序中。
    • 消息的输出方式应当易于修改,而非写死在程序中。

    为了达到这个需求,我们引入了 MsgProvider 和 MsgPrinter ,为了能够获取消息, MsgPrinter 接口依赖于MsgProvider接口。最后我们添加了一个工厂类,由它通过外部属性文件获取 MsgProvider 和 MsgPrinter 接口的具体实现类并实例化相应的对象。

    用 Spring 来实现 HelloWorld

    上面的代码最终达到了我们先前设定的目标,实现了程序的解耦,但是我们确实为此付出了昂贵的代价:

    • 为了将整个程序拼在一起,我们编写了大量的辅助性代码,如 HelloWorldFactory。
    • 我们手工为 MsgPrinter 设置 MsgProvider 的实例(printer.setMsgProvider(provider))。

    这是一个再简单不过的程序,我们尚且需要编写如此多的代码,对于复杂的程序来说,我们将无法应对,因此我们必须编写大量的工厂类,编写大量的配置文件,手工维护对象和对象之间的关系。而借助 Spring ,这一切的困难将迎刃而解。

    使用 Spring 对 HelloWorld 进行重构

    Spring 可以通过各种方式维护 Bean 和 Bean 之间的关系,一般情况通过 getXXX/setXXX 属性注入是最常用的方式。因此我们有必要对让 MsgProvider 和 MsgPrinter 联合工作的 HelloWorldFactory 进行重构,让它可以通过属性注入方式装配 MsgPrinter 实例:

    package com.laoqian.demo;
    
    public class SpringHelloWorld {
        private MsgPrinter msgPrinter;
        public void sayHelloWorld() {
            msgPrinter.print();
        }
        //用于注入 MsgPrinter
        public void setMsgPrinter(MsgPrinter msgPrinter) {
            this.msgPrinter = msgPrinter;
        }
    }
     

    在编写 SpringHelloWorld 后,我们需要通过 Spring 的配置文件将 MsgProvider 装配到 MsgPrinter 中,进而再将 MsgPrinter 装配到 SpringHelloWorld 中。

    Spring 配置文件是一个XML文件,具体内容如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd" > 
    
        <bean id="provider" class="com.laoqian.demo.SimpleMsgProvider" />
        <bean id="printer" class="com.laoqian.demo.ConsoleMsgPrinter" >
            <property name="msgProvider" ref="provider" />
        </bean>
        <bean id="helloWorld" class="com.laoqian.demo.SpringHelloWorld">
            <property name="msgPrinter" ref="printer" />
        </bean>
    </beans>

    我们配置了一个实现类为 SimpleMsgProvider ,名为 provider 的Bean;我们将provider通过属性注入装配到名为 printer 的Bean,而 printer 采用ConsoleMsgPrinter 作为实现类;按照相同的方式,我们将 printer 通过属性注入装配给名为 helloWorld 的Bean。这样 helloWorld 就装配好了。

    使用 BeanFactory

    我们将以上的配置文件保存到 src 目录下的 beans.xml 中。我们通过 Spring 的标准API引用这个配置文件并启动Spring容器。Spring容器启动时将根据配置文件实例化Bean并按照配置完成Bean和Bean之间的装配。等Spring容器启动完成后,我们就可以根据Spring API接口获取容器中准备就绪的Bean,像平常一样调用 Bean的各项功能。编写代码之前需要将依赖的类库文件加入到classpath中。包括 spring、commons-logging、和log4j 的类库文件。

    具体实现代码如下:

    package com.laoqian.demo;
    
    import org.springframework.beans.factory.BeanFactory;
    import org.springframework.beans.factory.xml.XmlBeanFactory;
    import org.springframework.core.io.ClassPathResource;
    import org.springframework.core.io.Resource;
    
    public class SpringHelloWorldTest {
    
        public static void main(String[] args) {
            Resource res = new ClassPathResource("beans.xml");
            BeanFactory factory = new XmlBeanFactory(res);
            System.out.println("Spring 容器启动成功");
            SpringHelloWorld helloWorld = (SpringHelloWorld)factory.getBean("helloWorld");
            helloWorld.sayHelloWorld();
        }
    
    }

    至此我们就通过 Spring 实现了 Helloworld。而且到达了之前设定的目标,实现了解耦,实现比较简单灵活。

  • 相关阅读:
    mysql(一) 关联查询的方式
    SpringBoot2.0(五) CORS跨域
    SpringBoot2.0(四) 远程调试
    SpringBoot2.0(三) 文件上传
    SpringBoot2.0(二) 配置文件多环境
    SpringBoot2.0(一) mybatis
    Java InputStream转File
    git 命令学习
    reids 中出现 (error) MOVED 原因和解决方案
    ibm 的 heapanalyzer 分析器
  • 原文地址:https://www.cnblogs.com/tinker/p/5766804.html
Copyright © 2011-2022 走看看