Spring容器与依赖注入(DI)

1 Spring框架简介

1.1 什么是Spring

        Spring框架是一个开源的轻量级的DI和AOP容器框架,致力于简化企业级应用开发,让开发者使用简单的Java Bean来实现从前只有EJB才能实现的功能。

1.2 为什么要使用Spring

        Spring堪称Java世界中最强大的框架,其亮点非常的多,主要体现在以下几个方面。

(1)使用Spring可以实现DI(依赖注入)。实现面向接口编程,以解决项目开发中组件间的解耦问题,让项目模块得以独立测试、灵活扩展和替换。

(2)使用Spring可以实现AOP(面向切面)。AOP可以在无需修改原有类源代码的情况下为它们切入增强功能。

(3)使用Spring可以实现声明式事务管理,无需在项目中写死事务处理边界,具有更高灵活性。

(4)Spring可与大部分的Java开源框架(如Hibernate、MyBatis、Struts2等)进行整合,并进一步简化这些框架的编码。

(5)Spring可以实现分布式远程调用、消息队列、安全验证等诸多大型应用所需的复杂功能,可以大大简化企业级系统的开发。在Spring出现之前,这些功能一般开发者都难以解决,只能依赖于大型厂商(如IBM、Oracle、SAP等)提供的昂贵的EJB容器;但现在,开源世界的Spring都可以做到,而且更为灵活和轻盈。

2 面向接口编程与容器框架

2.1 面向接口编程

Spring首先是一个容器框架,用于管理系统中的JavaBean。那么我们为何需要一个容器框架呢,这是由Java世界推崇的面向接口编程所决定的。

JavaEE平台的其中一个特点是倡导面向接口编程,JavaEE本身就是由SUN提出的各种规范和接口构成的。例如,JSP技术中的Servlet、Filter、Listener、ServletRequest、ServletResponse、HttpSession等对象统统都是接口。

为什么要面向接口编程呢,面向接口,可以降低组件与组件之间的依赖,实现弱耦合,被依赖组件随时可以被替代。例如Tomcat服务器,不过是一组JSP/Servlet接口的实现容器,我们完全可以用其它实现同样接口的容器(如Jetty)来替代它。此外,面向接口编程也使得组件的独立开发与测试提供了可能,否则开发上层模块的开发者就需要等待下层模块完成才能开工,各个模块无法并行开发。

虽然面向接口编程的想法不错,但使用时却要解决一个核心问题——具体对象从何而来?

参考如下代码:“CategoryDao”是一个接口,而“CategoryDaoImpl”是它的实现类,获取对象的常规方式是使用“new”调用实现类的构造方法。如果我们用这种方式来构建具体使用对象,就谈不上面向接口编程了,因为具体实现类已被写死在调用代码中了。

CategoryDao categoryDao = new CategoryDaoImpl();
categoryDao.save(categoryName);

2.2 工厂模式

在传统的面向对象编程中,对象是调用者创建(new)出来的,调用者和被调用者产生了强耦合,而工厂模式在可以在调用者中隐藏具体类型,解决这一问题。

参考如下代码:“ObjectFactory”是对象工厂,可以根据不同的DAO名称获取对应的DAO实现类对象。具体可以使用Java反射技术与XML配置来实现。

CategoryDao categoryDao = (CategoryDao)ObjectFactory.getInstance("categoryDao");
categoryDao.save(categoryName);

2.3 使用Spring充当Bean容器

        Spring框架首先实现了上述的“工厂模式”功能,它就是一个bean(对象)工厂,我们称为bean容器。我们可以把系统需要用到的所有功能对象通过配置的方式放入到Spring容器中,然后在需要使用时从Spring获取。

        当然,Spring远不止这么简单,它更进一步的实现了我们称为“依赖注入”的对象获取方式,我们将在下文讨论。

3 通过Spring容器配置并获取对象

        下面我们正式体验Spring框架的对象管理功能。

(1)获取并添加Spring框架的依赖

使用Spring前,我们需要浏览一下Spring的官方站点,上面有官方文档和相关Maven坐标的描述。

Spring Framework

Spring框架有众多部件,根据官方文档,我们可以通过以下maven坐标得到Spring框架的核心支持(spring-context)。

<!-- Spring DI容器 -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>4.2.5.RELEASE</version>
		</dependency>

(2)在“类路径”下加Spring的bean配置文件“applicationContext.xml”,并配置需要Spring管理的类对象。

<?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.xsd">
	<bean id="categoryDao" class="demo.dao.impl.CategoryDaoImpl" />
    ……
</beans>  

注意,所谓“类路径”,指的是Eclipse Web项目中的“src目录”或者Maven项目中的“src/main/resources”路径。

(3)创建Spring容器(工厂),并通过bean的名称获取bean对象

ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
	CategoryDao categoryDao = (CategoryDao)ctx.getBean("categoryDao");
	categoryDao.save("科幻");

 

4 使用Spring实现依赖注入

        所谓依赖注入DI(Dependency Injection),一些文献也称之控制反转IOC(Inversion of Control):是一种松耦合的开发模式,指对象是被动地接收它的依赖类对象,而非自己主动去查找或创建。在开发中A类依赖于B类(如业务对象依赖于数据访问对象),往往是A类中直接代码创建B类对象使用(或使用JNDI查找B类对象)。而在依赖注入中,A类中的B对象不由A自身创建,而是由容器C在实例化A类对象时主动将A所依赖的B对象注入给它。

        通过bean元素的property子元素,可以通过bean对象的属性实现依赖注入。        property子元素中,name属性用于声明属性名,ref属性用于引用已声明的复杂类型bean对象,value属性用于指定普通类型常量值。

<bean id="userBiz" class="com.demo.biz.impl.UserBizImpl">
		<property name="userDao" ref="userDao" />
</bean>

5 Bean的作用域。

        Spring默认使用单例模式管理bean对象,也就是说对于同一个类Spring中只保留一个实例,我们多次通过Spring的“getBean()”获取的是相同的一个实例。而一些实际的场合(单例模式,适用于无状态Bean,不适用于有状态Bean),单例模式不适用,这时我们可以通过bean的scope属性来改变Bean的作用域。

通过bean元素的scope属性,可以指定Bean对象在Spring容器中的作用域。其取值如下表所示。

scope属性取值

含义

singleton

默认值,Spring容器中对该bean做单例模式处理,对所有id与该bean定义匹配的bean请求,Spring只会返回bean的同一实例。

prototype

每一次请求都会产生一个新的bean实例,相当与一个new的操作,对于prototype作用域的bean,有一点非常重要,那就是Spring不能对一个prototype bean的整个生命周期负责,容器在初始化、配置、装饰或者是装配完一个prototype实例后,将它交给客户端,随后就对该prototype实例不闻不问了。

request

request表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP request内有效。

session

‍session作用域表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP session内有效。

global session

‍global session作用域类似于标准的HTTP Session作用域,不过它仅仅在基于portlet的web应用中才有意义。

示例:

<bean id="userDao" class="com.demo.dao.impl.UserDaoImpl" scope="prototype" />    

注意:若配置为request、session、global session时,若使用的是Servlet 2.4及以上的web容器,那么需要在web应用的XML声明文件web.xml中增加下述ContextListener声明:

<listener>
	<listener-class>
        org.springframework.web.context.request.RequestContextListener
    </listener-class>
</listener>

6 Web环境中启动Spring容器

        在Web环境中,应用程序是由Web服务器启动的,Spring要作为对象容器(对象工厂)为各层提供依赖注入功能,就必须在Web服务器启动时创建Spring实例,并在整个应用程序生命周期中保持唯一。这时,我们就不能在main函数中随便创建ApplicationContext()对象了,因为Web应用程序并不是由main函数启动的。

        针对这个问题,Spring提供了Web服务器的监听程序,使用监听器监听Web应用程序的启动事件,并在事件处理函数中创建Spring实例并使用单例模式缓存起来(存放到Web应用程序上下文中,即ServletContext类型的application对象)。这样在Web程序的任意地方,就可以获取到唯一的Spring实例并实现依赖注入了。

        下面介绍在Web环境中使用Spring的注意事项:

(1)在项目种添加Spring Web依赖

<!-- Spring容器 -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>4.2.5.RELEASE</version>
		</dependency>
		<!-- Spring Web -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
			<version>4.2.5.RELEASE</version>
		</dependency>

(2)配置web.xml,设置Spring Web监听器,在Web应用启动时创建并缓存Spring容器

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
	http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">	
	<!-- 启动Spring容器的监听器 -->
	<listener>
	<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:applicationContext.xml</param-value>
	</context-param>
</web-app>

(3)在Servlet/JSP等请求处理器中获取Spring容器和它所管理的Bean

        在请求上下文中,可以通过以下方法获取Spring容器

WebApplicationContextUtils.getRequiredWebApplicationContext(ServletContext object)

下面是Servlet中使用Spring容器的示例

@WebServlet("/admin/category-list")
public class CategoryListServlet extends HttpServlet {
	protected void doGet(HttpServletRequest request, HttpServletResponse response)
 throws ServletException, IOException {
		//获取Spring容器
		ApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(this.getServletContext());
    	//获取Spring管理的bean对象
		CategoryBiz categoryBiz = (CategoryBiz)ctx.getBean("categoryBiz");
		//执行请求处理
		request.setAttribute("categories", categoryBiz.getAll());
		request.getRequestDispatcher("/admin/category-list.jsp").forward(request, response);
	}
}

7 Spring DI的其它用法

7.1 构造方法(构造器)注入

        通过bean元素的constructor-arg子元素,可以通过bean对象的构造方法实现依赖注入。

        在constructor-arg子元素中,index用于指定参数的索引,name用于指定参数名,ref用于引用已声明的bean对象,value用于指定普通类型常量值。

<bean id="userBiz2" class="com.demo.biz.impl.UserBizImpl2">
	<constructor-arg index="0" ref="userDao" />
</bean>

7.2  三种实例化bean的方式

(1)使用类构造方法实例化

<bean id="userDao" class="com.demo.dao.impl.UserDaoImpl"></bean>

(2)使用静态工厂方法实例化

        首先定义静态工厂方法。

public static UserDao getInstance(){
		System.out.println("执行了静态工厂方法。");
		return new UserDaoImpl();
	}

然后配置,通过静态工厂实例化bean。

<bean id="userDao" 
		class="com.demo.dao.impl.UserDaoFactory" factory-method="getInstance" />

(3)使用实例工厂方法实例化

        首先定义实例工厂方法。

public UserDao createInstance(){
		System.out.println("执行了对象工厂方法。");
		return new UserDaoImpl();
	}

然后配置,通过实例(对象)工厂实例化bean

<bean id="userDaoFactory" class="com.demo.dao.impl.UserDaoFactory" />
<bean id="userDao" factory-bean="userDaoFactory" factory-method="createInstance" />

7.3 Bean的生命周期管理

        在实例化bean时,有时有必要执行一些初始化代码来使它处于可用状态,或者在丢弃bean时需要执行一些清理工作。Spring为这种需求提供了初始化方法init-method和销毁方法destory-method配置,使得bean对象的生命周期管理更为细致。

public class UserDaoImpl implements UserDao {
	public void init(){
		System.out.println("执行了UserDao的初始化方法");
	}
	public void save() {
		System.out.println("执行UserDao,用户信息保存成功。");
	}
	public void destory(){
		System.out.println("执行了UserDao的销毁化方法");
	}
}

通过配置即可在适当时机调用初始化和销毁方法。

<bean id="userDao" 
	class="com.demo.dao.impl.UserDaoImpl" init-method="init" destroy-method="destory" />
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

辰远YIL

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值