Spring是分层的Java平台应用一站式的轻量级开源框架,以反转控制(Inversion of Control,IoC)和面向切面编程(Aspect Oriented Programming,AOP)为内核,提供了展现层SpringMVC、持久层SpringJDBC以及业务层事务管理等众多企业级应用技术,此外Spring还整合了中国第三方框架和类库。

    Spring给我们带来以下几个好处:

  • 方便解耦,简化开发。通过Spring提供的IoC容器,可以将对象之间的依赖关系交由Spring进行控制,避免硬编码所造成的过度程序耦合。

  • AOP编程的支持。通过Spring提供的AOP功能,用户可以方便地进行面向切面编程

  • 声明式事务。在Spring中,用户可以从单调烦闷的事务管理代码中解脱出来,通过声明式事务灵活地进行事务管理,提高开发效率和质量

  • 方便程序的测试。可以用非容器依赖的编程方式进行所有的测试工作。

  • 方便集成各种优秀的框架。Spring是非***是框架,能够整合其他优秀框架,如Struts、Hibernate、Quartz等

  • 降低JavaEEAPI的使用难度。Spring为JavaEE API(如JDBC、JavaMail和JMS等)提供了一个薄薄的封装层,通过Spring的简易封装,大大降低了这些JavaEE API的使用难度

  • Spring是开源项目,并且是源码是Java技术的最佳实践规范。

    Spring的组成:IoC、AOP、数据访问和继承、Web及远程调用以及测试框架。

  • IoC,Spring核心模块实现了IoC的功能,它将类和类之间的依赖从代码中脱离出来,用配置的方式进行依赖关系描述,由IoC容器负责依赖类之间的创建、拼接、管理和获取等工作。BeanFactory接口是Spring框架的核心接口,它实现了容器的许多核心功能。Context模块构建于BeanFactory之上,扩展了BeanFactory的功能。提供了邮件服务、任务调度以及JNDI定位、远程访问等。ApplicationContext是Context模块的核心接口。

  • AOP模块,AOP是进行横切逻辑编程的思想。在AOP模块里,Spring提供了满足AOP Alliance规范的实现,此外和整合了AspectJ这种AOP语言级的框架。

  • 数据访问和集成。任何应用程序的核心问题都是对数据的访问和操作。数据有很多表现形式,如数据表、XML、消息等。Spring站在DAO的抽象层面,简历了一套面向DAO层统一的异常体系。其次,Spring通过模板化技术对各种数据访问技术进行了薄层封装,将模式化的代码隐藏起来,是数据访问的程序得到大幅简化。这样Spring就建立起了和数据形式及访问技术无关的统一的DAO层,借助AOP技术,Spring提供了声明式事务的功能。

  • Web及远程访问。Spring提供了MVC,SpringMVC类似于StrutsMVC框架。此外Spring在远程访问以及WebService上提供了很多著名框架的整合。

本章将通过一个登陆示例来演示SpringMVC。

1、准备工作,创建数据库和数据表

DROP DATABASE IF EXISTS spring_src;
CREATE DATABASE spring_src DEFAULT CHARACTER SET utf8;
CREATE USER 'spring_src'@'localhost' IDENTIFIED BY 'spring_src';
GRANT ALL ON spring_src.* TO 'spring_src'@'localhost';
USE spring_src;

##创建用户表
CREATE TABLE t_user (
   user_id   INT AUTO_INCREMENT PRIMARY KEY,
   user_name VARCHAR(30),
   password  VARCHAR(32),
   last_visit datetime,
   last_ip  VARCHAR(23)
)ENGINE=InnoDB; 

##创建用户登录日志表
CREATE TABLE t_login_log (
   login_log_id  INT AUTO_INCREMENT PRIMARY KEY,
   user_id   INT,
   ip  VARCHAR(23),
   login_datetime datetime
)ENGINE=InnoDB; 

##插入初始化数据
INSERT INTO t_user (user_name,password) 
             VALUES('admin','123456');
COMMIT;

2、创建的工程如下:

wKiom1da4x2j47gUAAAVN_NJxnE660.png-wh_50

maven项目sprProject的主目录的pom文件内容:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.smart</groupId>
    <artifactId>chapter</artifactId>
    <packaging>pom</packaging>
    <version>3.1-SNAPSHOT</version>
    <name>Spring3.1 </name>
    <description>Spring3.1</description>
    <properties>
        <file.encoding>UTF-8</file.encoding>
        <java.version>1.6</java.version>
        <spring.action.version>3.1-SNAPSHOT</spring.action.version>
        <org.springframework.version>3.1.1.RELEASE</org.springframework.version>
        <mysql.version>5.1.6</mysql.version>
        <commons-dbcp.version>1.2.2</commons-dbcp.version>
        <aspectjweaver.version>1.6.9</aspectjweaver.version>
        <apache.commons.version>1.1.1</apache.commons.version>
        <commons-collections.version>3.2.1</commons-collections.version>
        <javassist.version>3.9.0.GA</javassist.version>
        <commons-beanutils.version>1.8.3</commons-beanutils.version>
        <ehcache.version>1.6.2</ehcache.version>
        <hibernate.version>3.6.10.Final</hibernate.version>
        <hibernate-validator.version>4.0.2.GA</hibernate-validator.version>
        <slf4j-jdk14.version>1.6.1</slf4j-jdk14.version>

        <commons-fileupload.version>1.2.1</commons-fileupload.version>
        <jsp-api.version>2.0</jsp-api.version>
        <servlet-api.version>2.5</servlet-api.version>
        <jstl.version>1.2</jstl.version>
        <standard.version>1.1.2</standard.version>
        <freemarker.version>2.3.8</freemarker.version>
        <jetty.version>6.1.5</jetty.version>

        <mockito-all.version>1.8.5</mockito-all.version>
        <junit.version>4.9.0</junit.version>
        <testng.version>6.3.1</testng.version>
        <unitils.version>3.1</unitils.version>
        <dbunit.version>2.4.8</dbunit.version>
    </properties>
    <modules>
        <module>chapter1</module>
    </modules>
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-surefire-plugin</artifactId>
                <configuration>
                    <skip>true</skip>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

chapter1的pom文件配置依赖关系

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>com.smart</groupId>
        <artifactId>chapter</artifactId>
        <version>3.1-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>chapter1</artifactId>
    <version>3.1-SNAPSHOT</version>
    <name>第一章示例</name>
    <description>Spring 快速入门</description>
    <packaging>war</packaging>
    <dependencies>
        <!--① 依赖的Spring模块类库 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>${org.springframework.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${org.springframework.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>${org.springframework.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${org.springframework.version}</version>
        </dependency>
        <!--② 依赖的持久化类库-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.version}</version>
        </dependency>
        <!--③ 依赖的公共类库-->
        <dependency>
            <groupId>commons-dbcp</groupId>
            <artifactId>commons-dbcp</artifactId>
            <version>${commons-dbcp.version}</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>${aspectjweaver.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>com.springsource.org.apache.commons.logging</artifactId>
            <version>${apache.commons.version}</version>
        </dependency>
        <dependency>
            <groupId>fakepath</groupId>
            <artifactId>com.springsource.net.sf.cglib</artifactId>
            <version>2.1.3</version>
        </dependency>
        <!--④ 依赖的WEB类库-->
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>${jsp-api.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>taglibs</groupId>
            <artifactId>standard</artifactId>
            <version>${standard.version}</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>${servlet-api.version}</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>${jstl.version}</version>
        </dependency>
        <!--⑤ 依赖的测试类库-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${org.springframework.version}</version>
        </dependency>
        <dependency>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
            <version>${testng.version}</version>
        </dependency>
    </dependencies>
    <build>
        <finalName>chapter1</finalName>
        <plugins>
            <plugin>
                <artifactId>maven-surefire-plugin</artifactId>
                <configuration>
                    <skip>true</skip>
                </configuration>
            </plugin>
            <!-- jetty插件 -->
            <plugin>
                <groupId>org.mortbay.jetty</groupId>
                <artifactId>maven-jetty-plugin</artifactId>
                <version>6.1.5</version>
                <configuration>
                    <webAppSourceDirectory>src/main/webapp</webAppSourceDirectory>
                    <scanIntervalSeconds>3</scanIntervalSeconds>
                    <contextPath>/chapter1</contextPath>
                    <connectors>
                        <connector implementation="org.mortbay.jetty.nio.SelectChannelConnector">
                            <port>8088</port>
                        </connector>
                    </connectors>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

chapter项目的目录结构

wKiom1dblvSzENsTAABFydOLsqs491.png-wh_50

相对应的代码如下:

domain(领域对象)包代码如下:

package com.smart.domain;

import java.io.Serializable;
import java.util.Date;

public class User implements Serializable {
    private int userId;

    private String userName;

    private String password;

    private String lastIp;

    private Date lastVisit;

    public String getLastIp() {
        return lastIp;
    }

    public void setLastIp(String lastIp) {
        this.lastIp = lastIp;
    }

    public Date getLastVisit() {
        return lastVisit;
    }

    public void setLastVisit(Date lastVisit) {
        this.lastVisit = lastVisit;
    }

    public int getUserId() {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

}

package com.smart.domain;

import java.io.Serializable;
import java.util.Date;

public class LoginLog implements Serializable {
    private int loginLogId;

    private int userId;

    private String ip;

    private Date loginDate;

    public String getIp() {
        return ip;
    }

    public void setIp(String ip) {
        this.ip = ip;
    }

    public Date getLoginDate() {
        return loginDate;
    }

    public void setLoginDate(Date loginDate) {
        this.loginDate = loginDate;
    }

    public int getLoginLogId() {
        return loginLogId;
    }

    public void setLoginLogId(int loginLogId) {
        this.loginLogId = loginLogId;
    }

    public int getUserId() {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }

}

dao(持久层)代码如下:

package com.smart.dao;

import java.sql.ResultSet;
import java.sql.SQLException;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowCallbackHandler;
import org.springframework.stereotype.Repository;

import com.smart.domain.User;

@Repository
public class UserDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;


    public int getMatchCount(String userName, String password) {
        String sqlStr = " SELECT count(*) FROM t_user "
                + " WHERE user_name =? and password=? ";
        return jdbcTemplate.queryForInt(sqlStr, new Object[]{userName, password});
    }

    public User findUserByUserName(final String userName) {
        String sqlStr = " SELECT user_id,user_name "
                + " FROM t_user WHERE user_name =? ";
        final User user = new User();
        jdbcTemplate.query(sqlStr, new Object[]{userName},
                new RowCallbackHandler() {
                    public void proce***ow(ResultSet rs) throws SQLException {
                        user.setUserId(rs.getInt("user_id"));
                        user.setUserName(userName);
                    }
                });
        return user;
    }

    public void updateLoginInfo(User user) {
        String sqlStr = " UPDATE t_user SET last_visit=?,last_ip=?"
                + " WHERE user_id =?";
        jdbcTemplate.update(sqlStr, new Object[]{user.getLastVisit(),
                user.getLastIp(), user.getUserId()});
    }
}

package com.smart.dao;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import com.smart.domain.LoginLog;

@Repository
public class LoginLogDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    public void insertLoginLog(LoginLog loginLog) {
        String sqlStr = "INSERT INTO t_login_log(user_id,ip,login_datetime) "
                + "VALUES(?,?,?)";
        Object[] args = {loginLog.getUserId(), loginLog.getIp(),
                loginLog.getLoginDate()};
        jdbcTemplate.update(sqlStr, args);
    }
}

在Spring中装配dao, 本案例在applicationContext.xml配置中装配dao

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
   xmlns:context="http://www.springframework.org/schema/context"
   xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
   xsi:schemaLocation="http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
       http://www.springframework.org/schema/context 
       http://www.springframework.org/schema/context/spring-context-3.1.xsd
       http://www.springframework.org/schema/tx 
       http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop-3.1.xsd">
    
    <!-- 扫描类包,将标注Spring注解的类自动转化Bean,同时完成Bean的注入 -->
    <context:component-scan base-package="com.smart.dao"/>
    <context:component-scan base-package="com.smart.service"/>
    
    <!-- 配置数据源 -->
   <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
      destroy-method="close" 
      p:driverClassName="com.mysql.jdbc.Driver"
      p:url="jdbc:mysql://localhost:3306/sampledb31"
      p:username="root"
      p:password="" />

   <!-- 配置Jdbc模板  -->
   <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"
      p:dataSource-ref="dataSource" />
      
   <!-- 配置事务管理器 -->
   <bean id="transactionManager"
      class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
      p:dataSource-ref="dataSource" />
      
   <!-- 通过AOP配置提供事务增强,让service包下所有Bean的所有方法拥有事务 -->
   <aop:config proxy-target-class="true">
      <aop:pointcut id="serviceMethod"
         expression=" execution(* com.smart.service..*(..))" />
      <aop:advisor pointcut-ref="serviceMethod" advice-ref="txAdvice" />
   </aop:config>
   <tx:advice id="txAdvice" transaction-manager="transactionManager">
      <tx:attributes>
         <tx:method name="*" />
      </tx:attributes>
   </tx:advice>
</beans>

业务层(service)代码:

package com.smart.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.smart.dao.LoginLogDao;
import com.smart.dao.UserDao;
import com.smart.domain.LoginLog;
import com.smart.domain.User;

@Service
public class UserService {

    @Autowired
    private UserDao userDao;

    @Autowired
    private LoginLogDao loginLogDao;

    public boolean hasMatchUser(String userName, String password) {
        int matchCount = userDao.getMatchCount(userName, password);
        return matchCount > 0;
    }

    public User findUserByUserName(String userName) {
        return userDao.findUserByUserName(userName);
    }

    public void loginSuccess(User user) {
        LoginLog loginLog = new LoginLog();
        loginLog.setUserId(user.getUserId());
        loginLog.setIp(user.getLastIp());
        loginLog.setLoginDate(user.getLastVisit());
        loginLogDao.insertLoginLog(loginLog);
    }

}

在Spring中装配Service,具体内容将applicationContext.xml。


单元测试。TestNG是一种基于注解的新一代单元测试框架,通过添加灵活的配置、测试分类和参数设置以及依赖测试等特性来克服JUnit的不足之处。首先在pom.xml文件中配置testNG、Spring-test的两个类的依赖。详见chapter1项目下的pom.xml。

单元测试模块结构:

wKioL1dbnCmi-gL9AAAue8ffQck456.png-wh_50

单元测试代码如下:

package com.smart.service;


import java.util.Date;


import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.testng.annotations.*;

import static org.testng.Assert.*;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import com.smart.domain.User;

@ContextConfiguration(locations = {"/applicationContext.xml"})
public class UserServiceTest extends AbstractTestNGSpringContextTests {

    @Autowired
    private UserService userService;

    @Test
    public void hasMatchUser() {
        boolean b1 = userService.hasMatchUser("admin", "123456");
        boolean b2 = userService.hasMatchUser("admin", "1111");
        assertTrue(b1);
        assertTrue(!b2);
    }

    @Test
    public void findUserByUserName() {
        User user = userService.findUserByUserName("admin");
        assertEquals(user.getUserName(), "admin");
    }

    @Test
    public void loginSuccess() {
        User user = userService.findUserByUserName("admin");
        user.setUserId(1);
        user.setUserName("admin");
        user.setLastIp("192.168.12.7");
        user.setLastVisit(new Date());
        userService.loginSuccess(user);
    }
}

package com.smart.dao;


import com.smart.domain.User;
import com.smart.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.testng.annotations.Test;

import java.util.Date;

import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;

@ContextConfiguration(locations = {"/applicationContext.xml"})
public class UserDaoTest extends AbstractTestNGSpringContextTests {

    @Autowired
    private UserDao userDao;

    @Test
    public void hasMatchUser() {
        int count  = userDao.getMatchCount("admin", "123456");
        assertTrue(count>0);
    }

    @Test
    public void findUserByUserName() {
        User user = userDao.findUserByUserName("admin");
        assertNotNull(user);
        assertEquals(user.getUserName(), "admin");
    }

}

package com.smart.web;

import com.smart.domain.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertNull;

@ContextConfiguration(locations = {"classpath:applicationContext.xml","file:d:/actionSpring/chapter/chapter1/src/main/webapp/WEB-INF/viewspace-servlet.xml"})
public class LoginControllerTest extends AbstractTestNGSpringContextTests {
    @Autowired
    private AnnotationMethodHandlerAdapter handlerAdapter;
    @Autowired
    private LoginController controller;
   //声明Request与Response模拟对象
   private MockHttpServletRequest request;
   private MockHttpServletResponse response;

   //执行测试前先初始模拟对象
   @BeforeMethod
   public void before() {
      request = new MockHttpServletRequest();
      request.setCharacterEncoding("UTF-8");
      response = new MockHttpServletResponse();
        request.setAttribute(HandlerMapping.INTROSPECT_TYPE_LEVEL_MAPPING, true); //Spring3.1 存在的BUG

   }

   // 测试LoginController#loginCheck()方法
   @Test
   public void loginCheck() throws Exception {
        //测试登陆成功的情况
       request.setRequestURI("/admin/loginCheck.html");
      request.addParameter("userName", "admin"); // 设置请求URL及参数
      request.addParameter("password", "123456");

      //向控制发起请求 ” /loginCheck.html”
      ModelAndView mav = handlerAdapter.handle(request, response, controller);
      User user = (User) request.getSession().getAttribute("user");
      assertNotNull(mav);
      assertEquals(mav.getViewName(), "main");
      assertNotNull(user);
        request.getSession().removeAttribute("user");

        //测试登陆失败的情况
        request.setRequestURI("/admin/loginCheck.html");
        request.addParameter("userName", "test");
        request.addParameter("password", "123456");

        mav = handlerAdapter.handle(request, response, controller);
        user = (User) request.getSession().getAttribute("user");
        assertNotNull(mav);
        assertEquals(mav.getViewName(), "login");
        assertNull(user);

   }
}

示例更多内容及运行结果请见附件