目标:
Spring是分层的轻量级开源框架,以IOC(控制反转) 和 AOP(面向切面编程) 为内核,使用基本的javaBean来完成以前只能由EJB完成的工作。
在实际开发中,通常服务器端在采用三层体系架构,分别为表示层(Web)、业务逻辑层(Service)、持久层(Dao), Spring对每一层都提供了技术支持。
Spring拥有简单、可测试和松耦合等特点
Spring框架采用的是分层架构,它的一系列功能要素分为20个模块。
核心容器
Spring开发需要的jar包包括Spring框架包、和第三方依赖包。
Spring提供了两种核心容器BeanFactory和ApplicationContext。
创建beanFactory实例
BeanFactory beanFactory = new XmlBeanFactory(new FileSystemResource("F:/applicationContext.xml"));
//这种加载方式在实际开发中不常见,仅了解即可
不仅包含了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>contextConfigLocationparam-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);
依赖注入和控制反转的含义相同,只不过这两个称呼是从两个角度描述的同一个概念
控制反转
在使用Spring框架之后,对象的实例不再由调用者来创建,而是由Spring容器来创建,Spring容器会负责控制程序之间的关系,而不是由调用者的程序代码直接控制。
控制权由应用代码转移到了Spring容器,控制权发生了反转,这就是控制反转。
依赖注入
Spring容器负责将被依赖对象赋值给调用者的成员变量,这相当于为调用者注入了它依赖的实例,这就是Spring的依赖注入。
<bean id="userService" class="com.itheima.ioc.UserServiceImpl">
<property name="userDao" ref="userDao" />
bean>
思考
目标:
如果把Spring看做一个大型工厂,则Spring容器中的Bean就是该工厂的产品。要想使用这个工厂生产和管理Bean,就需要在配置文件中告诉它需要哪些Bean,以及需要使用何种方式将这些Bean装配到一起。
一般只配置id和class,如果不指定id或者name,默认使用class作为id名
<bean id="bean1" class="com.itheima.Bean1" />
有三种方式:构造器实例化(最常用)、 静态工厂方式实例化、实例工厂方式实例化
构造器实例化
指Spring容器通过Bean对应的类中默认的构造函数来实例化Bean。
静态工厂化
静态工厂是实例化Bean的另一种方式。该方式要求自己创建一个静态工厂的方法来创建Bean的实例。
实例工厂化
一共有7种作用域,其中singleton(Spring容器默认)和prototype最常用。
singleton作用域对于无会话状态的Bean(如Dao 组件、Service组件)来说,是最理想的选择。
配置:
<bean id="scope" class="com.itheima.scope.Scope" scope="singleton"/>
对需要保持会话状态的Bean(如Struts 2的Action类)应该使用prototype作用域。在使用prototype作用域时,Spring容器会为每个对该Bean的请求都创建一个新的实例。
了解的意义
了解Spring中Bean的生命周期的意义就在于,可以利用Bean在其存活期间的特定时刻完成一些相关操作。这种时刻可能有很多,但一般情况下,常会在Bean的postinitiation(初始化后)和predestruction(销毁前) 执行一些相关操作。
Spring容器可以管理生命周期的Bean
有3种,
<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>
注解
例子
配置文件中的配置。
<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:
所谓自动装配,就是将一个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" />
思考
目标:
全称:Aspect-Oriented Programming,面向切面编程。
----在传统的业务处理代码中,通常都会进行事务处理、日志记录等操作。虽然使用OOP可以通过组合或者继承的方式来达到代码的重用,但如果要实现某个功能(如日志记录),同样的代码仍然会分散到各个方法中。这样,如果想要关闭某个功能,或者对其进行修改,就必须要修改所有的相关方法。这不但增加了开发人员的工作量,而且提高了代码的出错率。
----为了解决这一问题,AOP思想随之产生。AOP采取横向抽取机制,将分散在各个方法中的重复代码提取出来,然后在程序编译或运行时,再将这些提取出来的代码应用到需要执行的地方。这种采用横向抽取机制的方式,采用传统的OOP思想显然是无法办到的,因为OOP只能实现父子关系的纵向的重用。虽然AOP是一种新的编程思想,但却不是OOP的替代品,它只是OOP的延伸和补充。
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。
略
Bean创建代理实例**。在Spring中,使用ProxyFactoryBean是创建AOP代理的基本方式。
ProxyFactoryBean类中的常用可配置属性如下:
AspectJ是一个基于Java语言的AOP框架。实现AOP有两种方式
通过XML文件来定义切面、切入点及通知,所有的切面、切入点和通知都必须定义在< aop:config >元素内。
配置切面使用的是< 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个子元素不支持使用子元素,但在使用时可以指定一些属性,其常用属性及其描述如下:
例子
@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);
}
}
思考
目标:
JDBC模块的作用
Spring的JDBC模块负责数据库资源管理和错误处理,大大简化了开发人员对数据库的操作,使得开发人员可以从繁琐的数据库操作中解脱出来,从而将更多的精力投入到编写业务逻辑当中。
JdbcTemplate类是Spring JDBC的核心类,继承结构如下:
JdbcTemplate类的直接父类是JdbcAccessor,该类为子类提供了一些访问数据库时使用的公共属性。
而JdbcOperations接口定义了在JdbcTemplate类中可以使用的操作集合,包括添加、修改、查询和删除等操作。
Spring JDBC模块主要由4个包组成,分别是core(核心包)、dataSource(数据源包)、object(对象包)和support(支持包)。
JdbcTemplate类 在core(核心包) 中。
配置文件
其中dataSource中的四个属性
在JdbcTemplate核心类中,提供了大量的更新和查询数据库的方法,我们就是使用的这些方法来操作数据库的。
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就是一个进行单元测试的开源框架。
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;
}
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提供了专门用于事务处理的API。Spring的事务管理简化了传统的事务管理流程,并且在一定程度上减少了开发者的工作量。
有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个方法,具体如下:
有两种方式
声明式事务管理最大的优点在于开发者无需通过编程的方式来管理事务,只需在配置文件中进行相关的事务规则声明,就可以将事务应用到业务逻辑中。这使得开发人员可以更加专注于核心业务逻辑代码的编写,在一定程度上减少了工作量,提高了开发效率,所以在实际开发中,通常都推荐使用声明式事务管理。
可以通过两种方式来实现,一种是基于XML的方式,另一种是基于Annotation的方式。
基于XML方式的声明式事务是在配置文件中通过<tx:advice>元素配置事务规则来实现的。当配置了事务的增强处理后,就可以通过编写的AOP配置,让Spring自动对目标生成代理。<tx:advice>元素及其子元素如下图所示:
配置< tx:advice >元素的重点是配置< tx:method >子元素,上图中使用灰色标注的几个属性是< tx:method >元素中的常用属性。其属性描述具体如下:
配置文件:
<property name="dataSource" ref="dataSource" />
bean>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*" propagation="REQUIRED"
isolation="DEFAULT" read-only="false" />
tx:attributes>
tx:advice>
<aop:config>
<aop:pointcut expression="execution(* com.itheima.jdbc.*.*(..))" id="txPointCut" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut" />
aop:config>
使用**@Transactional注解**时,可以通过参数配置事务详情:
配置文件:
<property name="dataSource" ref="dataSource" />
bean>
<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);
}
思考
目标:
MyBatis(前身是iBatis)是一个支持普通SQL查询、存储过程以及高级映射的持久层框架。
MyBatis框架也被称之为ORM(Object/Relation Mapping,即对象关系映射)框架。所谓的ORM就是一种为了解决面向对象与关系型数据库中数据类型不匹配的技术,它通过描述Java对象与数据库表之间的映射关系,自动将Java应用程序中的对象持久化到关系型数据库的表中。
Hibernate与MyBatis有什么区别?
Hibernate是一个全表映射的框架。
MyBatis是一个半自动映射的框架。
略
Mapper.xml
<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
<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对象的各个方法。
思考
目标:
SqlSessionFactory是MyBatis框架中十分重要的对象,它是单个数据库映射关系经过编译后的内存镜像,其主要作用是创建SqlSession。
SqlSessionFactory对象的实例可以通过SqlSessionFactoryBuilder对象来构建,而SqlSessionFactoryBuilder则可以通过XML配置文件或一个预先定义好的Configuration实例构建出SqlSessionFactory的实例。
InputStream inputStream = Resources.getResourceAsStream("配置文件位置");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession是MyBatis框架中另一个重要的对象,它是应用程序与持久层之间执行交互操作的一个单线程对象,其主要作用是执行持久化操作。
每一个线程都应该有一个自己的SqlSession实例,并且该实例是不能被共享的。同时,SqlSession实例也是线程不安全的,因此其使用范围最好在一次请求或一个方法中,绝不能将其放在一个类的静态字段、实例字段或任何类型的管理范围(如Servlet的HttpSession)中使用。
使用完SqlSession对象后要及时关闭,通常可以将其放在finally块中关闭。
除了增删改查,其他方法:
void commit(); 提交事务的方法。
void rollback(); 回滚事务的方法。
void close(); 关闭SqlSession对象。
T getMapper(Class type); 返回Mapper接口的代理对象。
Connection getConnection(); 获取JDBC数据库连接对象的方法。
< 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}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
dataSource>
< 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>
< typeAliases >元素用于为配置文件中的Java类型设置一个简短的名字,即设置别名。别名的设置与XML配置相关,其使用的意义在于减少全限定类名的冗余。 (以后用到全限定类名称时,可以其简写)
<typeAliases>
<typeAlias alias="user" type="com.itheima.po.User"/>
typeAliases>
当pojo类过多时,使用下面设置,别名为其注解的值
<typeAliases>
<package name="com.itheima.po"/>
typeAliases>
typeHandler的作用就是将预处理语句中传入的参数从javaType(Java类型)转换为jdbcType(JDBC类型),或者从数据库取出结果时将jdbcType转换为javaType。
< typeHandler >元素可以在配置文件中注册自定义的类型处理器,它的使用方式有两种。
<typeHandlers>
<typeHandler handler="com.itheima.type.CustomtypeHandler" />
typeHandlers>
<typeHandlers>
<package name="com.itheima.type" />
typeHandlers>
MyBatis中默认的ObjectFactory的作用是实例化目标类,它既可以通过默认构造方法实例化,也可以在参数映射存在的时候通过参数构造方法来实例化。通常使用默认的ObjectFactory即可。
< plugins >元素的作用就是配置用户所开发的插件。
< 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。关于这两个事务管理器的描述如下:
注意:
如果项目中使用的是Spring+ MyBatis,则没有必要在MyBatis中配置事务管理器,因为实际开发中,会使用Spring自带的管理器来实现事务管理。
< 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>
在映射文件中,< mapper >元素是映射文件的根元素,其他元素都是它的子元素。
<mapper namespace="org.example.dao.StudentDao">
<select id="selectStudent" resultType="org.example.doman.Student">
select * from student order by id;
select>
...
mapper>
< select >元素用来映射查询语句,它可以帮助我们从数据库中读取出数据,并组装数据给业务开发人员。
<select id="findCustomerById" parameterType="Integer"
resultType="com.itheima.po.Customer">
select * from t_customer where id = #{id}
select>
< 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>
常用属性如下:
<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>
< sql >元素的作用就是定义可重用的SQL代码片段,然后在文件中其他的sql语句中引用这一代码片段。
<sql id="customerColumns">id,username,jobs,phonesql>
<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 = ?;
< resultMap >元素表示结果映射集,是MyBatis中最重要也是最强大的元素。它的主要作用是定义映射规则、级联的更新以及定义类型转化器等。
<resultMap type="" id="">
<constructor>
<idArg/>
<arg/>
constructor>
<id/>
<result/>
<association property="" />
<collection property="" />
<discriminator javaType="">
<case value="" />
discriminator>
resultMap>
思考
目标:
作用:
开发人员在使用JDBC或其他类似的框架进行数据库开发时,通常都要根据需求去手动拼装SQL,这是一个非常麻烦且痛苦的工作,而MyBatis提供的对SQL语句动态组装的功能,恰能很好的解决这一麻烦工作。
元素如下:
在MyBatis中,< if >元素是最常用的判断语句,它类似于Java中的if语句,主要用于实现某些简单的条件选择。其基本使用示例如下:
select * from t_customer where 1=1
<if test="username !=null and username !=''">
and username like concat('%',#{username}, '%')
if>
<if test="jobs !=null and jobs !=''">
and jobs= #{jobs}
if>
当客户名称不为空,则只根据客户名称进行客户筛选;
当客户名称为空,而客户职业不为空,则只根据客户职业进行客户筛选。
当客户名称和客户职业都为空,则要求查询出所有电话不为空的客户信息。”
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>
动态SQL处理“where 1=1”
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>
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>
在Hibernate中,想要更新某个对象,就需要发送所有的字段给持久化对象,这种想更新的每一条数据都要将其所有的属性都更新一遍的方法,其执行效率是非常差的。为此,在MyBatis中可以使用动态SQL中的< set >元素进行处理:
<update id="updateCustomer" parameterType="com.itheima.po.Customer">
update t_customer
<set>
<if test="username !=null and username !=''">
username=#{username},
if>
<if test="jobs !=null and jobs !=''">
jobs=#{jobs},
if>
set>
where id=#{id}
update>
在一个客户表中有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 >元素中使用的几种属性的描述具体如下:
对于collection
模糊查询
select * from t_customer where username like '%${value}%'
存在的问题:
<select id="findCustomerByName" parameterType="com.itheima.po.Customer"
resultType="com.itheima.po.Customer">
<bind name="pattern_username" value="'%'+_parameter.getUsername()+'%'" />
select * from t_customer
where
username like #{pattern_username}
select>
思考
目标:
为什么学习MyBatis关联关系?
实际的开发中,对数据库的操作常常会涉及到多张表,这在面向对象中就涉及到了对象与对象之间的关联关系。针对多表之间的操作,MyBatis提供了关联映射,通过关联映射就可以很好的处理对象与对象之间的关联关系。本章中,将对MyBatis的关联关系映射进行详细的讲解。
在关系型数据库中,多表之间存在着三种关联关系,分别为一对一、一对多和多对多。
在Java中,通过对象也可以进行关联关系描述
在现实生活中,一对一关联关系是十分常见的。例如,一个人只能有一个身份证,同时一个身份证也只会对应一个人。
< resultMap >元素中,包含了一个< association >子元素,MyBatis就是通过该元素来处理一对一关联关系的。
在< association >元素中,通常可以配置以下属性:
MyBatis加载关联关系对象主要通过两种方式:嵌套查询和嵌套结果。
嵌套查询存在的问题
虽然使用嵌套查询的方式比较简单,但是嵌套查询的方式要执行多条SQL语句,这对于大型数据集合和列表展示不是很好,因为这样可能会导致成百上千条关联的SQL语句被执行,从而极大的消耗数据库性能并且会降低查询效率。
解决
使用MyBatis的延迟加载在一定程度上可以降低运行消耗并提高查询效率。MyBatis默认没有开启延迟加载,需要在核心配置文件中的< settings >元素内进行配置,具体配置方式如下:
<settings>
<setting name="lazyLoadingEnabled" value="true" />
<setting name="aggressiveLazyLoading" value="false"/>
settings>
在映射文件中,< association >元素和< collection >元素中都已默认配置了延迟加载属性,即默认属性fetchType=“lazy”(属性fetchType="eager"表示立即加载),所以在配置文件中开启延迟加载后,无需在映射文件中再做配置。
开发人员接触更多的关联关系是一对多(或多对一)。例如,一个用户可以有多个订单,同时多个订单归一个用户所有。
< resultMap >元素中,包含了一个< collection >子元素,MyBatis就是通过该元素来处理一对多关联关系的。
< collection >子元素的属性大部分与< association >元素相同,但其还包含一个特殊属性–ofType 。
ofType属性与javaType属性对应,它用于指定实体对象中集合类属性所包含的元素类型。
在实际项目开发中,多对多的关联关系也是非常常见的。以订单和商品为例,一个订单可以包含多种商品,而一种商品又可以属于多个订单。
多对多的关联关系通常使用一个中间表来维护
在MyBatis中,多对多的关联关系查询,同样可以使用前面介绍的< collection >元素进行处理(其用法和一对多关联关系查询语句用法基本相同)。
思考
目标:
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
<beans xmlns="http://www.springframework.org/schema/beans"
...
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>
…
<configuration>
<typeAliases>
<package name="com.itheima.po" />
typeAliases>
<mappers>
...
mappers>
configuration>
# 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
采用传统DAO开发方式进行MyBatis与Spring框架的整合时,可以使用mybatis-spring包中所提供的SqlSessionTemplate类或SqlSessionDaoSupport类来实现。
需要实现抽象Daol类的实现类
在MyBatis+Spring的项目中,虽然使用传统的DAO开发方式可以实现所需功能,但是采用这种方式在实现类中会出现大量的重复代码,在方法中也需要指定映射文件中执行语句的id,并且不能保证编写时id的正确性(运行时才能知道)。
为此,我们可以使用MyBatis提供的另外一种编程方式,即使用Mapper接口编程。
MapperFactoryBean是MyBatis-Spring团队提供的一个用于根据Mapper接口生成Mapper对象的类,该类在Spring配置文件中使用时可以配置以下参数:
application.xml
<bean id="customerMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="com.itheima.mapper.CustomerMapper" />
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.itheima.mapper" />
bean>
mybatis.xml
<mapper resource="com/itheima/mapper/CustomerMapper.xml" />
规范
MapperScannerConfigurer的作用是完成抽象Dao类的实现。
MapperScannerConfigurer类在Spring配置文件中可以配置以下属性:
applicationContext.xml
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.itheima.mapper" /> ###通过basePackage属性指定需要扫描的包即可
bean>
在项目中,Service层既是处理业务的地方,又是管理数据库事务的地方。要对事务进行测试,首先需要创建Service层,并在Service层编写添加客户操作的代码;然后在添加操作的代码后,有意的添加一段异常代码(如int i = 1/0;)来模拟现实中的意外情况;最后编写测试方法,调用业务层的添加方法。这样,程序在执行到错误代码时就会出现异常。
在没有事务管理的情况下,即使出现了异常,数据也会被存储到数据表中;
如果添加了事务管理,并且事务管理的配置正确,那么在执行上述操作时,所添加的数据将不能够插入到数据表中。
思考
目标:
Spring MVC是Spring提供的一个实现了Web MVC设计模式的轻量级Web框架。它与Struts2框架一样,都属于MVC框架,但其使用和性能等方面比Struts2更加优异。
特点:
在src目录下,创建一个com.itheima.controller包,并在包中创建控制器类FirstController,该类需要实现Controller接口,编辑后如下所示。
在WEB-INF目录下,创建一个jsp文件夹,并在文件夹中创建一个页面文件first.jsp,在该页面中使用EL表达式获取msg中的信息,如下所示。
①用户通过浏览器向服务器发送请求,请求会被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进行渲染(即将模型数据填充至视图中);
⑪视图渲染结果会返回给客户端浏览器显示。
思考
目标:
DispatcherServlet的全名是org.springframework.web.servlet.DispatcherServlet,它在程序中充当着前端控制器的角色。在使用时,只需将其配置在项目的web.xml文件中,其配置代码如下:
<servlet>
<servlet-name>springmvcservlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
servlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:springmvc-config.xmlparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>springmvcservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
org.springframework.stereotype.Controller注解类型用于指示Spring类的实例是一个控制器,其注解形式为@Controller。该注解在使用时不需要再实现Controller接口,只需要将@Controller注解加入到控制器类上,然后通过Spring的扫描机制找到标注了该注解的控制器即可。
<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>
Spring通过**@Controller注解找到相应的控制器类后,还需要知道控制器内部对每一个请求是如何处理的,这就需要使用@RequestMapping注解类型**,它用于映射一个请求或一个方法。使用时,可以标注在一个方法或一个类上。
@RequestMapping注解除了可以指定value属性外,还可以指定其他一些属性,如下表所示。
表中所有属性都是可选的,但其默认属性是value。当value是其唯一属性时,
可以省略属性名。例如,下面两种标注的含义相同:
@RequestMapping(value="/firstController")
@RequestMapping("/firstController")
@GetMapping(value="/user/{id}")
public String selectUserById(String id){
...
}
在控制器类中,每一个请求处理方法都可以有多个不同类型的参数,以及一个多种类型的返回结果。
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类型除了可以返回上述代码中的视图页面外,还可以进行重定向与请求转发,具体方式如下:
@RequestMapping(value="/update")
public String update(HttpServletRequest request,HttpServletResponse response, Model model){
...
return "redirect:queryUser";
}
@RequestMapping(value="/toEdit")
public String update(HttpServletRequest request,HttpServletResponse response, Model model){
...
return "forward:editUser";
}
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”,在访问时视图解析器会自动的增加前缀和后缀。
思考
目标:
在执行程序时,Spring MVC会根据客户端请求参数的不同,将请求消息中的信息以一定的方式转换并绑定到控制器类的方法参数中。这种将请求消息数据与后台方法参数 建立连接的过程就是Spring MVC中的数据绑定。
Spring MVC是怎样完成的数据绑定?
在数据绑定过程中,Spring MVC框架会通过**数据绑定组件(DataBinder)**将请求参数串的内容进行类型转换,然后将转换后的值赋给控制器类中方法的形参,这样后台方法就可以正确绑定并获取客户端请求携带的参数了。接下来,将通过一张数据流程图来介绍数据绑定的过程。
①Spring MVC将ServletRequest对象传递给DataBinder;
②将处理方法的入参对象传递给DataBinder;
③DataBinder调用ConversionService组件进行数据类型转换、数据格式化等工作,并将ServletRequest对象中的消息填充到参数对象中;
④调用Validator组件对已经绑定了请求消息数据的参数对象进行数据合法性校验;
⑤校验完成后会生成数据绑定结果BindingResult对象,Spring MVC会将BindingResult对象中的内容赋给处理方法的相应参数。
根据客户端请求参数类型和个数的不同,我们将Spring MVC中的数据绑定主要分为简单数据绑定和复杂数据绑定,接下来的几个小节中,就对这两种类型数据绑定进行详细讲解。
当前端请求的参数比较简单时,可以在后台方法的形参中直接使用Spring MVC提供的默认参数类型进行数据绑定。
@Controller
public class UserController {
@RequestMapping("/selectUser")
public String selectUser(HttpServletRequest request) {
String id = request.getParameter("id");
System.out.println("id="+id);
return "success";
}
}
简单数据类型的绑定,就是指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";
}
在使用简单数据类型绑定时,可以很容易的根据具体需求来定义方法中的形参类型和个数,然而在实际应用中,客户端请求可能会传递多个不同类型的参数数据,如果还使用简单数据类型进行绑定,那么就需要手动编写多个不同类型的参数,这种操作显然比较繁琐。
针对多类型、多参数的请求,可以使用POJO类型进行数据绑定。
POJO类型的数据绑定就是将所有关联的请求参数 封装在一个POJO中,然后在方法中直接使用该POJO作为形参来完成数据绑定。
在前端请求中,难免会有中文信息传递,此时后台方法绑定接收的中文信息却就会出现了中文乱码,为了防止前端传入的中文数据出现乱码问题,我们可以在web.xml中配置Spring提供的编码过滤器来统一编码。
<filter>
<filter-name>CharacterEncodingFilterfilter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
<init-param>
<param-name>encodingparam-name>
<param-value>UTF-8param-value>
init-param>
filter>
<filter-mapping>
<filter-name>CharacterEncodingFilterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
在用户查询订单时,页面传递的参数可能包括:订单编号、用户名称等信息,这就包含了订单和用户两个对象的信息,此时后台方法如何绑定请求信息呢?
所谓的包装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";
}
针对特殊数据类型,就需要开发者自定义转换器(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.format.support.FormattingConversionServiceFactoryBean">
<property name="formatters">
<set>
<bean class="com.itheima.convert.DateFormatter" />
set>
property>
bean>
可能遇到一些比较复杂的数据绑定问题,比如数组的绑定、集合的绑定
在实际开发时,可能会遇到前端请求需要传递到后台一个或多个相同名称参数的情况(如批量删除),此种情况采用前面讲解的简单数据绑定的方式显然是不合适的。
针对上述这种情况,如果将所有同种类型的请求参数封装到一个数组中,后台就可以进行绑定接收了。
前端
后端
在批量删除用户的操作中,前端请求传递的都是同名参数的用户id,只要在后台使用同一种数组类型的参数绑定接收,就可以在方法中通过循环数组参数的方式来完成删除操作。
如果是批量修改用户操作的话,前端请求传递过来的数据可能就会批量包含各种类型的数据,如Integer,String等。
针对上述这种情况,就可以使用集合数据绑定。即在包装类中定义一个包含用户信息类的集合,然后在接收方法中将参数类型定义为该包装类的集合。
包装类
前端
思考
目标:
JSON(JavaScript Object Notation,JS对象标记)是一种轻量级的数据交换格式。它是基于JavaScript的一个子集,使用了C、C++、C#、Java、JavaScript、Perl、Python等其他语言的约定,采用完全独立于编程语言的文本格式来存储和表示数据。
JSON有什么特点?
JSON与XML非常相似,都是用来存储数据的,并且都是基于纯文本的数据格式。
与XML相比,JSON解析速度更快,占用空间更小,且易于阅读和编写,同时也易于机器解析和生成。
JSON有如下两种数据结构
{"city":"Beijing","street":"Xisanqi","postcode":100096}
["abc",12345,false,null]
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 class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping" />
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" />
list>
property>
bean>
小提示:使用<bean>标签配置注解方式的处理器映射器和处理器适配器必须配对使用。
配置静态资源的访问方式
<mvc:default-servlet-handler />
<servlet-mapping>
<servlet-name>defaultservlet-name>
<url-pattern>*.jsurl-pattern>
servlet-mapping>
<servlet-mapping>
<servlet-name>defaultservlet-name>
<url-pattern>*.cssurl-pattern>
servlet-mapping>
...
以上两种方式本质上是一样的,都是使用Web服务器默认的Servlet来 处理静态资源文件的访问。其中Servelt名称也是由使用的服务器来确定的,不同的服务器需要使用不同的名称,常用服务器及其Servlet名称如下:
常用服务器及其Servlet名称如下:
RESTful也称之为REST,是英文“Representational State Transfer”的简称。可以将他理解为一种软件架构风格或设计风格,而不是一个标准。
思考
目标:
Spring MVC中的拦截器(Interceptor)类似于Servlet中的过滤器(Filter),它主要用于拦截用户请求并作相应的处理。例如通过拦截器可以进行权限验证、记录请求信息的日志、判断用户 是否登录等。
以实现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 {
}
}
要使自定义的拦截器类生效,还需要在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"/>
<bean class="com.itheima.interceptor.Interceptor2" />
mvc:interceptor>
...
mvc:interceptors>
注意:< mvc:interceptor >中的子元素必须按照上述代码的配置顺序进行编写,否则文件会报错。
思考
目标:
多数文件上传都是通过表单形式提交给后台服务器的,因此,要实现文件上传功能,就需要提供一个文件上传的表单,而该表单必须满足以下3个条件:
1.表单配置
<form action="uploadUrl" method="post" enctype="multipart/form-data">
<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">
<property name="defaultEncoding" value="UTF-8" />
<property name="maxUploadSize" value="2097152" />
...
bean>
在前面的配置代码中,除配置了CommonsMultipartResolver类外,还通过< property >元素配置了编码格式以及允许上传文件的大小。通过< property >元素可以对文件解析器类CommonsMultipartResolver的如下属性进行配置:
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接口中提供了获取上传文件、文件名称等方法,如下表所示:
文件下载就是将文件服务器中的文件下载到本机上。在Spring MVC环境中,实现文件下载大致可分为如下两个步骤:
<a href="${pageContext.request.contextPath }/download?filename=1.jpg">
文件下载
a>
@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,即服务器已成功处理了请求。
效果:
为了解决浏览器中文件下载时中文名称的乱码问题,可以在前端页面发送请求前先对中文名进行统一编码,然后在后台控制器类中对文件名称进行相应的转码。
前端:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
##需要引入Encoder类
<%@page import="java.net.URLEncoder"%>
...
<body>
## 使用Encoder类的encoder()方法对中文名进行编码
">
中文名称文件下载 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");
}
思考
目标:
如何进行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"/>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:mybatis-config.xml" />
bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.itheima.dao"/>
bean>
<context:component-scan base-package="com.itheima.service" />
mybatis-config.xml
<configuration>
<typeAliases>
<package name="com.itheima.po" />
typeAliases>
configuration>
springmvc-config.xml
<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>contextConfigLocationparam-name>
<param-value>classpath:applicationContext.xmlparam-value>
context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>
listener>
<filter>
<filter-name>encodingfilter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
<init-param>
<param-name>encodingparam-name>
<param-value>UTF-8param-value>
init-param>
filter>
<filter-mapping>
<filter-name>encodingfilter-name>
<url-pattern>*.actionurl-pattern>
filter-mapping>
<servlet>
<servlet-name>springmvcservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param><param-name>contextConfigLocationparam-name>
<param-value>classpath:springmvc-config.xmlparam-value>init-param>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>springmvcservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>