第三章 maven拆分ssm

一、父工程

创建 maven project

打包方式 pom

pom.xml中定义ssm所依赖的所有jar包

将父工程发布到本地仓库

二、子工程ssm-dao

1、创建 maven module 继承自父工程

2、打包方式 jar

3、将dao层相关的配置文件放到该工程下

  • jdbc.properties
  • mybatis.xml
  • spring/applicationContext-basic.xml
  • spring/applicationContext-dao.xml

applicationContext-basic.xml注册数据源、sqlSessionFactory以及事务

applicationContext-dao.xml生成dao的代理对象

4、将dao层相关的代码放到该工程下

  • beans
  • dao

5、发布工程到本地仓库供子工程ssm-service依赖

三、子工程ssm-service

1、创建 maven module 继承自父工程

2、打包方式 jar

3、将service层相关的配置文件放到该工程下

  • spring/applicationContext-service.xml

4、将service层相关的代码放到该工程下

  • service接口及实现类

5、发布工程到本地仓库供子工程ssm-web依赖

四、子工程ssm-web

1、创建 maven-module 继承自父工程

2、打包方式 war

3、将web层相关的配置文件放到该工程下

  • spring/applicationContext-web.xml(注册处理器)

4、将web层相关的代码放到该工程下

  • 处理器

5、web.xml

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath*:spring/applicationContext-*.xml</param-value>
</context-param>

classpath:指的是除了加载本项目下的spring/applicationContext-.xml配置文件,也加载所依赖的jar包下的spring/applicationContext-*.xml配置文件。这样就能将dao层与service层的spring配置文件也加载进来。

五、测试

1、测试dao层

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring/applicationContext-*.xml")
public class IStudentDaoTest {

    @Autowired
    private IStudentDao studentDao;

    @Test
    public void test() {
        //fail("Not yet implemented");
        studentDao.insertStudent(new Student("zhangsan",22));
    }

}

2、测试service层

public class IStudentServiceTest {

    @Test
    public void testAddStudent() {

        ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("classpath*:spring/applicationContext-*.xml");
        IStudentService service = (IStudentService)classPathXmlApplicationContext.getBean("studentService");
        service.addStudent(new Student("lisi",23));
    }

}

六、maven运行方式 comcat:run

1、运行父工程

父工程将各个子模块聚合到一起,将ssm-web打成war包发布到tomcat。

2、运行ssm-web工程

第二章 maven整合smm

一、创建maven project

1、创建maven project

配置安装目录:

preferences/maven/installations/add maven根目录

preferences/maven/user settings settings.xml目录

构建索引

windows/show view/others/maven/local repository

2、修正错误

补充web.xml文件

pom.xml中指定编译版本

二、搭建环境

1、pom.xml添加依赖

2、搭建springmvc环境

web.xml

  • 注册字符编码过滤器

  • 注册中央调度器

3、搭建spring环境

web.xml

  • 注册ServletContext监听器

监听器默认加载WEB-INF/applicationContext.xml

  • 指定spring配置文件位置classpath

4、搭建mybatis环境

  • mybatis核心配置文件

5、spring与springmvc整合

spring配置文件中,注册处理器

6、spring与mybatis整合

  • 注册数据源dataSource

  • 注册SessionFactory

  • 注册事务管理

  • 生成Dao代理对象

第一章 maven管理工具

1.1 maven的核心

依赖管理:对jar包统一管理

项目构建:对项目进行编译,测试,打包,部署

1.2 maven安装

maven程序是java工发的,它的运行依赖jkd。

1、maven下载: http://maven.apache.org/download.cgi
2、配置环境变量

(1)、看看~/下是否有.bash_profile文件,如果没有就创建一个,环境变量要配置在这个文件中。

创建文件:

touch ~/.bash_profile 

(2)、编辑bash_profile文件:

vi ~/.bash_profile  

(3)、配置如下四个环境变量:

# maven所在的目录  
export M2_HOME=/Users/haojie/Downloads/Java/maven/apache-maven-3.3.9  
# maven bin所在的目录  
export M2=$M2_HOME/bin  
# 将maven bin加到PATH变量中  
export PATH=$M2:$PATH  
# 配置JAVA_HOME所在的目录,注意这个目录下应该有bin文件夹,bin下应该有java等命令  
JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_151.jdk/Contents/Home
export JAVA_HOME  

(4)、在终端执行强制生效命令

source ~/.bash_profile  

(5)、在新的命令行窗口执行mvn -v,如果正常显示了maven的版本等信息,就是配置成功了。

1.3 配置本地仓库

/conf/settings.xml

<localRepository>/Users/haojie/Downloads/Java/maven/maven_repository/</localRepository>

1.4 maven的常用命令

  • clean:清理

将项目根目录下target目录清理掉

  • compile:编译

将于项目中.java文件编译为.class文件

  • test:测试

单元测试类名有要求:XxxxTest.java

将项目根目录下src/test/java目录下的单元测试类全部执行

  • package:打包

将项目打包到根目录下target目录

  • install:安装

打包jar包到本地仓库

1.5 maven生命周期

在maven中存在三套生命周期,每一套生命周期相互独立,互不影响

1、CleanLifeCycle:清理生命周期

  • clean

2、defaultLifeCycle:默认生命周期

  • compile,test,package,install,deploy

3、siteLifeCycle:站点生命周期

  • site

在一套生命周期内,执行后面的命令,前面操作会自动执行。

1.6 在eclipse中配置maven插件

1、Preferences/Maven/Installations/add

添加maven程序

2、Preferences/Maven/User Settings

添加maven settins.xml文件

3、window/show view/other/maven

右键Local Repository(….)/rebuild index构建索引

1.7 依赖范围

添加依赖范围:默认是compile

如果将servlet-api设置为compile,打包后包含servlet-api.jar,部署到tomcat后,跟tomcat中存在的servlet-api.jar包冲突,会导致运行失败。

因此,如果使用到tomcat自带jar包,需要将项目中依赖作用范围设置为:provided。

1.8 传递依赖冲突解决

1.8.1 maven自己调解原则
  • 第一声明者优先原则

谁先定义的就用谁的传递依赖

  • 路径近者优先原则

直接依赖级别高于传递依赖

1.8.2 排除依赖

排除掉冲突中的某一个依赖

1.8.3 版本锁定
<dependencyManagement>
    <dependency>
        ...        
    </dependency>
</dependencyManagement>

第一章 搭建SSM开发环境

1.1 导入Jar包

(1)、mybatis的jar包

  • asm-4.2.jar
  • cglib-3.1.jar
  • commons-logging-1.2.jar
  • log4j-1.2.17.jar
  • log4j-api-2.2.jar
  • log4j-core-2.2.jar
  • mybatis-3.3.0.jar
  • slf4j-api-1.7.12.jar
  • slf4j-log4j12-1.7.12.jar

(2)、ehcache的Jar包

  • ehcache-core-2.6.8.jar
  • mybatis-ehcache-1.0.3.jar

(3)、Spring的Jar包

  • com.springsource.org.aopalliance-1.0.0.jar
  • com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
  • spring-aop-4.2.1.RELEASE.jar
  • spring-aspects-4.2.1.RELEASE.jar
  • spring-beans-4.2.1.RELEASE.jar
  • spring-context-4.2.1.RELEASE.jar
  • spring-context-support-4.2.1.RELEASE.jar
  • spring-core-4.2.1.RELEASE.jar
  • spring-expression-4.2.1.RELEASE.jar
  • spring-jdbc-4.2.1.RELEASE.jar
  • spring-orm-4.2.1.RELEASE.jar
  • spring-tx-4.2.1.RELEASE.jar
  • spring-web-4.2.1.RELEASE.jar
  • spring-webmvc-4.2.1.RELEASE.jar

(4)、mybatis与spring整合的Jar包

  • mybatis-spring-1.2.3.jar

(5)、JSON的Jar包

  • commons-beanutils-1.8.0.jar

  • commons-collections-3.2.1.jar

  • commons-lang-2.5.jar

  • commons-logging-1.1.1.jar

  • ezmorph-1.0.6.jar

  • json-lib-2.4-jdk15.jar

(6)、Jackson的Jar包

  • jackson-annotations-2.6.0-xh.jar

  • jackson-core-2.6.0-xh.jar

  • jackson-databind-2.6.0-xh.jar

  • jackson-jr-all-2.4.3-xh.jar

(7)、Hebernate验证器Jar包

  • hibernate-validator-5.1.0.Final.jar

  • jboss-logging-3.1.3.GA.jar

  • validation-api-1.1.0.Final.jar

(8)、其它Jar包

  • mysql-connector-java-5.1.44-bin.jar

  • c3p0-0.9.2.1

  • c3p0-oracle-thin-extras-0.9.2.1

  • mchange-commons-java-0.2.3.4

1.2 配置式开发

1.2.1 定义实体类及DB表

package com.bjpowernode.beans;

public class Student {
private Integer id;
private String name;
private int age;

public Student() {
    super();
    // TODO Auto-generated constructor stub
}

public Student(String name, int age) {
    super();
    this.name = name;
    this.age = age;
}
...
getter and setter
...

}

1.2.2 定义表单页及处理器

index.jsp

<form action="${pageContext.request.contextPath }/test/register.do" method="POST">
    姓名:<input type="text" name="name" /><br />
    年龄:<input type="text" name="age" /><br />
    <input type="submit" value="注册" />
</form>

StudentController

package com.bjpowernode.handlers;

public class StudentController implements Controller {

    private IStudentService service;

    public void setService(IStudentService service) {
        this.service = service;
    }

    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {

        String name = request.getParameter("name");
        String ageStr = request.getParameter("age");
        Integer age = Integer.valueOf(ageStr);
        Student student = new Student(name,age);

        service.addStudent(student);

        return new ModelAndView("/welcome.jsp");
    }

}
1.2.3 定义service

service interface

package com.bjpowernode.service;
public interface IStudentService {    
    void addStudent(Student student);
}

service implements

package com.bjpowernode.service;
public class StudentServiceImpl implements IStudentService {

    private IStudentDao dao;

    public void setDao(IStudentDao dao) {
        this.dao = dao;
    }

    @Override
    public void addStudent(Student student) {
        dao.insertStudent(student);
    }

}
1.2.4 定义dao

dao interface

package com.bjpowernode.dao;
public interface IStudentDao {    
    void insertStudent(Student student);
}
1.2.5 定义mybatis两个配置文件

mybatis主配置文件:mybatis.xml

<?xml version="1.0" encoding="UTF-8" ?> 
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
    <!-- 为实体类指定别名,简单类名 -->
    <typeAliases>
        <package name="com.bjpowernode.beans"/>
    </typeAliases>
    <!-- 注册映射文件 -->
    <mappers>
        <package name="com.bjpowernode.dao"/>
    </mappers>
</configuration>

IStudentDao.xml

<?xml version="1.0" encoding="UTF-8" ?> 
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.bjpowernode.dao.IStudentDao">
    <insert id="insertStudent">
        insert into student(name,age) values(#{name},#{age})
    </insert>
</mapper>
1.2.6 注册数据源

jdbc.properties

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///mydatabase
jdbc.user=root
jdbc.password=haojie_123456

spring-db.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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx.xsd
    http://www.springframework.org/schema/mvc
    http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!-- 注册c3p0数据源 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driver}"></property>
        <property name="jdbcUrl" value="${jdbc.url}"></property>
        <property name="user" value="${jdbc.user}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>

    <!-- 注册属性文件 -->
    <context:property-placeholder location="classpath:resources/jdbc.properties" />

</beans>
1.2.7 生成Dao的代理对象

spring-mybatis.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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx.xsd
    http://www.springframework.org/schema/mvc
    http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!-- 注册sqlSessionFactory -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="configLocation" value="classpath:resources/mybatis.xml"></property>        
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!-- 生成Dao的代理对象 -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
        <property name="basePackage" value="com.bjpowernode.dao"></property>
    </bean>

</beans>
1.2.8 注册service

spring-service.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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <bean id="studentService" class="com.bjpowernode.service.StudentServiceImpl">
        <property name="dao" ref="IStudentDao"></property>
    </bean>

</beans>
1.2.9 注册springmvc处理器

spring-mvc.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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx.xsd
    http://www.springframework.org/schema/mvc
    http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!-- 注册处理器 -->
    <bean id="/test/register.do" class="com.bjpowernode.handlers.StudentController">
        <property name="service" ref="studentService"></property>
    </bean>

</beans> 
1.2.10 配置spring事务

spring-tx.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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!-- 注册事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!-- 注册事务通知 -->
    <tx:advice id="txAdvice">
        <tx:attributes>
            <tx:method name="add*" isolation="DEFAULT" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>

    <!-- AOP配置 -->
    <aop:config>
        <aop:pointcut expression="execution(* *..service.*.*(..))" id="studentPointcut"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="studentPointcut"/>
    </aop:config>

</beans>

1.3 注解式开发

1.3.1 将springmvc改为注解

springmvc.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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx.xsd
    http://www.springframework.org/schema/mvc
    http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!-- 注册处理器 -->
    <!-- 
    <bean id="/test/register.do" class="com.bjpowernode.handlers.StudentController">
        <property name="service" ref="studentService"></property>
    </bean>
     -->

     <!-- 注册组件扫描器 -->
     <context:component-scan base-package="com.bjpowernode.handlers"></context:component-scan>

</beans>

StudentController

  • 请求路径

  • 处理器

  • 依赖注入

============

package com.bjpowernode.handlers;

@Controller
@RequestMapping("/test")
public class StudentController {

    @Autowired
    @Qualifier("studentService")
    //@Resource(name="studentService")
    private IStudentService service;

    public void setService(IStudentService service) {
        this.service = service;
    }

    @RequestMapping("/register.do")
    public ModelAndView doRegister(String name, int age) {

        Student student = new Student(name,age);

        service.addStudent(student);

        return new ModelAndView("/welcome.jsp");
    }
}

1.3.2 将spring改为注解

spring-service.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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!-- 
    <bean id="studentService" class="com.bjpowernode.service.StudentServiceImpl">
        <property name="dao" ref="IStudentDao"></property>
    </bean>
    -->

    <!-- 注册组件扫描器 -->
    <context:component-scan base-package="com.bjpowernode.service"></context:component-scan> 

</beans>

student service implements

package com.bjpowernode.service;

@Service("studentService")
public class StudentServiceImpl implements IStudentService {

    @Resource(name="IStudentDao")
    private IStudentDao dao;

    public void setDao(IStudentDao dao) {
        this.dao = dao;
    }

    @Override
    @Transactional
    public void addStudent(Student student) {
        dao.insertStudent(student);
    }

}

spring-tx.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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!-- 注册事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--  
    注册事务通知
    <tx:advice id="txAdvice">
        <tx:attributes>
            <tx:method name="add*" isolation="DEFAULT" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>

    AOP配置
    <aop:config>
        <aop:pointcut expression="execution(* *..service.*.*(..))" id="studentPointcut"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="studentPointcut"/>
    </aop:config>
    -->

    <!-- 注册事务驱动 -->
    <tx:annotation-driven transaction-manager="transactionManager" />

</beans>

第四章 springmvc核心技术

4.1 请求转发与重定向

当处理器对请求处理完毕后,向其它资源进行跳转时,有两种跳转方式:请求转发与重定向。而根据所要跳转的资源类型,又可分为两类:跳转到其它页面与跳转到其它处理器。

注意,对于请求转发的页面,可以是WEB-INF中的页面;而重定向的页面,不能是WEB-INF中的页面。因为重定向相当于用户再次发出一次请求,而用户是不能直接访问WEB-INF中的资源的。

4.1.1 返回ModelAndView时的请求转发

(1)、请求转发到页面

(2)、请求转发到Controller

4.1.2 返回ModelAndView时的重定向

(1)、重定向到页面

A、通过ModelAndView的Model携带参数

index.jsp

<form action="${pageContext.request.contextPath }/test/register.do" method="POST">
    姓名:<input type="text" name="name" /><br />
    年龄:<input type="text" name="age" /><br />
    <input type="submit" value="注册" />
</form>

处理器

@org.springframework.stereotype.Controller
@RequestMapping("/test")
public class MyController {

    @RequestMapping("/register.do")
    public ModelAndView doSecond(String name, int age) {
        ModelAndView mv = new ModelAndView();
        mv.addObject("name", name);
        mv.addObject("age", age);
        mv.setViewName("redirect:/welcome.jsp");
        return mv;
    }
}

跳转到welcome.jsp页面获取接收参数

<!-- 
param.name底层执行的是request.getParameter("name") 
requestScope.name底层执行的是request.getAttribute("name")
-->
name = ${param.name}
<br/>
age = ${param.age}

B、通过HttpSession携带参数

(2)、重定向到Controller

A、通过ModelAndView的Model携带参数

B、通过HttpSession携带参数

4.1.3 返回String时的请求转发

(1)、请求转发到页面

(2)、请求转发到Controller

4.1.4 返回String时的重定向

(1)、重定向到页面

(2)、重定向到Controller

index.jsp

<form action="${pageContext.request.contextPath }/test/register.do" method="POST">
    姓名:<input type="text" name="name" /><br />
    年龄:<input type="text" name="age" /><br />
    <input type="submit" value="注册" />
</form>

处理器

@org.springframework.stereotype.Controller
@RequestMapping("/test")
public class MyController {

    @RequestMapping("/register.do")
    public String doRegister(String name, int age, Model model) {
        model.addAttribute("name",name);
        model.addAttribute("age",age);

        return "redirect:other.do";
    }

    @RequestMapping("/other.do")
    public String doOther(String name,int age) {
        System.out.println("name = " + name);
        System.out.println("age = " + age);
        return "/welcome.jsp";
    }
}

跳转到welcome.jsp页面获取接收参数

<!-- 
param.name底层执行的是request.getParameter("name") 
requestScope.name底层执行的是request.getAttribute("name")
-->
name = ${param.name}
<br/>
age = ${param.age}
4.1.5 返回void时的请求转发

(1)、请求转发到页面

(2)、请求转发到Controller

4.1.6 返回void时的重定向

(1)、重定向到页面

(2)、重定向到Controller

4.2 异常处理

常用的springmvc异常处理方式主要有三种:

  • 使用系统定义好的异常处理器SimpleMappingExceptionResolver

  • 使用自定义异常处理器

  • 使用异常处理注解

4.2.1 SimpleMappingExceptionResolver异常处理器

springmvc.xml

<context:component-scan base-package="com.bjpowernode.handlers"></context:component-scan>

<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    <property name="defaultErrorView" value="/errors/error.jsp"></property>
    <property name="exceptionAttribute" value="ex"></property>
    <property name="exceptionMappings">
        <props>
            <prop key="com.bjpowernode.exceptions.NameException">/errors/nameError.jsp</prop>
            <prop key="com.bjpowernode.exceptions.AgeException">/errors/ageError.jsp</prop>
        </props>
    </property>
</bean>

StudentException.class

public class StudentException extends Exception {

    public StudentException() {
        super();
    }

    public StudentException(String message) {
        super(message);
    }

}

NameException.class

public class NameException extends StudentException {

    public NameException() {
        super();
    }

    public NameException(String message) {
        super(message);
    }

}

AgeException.class

public class AgeException extends StudentException {

    public AgeException() {
        super();
    }

    public AgeException(String message) {
        super(message);
    }

}

处理器

@org.springframework.stereotype.Controller
@RequestMapping("/test")
public class MyController {

    @RequestMapping("/register.do")
    public ModelAndView doRegister(String name, int age) {

        int i = 3/0;

        ModelAndView mv = new ModelAndView();
        mv.addObject("name", name);
        mv.addObject("age", age);
        mv.setViewName("welcom.jsp");
        return mv;
    }

}

error.jsp

<body>
    error page.<br />
    ${ex.message}
</body>

nameError.jsp

name error page.
<br>
${ex.message}

ageError.jsp

age error page.
<br>
${ex.message}
4.2.2 自定义异常处理器

使用springmvc定义好的SimpleMappingExceptionResolver异常处理器,可以实现生指定异常后的跳转。但若要想实现在捕捉到异常时,进行一些操作,它是完成不了的。此时,就需要使用自定义异常。

自定义异常处理器,需要实现HandlerExceptionResolver接口,并且该类需要在springmvc配置文件中进行注册。

自定义异常处理器

public class MyHandlerExceptionResolver implements HandlerExceptionResolver {

    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
            Exception ex) {
        ModelAndView mv = new ModelAndView();
        mv.addObject("ex", ex);

        mv.setViewName("/errors/error.jsp");

        if(ex instanceof NameException) {
            mv.setViewName("/errors/nameError.jsp");
        }

        if(ex instanceof AgeException) {
            mv.setViewName("/errors/ageError.jsp");
        }

        return mv;
    }

}

springmvc.xml 注册

<!-- 注册自定义异常处理器 -->
<bean class="com.bjpowernode.resolvers.MyHandlerExceptionResolver"></bean>
4.2.3 异常处理器注解@ExceptionHandler

定义异常处理器

@org.springframework.stereotype.Controller
public class BaseController {

    @ExceptionHandler(NameException.class)
    public ModelAndView handlerNameException(Exception ex) {
        ModelAndView mv = new ModelAndView();
        mv.addObject("ex", ex);
        mv.setViewName("/errors/nameError.jsp");
        return mv;
    }

    @ExceptionHandler(AgeException.class)
    public ModelAndView handlerAgeException(Exception ex) {
        ModelAndView mv = new ModelAndView();
        mv.addObject("ex", ex);
        mv.setViewName("/errors/ageError.jsp");
        return mv;
    }

    @ExceptionHandler
    public ModelAndView handlerException(Exception ex) {
        ModelAndView mv = new ModelAndView();
        mv.addObject("ex", ex);
        mv.setViewName("/errors/error.jsp");
        return mv;
    }

}

处理器继承异常处理器

@org.springframework.stereotype.Controller
@RequestMapping("/test")
public class MyController extends BaseController {

    @RequestMapping("/register.do")
    public ModelAndView doRegister(String name, int age) throws StudentException {

        if(!"beijing".equals(name)) {
            throw new NameException("用户名不正确");
        }

        if(age > 60) {
            throw new AgeException("年龄太大了");
        }

        ModelAndView mv = new ModelAndView();
        mv.addObject("name", name);
        mv.addObject("age", age);
        mv.setViewName("/welcome.jsp");
        return mv;
    }

}

4.3 类型转换器和初始化参数绑定

可以解决提交的参数类型转换问题

4.4 数据验证

springmvc支持JSR(Java Specification Requests)303-bean validation数据验证规范。而该规范的实现者很多,其中较常用的是Hibernate Valicator。

需要注意的是,Hebernate Valicator是与Hibernate ORM并列的Hibernate的产品之一。

第一步:导入jar包

Hibernate Valicator的jar包:

  • hibernate-validator

  • jboss-logging

  • validation-api

第二步:注册验证器和驱动

springmvc.xml

<!-- 注册验证器 -->
<bean id="myValidator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
    <property name="providerClass" value="org.hibernate.validator.HibernateValidator"></property>
</bean>

<!-- 注册mvc注解驱动 -->
<mvc:annotation-driven validator="myValidator"></mvc:annotation-driven>

第三步:实体类-验证规则

public class Student {
    @NotNull(message="姓名不以为空")
    @Size(min=3, max=6, message="姓名长度在{min}-{max}个字符之间")
    private String name;

    @Min(value=0, message="成绩不能小于{value}")
    @Max(value=100, message="成绩不能大于{value}")
    private double score;

    @NotNull(message="电话不能为空")
    @Pattern(regexp="^1[34578]\\d{9}$",message="手机号格式不正确")
    private String mobile;

    ....
    getter and setter
    ...

}

第四步:处理器

@org.springframework.stereotype.Controller
@RequestMapping("/test")
public class MyController {
    @RequestMapping(value={"/register.do"}, method= RequestMethod.POST)
    public ModelAndView doRegister(@Validated Student student, BindingResult br) {

        ModelAndView mv = new ModelAndView();
        mv.addObject("name", student);
        mv.setViewName("/WEB-INF/jsp/welcome.jsp");

        int errorCount = br.getErrorCount();
        if(errorCount > 0) {
            FieldError nameError = br.getFieldError("name");
            FieldError scoreError = br.getFieldError("score");
            FieldError mobieError = br.getFieldError("mobile");

            if(nameError != null) {
                String nameErrorMSG = nameError.getDefaultMessage();
                mv.addObject("nameErrorMSG", nameErrorMSG);
            }

            if(scoreError != null) {
                String scoreErrorMSG = scoreError.getDefaultMessage();
                mv.addObject("scoreErrorMSG", scoreErrorMSG);
            }

            if(mobieError != null) {
                String mobileErrorMSG = mobieError.getDefaultMessage();
                mv.addObject("mobileErrorMSG", mobileErrorMSG);
            }
            mv.setViewName("/index.jsp");
        }


        return mv;
    }
}

index.jsp

<form action="${pageContext.request.contextPath }/test/register.do" method="POST">
    姓名:<input type="text" name="name" />${nameErrorMSG }<br />
    成绩:<input type="text" name="score" />${scoreErrorMSG }<br />
    电话:<input type="text" name="mobile" />${mobileErrorMSG }<br />

    <input type="submit" value="注册" />
</form>

4.5 文件上传

4.5.1 上传单个文件

第一步:导入jar包

  • commons-fileupload

  • commons-io

第二步:注册

<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <property name="defaultEncoding" value="utf-8"></property>
    <property name="maxUploadSize" value="1048576"></property>
</bean>

<mvc:annotation-driven></mvc:annotation-driven>

第三步:页面

<form action="${pageContext.request.contextPath }/test/upload.do" method="POST" enctype="multipart/form-data">
    文件:<input type="file" name="img" /><br />
    <input type="submit" value="提交" />
</form>

第四步:处理器

@org.springframework.stereotype.Controller
@RequestMapping("/test")
public class MyController {
    @RequestMapping(value={"/upload.do"}, method= RequestMethod.POST)
    public String doFileUpload(MultipartFile img, HttpSession session) throws Exception {
        String path = session.getServletContext().getRealPath("/images");

        if(img.getSize() > 0) {            
            String fileName = img.getOriginalFilename();
            if(fileName.endsWith("jpg") || fileName.endsWith("png")) {                
                File file = new File(path, fileName);
                img.transferTo(file);
                return "/success.jsp";
            }
        }
        return "/fail.jsp";        
    }

}
4.5.2 上传多个文件

处理器

@org.springframework.stereotype.Controller
@RequestMapping("/test")
public class MyController {
    @RequestMapping(value={"/upload.do"}, method= RequestMethod.POST)
    public String doFileUpload(@RequestParam MultipartFile[] imgs, HttpSession session) throws Exception {
        String path = session.getServletContext().getRealPath("/images");

        for(MultipartFile img:imgs) {        
            if(img.getSize() > 0) {            
                String fileName = img.getOriginalFilename();
                if(fileName.endsWith("jpg") || fileName.endsWith("png")) {                
                    File file = new File(path, fileName);
                    img.transferTo(file);
                }
            }
        }

        return "/success.jsp";        
    }

}

index.jsp

<form action="${pageContext.request.contextPath }/test/upload.do" method="POST" enctype="multipart/form-data">
    文件1:<input type="file" name="imgs" /><br />
    文件2:<input type="file" name="imgs" /><br />
    文件3:<input type="file" name="imgs" /><br />
    <input type="submit" value="提交" />
</form>

4.6 拦截器

springmvc中的拦截器是非常重要和相当有用的,它的主要作用是拦截指定的用户请求,并进行相应的预处理与后处理。其拦截的时间点在“处理器映射器根据用户提交的请求映射出了所要执行的处理器类,并且也找到了要执行该处理器类的处理器适配器,在处理器适配器执行处理器之前”。当然,在处理器映射器映射出所要执行的处理器类时,已经将拦截器与处理器组合为了一个处理器执行链,并返回给了中央调度器。

4.6.1 拦截器的实现

第一步:注册拦截器

<!-- 注册拦截器 -->
<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <bean class="com.bjpowernode.interceptors.OneInterceptor"></bean>
    </mvc:interceptor>
</mvc:interceptors>

第二步:拦截器

public class OneInterceptor implements HandlerInterceptor {    
    @Override
    public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception {
        System.out.println("execute prehandler");
        return true;
    }    

    @Override
    public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
            throws Exception {

        System.out.println("execute posthandler");
    }

    @Override
    public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
            throws Exception {
        System.out.println("execute aftercomplete");
    }
}

处理器

@org.springframework.stereotype.Controller
@RequestMapping("/test")
public class MyController {
    @RequestMapping("/some.do")
    public String doSome() {
        System.out.println("execute dosome");
        return "/WEB-INF/jsp/welcome.jsp";
    }

}
4.6.2 权限拦截器

处理器

@org.springframework.stereotype.Controller
@RequestMapping("/test")
public class MyController {
    @RequestMapping("/some.do")
    public String doSome() {
        System.out.println("execute dosome");
        return "/WEB-INF/jsp/welcome.jsp";
    }

}

拦截器

public class PermissionInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {

        String user = (String) request.getSession().getAttribute("user");
        if("beijing".equals(user)) {
            return true;
        }
        request.getRequestDispatcher("/fail.jsp").forward(request, response);
        return false;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
    }

}

注册

<!-- 注册拦截器 -->
<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <bean class="com.bjpowernode.interceptors.PermissionInterceptor"></bean>
    </mvc:interceptor>
</mvc:interceptors>

login.jsp

<%
    session.setAttribute("user", "beijing");
%>>
login success.

logout.jsp

<%
    session.removeAttribute("user");    
%>
logout success.

第三章 springmvc注解式开发

3.1 第一个程序

3.1.1 注解

springmvc.xml

<!-- 组件扫描器 -->
<context:component-scan base-package="com.bjpowernode.handlers"></context:component-scan>

控制器

@org.springframework.stereotype.Controller
public class MyController {
    @RequestMapping("/my.do")
    public ModelAndView doSome(HttpServletRequest arg0, HttpServletResponse arg1) {
        return new ModelAndView("/WEB-INF/jsp/welcome.jsp");
    }

}
3.1.2 多个请求指向同一个处理器、一个处理器中定义多个处理器方法
@org.springframework.stereotype.Controller
public class MyController {
    @RequestMapping({"/first.do","hello.do"})
    public ModelAndView doFirst(HttpServletRequest arg0, HttpServletResponse arg1) {
        return new ModelAndView("/WEB-INF/jsp/welcome.jsp");
    }

    @RequestMapping("/second.do")
    public ModelAndView doSecond(HttpServletRequest arg0, HttpServletResponse arg1) {
        return new ModelAndView("/WEB-INF/jsp/welcome.jsp");
    }
}
3.1.3 命名空间 @RequestMapping
@org.springframework.stereotype.Controller
@RequestMapping("xxx/ooo/jjj")
public class MyController {
    @RequestMapping({"/first.do","hello.do"})
    public ModelAndView doFirst(HttpServletRequest arg0, HttpServletResponse arg1) {
        return new ModelAndView("/WEB-INF/jsp/welcome.jsp");
    }

    @RequestMapping("/second.do")
    public ModelAndView doSecond(HttpServletRequest arg0, HttpServletResponse arg1) {
        return new ModelAndView("/WEB-INF/jsp/welcome.jsp");
    }
}
3.1.4 请求中的通配符
@RequestMapping("/second*.do")
3.1.5 对请求提交方式的定义
@RequestMapping(value={"/first.do","hello.do"}, method= RequestMethod.POST)

3.2 处理器方法的参数

处理器方法常用的参数有五类,这些参数会在系统调用时由系统自动赋值,即程序员可以在方法内直接使用。

  • HttpServleRequest

  • HttpServletResponse

  • HttpSession

  • 用于承载数据的Model

  • 请求中所携带的参数

3.3 处理器方法接收请求里的参数

index.jsp

<form action="${pageContext.request.contextPath }/test/register.do" method="POST">
    姓名:<input type="text" name="name" /><br />
    年龄:<input type="text" name="age" /><br />
    <input type="submit" value="注册" />
</form>

welcome.jsp

name=${name}
age=${age }

web.xml

添加过滤器,解决中文乱码问题

<filter>
      <filter-name>CharacterEncodingFilter</filter-name>
      <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
      <init-param>
          <param-name>encoding</param-name>
          <param-value>utf-8</param-value>
      </init-param>
      <init-param>
          <param-name>forceEncoding</param-name>
          <param-value>true</param-value>
      </init-param>
</filter>

<filter-mapping>
    <filter-name>CharacterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
3.3.1 接收请求参数 - 逐个接收

控制器

@org.springframework.stereotype.Controller
@RequestMapping("/test")
public class MyController {
    @RequestMapping(value={"/register.do"}, method= RequestMethod.POST)
    public ModelAndView doRegister(String name, int age) {
        System.out.println("name="+name);
        System.out.println("age="+age);

        ModelAndView mv = new ModelAndView();
        mv.addObject("name", name);
        mv.addObject("age", age);
        mv.setViewName("/WEB-INF/jsp/welcome.jsp");
        return mv;
    }

}
3.3.2 接收请求参数 - 校正请求参数名

参数pname

姓名:<input type="text" name="pname" /><br />

@RequestParam校正

public ModelAndView doRegister(@RequestParam("pname") String name, int age) {...}
3.3.3 接收请求参数 - 以对象形式整体接收

Student.class

public class Student {
    private String name;
    private int age;

    ...
    getter and setter
    ...

}

控制器

@org.springframework.stereotype.Controller
@RequestMapping("/test")
public class MyController {
    @RequestMapping(value={"/register.do"}, method= RequestMethod.POST)
    public ModelAndView doRegister(Student student) {
        System.out.println("name="+student.getName());
        System.out.println("age="+student.getAge());

        ModelAndView mv = new ModelAndView();
        mv.addObject("name", student);
        mv.setViewName("/WEB-INF/jsp/welcome.jsp");
        return mv;
    }

}

整体接收,要求参数名name,age要与实体类属性名相同

3.3.3 接收请求参数 - 域属性参数的接收

Student.class

public class Student {
    private String name;
    private int age;
    private School school;
    ...
        getter and setter
    ...
}

School.class

public class School {
    private String sname;
    private String address;
    public String getSname() {
        return sname;
    }
    ...
        getter and setter
    ...        
}

index.jsp

<form action="${pageContext.request.contextPath }/test/register.do" method="POST">
    姓名:<input type="text" name="name" /><br />
    年龄:<input type="text" name="age" /><br />
    学校:<input type="text" name="School.sname" /><br />
    校址:<input type="text" name="School.address" /><br />
    <input type="submit" value="注册" />
</form>

3.4 处理器方法的返回值

3.4.1 返回ModelAndView

当处理器方法处理完后,既需要跳转到其它资源,也需要在资源间传递数据,那么就应该使用ModelAndView。

@org.springframework.stereotype.Controller
@RequestMapping("/test")
public class MyController {
    @RequestMapping(value={"/register.do"}, method= RequestMethod.POST)
    public ModelAndView doRegister(Student student) {
        ModelAndView mv = new ModelAndView();
        mv.addObject("name", student);
        mv.setViewName("/WEB-INF/jsp/welcome.jsp");
        return mv;
    }

}
3.4.2 返回String

只需要进行跳转,而不需要进行数据传递时,可以使用String。

@org.springframework.stereotype.Controller
@RequestMapping("/test")
public class MyController {
    @RequestMapping(value={"/register.do"}, method= RequestMethod.POST)
    public String soSome() {
        return "/WEB-INF/jsp/welcome.jsp";
    }

}

物理视图:/WEB-INF/jsp/welcome.jsp

逻辑视图:welcome,需要视图解析器转换为物理视图

3.4.3 返回void

index.jsp

<script type="text/javascript">
    $(function (){
        $("button").click(function(){
            $.ajax({
                url:"test/ajax.do",
                data:{
                    name:"zhangsan",
                    age:23
                },
                success:function(data){
                    var json=eval("("+ data +")");
                    alert(json.name + json.age);
                }
            });
        });
    });
</script>
<button>ajax submit</button>

处理器

@org.springframework.stereotype.Controller
@RequestMapping("/test")
public class MyController {
    @RequestMapping("/ajax.do")
    public void doAjax(String name, int age, HttpServletResponse response) throws IOException {
        HashMap<String, Object> map = new HashMap<String, Object>();
        map.put("name", name);
        map.put("age", age);

        JSONObject json = JSONObject.fromObject(map);
        String jsonStr = json.toString();

        PrintWriter out = response.getWriter();
        out.println(jsonStr);
    }

}

需要导入json-lib相关的包

3.4.4 返回Object

处理器方法也可以返回Object对象,但返回的这个Object对象不是作为逻辑视图出现的,而是作为直接在页面显示的数据出现的。

返回Object对象,需要使用@ResponseBody注解,将转换后的JSON数据放入到响应体中。

#####(1)、环境搭建

A、导入jar包

由于返回Object数据,一般都是将数据转化为了JSON对象后传递给浏览器页面的。而这个由Object转换为JSON,是由Jackson工具完成的。所以需要导入Jackson的相关jar包。

jackson-annotations、jackson-core、jackson-databind

B、注册注解驱动

将Object数据转化为JSON数据,需要由http消息转换器HttpMessageConvert完成。而消息转换器的开启,需要由<mvc:annotation-driven>来完成。

当Spring容器进行初始化过程中在<mvc:annotation-driven>处创建注解驱动时,默认创建了七个HttpMessageConvert对象。也就是说,我们注册<mvc:annotation-driven>,就是为了让容器为我们创建HttpMessageConvert对象。

C、@ResponseBody

给处理器方法添加注解

(2)、返回Object-数值型

springmvc.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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!-- 注册组件扫描器 -->
    <context:component-scan base-package="com.bjpowernode.handlers"></context:component-scan>

    <!-- 注册注解驱动器 -->
    <mvc:annotation-driven></mvc:annotation-driven>

</beans>

处理器

@org.springframework.stereotype.Controller
@RequestMapping("/test")
public class MyController {

    @RequestMapping("/ajax.do")
    @ResponseBody //将要返回的数据放入到响应体中
    public Object doAjax() {
        return 123.321;
    }

}

index.jsp

<script type="text/javascript">
    $(function (){
        $("button").click(function(){
            $.ajax({
                url:"test/ajax.do",
                success:function(data){
                    alert(data);
                }
            });
        });
    });
</script>
 <button>ajax submit</button>
(3)、返回Object-字符串
@org.springframework.stereotype.Controller
@RequestMapping("/test")
public class MyController {

    @RequestMapping(value="/ajax.do",produces="text/html;charset=utf-8")
    @ResponseBody //将要返回的数据放入到响应体中
    public Object doAjax() {
        return "临沂";
    }

}

produces指定返回的字符串格式

(4)、返回Object-自定义对象

处理器

@org.springframework.stereotype.Controller
@RequestMapping("/test")
public class MyController {

    @RequestMapping("/ajax.do")
    @ResponseBody //将要返回的数据放入到响应体中
    public Object doAjax() {
        return new Student("zhangsan",23);
    }

}

index.jsp

<script type="text/javascript">
    $(function (){
        $("button").click(function(){
            $.ajax({
                url:"test/ajax.do",
                success:function(data){
                    alert(data.name + data.age);
                }
            });
        });
    });
</script>
<button>ajax submit</button>
(4)、返回Object-List集合

处理器

@org.springframework.stereotype.Controller
@RequestMapping("/test")
public class MyController {

    @RequestMapping("/ajax.do")
    @ResponseBody //将要返回的数据放入到响应体中
    public Object doAjax() {
        ArrayList<Student> list = new ArrayList<Student>();
        list.add(new Student("zhangsan",23));
        list.add(new Student("lisi",23));
        return list;
    }

}

index.jsp

<script type="text/javascript">
    $(function (){
        $("button").click(function(){
            $.ajax({
                url:"test/ajax.do",
                success:function(data){
                    $(data).each(function(index){
                        alert(data[index].name + data[index].age);
                    });
                }
            });
        });
    });
</script>
 <button>ajax submit</button>
(5)、返回Object-Map集合

处理器

@org.springframework.stereotype.Controller
@RequestMapping("/test")
public class MyController {

    @RequestMapping("/ajax.do")
    @ResponseBody //将要返回的数据放入到响应体中
    public Object doAjax() {
        HashMap<String, Student> map = new HashMap<String,Student>();
        map.put("stu1", new Student("zhangsan",23));
        map.put("stu2", new Student("lisi",24));
        return map;
    }

}

index.jsp

<script type="text/javascript">
    $(function (){
        $("button").click(function(){
            $.ajax({
                url:"test/ajax.do",
                success:function(data){
                    $(data).each(function(index){
                        alert(data.stu1.name + data.stu2.age);
                    });
                }
            });
        });
    });
</script>
<button>ajax submit</button>    

第二章 springmvc配置式开发

2.1 处理器映射器 HandlerMapping

HandlerMapping接口负责根据request请求找到相应的Handler处理器及Interceptor拦截器,并将它们封装在HandlerExecutionCahin对象中,返回给中央调度器。

其常用的两种实现类:

  • BeanNameUrlHandlerMapping

  • SimpleUrlHandlerMapping

(1)、BeanNameUrlHandlerMapping

BeanNameUrlHandlerMapping处理器映射器,会根据请求的url与spring容器中定义的处理器的bean的name值进行匹配,从而在spring容器中找到处理器bean的实例。

<bean id="/my.do" class="com.bjpowernode.handlers.MyController"></bean>
(2)、SimpleUrlHandlerMapping

SimpleUrlHandlerMapping处理器映射器,不仅可以将url与处理器分离,还可以对url进行统一映射管理。

SimpleUrlHandlerMapping处理器映射器,会根据请求的url与spring容器中定义的处理器映射器子标签的key属性进行匹配。匹配上后,再将该key的value值与处理器的bean的id值进行匹配,从而在spring容器中找到处理器bean。

<!-- 注册处理器 -->
<bean id="myController" class="com.bjpowernode.handlers.MyController"></bean>
<!-- 注册处理器映射器: SimpleUrlHandlerMapping-->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
    <!-- 
    <property name="mappings">
        <props>
            <prop key="/hello.do">myController</prop>
            <prop key="/world.do">myController</prop>
        </props>
    </property>
     -->
     <property name="urlMap">
         <map>
             <entry key="/hello.do" value="myController"></entry>
             <entry key="/world.do" value="myController"></entry>
         </map>
     </property>
</bean>

2.2 处理器适配器 HandlerAdapter

前面所写的代码中,之所以要将Handler定义为Controller接口的实现类,就是因为这里使用的处理器适配器是SimpleControllerAdapter。打开其源码,可以看到将handler强转为了Controller。在定义Handler时,若不将其定义为Controller接口的实现类,这里的强转会出错。

当然,中央调度器首先会调用该适配器的supports()方法,判断该Handler是否与Controller具有is-a的关系。在具有is-a的关系的前提下,才会强转。

常用的两种处理器适配器:

(1)、SimpleControllerHandlerAdapter

代码:

public class MyController implements Controller {

        @Override
        public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
            ModelAndView mv = new ModelAndView();
            //其底层执行的是request.setAttribute()方法
            mv.addObject("message", "hello world");
            mv.setViewName("welcome");
            return mv;
        }

    }

(2)、HttpRequstHandlerAdapter

代码:

public class MyController implements HttpRequestHandler{

    @Override
    public void handleRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        request.setAttribute("message", "hello sprngmvcworld");
        request.getRequestDispatcher("/WEB-INF/jsp/welcome.jsp").forward(request, response);
    }

}

2.3 处理器

处理器除了实现Controller接口外,还可以继承自一些其它的类来完成一些特殊的功能。

(1)、继承自AbstractController类

若处理器继承自AbstractController类,那么该控制器就具有了一些新的功能。因为AbstractController类还继承自一个父类WebContentGenerator。

WebContentGenerator类具有supportedMethods属性,可以设置支持的Http数据提交方式。默认支持GET,POST。

public class MyController extends AbstractController{

    @Override
    protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
            throws Exception {
        ModelAndView mv = new ModelAndView();
        //其底层执行的是request.setAttribute()方法
        mv.addObject("message", "hello world");
        mv.setViewName("welcome");
        return mv;
    }

}

springmvc.xml

<bean id="/my.do" class="com.bjpowernode.handlers.MyController">
    <property name="supportedMethods" value="POST"></property>
</bean>

(2)、继承自MultiActionController类

springmvc.xml

<!-- 注册处理器 -->
<bean id="myController" class="com.bjpowernode.handlers.MyController"></bean>
<!-- 注册处理器映射器: SimpleUrlHandlerMapping-->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
     <property name="urlMap">
         <map>
             <!-- 
                MultiActionController类具有一个属性:方法名解析器methodNameResolver,
                其具有默认值InternalPathMethodNameResolver,
                该解析器将控制器中的方法名作为url中的资源名称进行解析,
                那了就意味着,我们提交请求时,要将方法名作为资源名称出现。
             -->
             <entry key="/my/*.do" value="myController"></entry>
         </map>
     </property>
</bean>

控制器

public class MyController extends MultiActionController{

    public ModelAndView doFirst(HttpServletRequest request, HttpServletResponse response)
            throws Exception {
        ModelAndView mv = new ModelAndView();
        mv.addObject("message", "execute dofisrt method");
        mv.setViewName("/WEB-INF/jsp/welcome.jsp");
        return mv;
    }

    public ModelAndView doSecond(HttpServletRequest request, HttpServletResponse response)
            throws Exception {
        ModelAndView mv = new ModelAndView();
        mv.addObject("message", "execute dosecond method");
        mv.setViewName("/WEB-INF/jsp/welcome.jsp");
        return mv;
    }

}

访问路径

http://localhost:8080/03-MultiActionController-test/my/doFirst.do

http://localhost:8080/03-MultiActionController-test/my/doSecond.do

方法名解析器methodNameResolver,除了具有默认值InternalPathMethodNameResolver方法名解析器以外,还具有另外两个方法名解析器:PropertiesMethodNmaeResolver,ParameterMethodNameResolver。

2.4 视图解析器

ModelAndView 即模式与视图,通过addObject()方法向模型中添加数据,通过setViewName()方法向模型添加视图名称。

跟踪addObject()方法,可以知道这里的模型就是ModelMap,而ModelMap的本质就是个HashMap,向模型中添加数据,就是向ModelMap中添加数据。

(1)、内部资源视图解析器InternalResourceViewResolver

该视图解析器用于完成当前应用内部资源的封装与跳转。

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/jsp/"></property>
    <property name="suffix" value=".jsp"></property>
</bean>
(2)、BeanNameViewResolver视图解析器

该视图解析器用于将资源封装为spring容器中注册的bean实例。

  • RedirectView:定义外部资源视图对象

  • JstlView:定义内部资源视图对象

springmvc.xml

<!-- 注册视图解析器 -->
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver"></bean>

<!-- 注册内部和外部视图bean -->
<bean id="internalResource" class="org.springframework.web.servlet.view.JstlView">
    <property name="url" value="WEB-INF/jsp/welcome.jsp"></property>
</bean>
<bean id="taobao" class="org.springframework.web.servlet.view.RedirectView">
    <property name="url" value="http://www.taobao.com"></property>
</bean>

控制器

public class MyController implements Controller {

    @Override
    public ModelAndView handleRequest(HttpServletRequest arg0, HttpServletResponse arg1) throws Exception {
        return new ModelAndView("taobao");
    }

}
(3)、XmlViewResolver视图解析器

专门用来定义视图bean的配置文件myViews.xml

<bean id="internalResource" class="org.springframework.web.servlet.view.JstlView">
    <property name="url" value="WEB-INF/jsp/welcome.jsp"></property>
</bean>
<bean id="taobao" class="org.springframework.web.servlet.view.RedirectView">
    <property name="url" value="http://www.taobao.com"></property>
</bean>

springmvc.xml

<!-- 注册XmlViewResolver视图解析器 -->
<bean class="org.springframework.web.servlet.view.XmlViewResolver">
    <property name="location" value="classpath:myViews.xml"></property>
</bean>
(4)、ResourceBundleViewResolver视图解析器

myVews.properties

taobao.(class)=org.springframework.web.servlet.view.RedirectView
taobao.url=http://www.taobao.com

internalResource.(class)=org.springframework.web.servlet.view.JstlView
internalResource.url=/WEB-INF/jsp/welcome.jsp

springmvc.xml

<!-- 注册处理器 -->
<bean id="/my.do" class="com.bjpowernode.handlers.MyController"></bean>

<!-- 注册视图解析器 -->
<bean class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
    <property name="basename" value="myViews"></property>
</bean>

注:视图解析器的优先级

有时经常需要应用一些视图解析器策略来解析视图名称,即当同时存在多个视图解析器均可解析ModelAndView中的同一视图名称时,哪个视图解析器会起作用呢?

视图解析器有一个order属性,专门用于设置多个视图解析器的优先级。数字越小,优先级越高。一般不会InternalResourceViewResolver解析器指定优先级,即让其优先级最低。

第一章 springmvc概述

1.1 springmvc程序

1、配置web.xml

配置中央调度器

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
    <display-name>01-springmvc-hello</display-name>

    <!-- 中央调度器 -->
    <servlet>
        <servlet-name>springMVC</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- 指定springmvc配置文件的位置及文件名 -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
        <!-- 在Tomcat启动时直接创建当前Servlet -->
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>springMVC</servlet-name>
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>

     <welcome-file-list>
       <welcome-file>index.html</welcome-file>
       <welcome-file>index.htm</welcome-file>
       <welcome-file>index.jsp</welcome-file>
       <welcome-file>default.html</welcome-file>
       <welcome-file>default.htm</welcome-file>
       <welcome-file>default.jsp</welcome-file>
     </welcome-file-list>
</web-app>
2、配置springmvc.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"
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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!-- 注册视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>

    <!-- 注册处理器方式一:一个url对应一个controller 使用的处理器映射器:BeanNameUrlHandlerMapping -->
    <!-- <bean id="/my.do" class="com.bjpowernode.handlers.MyController"></bean> -->

    <!-- 注册处理器方式二:多个url对应一个controller -->    
        <!-- 注册处理器 -->
        <bean id="myController" class="com.bjpowernode.handlers.MyController"></bean>
        <!-- 注册处理器映射器: SimpleUrlHandlerMapping-->
        <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
            <!-- 
            <property name="mappings">
                <props>
                    <prop key="/hello.do">myController</prop>
                    <prop key="/world.do">myController</prop>
                </props>
            </property>
             -->
             <property name="urlMap">
                 <map>
                     <entry key="/hello.do" value="myController"></entry>
                     <entry key="/world.do" value="myController"></entry>
                 </map>
             </property>
        </bean>

</beans>
3、处理器
package com.bjpowernode.handlers;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

public class MyController implements Controller {

    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        ModelAndView mv = new ModelAndView();
        //其底层执行的是request.setAttribute()方法
        mv.addObject("message", "hello world");
        mv.setViewName("welcome");
        return mv;
    }

}
4、视图
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
${message}
aaaa
</body>
</html>

1.2 <url-pattern>详解

中央调度器的url-pattern详解

(1)、建议写成*.do形式

(2)、不能写为/*

因为DispatcherServlet会将向动态页面的跳转请求,即向JSP页面的跳转请求也当作是一个普通的Controller请求。中央调度器会调用处理器映射器为其查找相应的处理器。当然是找不到的,在这种情况下所有的jsp页面跳转均会报404错误。

(3)、最好也不要写为/

因为DispatcherServlet会将向静态资源的请求,例如css,js,jpg,pgn等资源的获取请求,当作是一个普通的Controller请求。中央调度器会调用处理器映射器为其查找相应的处理器。当然也是找不到的,所以在这种情况下,所有的表态资源获取请求也均会报404错误。

(4)、不得不配成/时,解决静态资源无法访问的问题。

解决方式一:使用Tomcat默认的Servlet解决

web.xml

<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.jpg</url-pattern>
</servlet-mapping>

<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.csss</url-pattern>
</servlet-mapping>

解决方式二:使用mvc的default-servlet-handler解决

其底层调用的还是tomcat默认的servlet

springmvc.xml

<mvc:default-servlet-handler/>

第七章 MyBatis - 查询缓存

7.1 一级缓存与二级缓存

MyBatis的查询缓存机制,根据缓存区的作用域与生命周期,可划分为两种:一级缓存与二级缓存。

MyBatis查询缓存的作用域是根据映射文件mapper的namespace划分的,相同的namespace的mapper查询数据存放在同一个缓存区域。不同namespace下的数据互不干扰。无论是一级缓存还是二级缓存,都是按照namespace进行分别存放的。

但一、二级缓存的不同之处在于,SqlSession一旦关闭,则SqlSession中的数据将不存在。而二级缓存的生命周期会与整个应用同步,与SqlSession是否关闭无关。换句话说,一级缓存是在同一线程(同一SqlSession)间共享数据,而二级缓存是在不同线程(不同SqlSession)间共享数据。

7.2 一级缓存

MyBatis一级缓存是基于org.apache.ibatis.cache.impl.PerpetualCache类的HashMap本地缓存,其作用域是SqlSession。在同一个SqlSession中两次执行相同的sql查询语句,第一次执行完毕后,会将查询结果写入到缓存中,第二次会从缓存中直接获取数据,而不再到数据库中进行查询,从而提高查询效率。

当一个SqlSession结束后,该SqlSession中的一级缓存也就不存在了。MyBatis默认一级缓存是开启状态,且不能关闭。

public void testSelectStudentsByCondition() {

    Student student = dao.selectStudentById(20);
    System.out.println(student);

    Student students = dao.selectStudentById(20);
    System.out.println(students);

}

查看控制台

[DEBUG][2018-05-13 10:08:15] com.bjpowernode.dao.IStudentDao.selectStudentById 159 ==>  Preparing: select id,name,age,score from students where id=? 
[DEBUG][2018-05-13 10:08:15] com.bjpowernode.dao.IStudentDao.selectStudentById 159 ==> Parameters: 20(Integer)
[DEBUG][2018-05-13 10:08:15] com.bjpowernode.dao.IStudentDao.selectStudentById 159 <==      Total: 1
Student [id=20, name=zhangsan2, age=24, score=1.0]
Student [id=20, name=zhangsan2, age=24, score=1.0]

sql语句只执行了一次,说明第二次是从缓存中读取的数据,证明了一级缓存是存在的。

7.3 一级缓存 - 从缓存中查找数据的依据

缓存底层对应的是map集合

  • key: 即查询依据,使用的ORM架构不同,查询依据不同。
  • value: 查询结果

一级缓存中读取数据的依据是:

  • mybatis的查询依据是:sql的id + sql语句
  • hibernate的查询依据是:查询结果的id

7.4 一级缓存 - 增删改对一级缓存的影响

增、删、改都会清空一级缓存,无论是否提交。

7.5 二级缓存 - 内置二级缓存的开启

由于MyBatis从缓存中读取数据的依据与SQL的id相关,所以MyBatis使用二级缓存的目的是为了防止同一查询(相同的Sql id、相同的Sql语句)的反复执行。

而Hibernate由于从缓存中读取数据的依据是查询结果的id,所以Hibernate使用二级缓存的目的是为了在多个查询间共享查询结果。比如第一次查询出了所有的学生,第二次查询id为5的学生,可以从缓存中直接读取。

MyBatis内置的二级缓存为org.apache.ibatis.cache.impl.PerpetualCache

内置二级缓存的开启:

(1)、映射文件中加cache

<mapper namespace="com.bjpowernode.dao.IStudentDao">

    <cache />
    ...
    ...
</mapper>

(2)、实体类实现序列化

Student.java

public class Student implements Serializable {
    ...
}

测试类

@Test
public void testSelectStudentsByCondition() {

    sqlSession = MyBatisUtils.getSqlSession();
    dao = sqlSession.getMapper(IStudentDao.class);
    Student student = dao.selectStudentById(20);
    System.out.println(student);

    //清空一级缓存        
    sqlSession.close();

    sqlSession = MyBatisUtils.getSqlSession();
    dao = sqlSession.getMapper(IStudentDao.class);
    Student students = dao.selectStudentById(20);
    System.out.println(students);

}

查看控制台

[DEBUG][2018-05-13 10:30:19] com.bjpowernode.dao.IStudentDao 62 Cache Hit Ratio [com.bjpowernode.dao.IStudentDao]: 0.0
Sun May 13 10:30:19 CST 2018 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
[DEBUG][2018-05-13 10:30:20] com.bjpowernode.dao.IStudentDao.selectStudentById 159 ==>  Preparing: select id,name,age,score from students where id=? 
[DEBUG][2018-05-13 10:30:21] com.bjpowernode.dao.IStudentDao.selectStudentById 159 ==> Parameters: 20(Integer)
[DEBUG][2018-05-13 10:30:21] com.bjpowernode.dao.IStudentDao.selectStudentById 159 <==      Total: 1
Student [id=20, name=zhangsan2, age=24, score=1.0]
[DEBUG][2018-05-13 10:30:21] com.bjpowernode.dao.IStudentDao 62 Cache Hit Ratio [com.bjpowernode.dao.IStudentDao]: 0.5
Student [id=20, name=zhangsan2, age=24, score=1.0]

sql语句也只执行了一次,说明了第二次读取数据是从二级缓存中读取的。

缓存命中率

Cache Hit Ratio [com.bjpowernode.dao.IStudentDao]: 0.5

7.6 二级缓存 - 增删改对二级缓存的影响

1、增、删、改同样也会清空二级缓存

2、对于二级缓存的清空,实质上是对所查找key对应的value置为null,而并非将对,即Entry对象删除。

3、从DB中进行select查询的条件是:

  • 缓存中根本就不存在key
  • 缓存中存在该key所对应的Entry对象,但其value为null

7.7 二级缓存 - 内置二级缓存的配置

<cache eviction="FIFO" flushInterval="10800000" readOnly="true" size="512" />

eviction: 逐出策略。当二级缓存中的对象达到最大值时,就需要通过逐出策略将缓存中的对象移出缓存。默认为LRU。常用策略有:

  • FIFO:First In First Out,先进先出。

  • LRU:Least Recently Used:未被使用时间最长的。

flushInterval: 刷新缓存的时间间隔,单位毫秒。即清空缓存间隔时间,一般不指定,即当执行增、删、改时刷新缓存。

readOnly: 设置缓存中数据是否只读。只读的缓存会给所有调用者返回缓存对象的相同实例,因此这些对象是不能被修改的,这提供了很重要的性能优势。但读写的缓存会返回缓存对象的拷贝,速度会慢一些,但是安全,因此默认是false。

flushInterval: 二级缓存中可以存放的最多对象个数。默认是1024个。

7.8 二级缓存 - 二级缓存的关闭

全局关闭

<configuration>
    <!-- 注册DB连接四要素属性文件 -->
    <properties resource="jdbc_mysql.properties"></properties>

    <settings>
        <!-- 关闭二级缓存 -->
        <setting name="cacheEnabled" value="false"/>
    </settings>

    ...

</configuration> 

局部关闭

<mapper namespace="com.bjpowernode.dao.IStudentDao">

    <cache />

    <select id="selectStudentById" useCache="false" resultType="Student">
        select id,name,age,score 
        from students 
        where id=#{xxx}
    </select>
    ...
</mapper>

7.9 二级缓存 - 使用原则

#####(1)、多个namespace不要操作同一张表

由于二级缓存中的数据是基于namespace的,即不同的namespace中的数据互不干扰。若某个用户在某个namespace下对表执行了增删改操作,该操作只会引发当前namespace下的二级缓存的刷新,而对其它namespace下的二级缓存没有影响。这样的话,其它二级缓存中的数据依然是未更新的数据,也就出现了多个namespace中的数据不一致的现象。

(2)、不要在关联关系表上执行增删改操作

一个namespace一般是对同一个表进行操作,若表间存在关联关系,也就意味着同一个表可能就会出现在多个namespace中。

(3)、查询多于修改时使用二级缓存

在查询操作远多于增删改操作的情况下可以使用二级缓存。因为任何增删改操作都将刷新二级缓存,对二级缓存的频繁刷新将降低系统性能。

7.10 二级缓存 - ehCache

mybatis的特长是sql操作,缓存数据管理不是其特长,为了提高缓存性能,mybatis允许使用第三方缓存产品。ecCache就是其中的一种。

注意,使用ehCache二级缓存,实体类无需实现序列化接口。

两个jar包:

一个是ehCache的核心jar包,一个是整合jar包。

下载地址:https://github.com/mybatis/ehcache-cache/releases

7.11 二级缓存 - ehCache - 开启

(1)、导入jar包

(2)、映射文件

<mapper namespace="com.bjpowernode.dao.IStudentDao">

    <cache type="org.mybatis.caches.ehcache.EhcacheCache" />
    ...
</mapper>     

(3)、ehcache.xml

<defaultCache
        maxElementsInMemory="10000"
        eternal="false"
        timeToIdleSeconds="120"
        timeToLiveSeconds="120"
        maxElementsOnDisk="10000000"
        diskExpiryThreadIntervalSeconds="120"
        memoryStoreEvictionPolicy="LRU">
    <persistence strategy="localTempSwap"/>
</defaultCache>

第六章 MyBatis - 延迟加载

6.1 延迟加载

MyBatis中的延迟加载,也称为懒加载,是指在进行关联查询时,按照设置的延迟规则推迟对关联对象的select查询。延迟加载可以有效的减少数据库压力。

需要注意的是,MyBatis的延迟加载只是对关联对象的查询有延迟设置,对于主加载对象都是直接执行查询语句的。

6.2 关联对象加载时机

MyBatis根据对关联对象查询的select语句的执行时机,分为三种类型:直接加载、侵入式延迟加载与深度延迟加载。

直接加载: 执行完对主加载对象的select语句,马上执行对关联对象的select查询。

侵入式延迟: 执行完对主加载对象的查询时,不会执行对关联对象的select查询。但当要访问主加载对象的详情时,就会马上执行关联对象的select查询。即对关联对象的查询执行,侵入到了主加载对象的详情访问中。也可以这样理解:将关联对象的详情侵入到了主加载对象的详情中,即将关联对象的详情作为主加载对象的详情的一部分出现了。

深度延迟: 执行对主加载对象的查询时,不会执行对关联对象的查询。访问主加载对象的详情时也不会执行关联对象的select查询。只有当真正访问关联对象的详情时,才会执行对关联对象的select查询。

需要注意的是,延迟加载的应用要求,关联对象的查询与主加载对象的查询必须是分别进行的select语句,不能使用多表连接所进行的查询。因为,多表连接查询,其实质是对一张表的查询,对由多个表连接后形成的一张表的查询。会一次性将多张表的所有信息查询出来。

MyBatis中对于延迟加载设置,可以应用到一对一、一对多、多对一、多对多的所有关联关系查询中。

6.3 懒加载配置

主配置文件:mybatis.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
    <!-- 注册DB连接四要素属性文件 -->
    <properties resource="jdbc_mysql.properties"></properties>

    <!-- 设置整个应用所使用的常量 -->
    <settings>
        <!-- 懒加载总开关 -->
        <setting name="lazyLoadingEnabled" value="true"/>
        <!-- 侵入式延迟,默认值为true
            执行主加载对象的查询时dao.selectCountryById(2),不会执行关联对象的查询。
            但是当访问主加载对象的详情时System.out.println(country.getCname()),就会马上执行关联对象的查询。
         -->
        <setting name="aggressiveLazyLoading" value="false"/>
        <!-- 深度延迟 :将侵入式延迟开关关闭
            执行对主加载对象的查询时dao.selectCountryById(2),不会执行关联对象的查询。
            访问主加载对象的详情时System.out.println(country.getCname()),也不会执行关联对象的查询。
            只有当访问关联对象的详情时System.out.println(ministers.size()),才会执行关联查询。
        -->
    </settings>

    <!-- 设置别名 -->
    <typeAliases>
        <!-- 将指定包中所有类的简单类名当作别名 -->
        <package name="com.bjpowernode.beans"/>
    </typeAliases>

    <!-- 配置运行环境 -->
    <environments default="mysqlEM">
        <environment id="mysqlEM">
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.user}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>

    <!-- 注册映射文件 -->
    <mappers>
        <mapper resource="com/bjpowernode/dao/mapper.xml" />    
    </mappers>

</configuration>