你的位置:首页 > Java教程

[Java教程]Spring整合MyBatis(Maven+MySQL)二


接着上一篇博客《Spring整合MyBatis(Maven+MySQL)一》继续。

Spring的开放性和扩张性在J2EE应用领域得到了充分的证明,与其他优秀框架无缝的集成是Spring最为强大的功能。Spring类似电脑的主板,可以将许多部件集成在一起协调工作。

一、在Web项目中启动Spring容器

在Web项目中当Web容器启动时我们要同时启动Spring容器,有三种办法,第一种使用监听器启动,第二使用Servlet启动,第三使用MVC框架的扩展点启动,这里主要选择第一种,因为监听器的启动时机早于Servlet。强烈建议使用办法一。

1.1、使用监听器启动Spring容器

我们需要使用到Spring定义好的一个监听器:org.springframework.web.context.ContextLoaderListener,该监听器在包Spring-web.x.x.x.jar下,修改pom.

    <dependency>      <groupId>org.springframework</groupId>      <artifactId>spring-web</artifactId>      <version>${spring.version}</version>    </dependency>

修改web.

  <listener>    <description>Spring容器启动监听器</description>    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>  </listener>

当监听器在启动Spring容器时会自动查找Web-INF/lib目录下名为applicationContext.

  <listener>    <description>Spring容器加载监听器</description>    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>  </listener>  <context-param>    <param-name>contextConfigLocation</param-name>    <param-value>classpath*:applicationContext.</param-value>  </context-param>

如果有多个配置文件则可以通过逗号分开。怎么判断是否启动成功,则可以参考本文第二点,获得ApplicationContext实例,也可以查看tomcat启动时的信息,如果没有出现错误且能找到如下说明基本成功。

 

启动失败也有几种可能,如applicationContext.

1.2、使用Servlet方式启动Spring容器

方法与1.1基本相同,只是配置有小的区别,修改web.

<servlet>  <servlet-name>context</servlet-name>  <servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>  <load-on-startup>1</load-on-startup></ servlet ><context-param>   <param-name>contextConfigLocation</param-name>    <!-- 多个配置文件之间以“,”隔开 -->  <param-value>      classpath:beans1.</param-value> </context-param>

需要注意的是两者都是继承类ContextLoader,但从Spring3.0开始已经移除了ContextLoaderServlet,用ContextLoaderListener的方式替代。第3种启动方式只有在特定的框架中才有效,所以不多用。

二、获取ApplicationContext实例

 当web容器启动时Spring容器如果也成功启动了,则可以在整个web应用程序中获得ApplicationContext完成IOC、AOP及Spring的其它功能,获得ApplicationContext的常用方法有两种:

2.1、使用工具类WebApplicationContextUtils获得Spring容器

2.1.1、定义一个Service类,BookTypeService代码如下:

package com.zhangguo.Spring61.service;import java.util.List;import javax.annotation.Resource;import org.springframework.stereotype.Service;import com.zhangguo.Spring61.entities.BookType;import com.zhangguo.Spring61.mapping.BookTypeDAO;/* * 图书类型服务 */@Servicepublic class BookTypeService {  @Resource  BookTypeDAO bookTypeDAO;  public List<BookType> getAllBookTypes() {    System.err.println("一些被省去的业务");    return bookTypeDAO.getAllBookTypes();  }}

@Service表示Spring容器将自动管理BookTypeService实例,@Resource表示自动装配,会自动从Spring容器中找到类型为BookTypeDAO的Bean完成bookTypeDAO字段的初始化。

2.1.2、定义一个Servlet,BookTypeList Servlet代码如下:

package com.zhangguo.Spring61.action;import java.io.IOException;import java.io.PrintWriter;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.springframework.web.context.WebApplicationContext;import org.springframework.web.context.support.WebApplicationContextUtils;import com.zhangguo.Spring61.service.BookTypeService;@WebServlet("/BookTypeList.do")public class BookTypeList extends HttpServlet {  private static final long serialVersionUID = 1L;     BookTypeService bookTypeService;     @Override  public void init() throws ServletException {   //在当前上下文中获得Spring容器   WebApplicationContext ctx=WebApplicationContextUtils.getWebApplicationContext(getServletContext());   //从容器中获得bean   bookTypeService=ctx.getBean(BookTypeService.class);  }  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {    PrintWriter writer=response.getWriter();    writer.print(bookTypeService.getAllBookTypes().size());  }  protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {    doGet(request, response);  }}

在Servlet中我们重写了父类的init方法,注意如果重写带参数的那个init方法init(ServletConfig config),则一定要记得调用父类的init方法完成参数的初始化,即不要删除super.init(config),如果不这样将获不到servlet下上文;在init方法中我们通过WebApplicationContextUtils类获得得了Spring容器。

此时的applicationContext.

<??><beans ="http://www.springframework.org/schema/beans"  ="http://www.w3.org/2001/ ="http://www.springframework.org/schema/p"  ="http://www.springframework.org/schema/aop" ="http://www.springframework.org/schema/context"  xsi:schemaLocation="http://www.springframework.org/schema/beans    http://www.springframework.org/schema/beans/spring-beans.xsd    http://www.springframework.org/schema/context    http://www.springframework.org/schema/context/spring-context-4.3.xsd    http://www.springframework.org/schema/aop    http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">  <!--属性占位文件引入,可以通过${属性名}获得属性文件中的内容 -->  <context:property-placeholder location="classpath:db.properties" />  <!--定义一个jdbc数据源,创建一个驱动管理数据源的bean -->  <bean id="jdbcDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"    destroy-method="close">    <property name="driverClass" value="${jdbc.driver}" />    <property name="jdbcUrl" value="${jdbc.url}" />    <property name="user" value="${jdbc.uid}" />    <property name="password" value="${jdbc.pwd}" />    <property name="acquireIncrement" value="5"></property>    <property name="initialPoolSize" value="10"></property>    <property name="minPoolSize" value="5"></property>    <property name="maxPoolSize" value="20"></property>  </bean>  <!--定义一个jdbc数据源,创建一个驱动管理数据源的bean -->  <bean id="jdbcDataSourceBak"    class="org.springframework.jdbc.datasource.DriverManagerDataSource">    <property name="driverClassName" value="${jdbc.driver}" />    <property name="url" value="${jdbc.url}" />    <property name="username" value="${jdbc.uid}" />    <property name="password" value="${jdbc.pwd}" />  </bean>  <!--创建一个sql会话工厂bean,指定数据源 -->  <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">    <!-- 指定数据源 -->    <property name="dataSource" ref="jdbcDataSource" />    <!--类型别名包,默认引入com.zhangguo.Spring61.entities下的所有类 -->    <property name="typeAliasesPackage" value="com.zhangguo.Spring61.entities"></property>    <!--指定sql映射-->    <property name="mapperLocations"      value="classpath:com/zhangguo/Spring61/mapping/*Mapper.></property>  </bean>  <!--自动扫描映射接口 -->  <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">    <!-- 指定sql会话工厂,在上面配置过的 -->    <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>    <!-- 指定基础包,即自动扫描com.zhangguo.Spring61.mapping这个包以及它的子包下的所有映射接口类 -->    <property name="basePackage" value="com.zhangguo.Spring61.mapping"></property>  </bean>  <!-- 创建一个sqlSession对象 -->  <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">    <constructor-arg index="0" ref="sqlSessionFactory" />  </bean>  <!--自动扫描组件 -->  <context:component-scan base-package="com.zhangguo.Spring61">    <context:exclude-filter type="aspectj" expression="com.zhangguo.Spring61.dao.*"/>  </context:component-scan>  <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy></beans>

View Code

运行结果:

2.2、实现接口ApplicationContextAware

 当一个类实现了org.springframework.context.ApplicationContextAware接口时且实现该接口的类被Spring容器管理,则Spring容器会自动意识到需要调用接口中的方法setApplicationContext设置当前的Spring上下文。通俗说实现这个接口可以方便获得Spring上下文。该接口如下:

/* * Copyright 2002-2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * *   http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package org.springframework.context;import org.springframework.beans.BeansException;import org.springframework.beans.factory.Aware;/** * Interface to be implemented by any object that wishes to be notified * of the {@link ApplicationContext} that it runs in. * * <p>Implementing this interface makes sense for example when an object * requires access to a set of collaborating beans. Note that configuration * via bean references is preferable to implementing this interface just * for bean lookup purposes. * * <p>This interface can also be implemented if an object needs access to file * resources, i.e. wants to call {@code getResource}, wants to publish * an application event, or requires access to the MessageSource. However, * it is preferable to implement the more specific {@link ResourceLoaderAware}, * {@link ApplicationEventPublisherAware} or {@link MessageSourceAware} interface * in such a specific scenario. * * <p>Note that file resource dependencies can also be exposed as bean properties * of type {@link org.springframework.core.io.Resource}, populated via Strings * with automatic type conversion by the bean factory. This removes the need * for implementing any callback interface just for the purpose of accessing * a specific file resource. * * <p>{@link org.springframework.context.support.ApplicationObjectSupport} is a * convenience base class for application objects, implementing this interface. * * <p>For a list of all bean lifecycle methods, see the * {@link org.springframework.beans.factory.BeanFactory BeanFactory javadocs}. * * @author Rod Johnson * @author Juergen Hoeller * @author Chris Beams * @see ResourceLoaderAware * @see ApplicationEventPublisherAware * @see MessageSourceAware * @see org.springframework.context.support.ApplicationObjectSupport * @see org.springframework.beans.factory.BeanFactoryAware */public interface ApplicationContextAware extends Aware {  /**   * Set the ApplicationContext that this object runs in.   * Normally this call will be used to initialize the object.   * <p>Invoked after population of normal bean properties but before an init callback such   * as {@link org.springframework.beans.factory.InitializingBean#afterPropertiesSet()}   * or a custom init-method. Invoked after {@link ResourceLoaderAware#setResourceLoader},   * {@link ApplicationEventPublisherAware#setApplicationEventPublisher} and   * {@link MessageSourceAware}, if applicable.   * @param applicationContext the ApplicationContext object to be used by this object   * @throws ApplicationContextException in case of context initialization errors   * @throws BeansException if thrown by application context methods   * @see org.springframework.beans.factory.BeanInitializationException   */  void setApplicationContext(ApplicationContext applicationContext) throws BeansException;}

View Code

为了达到目的,我们现在创建一个名为CtxUtil.java的类,实现该接口,代如下:

package com.zhangguo.Spring61.action;import org.springframework.beans.BeansException;import org.springframework.context.ApplicationContext;import org.springframework.context.ApplicationContextAware;import org.springframework.stereotype.Component;@Componentpublic class CtxUtil implements ApplicationContextAware {  public static ApplicationContext springCtx;    @Override  public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {    springCtx=applicationContext;  }    //根据名称获得容器中的bean  public static Object getBean(String name){    return springCtx.getBean(name);  }    //根据类型获得容器中的bean  public static <T> T getBean(Class<T> clazz){    return springCtx.getBean(clazz);  }}

 为了让Spring容器在加载时能扫描到该类,我们在类上注解了@Component;其实也可以直接在ApplicationContext.

  <bean id="ctxUtil" class="com.zhangguo.Spring61.action.CtxUtil"></bean>  <!--自动扫描组件 -->  <context:component-scan base-package="com.zhangguo.Spring61">    <context:exclude-filter type="aspectj" expression="com.zhangguo.Spring61.dao.*"/>  </context:component-scan>

 

 测试代码如下: