1 Spring
目标:
- ApplicationContext容器使用 属性setter方法注入的实现(掌握)
- Spring中的IoC和DI(熟悉)
- Spring的概念和优点(了解)
1.1 概述
Spring是分层的轻量级开源框架,以IOC(控制反转) 和 AOP(面向切面编程) 为内核,使用基本的javaBean来完成以前只能由EJB完成的工作。
1.1.1 什么是Spring
在实际开发中,通常服务器端在采用三层体系架构,分别为表示层(Web)、业务逻辑层(Service)、持久层(Dao), Spring对每一层都提供了技术支持。
1.1.2 Spring的特点
Spring拥有简单、可测试和松耦合等特点
1.1.3 Spring体系结构
Spring框架采用的是分层架构,它的一系列功能要素分为20个模块。
核心容器
- Beans
提供了BeanFactory,Spring将管理对象成为Bean - Core
提供了Spring框架的基本组成部分,包括IOC和DI功能。 - Context
建立在Beans和Core模块的基础上,他是访问定义和配置任何对象的媒介
1.1.4 Spring的目录结构
Spring开发需要的jar包包括Spring框架包、和第三方依赖包。
1.2 Spring的核心容器
Spring提供了两种核心容器BeanFactory和ApplicationContext。
1.2.1 BeanFactory
创建beanFactory实例
BeanFactory beanFactory = new XmlBeanFactory(new FileSystemResource("F:/applicationContext.xml"));
//这种加载方式在实际开发中不常见,仅了解即可
1.2.2 ApplicationContext
不仅包含了BeanFactory的所有功能,还添加了对国际化、资源访问、事件传播等方面的支持。
创建applicationContext实例
//1 java项目中通过该方法实例化
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("F:/applicationContext.xml");
//
//2
ApplicationContext applicationContext =new FileSystemXmlApplicationContext("F:/applicationContext.xml");
web项目中applicationContext的实例化工作由web服务器完成。通常由ContextLoaderListener实现,此方式需要在web.xml中添加如下代码
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:spring/applicationContext.xml
</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
Spring获取Bean实例
Object getBean(String name);
T getBean(Class requredType);
1.3 依赖注入DI
依赖注入和控制反转的含义相同,只不过这两个称呼是从两个角度描述的同一个概念
控制反转
在使用Spring框架之后,对象的实例不再由调用者来创建,而是由Spring容器来创建,Spring容器会负责控制程序之间的关系,而不是由调用者的程序代码直接控制。
控制权由应用代码转移到了Spring容器,控制权发生了反转,这就是控制反转。
依赖注入
Spring容器负责将被依赖对象赋值给调用者的成员变量,这相当于为调用者注入了它依赖的实例,这就是Spring的依赖注入。
1.3.1 依赖注入的实现
- 为调用者需要注入的成员变量提供setter方法
- 在配置文件中写好配置, 例如调用者userService中的成员变量userDao。下面会将对象userDao注入对象userService的成员变量userDao。
<bean id="userService" class="com.itheima.ioc.UserServiceImpl">
<property name="userDao" ref="userDao" />
</bean>
思考
- 请简述Spring框架的优点。
- 请简述什么是Spring的IoC和DI。
2 Spring中的Bean
目标:
- 实例化Bean的三种方式和Bean的三种装配方式(掌握)
- Bean的作用域和生命周期(熟悉)
- Bean的常用属性及其子元素(了解)
2.1 Bean的配置
如果把Spring看做一个大型工厂,则Spring容器中的Bean就是该工厂的产品。要想使用这个工厂生产和管理Bean,就需要在配置文件中告诉它需要哪些Bean,以及需要使用何种方式将这些Bean装配到一起。
一般只配置id和class,如果不指定id或者name,默认使用class作为id名
<bean id="bean1" class="com.itheima.Bean1" />
2.2 Bean的实例化
有三种方式:构造器实例化(最常用)、 静态工厂方式实例化、实例工厂方式实例化
构造器实例化
指Spring容器通过Bean对应的类中默认的构造函数来实例化Bean。
- 创建Web项目,导入相关Jar包
- 创建java类
- 创建Spring配置文件,并配置java类的bean
静态工厂化
静态工厂是实例化Bean的另一种方式。该方式要求自己创建一个静态工厂的方法来创建Bean的实例。
- 创建一个Java类
- 创建一个生产该Java类的工厂类,工厂类中提供静态方法获得Java类
- 创建Spring配置文件,并配置工厂类的bean
实例工厂化
- 创建一个Java类
- 创建一个生产该Java类的工厂类,工厂类中提供非静态方法获得Java类
- 创建Spring配置文件,并配置工厂类的bean
2.3 Bean的作用域
2.3.1 作用域的种类
一共有7种作用域,其中singleton(Spring容器默认)和prototype最常用。
2.3.2 singleton的作用域
singleton作用域对于无会话状态的Bean(如Dao 组件、Service组件)来说,是最理想的选择。
配置:
<bean id="scope" class="com.itheima.scope.Scope" scope="singleton"/>
2.3.3 prototype的作用域
对需要保持会话状态的Bean(如Struts 2的Action类)应该使用prototype作用域。在使用prototype作用域时,Spring容器会为每个对该Bean的请求都创建一个新的实例。
2.4 Bean的生命周期
了解的意义
了解Spring中Bean的生命周期的意义就在于,可以利用Bean在其存活期间的特定时刻完成一些相关操作。这种时刻可能有很多,但一般情况下,常会在Bean的postinitiation(初始化后)和predestruction(销毁前) 执行一些相关操作。
Spring容器可以管理生命周期的Bean
- singleton作用域的bean,在此作用域下,Spring能够精确的知道该Bean何时被创建,何时初始化完成,以及何时被销毁。
- prototype作用域下的bean不行,当容器创建了Bean实例后Bean的实例就交给客户端代码来管理,Spring容器将不再跟踪其生命周期。
2.5 Bean的装配方式
有3种,
- 基于XML的装配
- 基于注解(Annotation)的装配
- 自动装配(其中最常用的是基于注解的装配)
2.5.1 基于XML的装配
- 设值注入(必须有一个无参构造方法为属性提供setter方法),在配置文件中,使用property为bean赋值
- 构造注入(必须提供有参构造方法),在配置文件中,使用constructor-arg为bean赋值
<bean id="user1" class="com.itheima.assemble.User">
<constructor-arg index="0" value="张三" />
...
</bean>
<bean id="user2" class="com.itheima.assemble.User">
<property name=“username” value=“张三” />
...
</bean>
2.5.2 基于注解的装配
注解
- @Component:用于描述Spring中的Bean,它是一个泛化的概念,仅仅表示一个组件。
- @Repository:用于将数据访问层(DAO)的类标识为Spring中的Bean 。
- @Controller:用于将控制层(Controller)的类标识为Spring中的Bean 。
- @Service:用于将业务层(Service)的类标识为Spring中的Bean。
- @Autowired:用于对Bean的属性变量、属性的setter方法及构造方法进行标注,配合对应的注解处理器完成Bean的自动配置工作。
- @Resource:其作用与Autowired一样。@Resource中有两个重要属性:name和type。Spring将name属性解析为Bean实例名称,type属性解析为Bean实例类型。
- @Qualifier:与@Autowired注解配合使用,会将默认的按Bean类型装配修改为按Bean的实例名称装配,Bean的实例名称由@Qualifier注解的参数指定。
例子
配置文件中的配置。
<!--扫描这些bean中的注解,并为bean中的成员进行赋值-->
<context:annotation-config />
<bean id="userDao" class="com.itheima.annotation.UserDaoImpl" />
<bean id="userService" class="com.itheima.annotation.UserServiceImpl" />
<bean id="userController" class="com.itheima.annotation.UserController" />
除了可以像示例中通过元素来配置Bean外,还可以通过包扫描的形式来配置一个包下的所有Bean:
<context:component-scan base-package=“com.itheima.annotation” />
2.5.3 自动装配
所谓自动装配,就是将一个Bean自动的注入到到其他Bean的Property中。
可以通过设置bean的autowire实现
<bean id="userDao" class="com.itheima.annotation.UserDaoImpl" />
<bean id="userService" class="com.itheima.annotation.UserServiceImpl" autowire="byName" />
<bean id="userController" class="com.itheima.annotation.UserController" autowire="byName" />
思考
- 请简述Bean的生命周期。
一阶段:Bean的实例化和DI(dependency injection)
二阶段:检查Spring Awareness
三阶段:创建bean生命周期回调
四阶段:销毁bean生命周期回调 - 请简述Bean的几种装配方式的基本用法。
3 Spring AOP
目标:
- 基于代理类的AOP实现和基于XML和注解的AspectJ开发(掌握)
- 两种动态代理方式的区别(熟悉)
- AOP中的相关术语(理解)
- AOP的概念和作用(了解)
3.1 Spring AOP简介
3.1.1 AOP简介
全称:Aspect-Oriented Programming,面向切面编程。
----在传统的业务处理代码中,通常都会进行事务处理、日志记录等操作。虽然使用OOP可以通过组合或者继承的方式来达到代码的重用,但如果要实现某个功能(如日志记录),同样的代码仍然会分散到各个方法中。这样,如果想要关闭某个功能,或者对其进行修改,就必须要修改所有的相关方法。这不但增加了开发人员的工作量,而且提高了代码的出错率。
----为了解决这一问题,AOP思想随之产生。AOP采取横向抽取机制,将分散在各个方法中的重复代码提取出来,然后在程序编译或运行时,再将这些提取出来的代码应用到需要执行的地方。这种采用横向抽取机制的方式,采用传统的OOP思想显然是无法办到的,因为OOP只能实现父子关系的纵向的重用。虽然AOP是一种新的编程思想,但却不是OOP的替代品,它只是OOP的延伸和补充。
3.1.2 AOP术语
3.2 动态代理
3.2.1 JDK动态代理
JDK动态代理是通过java.lang.reflect.Proxy 类来实现的,我们可以调用Proxy类的newProxyInstance() 方法来创建代理对象。对于使用业务接口的类,Spring默认会使用JDK动态代理来实现AOP。
实现
1.创建目标类,SomeServiceImpl目标类,我们要给它的doSome,doOhter方法增加开头结尾。
2.创建InvocationHandler接口的实现类,在这个类实现,给目标方法增加功能。
3.使用jdk中 类proxy,创建代理对象。实现创建对象的能力。
2.
//实现动态代理接口
MyincationHandler.java
public class MyIncationHandler implements InvocationHandler {
//目标对象
private Object target; //SomeServiceImpl类
public void setTarget(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//通过动态代理执行方法时,会执行这个invoke()方法
Object res = null;
System.out.println("在方法前添加东西");
//执行目标类的方法,通过method类实现
res = method.invoke(target,args);
//目标执行的结果
System.out.println("在方法后添加东西");
return res;
}
}
3.创建代理对象
SomeService target = new SomeServiceImpl();
InvocationHandler handler = new MyIncationHandler(target);
//使用Proxy创建代理
SomeService proxy = (SomeService) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
handler
);
proxy.doSome(); //通过代理执行方法,会执行InvocationHandler中的invoke。
3.2.2 CGLIB代理
略
3.3 基于代理类的AOP实现
3.3.1 Spring的通知类型
- org.springframework.aop.MethodBeforeAdvice(前置通知)
在目标方法执行前实施增强,可以应用于权限管理等功能。 - org.springframework.aop.AfterReturningAdvice(后置通知)
在目标方法执行后实施增强,可以应用于关闭流、上传文件、删除临时文件等功能。 - org.aopalliance.intercept.MethodInterceptor(环绕通知)
在目标方法执行前后实施增强,可以应用于日志、事务管理等功能。 - org.springframework.aop.ThrowsAdvice(异常抛出通知)
在方法抛出异常后实施增强,可以应用于处理异常记录日志等功能。 - org.springframework.aop.IntroductionInterceptor(引介通知)
在目标类中添加一些新的方法和属性,可以应用于修改老版本程序。
ProxyFactoryBean是FactoryBean接口的实现类,FactoryBean负责实例化一个Bean,而ProxyFactoryBean**负责为其他
Bean创建代理实例**。在Spring中,使用ProxyFactoryBean是创建AOP代理的基本方式。
ProxyFactoryBean类中的常用可配置属性如下:
3.4 AspectJ开发
AspectJ是一个基于Java语言的AOP框架。实现AOP有两种方式
- 基于XML的声明式AspectJ
- 基于注解的声明式AspectJ
3.4.1 基于XML的声明式AspectJ
通过XML文件来定义切面、切入点及通知,所有的切面、切入点和通知都必须定义在< aop:config >元素内。
配置切面:
- 配置切面
< aop:aspect id=“aspect” ref=“myAspect” >
配置切面使用的是< aop:aspect >元素,该元素会将一个已定义好的Spring Bean转换成切面Bean,所以要在配置文件中先定义一个普通的Spring Bean。配置切面 aop:aspect时通常配置id和ref属性
-
配置切入点
< aop:pointcut expression=“execution(* com.itheima.jdk.*.*(…))” id=“myPointCut” / >
当< aop:pointcut >元素作为< aop:config >元素的子元素定义时,表示该切入点是全局切入点,它可被多个切面所共享;
当< aop:pointcut >元素作为< aop:aspect >元素的子元素时,表示该切入点只对当前切面有效。
execution(* com.itheima.jdk.*.*(…)) 是定义的切入点表达式,该切入点表达式的意思是匹配com.itheima.jdk包中任意类的任意方法的执行。 返回类型 包名.类名.方法(参数)。 -
配置通知
使用< aop:aspect >的子元素可以配置5种常用通知,这5个子元素不支持使用子元素,但在使用时可以指定一些属性,其常用属性及其描述如下:
3.4.2基于注解的声明式AspectJ
例子
@Aspect
public class MyAspect {
//AfterReturning是切入的时间点, value是目标类
@AfterReturning(value="execution(public void com.vashon.service.impl.SomeServiceImpl.doSome())",returning="res")
public void myAfter(JoinPoint jp, Object res) { //当方法调用该切面方法,这个方法的返回值会赋值给res
System.out.println("后置通知");
System.out.println(res);
}
}
思考
- 请列举你所知道的AOP专业术语并解释。
- 请列举你所知道的Spring的通知类型并解释。
4 Spring的数据库开发
目标:
- JdbcTemplate类中几个常用方法的使用(掌握)
- Spring JDBC的配置(熟悉)
- Spring中JDBC模块的作用(了解)
4.1 Spring JDBC
JDBC模块的作用
Spring的JDBC模块负责数据库资源管理和错误处理,大大简化了开发人员对数据库的操作,使得开发人员可以从繁琐的数据库操作中解脱出来,从而将更多的精力投入到编写业务逻辑当中。
4.1.1 Spring JdbcTemplate的解析
JdbcTemplate类是Spring JDBC的核心类,继承结构如下:
JdbcTemplate类的直接父类是JdbcAccessor,该类为子类提供了一些访问数据库时使用的公共属性。
- DataSource
其主要功能是获取数据库连接,还可以引入对数据库连接的缓冲池和分布式事务的支持,它可以作为访问数据库资源的标准接口。 - SQLExceptionTranslator
该接口负责对SQLException进行转译工作。通过必要的设置获取SQLExceptionTranslator中的方法,可以使JdbcTemplate在需要处理SQLException时,委托SQLExceptionTranslator的实现类来完成相关的转译工作。
而JdbcOperations接口定义了在JdbcTemplate类中可以使用的操作集合,包括添加、修改、查询和删除等操作。
4.1.2 Spring JDBC的配置
Spring JDBC模块主要由4个包组成,分别是core(核心包)、dataSource(数据源包)、object(对象包)和support(支持包)。
JdbcTemplate类 在core(核心包) 中。
配置文件
其中dataSource中的四个属性
4.2 Spring JdbcTemplate的常用方法
在JdbcTemplate核心类中,提供了大量的更新和查询数据库的方法,我们就是使用的这些方法来操作数据库的。
- execute()
execute(String sql)方法可用于执行sql语句 - update()
update()用于执行插入、更新和删除操作 - query()
query()用于执行数据查询操作
4.2.1 execute()的使用
public class JdbcTemplateTest {
public static void main(String[] args) {
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("applicationContext.xml");
JdbcTemplate jdTemplate = (JdbcTemplate) applicationContext.getBean("jdbcTemplate");
//此处使用了jdbc Template类的execute方法
jdTemplate.execute("create table account(" id int primary key auto_increment," +
"username varchar(50)," +
"balance double)");
}
}
JUnit就是一个进行单元测试的开源框架。
4.2.2 update()的使用
update()方法可以完成插入、更新和删除数据的操作。在JdbcTemplate类中,提供了一系列的update()方法,其常用方法下表所示:
update的使用
// 添加账户
public int addAccount(Account account) {
// 定义SQL
String sql = "insert into account(username,balance) value(?,?)";
// 定义数组来存放SQL语句中的参数
Object[] obj = new Object[] {
account.getUsername(),
account.getBalance()
};
// 执行添加操作,返回的是受SQL语句影响的记录条数
int num = this.jdbcTemplate.update(sql, obj);###此处执行update(String sql,PreparedStatementSetter pss)方法
return num;
}
// 删除账户
public int deleteAccount(int id) {
// 定义SQL
String sql = "delete from account where id = ? ";
// 执行添加操作,返回的是受SQL语句影响的记录条数
int num = this.jdbcTemplate.update(sql, id); ###此处执行update(String sql,Object...args)方法
return num;
}
4.2.3 query()的使用
JdbcTemplate类中还提供了大量的query()方法来处理各种对数据库表的查询操作。其中,常用的几个query()方法如下表所示:
// 通过id查询账户数据信息
public Account findAccountById(int id) {
//定义SQL语句
String sql = "select * from account where id = ?";
// 创建一个新的BeanPropertyRowMapper对象
RowMapper<Account> rowMapper =
new BeanPropertyRowMapper<Account>(Account.class);
// 将id绑定到SQL语句中,并通过RowMapper返回一个Object类型的单行记录
return this.jdbcTemplate.queryForObject(sql, rowMapper, id); ###此处执行queryForObject方法
}
// 查询所有账户信息
public List<Account> findAllAccount() {
// 定义SQL语句
String sql = "select * from account";
// 创建一个新的BeanPropertyRowMapper对象
RowMapper<Account> rowMapper =
new BeanPropertyRowMapper<Account>(Account.class);
// 执行静态的SQL查询,并通过RowMapper返回结果
return this.jdbcTemplate.query(sql, rowMapper); ###此处执行query方法
}
思考
- 请简述Spring JDBC是如何进行配置的。
- 请简述Spring JdbcTemplate类中几个常用方法的作用。
5 Spring的事务管理
目标:
- 基于XML和Annotation的声明式事务的使用(掌握)
- Spring事务管理的三个核心接口(熟悉)
- Spring事务管理的两种方式(了解)
5.1 Spring事务管理概述
在实际开发中,操作数据库时都会涉及到事务管理问题,为此Spring提供了专门用于事务处理的API。Spring的事务管理简化了传统的事务管理流程,并且在一定程度上减少了开发者的工作量。
5.1.1 核心接口文件
有3个接口文件PlatformTransactionManager、TransactionDefinition和TransactionStatus
-
Platform TransactionManager
PlatformTransactionManager接口是Spring提供的平台事务管理器,主要用于管理事务。该接口中提供了三个事务操作的方法,具体如下:
1. TransactionStatus getTransaction(TransactionDefinition definition);
用于获取事务状态信息
2. void commit(TransactionStatus status);
用于提交事务
3. void rollback(TransactionStatus status);
用于回滚事务
实现类:
1. org.springframework.jdbc.datasource.DataSourceTransactionManager
用于配置JDBC数据源的事务管理器
2. org.springframework.orm.hibernate4.HibernateTransactionManager
用于配置Hibernate的事务管理器
3. org.springframework.transaction.jta.JtaTransactionManager
用于配置全局事务管理器 -
TransactionDefinition
TransactionDefinition接口是事务定义(描述)的对象,该对象中定义了事务规则,并提供了获取事务相关信息的方法,具体如下:
传播行为:(默认required)
3. TransactionStatus
TransactionStatus接口是事务的状态,它描述了某一时间点上事务的状态信息。该接口中包含6个方法,具体如下:
5.1.2 事务管理的方式
有两种方式
- 编程式事务管理
通过编写代码实现的事务管理,包括定义事务的开始、正常执行后的事务提交和异常时的事务回滚 - 声明式事务管理
通过AOP技术实现的事务管理,主要思想是将事务作为一个“切面”代码单独编写,然后通过AOP技术将事务管理的“切面”植入到业务目标类中
声明式事务管理最大的优点在于开发者无需通过编程的方式来管理事务,只需在配置文件中进行相关的事务规则声明,就可以将事务应用到业务逻辑中。这使得开发人员可以更加专注于核心业务逻辑代码的编写,在一定程度上减少了工作量,提高了开发效率,所以在实际开发中,通常都推荐使用声明式事务管理。
5.2 声明式事务管理
可以通过两种方式来实现,一种是基于XML的方式,另一种是基于Annotation的方式。
5.2.1 基于XML方式的声明式事务
基于XML方式的声明式事务是在配置文件中通过<tx:advice>元素配置事务规则来实现的。当配置了事务的增强处理后,就可以通过编写的AOP配置,让Spring自动对目标生成代理。<tx:advice>元素及其子元素如下图所示:
配置< tx:advice >元素的重点是配置< tx:method >子元素,上图中使用灰色标注的几个属性是< tx:method >元素中的常用属性。其属性描述具体如下:
配置文件:
<!-- 4.事务管理器,依赖于数据源 -->
<bean id="transactionManager" class=
"org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 5.编写通知:对事务进行增强(通知),需要编写对切入点和具体执行事务细节 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- name:*表示任意方法名称 -->
<tx:method name="*" propagation="REQUIRED"
isolation="DEFAULT" read-only="false" />
</tx:attributes>
</tx:advice>
<!-- 6.编写aop,让spring自动对目标生成代理,需要使用AspectJ的表达式 -->
<aop:config>
<!-- 切入点 -->
<aop:pointcut expression="execution(* com.itheima.jdbc.*.*(..))" id="txPointCut" />
<!-- 切面:将切入点与通知整合 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut" />
</aop:config>
5.2.2 基于Annotation方式的声明式事务
- 在Spring容器中注册事务注解驱动;
< tx:annotation-driven transaction-manager=“transactionManager”/> - 在需要事务管理的类或方法上使用@Transactional注解。
如果将注解添加在Bean类上,则表示事务的设置对整个Bean类的所有方法都起作用;
如果将注解添加在Bean类中的某个方法上,则表示事务的设置只对该方法有效。
使用**@Transactional注解**时,可以通过参数配置事务详情:
配置文件:
<!-- 4.事务管理器,依赖于数据源 -->
<bean id="transactionManager" class=
"org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 5.注册事务管理器驱动 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
使用注解:
@Transactional(propagation = Propagation.REQUIRED,
isolation = Isolation.DEFAULT, readOnly = false)
public void transfer(String outUser, String inUser, Double money) {
// 收款时,收款用户的余额=现有余额+所汇金额
this.jdbcTemplate.update("update account set balance = balance +? "
+ "where username = ?",money, inUser);
// 模拟系统运行时的突发性问题
int i = 1/0;
// 汇款时,汇款用户的余额=现有余额-所汇金额
this.jdbcTemplate.update("update account set balance = balance-? "
+ "where username = ?",money, outUser);
}
思考
- 请简述Spring中事务管理的两种方式
- 请简述如何使用Annotation方式进行声明式事务管理。
6 初始Mybatis
目标:
- MyBatis入门程序的编写(掌握)
- MyBatis的工作原理(熟悉)
- MyBatis的基础知识(了解)
6.1 什么是MyBatis
MyBatis(前身是iBatis)是一个支持普通SQL查询、存储过程以及高级映射的持久层框架。
MyBatis框架也被称之为ORM(Object/Relation Mapping,即对象关系映射)框架。所谓的ORM就是一种为了解决面向对象与关系型数据库中数据类型不匹配的技术,它通过描述Java对象与数据库表之间的映射关系,自动将Java应用程序中的对象持久化到关系型数据库的表中。
Hibernate与MyBatis有什么区别?
Hibernate是一个全表映射的框架。
- 通常开发者只需定义好持久化对象到数据库表的映射关系,就可以通过Hibernate提供的方法完成持久层操作。
- 开发者并不需要熟练的掌握SQL语句的编写,Hibernate会根据制定的存储逻辑,自动的生成对应的SQL,并调用JDBC接口来执行,所以其开发效率会高于MyBatis。
- Hibernate也存在一些缺点,例如它在多表关联时,对SQL查询的支持较差;更新数据时,需要发送所有字段;不支持存储过程;不能通过优化SQL来优化性能等。
MyBatis是一个半自动映射的框架。
- “半自动”是相对于Hibernate全表映射而言的,MyBatis需要手动匹配提供POJO、SQL和映射关系,而Hibernate只需提供POJO和映射关系即可。
- 与Hibernate相比,虽然使用MyBatis手动编写SQL要比使用Hibernate的工作量大,但MyBatis可以配置动态SQL并优化SQL,可以通过配置决定SQL的映射规则,它还支持存储过程等。对于一些复杂的和需要优化性能的项目来说,显然使用MyBatis更加合适。
6.2 MyBatis的下载和使用
略
6.3 MyBatis的工作原理
6.4 MyBatis入门程序
6.4.1查询用户
Mapper.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.itheima.mapper.CustomerMapper"> //namesace指的是对应的Dao类,此处为CustomerMapper
<select id="findCustomerById" parameterType="Integer"
resultType="com.itheima.po.Customer">
select * from t_customer where id = #{id}
</select>
</mapper>
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> //配置数据库
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/mybatis" />
<property name="username" value="root" />
<property name="password" value="root" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/itheima/mapper/CustomerMapper.xml" /> //扫描到上面那个xml文件
</mappers>
</configuration>
使用
public class MybatisTest {
public void findCustomerByIdTest() throws Exception {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
//3 创建会话工厂
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//4 获得会话对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//5 使用会话
Customer customer = sqlSession.selectOne("com.itheima.mapper" + “.CustomerMapper.findCustomerById”, 1);
System.out.println(customer.toString());
sqlSession.close();
}
}
sqlSession对象的各个方法。
- sqlSession.selectList(“com.autohome.mapper.User.queryUsers”);
- sqlSession.selectOne(“com.autohome.mapper.User.queryUserById”,2);
- sqlSession.insert(“com.autohome.mapper.User.insertUsers”,user);
- sqlSession.update(“com.autohome.mapper.User.updateUsers”,user);
- sqlSession.delete(“com.autohome.mapper.User.deleteUsers”,49);
思考
- 请简述MyBatis框架与Hibernate框架的区别。
- 请简述MyBatis的工作执行流程。
7 MyBatis的核心配置
目标:
- 映射文件中常用元素的使用(掌握)
- 配置文件中各个元素的作用(熟悉)
- 核心对象的作用(了解)
7.1 MyBatis的核心对象
7.1.1 SqlSessionFactory
SqlSessionFactory是MyBatis框架中十分重要的对象,它是单个数据库映射关系经过编译后的内存镜像,其主要作用是创建SqlSession。
SqlSessionFactory对象的实例可以通过SqlSessionFactoryBuilder对象来构建,而SqlSessionFactoryBuilder则可以通过XML配置文件或一个预先定义好的Configuration实例构建出SqlSessionFactory的实例。
InputStream inputStream = Resources.getResourceAsStream("配置文件位置");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
7.1.2 SqlSession
SqlSession是MyBatis框架中另一个重要的对象,它是应用程序与持久层之间执行交互操作的一个单线程对象,其主要作用是执行持久化操作。
每一个线程都应该有一个自己的SqlSession实例,并且该实例是不能被共享的。同时,SqlSession实例也是线程不安全的,因此其使用范围最好在一次请求或一个方法中,绝不能将其放在一个类的静态字段、实例字段或任何类型的管理范围(如Servlet的HttpSession)中使用。
使用完SqlSession对象后要及时关闭,通常可以将其放在finally块中关闭。
除了增删改查,其他方法:
void commit(); 提交事务的方法。
void rollback(); 回滚事务的方法。
void close(); 关闭SqlSession对象。
<T> T getMapper(Class<T> type); 返回Mapper接口的代理对象。
Connection getConnection(); 获取JDBC数据库连接对象的方法。
7.2 配置文件
7.2.1 主要元素
7.2.2 < properties >元素
< properties >是一个配置属性的元素,该元素通常用来将内部的配置外在化,即通过外部的配置来动态的替换内部定义的属性。例如,数据库的连接等属性,就可以通过典型的Java属性文件中的配置来替换,具体方式如下:
db.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=root
mybatis.xml
<properties resource="db.properties" /> ###引入外部配置文件
<dataSource type="POOLED">
<!-- 数据库驱动 -->
<property name="driver" value="${jdbc.driver}" />
<!-- 连接数据库的url -->
<property name="url" value="${jdbc.url}" />
<!-- 连接数据库的用户名 -->
<property name="username" value="${jdbc.username}" />
<!-- 连接数据库的密码 -->
<property name="password" value="${jdbc.password}" />
</dataSource>
7.2.3 < settings >元素
< settings >元素主要用于改变MyBatis运行时的行为,例如开启二级缓存、开启延迟加载等。
mybatis.xml
<!-- 设置 了解即可-->
<settings>
<setting name="cacheEnabled" value="true" />
<setting name="lazyLoadingEnabled" value="true" />
<setting name="multipleResultSetsEnabled" value="true" />
<setting name="useColumnLabel" value="true" />
<setting name="useGeneratedKeys" value="false" />
<setting name="autoMappingBehavior" value="PARTIAL" />
...
</settings>
7.2.4 < typeAliases >元素
< typeAliases >元素用于为配置文件中的Java类型设置一个简短的名字,即设置别名。别名的设置与XML配置相关,其使用的意义在于减少全限定类名的冗余。 (以后用到全限定类名称时,可以其简写)
<typeAliases>
<typeAlias alias="user" type="com.itheima.po.User"/>
</typeAliases>
当pojo类过多时,使用下面设置,别名为其注解的值
<typeAliases>
<package name="com.itheima.po"/>
</typeAliases>
默认别名
7.2.5 < typeHandler >元素
typeHandler的作用就是将预处理语句中传入的参数从javaType(Java类型)转换为jdbcType(JDBC类型),或者从数据库取出结果时将jdbcType转换为javaType。
< typeHandler >元素可以在配置文件中注册自定义的类型处理器,它的使用方式有两种。
- 注册一个类的类型处理器
<typeHandlers>
<typeHandler handler="com.itheima.type.CustomtypeHandler" />
</typeHandlers>
- 注册一个包中所有的类型处理器
<typeHandlers>
<package name="com.itheima.type" />
</typeHandlers>
7.2.6 < objectFactory >元素(不怎么用)
MyBatis中默认的ObjectFactory的作用是实例化目标类,它既可以通过默认构造方法实例化,也可以在参数映射存在的时候通过参数构造方法来实例化。通常使用默认的ObjectFactory即可。
7.2.7 < plugins >元素(你得会写mybatis插件)
< plugins >元素的作用就是配置用户所开发的插件。
7.2.8 < environments >元素
< environments >元素用于对环境进行配置。MyBatis的环境配置实际上就是数据源的配置,我们可以通过< environments >元素配置多种数据源,即配置多种数据库。
<environments default="development"> 表示默认使用id为development数据源
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</dataSource>
</environment>
...
</environments>
在MyBatis中,可以配置两种类型的事务管理器,分别是JDBC和MANAGED。关于这两个事务管理器的描述如下:
- JDBC:此配置直接使用了JDBC的提交和回滚设置,它依赖于从数据源得到的连接来管理事务的作用域。
- MANAGED:此配置从来不提交或回滚一个连接,而是让容器来管理事务的整个生命周期。默认情况下,它会关闭连接,但一些容器并不希望这样,为此可以将closeConnection属性设置为false来阻止它默认的关闭行为。
注意:
如果项目中使用的是Spring+ MyBatis,则没有必要在MyBatis中配置事务管理器,因为实际开发中,会使用Spring自带的管理器来实现事务管理。
数据源的配置
7.2.9 < mappers >元素
< mappers >元素用于指定MyBatis映射文件的位置,一般可以使用以下4种方法引入映射器文件,具体如下。
2、3不常用
1.使用类路径引入
<mappers>
<mapper resource="com/itheima/mapper/UserMapper.xml"/>
</mappers>
2.使用本地文件路径引入
<mappers>
<mapper url="file:///D:/com/itheima/mapper/UserMapper.xml"/>
</mappers>
3.使用接口类引入
<mappers>
<mapper class="com.itheima.mapper.UserMapper"/>
</mappers>
4.使用包名引入
<mappers>
<package name="com.itheima.mapper"/>
</mappers>
7.3 映射文件
7.3.1 主要元素
在映射文件中,< mapper >元素是映射文件的根元素,其他元素都是它的子元素。
<mapper namespace="org.example.dao.StudentDao">
<select id="selectStudent" resultType="org.example.doman.Student">
select * from student order by id;
</select>
...
</mapper>
7.3.2 < select >元素
< select >元素用来映射查询语句,它可以帮助我们从数据库中读取出数据,并组装数据给业务开发人员。
<select id="findCustomerById" parameterType="Integer"
resultType="com.itheima.po.Customer">
select * from t_customer where id = #{id}
</select>
常用属性
7.3.3 < insert >元素
< insert >元素用于映射插入语句,在执行完元素中定义的SQL语句后,会返回一个表示插入记录数的整数。
<insert id="addCustomer" parameterType="com.itheima.po.Customer"
keyProperty="id" useGeneratedKeys="true" >
insert into t_customer(username,jobs,phone)
values(#{username},#{jobs},#{phone})
</insert>
insert的属性
插入后返回主键值(此处支持主键自动增长的数据库)
<insert id="addCustomer" parameterType="com.itheima.po.Customer"
keyProperty="id" useGeneratedKeys="true" >
insert into t_customer(username,jobs,phone)
values(#{username},#{jobs},#{phone})
</insert>
7.3.4 < update >和< delete >元素
常用属性如下:
<update
id="updateCustomer"
parameterType="com.itheima.po.Customer"
flushCache="true"
statementType="PREPARED"
timeout="20">
<delete
id="deleteCustomer"
parameterType="com.itheima.po.Customer"
flushCache="true"
statementType="PREPARED"
timeout="20">
示例:
<update id="updateCustomer" parameterType="com.itheima.po.Customer">
update t_customer
set username=#{username},jobs=#{jobs},phone=#{phone}
where id=#{id}
</update>
<delete id="deleteCustomer" parameterType="Integer">
delete from t_customer where id=#{id}
</delete>
7.3.5 < sql >元素
< sql >元素的作用就是定义可重用的SQL代码片段,然后在文件中其他的sql语句中引用这一代码片段。
<sql id="customerColumns">id,username,jobs,phone</sql>
<select id="findCustomerById" parameterType="Integer"
resultType="com.itheima.po.Customer">
select <include refid="customerColumns"/> ###使用了这一个代码片段
from t_customer
where id = #{id}
</select>
又一例子
<!--定义表的前缀名 -->
<sql id="tablename">
${prefix}customer
</sql>
<!--定义要查询的表 -->
<sql id="someinclude">
from
<include refid="${include_target}" />
</sql>
<!--定义查询列 -->
<sql id="customerColumns">
id,username,jobs,phone
</sql>
<select id="findCustomerById" parameterType="Integer"
resultType="com.itheima.po.Customer">
select
<include refid="customerColumns"/>
<include refid="someinclude">
<property name="prefix" value="t_" />
<property name="include_target" value="tablename" />
</include>
where id = #{id}
</select>
select id,username,jobs,phone
from t_customer
where id = ?;
7.3.6 < resultMap >元素
< resultMap >元素表示结果映射集,是MyBatis中最重要也是最强大的元素。它的主要作用是定义映射规则、级联的更新以及定义类型转化器等。
<resultMap type="" id="">
<constructor> <!-- 类在实例化时,用来注入结果到构造方法中-->
<idArg/> <!-- ID参数;标记结果作为ID-->
<arg/> <!-- 注入到构造方法的一个普通结果-->
</constructor>
<id/> <!-- 用于表示哪个列是主键-->
<result/> <!-- 注入到字段或JavaBean属性的普通结果-->
<association property="" /> <!-- 用于一对一关联 -->
<collection property="" /> <!-- 用于一对多关联 -->
<discriminator javaType=""> <!-- 使用结果值来决定使用哪个结果映射-->
<case value="" /> <!-- 基于某些值的结果映射 -->
</discriminator>
</resultMap>
思考
- 简述MyBatis核心对象SqlSessionFactory的获取方式。
- 简述MyBatis映射文件中的主要元素及其作用。
8 动态SQL
目标:
- 动态SQL中主要元素的使用(掌握)
- 常用的动态SQL元素及其作用(了解)
8.1 动态SQL中的元素
作用:
开发人员在使用JDBC或其他类似的框架进行数据库开发时,通常都要根据需求去手动拼装SQL,这是一个非常麻烦且痛苦的工作,而MyBatis提供的对SQL语句动态组装的功能,恰能很好的解决这一麻烦工作。
元素如下:
8.2 < if >元素
在MyBatis中,< if >元素是最常用的判断语句,它类似于Java中的if语句,主要用于实现某些简单的条件选择。其基本使用示例如下:
select * from t_customer where 1=1
<if test="username !=null and username !=''">
<!--当username不为空或者null时,才会往sql语句添加and username like concat('%',#{username}, '%')-->
and username like concat('%',#{username}, '%')
</if>
<if test="jobs !=null and jobs !=''">
and jobs= #{jobs}
</if>
8.3 < choose >及其子元素
当客户名称不为空,则只根据客户名称进行客户筛选;
当客户名称为空,而客户职业不为空,则只根据客户职业进行客户筛选。
当客户名称和客户职业都为空,则要求查询出所有电话不为空的客户信息。”
select * from t_customer where 1=1
<choose>
<!--三者选一-->
<when test="username !=null and username !=''">
and username like concat('%',#{username}, '%')
</when>
<when test="jobs !=null and jobs !=''">
and jobs= #{jobs}
</when>
<otherwise>
and phone is not null
</otherwise>
</choose>
8.4 < when >、< trim >元素
动态SQL处理“where 1=1”
- < where >元素处理
< where >会自动判断SQL语句,只有< where >内的条件成立时,才会在拼接SQL中加入where关键字,否则将不会添加;还会去除多余的“AND”或“OR”。
select * from t_customer
<where>
<if test="username !=null and username !=''">
and username like concat('%',#{username}, '%')
</if>
<if test="jobs !=null and jobs !=''">
and jobs= #{jobs}
</if>
</where>
- < trim >元素处理
< trim >的作用是去除特殊的字符串,它的prefix属性代表语句的前缀,prefixOverrides属性代表需要去除的哪些特殊字符串,功能和< where >基本是等效的。
select * from t_customer
<trim prefix="where" prefixOverrides="and">
<if test="username !=null and username !=''">
and username like concat('%',#{username}, '%')
</if>
<if test="jobs !=null and jobs !=''">
and jobs= #{jobs}
</if>
</trim>
8.5 < set >元素
在Hibernate中,想要更新某个对象,就需要发送所有的字段给持久化对象,这种想更新的每一条数据都要将其所有的属性都更新一遍的方法,其执行效率是非常差的。为此,在MyBatis中可以使用动态SQL中的< set >元素进行处理:
<update id="updateCustomer" parameterType="com.itheima.po.Customer">
update t_customer
<!--使用<set>和<if>元素对username和jobs进行更新判断,并动态组装SQL。这样就只需要传入想要更新的字段即可-->
<set>
<if test="username !=null and username !=''">
username=#{username},
</if>
<if test="jobs !=null and jobs !=''">
jobs=#{jobs},
</if>
</set>
where id=#{id}
</update>
8.6 < foreach >元素
在一个客户表中有1000条数据,现在需要将id值小于100的客户信息全部查询出来,这要怎么做呢?
<select id="findCustomerByIds" parameterType="List"
resultType="com.itheima.po.Customer">
select * from t_customer where id in
<foreach item="id" index="index" collection="list"
open="(" separator="," close=")">
#{id}
</foreach>
</select>
若想调用上面的sql需传入一个list集合。供foreach遍历。
关于上述示例中< foreach >元素中使用的几种属性的描述具体如下:
- item:配置的是循环中当前的元素。
- index:配置的是当前元素在集合的位置下标。
- collection:配置的list是传递过来的参数类型(首字母小写),它可以是一个array、list(或collection)、Map集合的键、POJO- 包装类中数组或集合类型的属性名等。
- open和close:配置的是以什么符号将这些集合元素包装起来。
- separator:配置的是各个元素的间隔符。
对于collection
- 如果传入的是单参数且参数类型是一个数组或者List的时候,collection属性值分别为array和list(或collection)。
- 如果传入的参数是多个的时候,就需要把它们封装成一个Map了,当然单参数也可以封装成Map集合,这时候collection属性值就为Map的键。
- 如果传入的参数是POJO包装类的时候,collection属性值就为该包装类中需要进行遍历的数组或集合的属性名。
8.7 < bind >元素
模糊查询
select * from t_customer where username like '%${value}%'
存在的问题:
- 如果使用“${}”进行字符串拼接,则无法防止SQL注入问题;
- 如果改用concat函数进行拼接,则只针对MySQL数据库有效;
- 如果改用“||”进行字符串拼接,则只针对Oracle数据库有效。
< bind >元素来解决这一问题。可解决这一问题
<select id="findCustomerByName" parameterType="com.itheima.po.Customer"
resultType="com.itheima.po.Customer">
<bind name="pattern_username" value="'%'+_parameter.getUsername()+'%'" />
<!--_parameter.getUsername()表示传递进来的参数(也可以直接写成对应的参数变量名,如username)-->
select * from t_customer
where
username like #{pattern_username}
</select>
思考
- 请简述MyBatis框架动态SQL中的主要元素及其作用。
- 请简述MyBatis框架动态SQL中< foreach >元素collection属性的注意事项。
9 MyBatis的关联映射
目标:
- 一对一、一对多和多对多关联映射的使用(掌握)
- 关联关系中的嵌套查询和嵌套结果(熟悉)
- 数据表之间以及对象之间的三种关联关系(了解)
9.1 关联关系概述
为什么学习MyBatis关联关系?
实际的开发中,对数据库的操作常常会涉及到多张表,这在面向对象中就涉及到了对象与对象之间的关联关系。针对多表之间的操作,MyBatis提供了关联映射,通过关联映射就可以很好的处理对象与对象之间的关联关系。本章中,将对MyBatis的关联关系映射进行详细的讲解。
在关系型数据库中,多表之间存在着三种关联关系,分别为一对一、一对多和多对多。
- 一对一 在任意一方引入对方主键作为外键;
- 一对多 在“多”的一方,添加“一”的一方的主键作为外键;
- 多对多 产生中间关系表,引入两张表的主键作为外键,两个主键成为联合主键或使用新的字段作为主键。
在Java中,通过对象也可以进行关联关系描述
- 一对一 在本类中定义对方类型的对象,如A类中定义B类类型的属性b,B类中定义A类类型的属性a;
- 一对多 一个A类类型对应多个B类类型的情况,需要在A类中以集合的方式引入B类类型的对象,在B类中定义A类类型的属性a;
- 多对多 在A类中定义B类类型的集合,在B类中定义A类类型的集合。
9.2 一对一
在现实生活中,一对一关联关系是十分常见的。例如,一个人只能有一个身份证,同时一个身份证也只会对应一个人。
< resultMap >元素中,包含了一个< association >子元素,MyBatis就是通过该元素来处理一对一关联关系的。
在< association >元素中,通常可以配置以下属性:
MyBatis加载关联关系对象主要通过两种方式:嵌套查询和嵌套结果。
- 嵌套查询
嵌套查询是通过执行另外一条SQL映射语句来返回预期的复杂类型。
嵌套查询是在查询SQL中嵌入一个子查询SQL;
嵌套查询会执行多条SQL语句;
嵌套查询SQL语句编写较为简单; - 嵌套结果
嵌套结果是使用嵌套结果映射来处理重复的联合结果的子集。
嵌套结果是一个嵌套的多表查询SQL;
嵌套结果只会执行一条复杂的SQL语句;
嵌套结果SQL语句编写比较复杂;
嵌套查询存在的问题
虽然使用嵌套查询的方式比较简单,但是嵌套查询的方式要执行多条SQL语句,这对于大型数据集合和列表展示不是很好,因为这样可能会导致成百上千条关联的SQL语句被执行,从而极大的消耗数据库性能并且会降低查询效率。
解决
使用MyBatis的延迟加载在一定程度上可以降低运行消耗并提高查询效率。MyBatis默认没有开启延迟加载,需要在核心配置文件中的< settings >元素内进行配置,具体配置方式如下:
<settings>
<setting name="lazyLoadingEnabled" value="true" />
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
在映射文件中,< association >元素和< collection >元素中都已默认配置了延迟加载属性,即默认属性fetchType=“lazy”(属性fetchType="eager"表示立即加载),所以在配置文件中开启延迟加载后,无需在映射文件中再做配置。
9.3 一对多
开发人员接触更多的关联关系是一对多(或多对一)。例如,一个用户可以有多个订单,同时多个订单归一个用户所有。
< resultMap >元素中,包含了一个< collection >子元素,MyBatis就是通过该元素来处理一对多关联关系的。
< collection >子元素的属性大部分与< association >元素相同,但其还包含一个特殊属性–ofType 。
ofType属性与javaType属性对应,它用于指定实体对象中集合类属性所包含的元素类型。
9.4 多对多
在实际项目开发中,多对多的关联关系也是非常常见的。以订单和商品为例,一个订单可以包含多种商品,而一种商品又可以属于多个订单。
多对多的关联关系通常使用一个中间表来维护
在MyBatis中,多对多的关联关系查询,同样可以使用前面介绍的< collection >元素进行处理(其用法和一对多关联关系查询语句用法基本相同)。
思考
- 请简述不同对象之间的三种关联关系。
- 请简述MyBatis关联查询映射的两种处理方式
10 MyBatis与Spring的整合
目标:
- 传统DAO方式的开发整合(掌握)
Mapper接口方式的开发整合(掌握)
10.1 整合环境搭建
10.1.1 准备所需JAR包
- 导入Spring框架需要的jar包
- 导入Mybatis框架需要的jar包
- 导入Spring和Mybatis整合的中间jar
- 导入数据库驱动jar
- 导入数据源所需jar
10.1.2 编写配置文件
- 编写db.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=root
jdbc.maxTotal=30
jdbc.maxIdle=10
jdbc.initialSize=5
- 编写Spring配置文件applicationContext.xml
<beans xmlns="http://www.springframework.org/schema/beans"
...
<context:property-placeholder location="classpath:db.properties"/>###读取db.properties
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"> ###配置数据源
<property name="driverClassName" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<property name="maxTotal" value="${jdbc.maxTotal}" />
<property name="maxIdle" value="${jdbc.maxIdle}" />
<property name="initialSize" value="${jdbc.initialSize}" />
</bean>
###配置事务管理器
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
### 开启事务注解
<tx:annotation-driven transaction-manager="transactionManager"/>
### 配置MyBatis工厂
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
</beans>
- 编写MyBatis配置文件mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
…
<configuration>
<typeAliases>
<package name="com.itheima.po" />
</typeAliases>
<mappers>
...
</mappers>
</configuration>
- 引入log4j.properties
# Global logging configuration
log4j.rootLogger=ERROR, stdout
# MyBatis logging configuration...
log4j.logger.com.itheima=DEBUG
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
10.2 传统DAO方式的开发整合
采用传统DAO开发方式进行MyBatis与Spring框架的整合时,可以使用mybatis-spring包中所提供的SqlSessionTemplate类或SqlSessionDaoSupport类来实现。
- SqlSessionTemplate:
是mybatis-spring的核心类,它负责管理MyBatis的SqlSession,调用MyBatis的SQL方法。当调用SQL方法时,SqlSessionTemplate将会保证使用的SqlSession和当前Spring的事务是相关的。它还管理SqlSession的生命周期,包含必要的关闭、提交和回滚操作。 - SqlSessionDaoSupport:是一个抽象支持类,它继承了DaoSupport类,主要是作为DAO的基类来使用。可以通过SqlSessionDaoSupport类的getSqlSession()方法来获取所需的SqlSession。
需要实现抽象Daol类的实现类
10.3 Mapper接口方式的开发整合
在MyBatis+Spring的项目中,虽然使用传统的DAO开发方式可以实现所需功能,但是采用这种方式在实现类中会出现大量的重复代码,在方法中也需要指定映射文件中执行语句的id,并且不能保证编写时id的正确性(运行时才能知道)。
为此,我们可以使用MyBatis提供的另外一种编程方式,即使用Mapper接口编程。
MapperFactoryBean是MyBatis-Spring团队提供的一个用于根据Mapper接口生成Mapper对象的类,该类在Spring配置文件中使用时可以配置以下参数:
- mapperInterface:用于指定接口;
- SqlSessionFactory:用于指定SqlSessionFactory;
- SqlSessionTemplate:用于指定SqlSessionTemplate。如果与SqlSessionFactory同时设定,则只会启用SqlSessionTemplate。
application.xml
<!-- Mapper代理开发(基于MapperFactoryBean) -->
<bean id="customerMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="com.itheima.mapper.CustomerMapper" />
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
<!-- Mapper代理开发(基于MapperScannerConfigurer) -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.itheima.mapper" />
</bean>
mybatis.xml
<!-- Mapper接口开发方式 -->
<mapper resource="com/itheima/mapper/CustomerMapper.xml" />
10.3.1 基于MapperFactoryBean的整合
规范
- Mapper接口的名称和对应的Mapper.xml映射文件的名称必须一致。
- Mapper.xml文件中的namespace与Mapper接口的类路径相同。
- Mapper接口中的方法名和Mapper.xml中定义的每个执行语句的id相同。
- Mapper接口中方法的输入参数类型要和Mapper.xml中定义的每个sql的parameterType的类型相同。
- Mapper接口方法的输出参数类型要和Mapper.xml中定义的每个sql的resultType的类型相同。
10.3.2 基于MapperScannerConfigurer的整合
MapperScannerConfigurer的作用是完成抽象Dao类的实现。
MapperScannerConfigurer类在Spring配置文件中可以配置以下属性:
- basePackage:指定映射接口文件所在的包路径,当需要扫描多个包时可以使用分号或逗号作为分隔符。指定包路径后,会扫描该包及其子包中的所有文件。
- annotationClass:指定了要扫描的注解名称,只有被注解标识的类才会被配置为映射器。
- sqlSessionFactoryBeanName:指定在Spring中定义的SqlSessionFactory的Bean名称。
- sqlSessionTemplateBeanName:指定在Spring中定义的SqlSessionTemplate的Bean名称。如果定义此属性,则sqlSessionFactoryBeanName将不起作用。
- markerInterface:指定创建映射器的接口。
applicationContext.xml
<!-- Mapper代理开发(基于MapperScannerConfigurer) -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.itheima.mapper" /> ###通过basePackage属性指定需要扫描的包即可
</bean>
10.4 测试事务
在项目中,Service层既是处理业务的地方,又是管理数据库事务的地方。要对事务进行测试,首先需要创建Service层,并在Service层编写添加客户操作的代码;然后在添加操作的代码后,有意的添加一段异常代码(如int i = 1/0;)来模拟现实中的意外情况;最后编写测试方法,调用业务层的添加方法。这样,程序在执行到错误代码时就会出现异常。
在没有事务管理的情况下,即使出现了异常,数据也会被存储到数据表中;
如果添加了事务管理,并且事务管理的配置正确,那么在执行上述操作时,所添加的数据将不能够插入到数据表中。
思考
- 请简述MyBatis与Spring整合所需JAR包的种类。
- 请简述MapperFactoryBean和MapperScannerConfigurer的作用。
11 Spring MVC入门
目标:
- Spirng MVC入门程序的编写(掌握)
- Spring MVC的工作流程(熟悉)
- Spring MVC的特点(了解)
1.1 Spring MVC概述
Spring MVC是Spring提供的一个实现了Web MVC设计模式的轻量级Web框架。它与Struts2框架一样,都属于MVC框架,但其使用和性能等方面比Struts2更加优异。
特点:
- 是Spring框架的一部分,可以方便的利用Spring所提供的其他功能。
- 灵活性强,易于与其他框架集成。
- 提供了一个前端控制器DispatcherServlet,使开发人员无需额外开发控制器对象。
- 可自动绑定用户输入,并能正确的转换数据类型。
- 内置了常见的校验器,可以校验用户输入。如果校验不能通过,那么就会重定向到输入表单。
- 支持国际化。可以根据用户区域显示多国语言。
- 支持多种视图技术。它支持JSP、Velocity和FreeMarker等视图技术。
- 使用基于XML的配置文件,在编辑后,不需要重新编译应用程序。
11.2 第一个Spring MVC应用
-
在web.xml中,配置Spring MVC的前端控制器DispatcherServlet。
-
在src目录下,创建一个com.itheima.controller包,并在包中创建控制器类FirstController,该类需要实现Controller接口,编辑后如下所示。
-
在src目录下,创建配置文件springmvc-config.xml,并在文件中配置控制器信息。
-
在WEB-INF目录下,创建一个jsp文件夹,并在文件夹中创建一个页面文件first.jsp,在该页面中使用EL表达式获取msg中的信息,如下所示。
11.3 Spring MVC的工作流程
①用户通过浏览器向服务器发送请求,请求会被Spring MVC的前端控制器DispatcherServlet所拦截;
②DispatcherServlet拦截到请求后,会调用HandlerMapping处理器映射器;
③处理器映射器根据请求URL找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回DispatcherServlet;
④DispatcherServlet会通过返回信息选择合适的HandlerAdapter(处理器适配器);
⑤HandlerAdapter会调用并执行Handler(处理器),这里的处理器指的就是程序中编写的Controller类,也被称之为后端控制器;
⑥Controller执行完成后,会返回一个ModelAndView对象,该对象中会包含视图名或包含模型和视图名;
⑦HandlerAdapter将ModelAndView对象返回给DispatcherServlet;
⑧DispatcherServlet会根据ModelAndView对象选择一个合适的ViewReslover(视图解析器);
⑨ViewReslover解析后,会向DispatcherServlet中返回具体的View(视图);
⑩DispatcherServlet对View进行渲染(即将模型数据填充至视图中);
⑪视图渲染结果会返回给客户端浏览器显示。
思考
- 请简述Spring MVC框架的优点。
- 请简述Spring MVC框架的工作执行流程。
12 Spring MVC的核心类和注解
目标:
- Spring MVC常用注解的使用(掌握)
- Spring MVC核心类的作用(了解)
12.1 DispatcherServlet
DispatcherServlet的全名是org.springframework.web.servlet.DispatcherServlet,它在程序中充当着前端控制器的角色。在使用时,只需将其配置在项目的web.xml文件中,其配置代码如下:
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<!--如果没有通过<init-param>元素配置,则应用程序会默认去WEB-INF目录下寻找以servletName-servlet.xml方式命名的配置文件,
这里servletName指下面的springmvc-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
12.2 @Controller注解类型
org.springframework.stereotype.Controller注解类型用于指示Spring类的实例是一个控制器,其注解形式为@Controller。该注解在使用时不需要再实现Controller接口,只需要将@Controller注解加入到控制器类上,然后通过Spring的扫描机制找到标注了该注解的控制器即可。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<context:component-scan base-package="com.itheima.controller" /> ###要扫描的包
</beans>
12.3 @RequestMapping注解类型
12.3.1 RequestMapping注解的使用
Spring通过**@Controller注解找到相应的控制器类后,还需要知道控制器内部对每一个请求是如何处理的,这就需要使用@RequestMapping注解类型**,它用于映射一个请求或一个方法。使用时,可以标注在一个方法或一个类上。
12.3.2 RequestMapping注解的属性
@RequestMapping注解除了可以指定value属性外,还可以指定其他一些属性,如下表所示。
表中所有属性都是可选的,但其默认属性是value。当value是其唯一属性时,
可以省略属性名。例如,下面两种标注的含义相同:
@RequestMapping(value="/firstController")
@RequestMapping("/firstController")
12.3.3 组合注解
- @GetMapping:匹配GET方式的请求;
以@GetMapping为例,该组合注解是@RequestMapping(method = RequestMethod.GET)的缩写,它会将HTTP GET请求映射到特定的处理方法上。
@GetMapping(value="/user/{id}")
public String selectUserById(String id){
...
}
- @PostMapping:匹配POST方式的请求;
- @PutMapping:匹配PUT方式的请求;
- @DeleteMapping:匹配DELETE方式的请求;
- @PatchMapping:匹配PATCH方式的请求。
12.3.4 请求处理方法的参数和返回类型
在控制器类中,每一个请求处理方法都可以有多个不同类型的参数,以及一个多种类型的返回结果。
Spring MVC所支持的常见方法返回类型如下:
由于ModelAndView类型****未能实现数据与视图之间的解耦,所以在企业开发时,方法的返回类型通常都会使用String。
通过Model参数类型,即可添加需要在视图中显示的属性,其示例代码如下:
@RequestMapping(value="/firstController")
public String handleRequest(HttpServletRequest request,
HttpServletResponse response, Model model) {
model.addAttribute("msg", "这是我的第一个Spring MVC程序");
return "/WEB-INF/jsp/first.jsp";
}
String类型除了可以返回上述代码中的视图页面外,还可以进行重定向与请求转发,具体方式如下:
- **redirect 重定向。**例如,在修改用户信息操作后,将请求重定向到用户查询方法的实现代码如下:
@RequestMapping(value="/update")
public String update(HttpServletRequest request,HttpServletResponse response, Model model){
...
return "redirect:queryUser";
}
- **forward 请求转发。**例如,用户执行修改操作时,转发到用户修改页面的实现代码如下:
@RequestMapping(value="/toEdit")
public String update(HttpServletRequest request,HttpServletResponse response, Model model){
...
return "forward:editUser";
}
12.4 ViewResolver
Spring MVC中的视图解析器负责解析视图。可以通过在配置文件中定义一个ViewResolver来配置视图解析器,其配置示例如下:
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" /> ##前缀
<property name="suffix" value=".jsp" /> ##后缀
</bean>
在上述代码中,定义了一个视图解析器,并设置了视图的前缀和后缀属性。这样设置后,方法中所定义的view路径将可以简化。例如,入门案例中的逻辑视图名只需设置为“first”,而不再需要设置为“/WEB-INF/jsp/first.jsp”,在访问时视图解析器会自动的增加前缀和后缀。
12.5 基于注解的Spring MVC应用
思考
- 请简述Controller注解的使用步骤。
- 请列举请求处理方法的参数类型和返回类型(至少5个)。
13 数据绑定
目标:
- Spring MVC数据绑定的使用(掌握)
- Spring MVC中的几种数据绑定类型(熟悉)
- Spring MVC中数据绑定的概念(了解)
13.1 数据绑定介绍
在执行程序时,Spring MVC会根据客户端请求参数的不同,将请求消息中的信息以一定的方式转换并绑定到控制器类的方法参数中。这种将请求消息数据与后台方法参数 建立连接的过程就是Spring MVC中的数据绑定。
Spring MVC是怎样完成的数据绑定?
在数据绑定过程中,Spring MVC框架会通过**数据绑定组件(DataBinder)**将请求参数串的内容进行类型转换,然后将转换后的值赋给控制器类中方法的形参,这样后台方法就可以正确绑定并获取客户端请求携带的参数了。接下来,将通过一张数据流程图来介绍数据绑定的过程。
①Spring MVC将ServletRequest对象传递给DataBinder;
②将处理方法的入参对象传递给DataBinder;
③DataBinder调用ConversionService组件进行数据类型转换、数据格式化等工作,并将ServletRequest对象中的消息填充到参数对象中;
④调用Validator组件对已经绑定了请求消息数据的参数对象进行数据合法性校验;
⑤校验完成后会生成数据绑定结果BindingResult对象,Spring MVC会将BindingResult对象中的内容赋给处理方法的相应参数。
13.2 简单数据绑定
根据客户端请求参数类型和个数的不同,我们将Spring MVC中的数据绑定主要分为简单数据绑定和复杂数据绑定,接下来的几个小节中,就对这两种类型数据绑定进行详细讲解。
当前端请求的参数比较简单时,可以在后台方法的形参中直接使用Spring MVC提供的默认参数类型进行数据绑定。
- HttpServletRequest:通过request对象获取请求信息;
- HttpServletResponse:通过response处理响应信息;
- HttpSession:通过session对象得到session中存放的对象;
- Model/ModelMap:Model是一个接口,ModelMap是一个接口实现,作用是将model数据填充到request域。
13.2.1 绑定默认数据类型
@Controller
public class UserController {
@RequestMapping("/selectUser")
public String selectUser(HttpServletRequest request) {
String id = request.getParameter("id");
System.out.println("id="+id);
return "success";
}
}
13.2.2 绑定简单数据类型
简单数据类型的绑定,就是指Java中几种基本数据类型的绑定,例如int、String、Double等类型。这里仍然以上一小节中的参数id为1的请求为例,来讲解简单数据类型的绑定。
@RequestMapping("/selectUser")
public String selectUser(Integer id) {
System.out.println("id="+id);
return "success";
}
这里需要注意的是,有时候前端请求中参数名和后台控制器类方法中的形参名不一样,这就会导致后台无法正确绑定并接收到前端请求的参数。
使用Spring MVC提供的@RequestParam注解类型来进行间接数据绑定。
@RequestParam注解的属性声明如下:
@RequestMapping("/selectUser")
public String selectUser(@RequestParam(value="user_id")Integer id) {
System.out.println("id="+id);
return "success";
}
13.2.3 绑定POJO类型
在使用简单数据类型绑定时,可以很容易的根据具体需求来定义方法中的形参类型和个数,然而在实际应用中,客户端请求可能会传递多个不同类型的参数数据,如果还使用简单数据类型进行绑定,那么就需要手动编写多个不同类型的参数,这种操作显然比较繁琐。
针对多类型、多参数的请求,可以使用POJO类型进行数据绑定。
POJO类型的数据绑定就是将所有关联的请求参数 封装在一个POJO中,然后在方法中直接使用该POJO作为形参来完成数据绑定。
在前端请求中,难免会有中文信息传递,此时后台方法绑定接收的中文信息却就会出现了中文乱码,为了防止前端传入的中文数据出现乱码问题,我们可以在web.xml中配置Spring提供的编码过滤器来统一编码。
<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>
</filter>
<filter-mapping>
<!--拦截所有URL请求,交由编码过滤器-->
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
13.2.4 绑定包装POJO
在用户查询订单时,页面传递的参数可能包括:订单编号、用户名称等信息,这就包含了订单和用户两个对象的信息,此时后台方法如何绑定请求信息呢?
所谓的包装POJO,就是在一个POJO****中包含另一个简单POJO。例如,在订单对象中包含用户对象。这样在使用时,就可以通过订单查询到用户信息。
public class Orders {
private Integer ordersId;
private User user;
//...省略getter/setter方法
}
前端传递参数
后端获取参数
@RequestMapping("/findOrdersWithUser")
public String findOrdersWithUser(Orders orders) {
Integer orderId = orders.getOrdersId();
User user = orders.getUser();
String username = user.getUsername();
System.out.println("orderId="+orderId);
System.out.println("username="+username);
return "success";
}
13.2.5 自定义数据绑定
针对特殊数据类型,就需要开发者自定义转换器(Converter) 或格式化(Formatter) 来进行数据绑定。
Spring框架提供了一个Converter用于将一种类型的对象转换为另一种类型的对象。
自定义Converter类需要实现 org.springframework.core.convert.converter.Converter接口。
Formatter与Converter的作用相同,只是Formatter的源类型必须是一个String类型,而Converter可以是任意类型。
使用Formatter自定义转换器类需要实现org.springframework.format.Formatter接口。
/**
* 自定义日期转换器
*/
public class DateConverter implements Converter<String, Date> {
// 定义日期格式
private String datePattern = "yyyy-MM-dd HH:mm:ss";
@Override
public Date convert(String source) {
// 格式化日期
SimpleDateFormat sdf = new SimpleDateFormat(datePattern);
try {
return sdf.parse(source);
} catch (ParseException e) {
throw new IllegalArgumentException(
"无效的日期格式,请使用这种格式:"+datePattern);
}
}
}
/**
* 使用Formatter自定义日期转换器
*/
public class DateFormatter implements Formatter<Date>{
// 定义日期格式
String datePattern = "yyyy-MM-dd HH:mm:ss";
// 声明SimpleDateFormat对象
private SimpleDateFormat simpleDateFormat;
@Override
public String print(Date date, Locale locale) {
return new SimpleDateFormat().format(date);
}
@Override
public Date parse(String source, Locale locale) throws ParseException
{
simpleDateFormat = new SimpleDateFormat(datePattern);
return simpleDateFormat.parse(source);
}
}
application.xml
<!-- 显示的装配自定义类型转换器 -->
<mvc:annotation-driven conversion-service="conversionService" />
<!-- 自定义类型转换器配置 -->
<!-- <bean id="conversionService" class=
"org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.itheima.convert.DateConverter" />
</set>
</property>
</bean> -->
<!-- 自定义类型格式化转换器配置 -->
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="formatters">
<set>
<bean class="com.itheima.convert.DateFormatter" />
</set>
</property>
</bean>
13.3 复杂数据绑定
可能遇到一些比较复杂的数据绑定问题,比如数组的绑定、集合的绑定
在实际开发时,可能会遇到前端请求需要传递到后台一个或多个相同名称参数的情况(如批量删除),此种情况采用前面讲解的简单数据绑定的方式显然是不合适的。
针对上述这种情况,如果将所有同种类型的请求参数封装到一个数组中,后台就可以进行绑定接收了。
13.3.1 绑定数组
前端
后端
在批量删除用户的操作中,前端请求传递的都是同名参数的用户id,只要在后台使用同一种数组类型的参数绑定接收,就可以在方法中通过循环数组参数的方式来完成删除操作。
13.3.2 绑定集合
如果是批量修改用户操作的话,前端请求传递过来的数据可能就会批量包含各种类型的数据,如Integer,String等。
针对上述这种情况,就可以使用集合数据绑定。即在包装类中定义一个包含用户信息类的集合,然后在接收方法中将参数类型定义为该包装类的集合。
包装类
前端
后端
思考
- 请简述简单数据类型中的@RequestParam注解及其属性作用。
- 请简述包装POJO类型绑定时的注意事项。
14 JSON数据交互和RESTful支持
目标:
- Spring MVC中JSON数据交互的使用和Spring MVC中RESTful风格请求的使用(掌握)
- RESTful风格的请求样式(熟悉)
- JSON的数据结构(了解)
14.1 JSON数据交互
14.1.1 JSON概述
JSON(JavaScript Object Notation,JS对象标记)是一种轻量级的数据交换格式。它是基于JavaScript的一个子集,使用了C、C++、C#、Java、JavaScript、Perl、Python等其他语言的约定,采用完全独立于编程语言的文本格式来存储和表示数据。
JSON有什么特点?
JSON与XML非常相似,都是用来存储数据的,并且都是基于纯文本的数据格式。
与XML相比,JSON解析速度更快,占用空间更小,且易于阅读和编写,同时也易于机器解析和生成。
JSON有如下两种数据结构
- 对象结构
在对象结构以“{”开始,以“}”结束。中间部分由0个或多个以英文“,”分隔的name:value对构成(注意name和value之间以英文“:”分隔),其存储形式如下图所示。
{"city":"Beijing","street":"Xisanqi","postcode":100096}
- 数组结构
数组结构以“[”开始,以“]”结束。中间部分由0个或多个以英文“,”分隔的值的列表组成,其存储形式如下图所示。
["abc",12345,false,null]
14.1.2 JSON数据转换
Spring提供了一个HttpMessageConverter< T >接口来实现浏览器与控制器类(Controller)之间的数据交互。该接口主要用于将请求信息中的数据转换为一个类型为T的对象,并将类型为T的对象绑定到请求方法的参数中,或者将对象转换为响应信息传递给浏览器显示。
HttpMessageConverter< T >接口有很多实现类,这些实现类可以对不同类型的数据进行信息转换。其中MappingJackson2HttpMessageConverter是Spring MVC默认处理JSON格式请求响应的实现类。该实现类利用Jackson开源包读写JSON数据,将Java对象转换为JSON对象和 XML文档,同时也可以将JSON对象和XML文档转换为Java对象。
使用注解
在使用注解式开发时,需要用到2个重要的JSON格式转换注解,分别为@RequestBody和@ResponseBody,关于这两个注解的说明如下表所示:
Json注解驱动配置
在配置JSON转换器时,除了常用的< mvc:annotation-drivern />方式配置外,还可以使用< bean >标签的方式进行显示的配置。具体配置方式如下:
<!-- <bean>标签配置注解方式的处理器映射器和处理器适配器必须配对使用 -->
<!-- 使用<bean>标签配置注解方式的处理器映射器 -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping" />
<!--配置处理器适配器-->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<!--在注解适配器中配置JSON转换器-->
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" />
</list>
</property>
</bean>
小提示:使用<bean>标签配置注解方式的处理器映射器和处理器适配器必须配对使用。
配置静态资源的访问方式
- 在springmvc-config.xml文件中,使用< mvc:default-servlet-handler >标签
<mvc:default-servlet-handler />
- 激活Tomcat默认的Servlet来处理静态文件访问
<!--激活tomcat的静态资源拦截,需要哪些静态文件,再往下追加-->
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.js</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.css</url-pattern>
</servlet-mapping>
...
以上两种方式本质上是一样的,都是使用Web服务器默认的Servlet来 处理静态资源文件的访问。其中Servelt名称也是由使用的服务器来确定的,不同的服务器需要使用不同的名称,常用服务器及其Servlet名称如下:
常用服务器及其Servlet名称如下:
14.2 RESTful支持
RESTful也称之为REST,是英文“Representational State Transfer”的简称。可以将他理解为一种软件架构风格或设计风格,而不是一个标准。
思考
- 请简述JSON数据交互两个注解的作用。
- 请简述静态资源访问的几种配置方式。
15 拦截器
目标:
- 拦截器的使用(掌握)
- 拦截器的执行流程(熟悉)
- 拦截器的定义和配置方式(了解)
15.1 拦截器概述
Spring MVC中的拦截器(Interceptor)类似于Servlet中的过滤器(Filter),它主要用于拦截用户请求并作相应的处理。例如通过拦截器可以进行权限验证、记录请求信息的日志、判断用户 是否登录等。
15.1.1 拦截器的定义
- 通过实现HandlerInterceptor接口,或继承HandlerInterceptor接口的实现类(如HandlerInterceptorAdapter)来定义。
- 通过实现WebRequestInterceptor接口,或继承WebRequestInterceptor接口的实现类来定义。
以实现HandlerInterceptor接口方式为例,自定义拦截器类的代码如下:
public class CustomInterceptor implements HandlerInterceptor{
//该方法会在控制器方法前执行,其返回值表示是否中断后续操作。当其返回值为true时,表示继续向下执行;
//当其返回值为false时,会中断后续的所有操作。
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler)throws Exception {
return false;
}
//该方法会在控制器方法调用之后,且解析视图之前执行。可以通过此方法对请求域中的模型和视图做出进一步的修改。
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
}
//该方法会在整个请求完成,即视图渲染结束之后执行。可以通过此方法实现一些资源清理、记录日志信息等工作。
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler,
Exception ex) throws Exception {
}
}
15.1.2 拦截器的配置
要使自定义的拦截器类生效,还需要在Spring MVC的配置文件中进行配置。
<mvc:interceptors>
<!--全局拦截器,拦截所有请求-->
<bean class="com.itheima.interceptor.CustomInterceptor"/>
<mvc:interceptor>
<mvc:mapping path="/**"/> <!--/**配置,表示拦截所有路径-->
<mvc:exclude-mapping path=""/><!--配置不需要拦截的路径-->
<bean class="com.itheima.interceptor.Interceptor1" />
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/hello"/><!--/hello表示拦截所有以“/hello”结尾的路径-->
<bean class="com.itheima.interceptor.Interceptor2" />
</mvc:interceptor>
...
</mvc:interceptors>
注意:< mvc:interceptor >中的子元素必须按照上述代码的配置顺序进行编写,否则文件会报错。
15.2 拦截器的执行流程
15.2.1 单个拦截器的执行流程
15.2.2 多个拦截器的执行流程
思考
- 请简述Spring MVC拦截器的定义方式。
- 请简述单个拦截器和多个拦截器的执行流程。
16 文件上传和下载
目标:
- 文件下载程序的编写和文件上传案例的编写(掌握)
- Spring MVC中文件上传的实现步骤(了解)
16.1 文件上传
16.1.1 文件上传概述
多数文件上传都是通过表单形式提交给后台服务器的,因此,要实现文件上传功能,就需要提供一个文件上传的表单,而该表单必须满足以下3个条件:
- form表单的method属性设置为post;
- form表单的enctype属性设置为multipart/form-data;
- 提供< input type=“file” name=“filename” />的文件上传输入框。
1.表单配置
<form action="uploadUrl" method="post" enctype="multipart/form-data">
<!--multiple属性是HTML5中新属性,可实现多文件上传-->
<input type="file" name="filename" multiple="multiple" />
<input type="submit" value="文件上传" />
</form>
当form表单的enctype属性为multipart/form-data时,浏览器就会采用二进制流来处理表单数据,服务器端就会对文件上传的请求进行解析处理。Spring MVC通过MultipartResolver实现文件上传功能。MultipartResolver是一个接口对象,需要通过它的实现类CommonsMultipartResolver来完成文件上传工作。
2.MultipartResolver配置示例如下
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!--设置请求编码格式,必须与JSP中的pageEncoding属性一致,默认为ISO-8859-1-->
<property name="defaultEncoding" value="UTF-8" />
<!--设置允许上传文件的最大值(2M),单位为字节-->
<property name="maxUploadSize" value="2097152" />
...
</bean>
在前面的配置代码中,除配置了CommonsMultipartResolver类外,还通过< property >元素配置了编码格式以及允许上传文件的大小。通过< property >元素可以对文件解析器类CommonsMultipartResolver的如下属性进行配置:
- maxUploadSize:上传文件最大长度(以字节为单位);
- maxInMemorySize:缓存中的最大尺寸;
- defaultEncoding:默认编码格式;
- resolveLazily:推迟文件解析,以便在Controller中捕获文件大小异常。
注意:因为MultipartResolver接口的实现类CommonsMultipartResolver内部是引用multipartResolver字符串获取该实现类对象并完成文件解析的,所以在配置CommonsMultipartResolver时必须指定该Bean的id为multipartResolver。
3.当完成页面表单和文件上传解析器的配置后,在Controller中编写文件上传的方法即可实现文件上传,其代码如下所示:
@Controller
public class FileUploadController {
@RequestMapping("/fileUpload ")
//使用MultipartFile 绑定接收上传文件
public String handleFormUpload(@RequestParam("name") String name,
@RequestParam("filename") MultipartFile file,...) {
//判断上传文件是否为空,然后进行解析存放处理
if (!file.isEmpty()) {
...
return "uploadSuccess";
}
return "uploadFailure";
}
}
org.springframework.web.multipart.MultipartFile接口中提供了获取上传文件、文件名称等方法,如下表所示:
16.2 文件下载
16.2.1 实现文件下载
文件下载就是将文件服务器中的文件下载到本机上。在Spring MVC环境中,实现文件下载大致可分为如下两个步骤:
- 在客户端页面使用一个文件下载的超链接,该链接的href属性要指定后台文件下载的方法以及文件名(需要先在文件下载目录中添加了一个名称为“1.jpg”的文件)。
<a href="${pageContext.request.contextPath }/download?filename=1.jpg">
文件下载
</a>
- 在后台使用Spring MVC提供的ResponseEntity类型对象完成文件下载,使用它可以很方便的定义返回的HttpHeaders对象和HttpStatus对象,通过对这两个对象的设置,即可完成下载文件时所需的配置信息。
@RequestMapping("/download")
public ResponseEntity<byte[]> fileDownload(HttpServletRequest request, String filename) throws Exception{
//指定要下载的文件所在路径
String path = request.getServletContext().getRealPath("/upload/");
File file = new File(path+File.separator+filename);
HttpHeaders headers = new HttpHeaders();
//通知浏览器以下载的方式打开文件
headers.setContentDispositionFormData("attachment", filename);
//定义以流的形式下载返回文件数据
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
//使用Sring MVC框架的ResponseEntity对象封装返回下载数据
return new ResponseEntity<byte[]>(FileUtils.readFileToByteArray(file),headers,HttpStatus.OK);
}
文件下载中的ResponseEntity对象有些类似前面章节中的@ResponseBody注解,它用于直接返回结果对象。
响应头信息中的MediaType代表的是Interner Media Type(即互联网媒体类型),也叫做MIME类型,MediaType.APPLICATION_OCTET_STREAM的值为application/octet-stream,即表示以二进制流的形式下载数据;
HttpStatus类型代表的是Http协议中的状态,示例中的HttpStatus.OK表示200,即服务器已成功处理了请求。
效果:
16.2.2 中文名称的文件下载
为了解决浏览器中文件下载时中文名称的乱码问题,可以在前端页面发送请求前先对中文名进行统一编码,然后在后台控制器类中对文件名称进行相应的转码。
前端:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
##需要引入Encoder类
<%@page import="java.net.URLEncoder"%>
...
<body>
## 使用Encoder类的encoder()方法对中文名进行编码
<a href="${pageContext.request.contextPath }/download?filename=<%=URLEncoder.encode(“
壁纸.jpg", "UTF-8")%>">
中文名称文件下载 </a>
</body>
</html>
后端:
public String getFilename(HttpServletRequest request,String filename) throws Exception {
//IE不同版本User-Agent中出现的关键词
String[] IEBrowserKeyWords = {"MSIE", "Trident", "Edge"};
String userAgent = request.getHeader("User-Agent");
//对不同的浏览器进行不同编码格式的转码
for (String keyWord : IEBrowserKeyWords) {
if (userAgent.contains(keyWord)) {
return URLEncoder.encode(filename, "UTF-8");
}
}
return new String(filename.getBytes("UTF-8"), "ISO-8859-1");
}
思考
- 请简述上传表单需要满足的3个条件。
- 请简述如何解决中文文件名称下载时的乱码问题。
17 SSM框架整合
目标:
- SSM框架整合应用程序的编写(掌握)
- SSM框架整合时的配置文件内容(熟悉)
- SSM框架的整合思路(了解)
17.1 整合环境搭建
17.1.1 整合思路
如何进行SSM框架整合?
由于Spring MVC是Spring框架中的一个模块,所以Spring MVC与Spring之间不存在整合的问题,只要引入相应JAR包就可以直接使用。因此SSM框架的整合就只涉及到了Spring与MyBatis的整合,以及Spring MVC与MyBatis的整合。
SSM框架整合图如下所示:
如何判断整合成功
在第10章讲解Spring与MyBatis框架的整合时,我们是通过Spring实例化Bean,然后调用实例对象中的查询方法来执行MyBatis映射文件中的SQL语句的,如果能够正确查询出数据库中的数据,那么我们就认为Spring与MyBatis框架整合成功。同样,整合之后,如果我们可以通过前台页面来执行查询方法,并且查询出的数据能够在页面中正确显示,那么我们也可以认为三大框架整合成功。
db.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=root
jdbc.maxTotal=30
jdbc.maxIdle=10
jdbc.initialSize=5
applicationContext.xml
<context:property-placeholder location="classpath:db.properties"/>
<!--数据源配置-->
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource">
<property name="driverClassName" value="${jdbc.driver}" />
</bean>
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!--开启事务注解-->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!--配置MyBatis工厂-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:mybatis-config.xml" />
</bean>
<!--配置mapper文件扫描器-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.itheima.dao"/>
</bean>
<!--Service层注解扫描-->
<context:component-scan base-package="com.itheima.service" />
mybatis-config.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.itheima.po" />
</typeAliases>
</configuration>
springmvc-config.xml
<!--Controller层注解扫描-->
<context:component-scan base-package="com.itheima.controller" />
<!--加载注解驱动-->
<mvc:annotation-driven />
<!--配置视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
在web.xml中,配置Spring的文件监听器、编码过滤器以及Spring MVC的前端控制器等信息。
web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!--配置Spirng文件监听器-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<filter>
<!--配置编码过滤器-->
<filter-name>encoding</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>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>*.action</url-pattern>
</filter-mapping>
<servlet>
<!--配置Spirng MVC前端控制器-->
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param><param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-config.xml</param-value></init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>