Spring中的ApplicationContext所体现出来的工厂模式

Spring初体验

通常我们写代码时,先定义一个Class,然后再别的地方实例化,再进行调用,比如下边的Hello World的例子,类Hello有一个say()方法,用于打印出“Hello World”的字符串,另一个类App,承载程序的入口方法main(),我们在main()方法中实例化Hello类,并调用Hello.say()方法,代码如下:

Hello.java 

package demo;

public class Hello {
	public Hello() {
	}
	
	public void say() {
		System.out.println("Hello world!");
	}
}

 App.java

package demo;

public class App {

	public static void main(String[] args) {
//手动硬编码创建一个Hello对象
		Hello hello=new Hello();
		hello.say();	
		

	}

}

上边是入门的编程方式,在程序规模较小的情况是没啥问题的,但程序的规模成指数级增长后,可能成千上万的对象需要创建,可能很多对象的方法还是事务性,你如何在这成千上万个new出来的对象间进行事务处理?当然此处先点到为止,暂时还没进入这么高深的步骤。总之,Sun的工程师们为此发明了一个叫EJB的东西,将某些对象的创建进行统一管理,后来的Spring也继承了这一衣钵,EJB和Spring的差别就是,EBJ很臃肿并依赖程序运行的容器,而Spring则轻巧灵活,直接内嵌在程序中。总之就是不要自己去创建对象,统一由容器创建。

Spring定义了一个叫做ApplicationContext的接口,由其实现类完成对象的创建。按照Spring的思路,将上边的代码稍作转换后写法变成了这样:

package demo;

import org.springframework.context.support.StaticApplicationContext;

public class App {

	public static void main(String[] args) {

		//StaticApplicationContext是ApplicationContext的一个具体实现,用在独立运行的程序中
		StaticApplicationContext context = new StaticApplicationContext();
		context.registerBean(Hello.class);
		Hello hello = context.getBean(Hello.class);
		hello.say();

	}

}

这里我们看到多了一个StaticApplicationContext类的引用,实例化之后,对这Hello作了一些操作,然后通过这个context就可以获取Hello的实例,这里没有了new Hello()的代码,取而代之的是context.getBean(Class),后边的内容就跟前边一样了。这个例子暂时还没看到Spring有什么好处,但我们可以看到Spring的作用,就是用ApplicationContext接管了对象的实例化。当然,一般情况下我们也不会这样去使用Spring,并不会再实际项目中使用上下文获取对象,而是全权托管给Spring。

Spring用了反射机制创建对象,实际效果跟new一个对象是一样的。所以ApplicationContext在创建实例的时候也是调用构造方法的。为了演示Spring的托管效果,我们可以把say()方法在构造方法中调用,这样创建对象时就会调用say()方法,我们改一下Hello及App的代码:

Hello.java

package demo;

public class Hello {
	public Hello() {
		say();
	}
	
	public void say() {
		System.out.println("Hello world!");
	}
}

然后将main方法再改造一下,便只剩下Spring的内容,而无需编写自己的程序的调用代码,修改之后变成这样: 

App.java

package demo;

import org.springframework.context.support.StaticApplicationContext;

public class App {

	public static void main(String[] args) {

		
		StaticApplicationContext context = new StaticApplicationContext();
		context.registerBean(Hello.class);
//因为没有显式getBean()的调用,所以不会主动创建实例,我们用下边的语句进行预实例化。
		context.getBeanFactory().preInstantiateSingletons();
		

	}

}

运行App控制台打出“Hello World!”。

StaticApplicationContext就是所谓的上下文,我们将对象的生命周期交给这个上下文来管理。前边我只体验到了使用上下文来生成对象,实际上上下文还有更多的用法,可以提供除构造方法为的初始化以及销毁方法。由于StaticApplicationContext这个实现只用于测试,所以有些功能并未提供,为了体验更多的功能,接下来我们使用外部xml配置文件来指定要托管的类型。

ApplicationContext本身也有多种具体实现,使用不同的方式来进行自的初始化。ClassPathXmlApplicationContext就是其中之一,其目的是在classpath中搜索xml配置文件来进行上下的初始化。具体xml的配置文件怎么写可以参考下边的链接:Core Technologiesicon-default.png?t=M85Bhttps://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-definition

我们创建一个app.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
        https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--bean结点里可以用init-method属性指定被实例化对象的初始方法,类似前边的构造函数中调用。也可以用destroy-method属性指定对象销毁时调用的方法-->
	<bean class="demo.Hello" init-method="say" destroy-method="bye">
	</bean>
</beans>

Hello.java改成最初的样子:

package demo;

public class Hello {
	public Hello() {
	}
	
	public void say() {
		System.out.println("Hello world!");
	}

    public void bye() {
		System.out.println("Bye bye!");
	}
}

App.java改成如下:

package demo;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {

	public static void main(String[] args) {

		ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("app.xml");
//关闭上下文时会销毁上下中创建的所有对象。
		context.close();

	}

}

运行App控制台打出“Hello World!”及“Bye bye!”。

工厂模式的应用

前文所体现出来的是设计模式中标准的工厂模式。每个上下文中都有一个beanFactory的实例,然后通过beanFactory产生出对象实例。意思就是说,就像工厂一样,你给定设计模型,工厂就照样子给你生产。

App.java中我们只实例化了一个上下文,但程序正常运行了。当然此处我们依然没有体验到Spring带来了什么好处,但可以看出,Spring的上下文就像一个容器,里边装载了我们需要的对象,本来我们需要在main()方法要写的代码,全部由Spring接管。同时不是说我们不需要处理对象的初始化,而是将对象的初始代码与逻辑代码进行分离,并且使用基于配置的方式实现,并不需要进行硬编码。

接下来,我们研究一下,在自己的程序之上,再套一层Spring究竟有什么好处,为什么要将对象初始化进行单独处理。

我们创建一个跟Hello类差不多的HelloTom类:

package demo;

public class HelloTom {

	public void say() {
		System.out.println("Hello Tome!");
	}
}

因为跟Hello长得一样,所以无论用不用Spring,调用方式都是一样的,我们先不用Spring来写个示例。为了使程序有一定扩展性,我们可以抽象出一个接口来,我们建一个SayHello的接口:

package demo;

public interface SayHello {
	void say();
}

然后Hello与HelloTom都实现这个接口:

画个UML类图看起来更直观一点

 

Hello.java

package demo;

public class Hello implements SayHello {

	public void say() {
		System.out.println("Hello world!");
	}
}

HelloTom.java

package demo;

public class HelloTom implements SayHello{

	public void say() {
		System.out.println("Hello Tom!");
	}
}

然后修改App.java第一次调用Hello.say():

package demo;

public class App {

	public static void main(String[] args) {
		SayHello hello=new Hello();
		hello.say();

	}

}

修改App.java再次调用HelloTom.say():

package demo;

public class App {

	public static void main(String[] args) {
		SayHello hello=new HelloTom();
		hello.say();

	}

}

从这两个示例中,可以看出两个不同的类型,却可以使用同一个接口类型来表示。不过,这个示例依然需要改两次代码才能实现不同的调用,能不能不改App.java就可以实现不同的调用呢?答案是可以的。我们可以使用工厂模式,通过运行参数告诉程序来选择哪个类型,但我们不必自己去写这样的逻辑,Spring已经自带了这个功能,可以直接拿用。这次我们还是用xml配置加载的方式,这样写的好处是不用在程序中硬编码,只要修改外部的xml文件后,重新运行程序就可以了。

先将App.java改成如下:

package demo;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {

	public static void main(String[] args) {

		ApplicationContext context = new ClassPathXmlApplicationContext("app.xml");

		SayHello hello = context.getBean(SayHello.class);
		hello.say();

	}

}

然后修改app.xml后运行App.java:

<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

	<bean  class="demo.Hello" >	
	</bean>
</beans>

然后再修改app.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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

	<bean  class="demo.HelloTom" >	
	</bean>
</beans>

 再次运行,发现两次结果是不同的,这个示例中我们除了刚开始设计需要改了下App.java,后边我们就不用再改了,而是修改明文的app.xml文件。这样做的好处就是,对于一个明确的功能,为了对环境做出一定的适应性,我们只要在外部调整一下配置文件,而不必要对项目的主体进行修改。实现了程序代码的轻耦合,也就是减少无关代码的之间的联系。这个示例中,任意一个实现SayHello接口的子类,都可以这样注册到Spring的上下文中,以达到不一样的效果。

当然,ApplicationContext并不仅仅是个工厂实现,它还给了我们统一的编程框架。无论是CS程序,还是WEB程序,我们写Spring都是类似乐高一样的组装方式。区别就在于使用什么样的上下文。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值