天天看点

springMVC学习(什么是IOC)

      接下来这段时间,我将会写一个关于springMVC的博客专栏,首先看看百度百科上是怎么定义springMVC的。

         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(一站式) 轻量级开源框架。

        先来看看spring官方为我们提供的一张图片:

springMVC学习(什么是IOC)

        是不是看的云里雾里的,什么是IOC,什么又是AOP还有什么又是springMVC,接下来的这个系列的博客,我将会带大家逐步学习spring。我们首先来学习spring 的ioc模块吧。

       ioc又名控制反转,说白了就是把对象的创建,销毁以及初始化等一系列操作交给spring容器来处理,由spring来控制对象的声明周期。举个栗子:我们现在写了很多java类,然后书写一个spring的配置文件,将这些类交给spring容器来处理,此时这些对象的初始化以及销毁就会由spring来管理,如果我们需要使用对象的话,只需要遵守spring中的规范来书写代码,就可以得到指定的对象,对于应该遵守什么样的规范,这个以后我们在详细来探讨。这也就是我们ioc(控制反转的来历)。即将对象交给spring来控制管理。那么ioc带给我们的优势又有那些呢??大家仔细想想,到现在我们如果需要用到某一个类对象,只能通过手动new出来,那有人可能会说了,我不是可以使用工厂模式吗?注意:工厂模式只是提供给我们创建对象的一个简单调用,其内部同样是new一个我们需要的对象,但是如果使用spring以后,我们只需要在spring的配置文件里边配置好需要有spring来管理的类,具体的该对象的创建管理等都是由spring来做的。废话不多说,我们先来看一个例子:

       我们新建一个java工程,将spring需要的核心jar包拷贝进去。

1.dist/spring.jar

2.lib/jakarta-commons/commons-logging.jar

       这两个就是spring需要的核心jar文件,接下来我们新建一个类HelloIoc.java

package com.test.ioc;
public class HelloIoc {
	
	public void helloIoc() {
		System.out.println("hello ioc first");
	}
}
           

       接下来,给大家演示用spring来管理HelloIoc这个类。首先,我新建一个applicationContext.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.5.xsd">

</beans>
           

         那么,有人可能会问,这么多的东西我怎么记得住呢,不用急,我也是在官方文档中找到的。可以看到,这里的根节点的名称叫做beans,即包含了很多其他的bean,在这里一个bean就是一个具体的类。

       说明一下:这里bean中的id就是唯一标识一个bean的,一般是类名称的第一个字母小写。class顾名思义就是这个类的全类名,大家写的时候,可以按住ctrl键然后点击,如果可以进入,说明class的值是正确的。

       spring容器到现在已经创建好了,现在我们写一个测试类,加载spring容器,将HelloIoc从容器中拿出来。

package com.test.ioc;

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

public class HelloIocTest {
	
	public static void main(String[] args) {
		
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("com/test/ioc/applicationContext.xml");
		HelloIoc helloIoc = (HelloIoc) applicationContext.getBean("helloIoc");
		helloIoc.helloIoc();
	}
}
           

         此时运行我们的main方法,会发现helloIoc()方法已经被执行了,发现我们是没有new HelloIoc这样的方式来创建,而是通过spring容器来为我们创建该对象的。这里需要注意,ApplicationContext就是我们的上下文,通过它来加载配置文件的,然后通过

applicationContext.getBean("helloIoc");来获取我们的实体类,这里getBean中的参数就是我们在spring配置文件中配置的bean的id。

         接下来我们在HelloIoc中加上构造方法,如下:

package com.test.ioc;

public class HelloIoc {
	
	public void helloIoc() {
		System.out.println("hello ioc first");
	}

	public HelloIoc() {
		super();
		System.out.println("HelloIoc has been init");
	}
}
           

然后两次获取该HelloIoc的对象,如下:

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("com/test/ioc/applicationContext.xml");
HelloIoc helloIoc = (HelloIoc) applicationContext.getBean("helloIoc");
HelloIoc helloIoc2 = (HelloIoc) applicationContext.getBean("helloIoc");
System.out.println(helloIoc);
System.out.println(helloIoc2);
           

      此时,控制台打印结果是这样的:

HelloIoc has been init

[email protected]

[email protected]

      可以发现虽然我们得到了两个HelloIoc对象,可是这两个对象是一样的,同时发现构造方法也执行了一次,这就充分说明spring默认为我们创建对象是用的单例模式。可是这里有一个问题,就是单例模式的时候,会出现线程安全问题,比如我现在在为HelloIoc中添加一个String name变量,并且提供get,set方法:

package com.test.ioc;

public class HelloIoc {
	private String name = "";
	public void helloIoc() {
		System.out.println("hello ioc first");
	}

	public HelloIoc() {
		super();
		System.out.println("HelloIoc has been init");
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	
}
           

然后将测试类的代码修改如下:

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("com/test/ioc/applicationContext.xml");
HelloIoc helloIoc = (HelloIoc) applicationContext.getBean("helloIoc");
HelloIoc helloIoc2 = (HelloIoc) applicationContext.getBean("helloIoc");
helloIoc.setName("xiaoming");
helloIoc2.setName("test");
System.out.println(helloIoc);
System.out.println(helloIoc2);
System.out.println(helloIoc.getName());
System.out.println(helloIoc2.getName());
           

此时打印的结果如下:

HelloIoc has been init

[email protected]

[email protected]

test

test

        根据结果我们可以看出现在由于是单例模式,所以出现了helloIoc2将helloIoc的name变量修改了。那么如何避免该问题的出现呢,其实在配置文件中添加这样一句话"scope="prototype""就可以取消默认的单例模式创建该bean了。如下:

<bean id="helloIoc" class="com.test.ioc.HelloIoc" scope="prototype">
	
</bean>
           

此时的打印结果如下:

HelloIoc has been init

HelloIoc has been init

[email protected]

[email protected]

xiaoming

test

可以发现这个时候,spring为我们创建了两个不同的对象。

         这个时候我们已经利用了spring容器来创建我们的对象,现在,我们来演示对象的初始化和销毁的操作。

在spring配置文件中,bean的配置文件中,spring已经为我们提供了对象的初始化和销毁的设置方法:init-method="" destroy-method=""可以看到这样的设置就可以了。接下来我新建一个类,用来演示spring操作对象中初始化和销毁的行为。

package com.test.ioc;

public class HelloIocInitDes {

	public HelloIocInitDes() {
		super();
		System.out.println("HelloIocInitDes runs..");
	}
	
	public void init() {
		System.out.println("init runs ...");
	}
	
	public void destroy() {
		System.out.println("destroy runs ...");
	}
}
           

记住只要我们需要让spring来管理该对象,那么必须在spring的配置文件中配置该bean,这里我加上初始化和销毁方法的设置。

<bean id="helloIocInitDes" class="com.test.ioc.HelloIocInitDes" init-method="init" destroy-method="destroy"></bean>
           

然后新建一个HelloIocInitDesTest.java来测试初始化和销毁的操作。代码如下:

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("com/test/ioc/applicationContext.xml");
HelloIocInitDes helloIocInitDes = (HelloIocInitDes) applicationContext.getBean("helloIocInitDes");
           

此时运行该 HelloIocInitDesTest.java的main方法,结果如下:

HelloIocInitDes runs..

init runs ...

       可以看到init方法是在构造方法之后执行的,可是我们发现执行了init方法,而destroy方法并没有执行,这是怎么回事,因为spring还没有准备销毁我们的对象,我们现在模拟关闭spring容器,来让destroy方法得到执行。

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("com/test/ioc/applicationContext.xml");
HelloIocInitDes helloIocInitDes = (HelloIocInitDes) applicationContext.getBean("helloIocInitDes");
ClassPathXmlApplicationContext context = (ClassPathXmlApplicationContext) applicationContext;
context.close();
           

此时打印如下:

springMVC学习(什么是IOC)

可以发现此时destroy方法得到了执行。

        可是,如果此时我在HelloIocInitDes中的bean配置文件中加上这样一句话:scope="prototype",即不是用默认单例模式创建对象,会发想destroy-method并没有执行。即使我关闭了spring容器。

        总结一下:init-method="init"方法是在构造方法之后由spring容器执行的,我们可以用他来做一些初始化的操作,在destroy-method中做关闭资源等操作,如果该bean不是默认的单例模式创建,此时将不会执行destroy-method。那么关闭资源等操作,将由人为来完成。

         那么spring又是什么时候帮我们创建bean对象的呢??在bean的配置文件中有这么一句配置:lazy-init="",该配置存在两个值:

lazy-init="default" 和lazy-init="true"默认是false。这里,当我lazy-init="default"的时候,即spring容器加载进内存以后就会创建该bean对象,如果lazy-init="true",那么只有在执行geBean("")方法的时候才会创建该bean对象。

         大家注意一下,目前我们所采用的spring容器中的所有的bean的配置都是采用构造函数来创建bean对象的,接下来我介绍一种利用静态工厂的方式来创建bean对象。首先我新建一个HelloIocFactory.java里边定义一个getInstance方法,用来创建HelloIoc对象。

package com.test.ioc;

public class HelloIocFactory {
	
	public static HelloIoc getInstance() {
		return new HelloIoc();
	}
}
           

      然后再bean中这样配置即可:

<bean id="helloIoc2" class="com.test.ioc.HelloIocFactory"
		factory-method="getInstance"></bean>
           

此时如果想要到的HelloIoc对象,只需要像往常那样通过getBean("")方法调用就行了。

            今天的springIOC就到这里吧,下一篇我会写一个spring依赖注入的博文,希望大家喜欢。

源码下载