Spring之Hello World
在开始讲述我对Spring的理解之前,我想先讲讲关于Spring的解耦合,控制反转(Inverstion of Control)以及依赖注入(Dependency Injection)这些概念。
其实这三个概念在我看来都是相关联的,因为他们的含义都是彼此相同的。
首先来看解耦合,我们知道软件工程师编程的要求是高内聚,低耦合。那么,什么是高内聚,低耦合呢。
内聚就是一个代码内各元素的相关程度,高内聚就是一个代码内各元素的相关程度高。高内聚就是指指一个软件模块是由相关性很强的代码组成,只负责一项任务,也就是常说的单一责任原则。也就是说我们尽量让一个模块只负责一项任务。同样低耦合就是在一个模块中尽量少的涉及业务逻辑,尤其是在最底层对于数据库的操作层。尽量做到一个代码块只涉及对数据库的一项操作(如果这项操作涉及到了触发器那就另当别论了)。关于低耦合,我们可以实现业务逻辑层的多层次,使每一个层次的业务逻辑较为简单。从而实现我们所说的高内聚,低耦合。所谓解耦合,就是为了降低代码块的耦合程度。
那么我们再来看一下控制反转。它是指将组件的依赖关系的创建和管理交给程序外部的技术(BeanFactory或者ApplicationContext)。传统的模式中,我们如果想要实例化一个类,但是这个类中还依赖与另一个类。我们需要使用new操作符或者通过某个工厂类来实现这一过程。使用IoC方法,这个类依赖的实例是通过某些外部处理过程在运行动态传递给Foo的。
这种在运行时注入依赖的行为方式,我们成为依赖注入。
那么在解释完这些基本的概念后,我们的spring将从最简单的HelloWorld开始
package com.apress.prospring.ch2.common;
/**
* 一个典型的HelloWorld实例
* 就实例而言,这个算是相当简单的。
* 但是如果我们想要改变消息的输出形式或者消息的获取形式呢
* <hr>
* @author Administrator
*
*/
public class HelloWord {
public static void main(String[] args){
System.out.println("Hello World");
}
}
这是一个再简单不过的java程序了,那么如果我们仔细思考一下。关于“Hello World”内容的获取和显示是不是都在一个模块中进行的。所以我们要将消息的获取移除到代码的外部。
下面这个代码我采用的是从命令行参数中修改args入参值实现在运行时读入消息内容。
package com.apress.prospring.ch2.common;
/**
* 将消息的内容移到代码外部,并在运行时读入消息内容,比如从命令行参数
* 无序更改代码就可以获得消息
* 使用命令行参数的Hello World
* @author Administrator
*
*/
public class HelloWorldWithCommandLine {
public static void main(String[] args){
if(args.length>0){
System.out.println(args[0]);
}else{
System.out.println("Hello World");
}
}
}
上面的代码实际上理论还有一些问题,负责消息获取的组件还有负责消息的显示,这是很不合理的。我们不能通过修改显示部分的代码来实现我们的获取方面的功能需求。
那么我们就将消息的获取和显示分开。
我们先声明两个接口(Spring也是面向接口编程的思想)
消息获取接口
package com.apress.prospring.ch2;
/**
* 从实际触发,之前的两个测试类不仅要负责消息的显示还要负责消息的接受。<br>
* 也就是说我们要在显示器里修改代码来改变我们要获取的消息。<br>
* 所以我们需要重构这个代码,将消息显示和消息获取逻辑彻底分开。是指称为独立的组件。<br>
* 让这些组件实现相应的接口<br>
* <hr>
* @author Administrator
* 重构消息获取逻辑
*/
public interface MessageProvider {
public String getMessage();
}
消息显示接口
package com.apress.prospring.ch2;
/**
*
* 重构消息显示逻辑
* @author MyTest
* 任何MessageRenderer的实现都和消息的获取部分相分离,把这部分的功能委托给提供了该功能的<br>
* MessageProvider
*/
public interface MessageRenderer {
public void render();
//JavaBean风格的属性
public void setMessageProvider(MessageProvider provider);
public MessageProvider getMessageProvider();
}
这个地方我们可以看到我们设置了JavaBean风格的属性。因为在Spring中关于属性的注入方式有一种就是通过get/set方法。
之后我们需要两个实现类来实现这两个接口的功能。
消息获取功能
package com.apress.prospring.ch2;
/**
*
* 2018年6月17日
* 下午4:00:24
* @author MyTest
*
*/
public class HelloWorldMessageProvider implements MessageProvider {
public String getMessage() {
return "Hello World";
}
}
消息显示功能
package com.apress.prospring.ch2;
public class StandardOutMessageRenderer implements MessageRenderer {
private MessageProvider messageProvider = null;
public void render() {
if(messageProvider == null){
throw new RuntimeException("You must set the property provider of class: "+
StandardOutMessageRenderer.class.getName());
}
System.out.println(messageProvider.getMessage());
}
public void setMessageProvider(MessageProvider provider) {
this.messageProvider = provider;
}
public MessageProvider getMessageProvider() {
return this.messageProvider;
}
}
可以看到这些代码都是非常简单的,实际上我们这么做看似复杂了很多,实际上是为了实现我们的高内聚,低耦合的思想。
我们来进行一下测试(重构入口类)
package com.apress.prospring.ch2.common;
import com.apress.prospring.ch2.HelloWorldMessageProvider;
import com.apress.prospring.ch2.MessageProvider;
import com.apress.prospring.ch2.MessageRenderer;
import com.apress.prospring.ch2.StandardOutMessageRenderer;
/**
*
* 2018年6月17日
*下午4:04:11
* @author MyTest
*重构入口类
*/
public class HelloWorldDecoupled {
public static void main(String[] args){
MessageRenderer mr = new StandardOutMessageRenderer();
MessageProvider mp = new HelloWorldMessageProvider();
mr.setMessageProvider(mp);
mr.render();
}
}
接下来我再来说一下如果我们使用一个工厂来管理我们的bean,因为我们的实现类会有很多,那么我们可以通过工厂来为我们的接口提供合适的实现类。
具体代码如下
package com.apress.prospring.ch2.factory;
import java.io.FileInputStream;
import java.util.Properties;
import com.apress.prospring.ch2.MessageProvider;
import com.apress.prospring.ch2.MessageRenderer;
/**
* 创建一个简单的工厂类,从properties文件中获取实现类的名称
* 2018年6月17日
*下午4:16:55
* @author MyTest
*
*/
public class MessageSupportFactory {
private static MessageSupportFactory instance = null;
private Properties props = null;
private MessageRenderer renderer = null;
private MessageProvider provider = null;
private MessageSupportFactory(){
props = new Properties();
try {
props.load(new FileInputStream("D:\\mythird\\tim\\myspring\\src\\main\\resources\\ch2\\msf.properties"));
//获取实现类
String providerImpl = props.getProperty("helloWorldProvider");
String rendererImpl = props.getProperty("standardOutMessageRenderer");
renderer = (MessageRenderer)Class.forName(rendererImpl).newInstance();
provider = (MessageProvider)Class.forName(providerImpl).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
}
static{
instance = new MessageSupportFactory();
}
public static MessageSupportFactory getInstance(){
return instance;
}
public MessageRenderer getMessageRenderer(){
return renderer;
}
public MessageProvider getMessagerProvider(){
return provider;
}
}
在这个代码块中我们用到了Properties这个类,那么我们需要一个msf.proerties来提供我们所需要的属性
helloWorldProvider = com.apress.prospring.ch2.HelloWorldMessageProvider
standardOutMessageRenderer = com.apress.prospring.ch2.StandardOutMessageRenderer
接下来是测试类
/**
*
*/
package com.apress.prospring.ch2.factory;
import com.apress.prospring.ch2.MessageProvider;
import com.apress.prospring.ch2.MessageRenderer;
/**
* 2018年6月17日
*下午4:32:14
* @author MyTest
* 工厂获取方法测试
*/
public class HelloWorldDecoupledWithFactory {
/**
* @param args
*/
public static void main(String[] args) {
MessageRenderer mr = MessageSupportFactory.getInstance().getMessageRenderer();
MessageProvider mp = MessageSupportFactory.getInstance().getMessagerProvider();
mr.setMessageProvider(mp);
mr.render();
}
}
那么接下来我们就使用spring为我们提供的DefaultListableBeanFactory,将bean交给它进行管理。
package com.apress.prospring.ch2.spring;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.PropertiesBeanDefinitionReader;
import com.apress.prospring.ch2.MessageProvider;
import com.apress.prospring.ch2.MessageRenderer;
/**
* 为了解决粘合代码过多这一个问题,我们可以完全移除程序内部的MessageSupportFactory类,并用
* Spring的类DefaultListableFactory取而代之
* 2018年6月17日
*下午4:51:43
* @author MyTest
*
*/
public class HelloWorldSpring {
public static void main(String[] args) throws IOException, Exception{
//获取BeanFactroy
BeanFactory factory = getBeanFactory();
MessageRenderer mr = (MessageRenderer)factory.getBean("renderer");
MessageProvider mp = (MessageProvider)factory.getBean("provider");
mr.setMessageProvider(mp);
mr.render();
}
private static BeanFactory getBeanFactory() throws Exception, IOException {
//获取the bean factory
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
//创建definition reader
PropertiesBeanDefinitionReader reader = new PropertiesBeanDefinitionReader(factory);
//加载配置项
Properties props = new Properties();
props.load(new FileInputStream("D:\\mythird\\tim\\myspring\\src\\main\\resources\\ch2\\beans.properties"));
//将配置项通过reader将bean的定义注册到factory中
reader.registerBeanDefinitions(props);
return factory;
}
}
同样我们需要一个beans.properties文件
renderer.(class)=com.apress.prospring.ch2.StandardOutMessageRenderer
provider.(class)= com.apress.prospring.ch2.HelloWorldMessageProvider
其实我们可以看到,消息的显示模块需要一个消息获取对象。那么我们可以在properties文件中进行如下的调整。
renderer.(class)=com.apress.prospring.ch2.StandardOutMessageRenderer
renderer.messageProvider(ref) = provider
provider.(class)= com.apress.prospring.ch2.HelloWorldMessageProvider
这样我们只要获取一个MessageRenderer实现类就足够了
public static void main(String[] args) throws IOException, Exception {
//获取BeanFactroy
BeanFactory factory = getBeanFactory();
MessageRenderer mr = (MessageRenderer)factory.getBean("renderer");
mr.render();
}
其实关于Spring的HelloWorld入门实际上为我们提供的是一种思想。Spring的面向接口编程以及它的解耦合特性,bean的工厂化管理。为我们进一步接触Spring打下了很好的基础。
还没有评论,来说两句吧...