Maven项目整合MyBatis、Spring、Springmvc

一、MyBaits

1. MyBatis简介(了解)

1.1. 什么是MyBatis
MyBatis 本是apache的一个开源项目iBatis,2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。 MyBatis是一个优秀的持久层框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注SQL本身,而不需要花费精力去处理例如注册驱动、创建connection、创建statement、手动设置参数、结果集检索等jdbc繁杂的过程代码。
Mybatis通过xml或注解的方式将要执行的各种statement(statement、 preparedStatemnt)配置起来,并通过java对象和statement中的sql进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射成java对象并返回。
总之,Mybatis对JDBC访问数据库的过程进行了封装,简化了JDBC代码,解决JDBC将结果集封装为Java对象的麻烦。
下图是MyBatis架构图:
在这里插入图片描述
(1)mybatis-config.xml是Mybatis的核心配置文件,通过其中的配置可以生成SqlSessionFactory,也就是SqlSession工厂
(2)基于SqlSessionFactory可以生成SqlSession对象
(3)SqlSession是一个既可以发送SQL去执行,并返回结果,类似于JDBC中的Connection对象,也是Mybatis中至关重要的一个对象。
(4)Executor是SqlSession底层的对象,用于执行SQL语句
(5)MapperStatement对象也是SqlSession底层的对象,用于接收输入映射(SQL语句中的参数),以及做输出映射(即将SQL查询的结果映射成相应的结果)

1.2. 为什么要使用MyBatis
思考:在开始之前,思考下如何通过JDBC查询Emp表中的所有记录,并封装到 一个List集合中返回。(演示:准备数据、导包、导入JDBC程序)
1、使用传统方式JDBC访问数据库:
(1)使用JDBC访问数据库有大量重复代码(比如注册驱动、获取连接、获取传输器、释放资源等);
(2)JDBC自身没有连接池,会频繁的创建连接和关闭连接,效率低;
(3)SQL是写死在程序中,一旦修改SQL,需要对类重新编译;
(4)对查询SQL执行后返回的ResultSet对象,需要手动处理,有时会特别麻烦;
2、使用mybatis框架访问数据库:
(1)Mybatis对JDBC对了封装,可以简化JDBC代码;
(2)Mybatis自身支持连接池(也可以配置其他的连接池),因此可以提高程序的效率;
(3)Mybatis是将SQL配置在mapper文件中,修改SQL只是修改配置文件,类不需要重新编译。
(4)对查询SQL执行后返回的ResultSet对象,Mybatis会帮我们处理,转换成Java对象。

总之,JDBC中所有的问题(代码繁琐、有太多重复代码、需要操作太多对象、释放资源、对结果的处理太麻烦等),在Mybatis框架中几乎都得到了解决!!

2. MyBatis快速入门

2.1. 准备数据

-- 1、创建数据库 yonghedb 数据库 
create database if not exists yonghedb charset utf8; use yonghedb; -- 选择yonghedb数据库
-- 2、删除emp表(如果存在) 
drop table if exists emp;
-- 3、在 yonghedb 库中创建 emp 表 
create table emp( id int primary key auto_increment, name varchar(50), job varchar(50), salary double );
-- 4、往 emp 表中, 插入若干条记录
...

2.2. 创建工程,准备开发环境
创建工程,导入所需jar包、创建测试类
1、创建Maven的java工程
2、导入junit、mysql、mybaits、log4j等开发包

<dependencies>
 <!-- junit单元测试 -->
<dependency> 
<groupId>junit</groupId> <artifactId>junit</artifactId> 
<version>4.9</version>
 </dependency>
  <!-- mysql驱动 -->
<dependency>
 <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId> <version>5.1.32</version> 
  </dependency> 
  <!-- mybatis -->
<dependency> 
<groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.2.8</version> 
</dependency> 
<!-- 整合log4j -->
<dependency> 
<groupId>org.slf4j</groupId> 
<artifactId>slf4j-log4j12</artifactId> <version>1.6.4</version> 
</dependency> 
</dependencies>

3、创建com.tedu.mybatis.TestMybatis01测试类,并提供findAll方法(查询emp表中所有的员工信息),开发步骤如下:

/** 练习1(快速入门): 查询emp表中的所有员工, 返回一个List<Emp>集合 * @throws IOException */ 
@Test 
public void findAll() throws IOException { 
//1.读取mybatis的核心配置文件(mybatis-config.xml) 
//2.通过配置信息获取一个SqlSessionFactory工厂对象 
//3.通过工厂获取一个SqlSession对象 //4.通过namespace+id找到要执行的sql语句并执行sql语句 
//5.输出结果 }

2.3. 添加mybatis-config.xml文件
1、在src/main/resources目录下,创建mybatis-config.xml文件(MyBatis的核心配置文件)
在这里插入图片描述
2、mybatis-config.xml文件配置如下:
mybatis-config文件头信息如下:

<?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"> 
 <!-- MyBatis的全局配置文件 -->
<configuration > 

</configuration>

mybatis-config文件详细配置如下:

<?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"> 
 <!-- MyBatis的全局配置文件 -->
<configuration > 
<!-- 1.配置环境,可配置多个环境(比如:develop开发、test测试) -->
<environments default="develop"> 
<environment id="develop"> 
<!-- 1.1.配置事务管理方式:JDBC/MANAGED JDBC:将事务交给JDBC管理(推荐) MANAGED:自己管理事务
-->
<transactionManager type="JDBC"></transactionManager> <!-- 1.2.配置数据源,即连接池 JNDI/POOLED/UNPOOLED JNDI:已过时 POOLED:使用连接池(推荐) UNPOOLED:不使用连接池
-->
<dataSource type="POOLED"> 
<property name="driver" value="com.mysql.jdbc.Driver"/> 
<property name="url" value="jdbc:mysql://localhost:3306/yonghedb?characterEncoding=utf-8"/> 
<property name="username" value="root"/> 
<property name="password" value="root"/> </dataSource> 
</environment> 
</environments> 
<!-- 2.导入Mapper配置文件,如果mapper文件有多个,可以通过多个mapper标 签导入 -->
<mappers>
 <mapper resource="EmpMapper.xml"/> 
 </mappers>
</configuration>

2.4. 添加EmpMapper.xml文件
1、在src/main/resources目录下,创建EmpMapper.xml文件 (实体类的映射文件)
在这里插入图片描述
2、EmpMapper.xml文件配置如下:
EmpMapper文件头信息如下:

<?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值应该保证唯一 在程序中通过[ namespace + id ]定位到要执行哪一条SQL语句
-->
<mapper namespace=""> 
</mapper>

EmpMapper文件详细配置如下:

<?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值应该保证唯一 在程序中通过[ namespace + id ]定位到要执行哪一条SQL语句
-->
<mapper namespace="EmpMapper"> 
<!-- 通过select、insert、update、delete标签声明要执行的SQL -->
<!-- 练习1: 查询emp表中的所有员工信息 resultType指定查询的结果将会封装到什么类型中 即使最终返回的结果是集合(List<Emp>),resultType也只需要指定集合中的泛 型即可!
-->
<select id="findAll" resultType="com.tedu.pojo.Emp"> select * from emp 
</select>
</mapper>

2.5. 添加并编写Emp实体类
注意:在当前实例中,Emp类中的属性和数据库表的字段名称必须一致,否则将会无法将结果集封装到Java对象中。
在src/main/java目录下创建 com.xxx.pojo.Emp类,并编辑Emp类:提供私有属性以及对应的getter方法、setter方法,并重写toString方法

package com.tedu.pojo;
 /*** 实体类,用于封装Emp表中的一条用户信息 */ 
 public class Emp { 
 //1.声明实体类中的属性 
 private Integer id; 
 private String name; 
 private String job; 
 private Double salary; 
 //2.提供对应的getter和setter方法 
 public Integer getId() { 
 return id; 
 }
 public void setId(Integer id) { 
 this.id = id; 
 }
 public String getName() { 
 return name; 
 }
 public void setName(String name) {
  this.name = name; 
  }
  public String getJob() { 
  return job; 
  }
  public void setJob(String job) { 
  this.job = job; 
  }
  public Double getSalary() { 
  return salary; 
  }
  public void setSalary(Double salary) {
   this.salary = salary; 
   }
   //3.重写toString方法 
   @Override 
   public String toString() {
    return "Emp [id=" + id + ", name=" + name + ", job=" + job + ", salary=" + salary + "]"; 
    } 
    }

2.6. 实现测试类,并测试
1、实现findAll方法,代码如下:

/** 练习1(快速入门): 查询emp表中的所有员工, 返回一个List<Emp>集合 * @throws IOException */ 
@Test 
public void findAll() throws IOException { 
//1.读取mybatis的核心配置文件(mybatis-config.xml) 
InputStream in = Resources .getResourceAsStream("mybatis-config.xml");
 //2.通过配置信息获取一个SqlSessionFactory工厂对象 
 SqlSessionFactory fac = new SqlSessionFactoryBuilder().build( in ); 
 //3.通过工厂获取一个SqlSession对象 
 SqlSession session = fac.openSession(); 
 //4.通过namespace+id找到要执行的sql语句并执行sql语句 
 List<Emp> list = session .selectList("EmpMapper.findAll");
  //提交事务 
  session.commit();
 
 //5.输出结果
for(Emp e : list) { System.out.println( e );
 } 
 }

2、执行findAll方法,输出结果

2.7. 加入log4j日志框架

# Global logging configuration log4j.rootLogger=DEBUG, stdout 
# 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

mybatis默认使用log4j作为输出日志信息。
只要将该文件放在指定的位置,log4j工具会自动到指定位置加载上述文件,读取文件中的配置信息并使用!

3. MyBatis入门细节

3.1. 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"> 
 <!-- MyBatis的全局配置文件 -->
<configuration > 

</configuration>

mybatis-config文件详细配置如下:

<?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"> 
 <!-- MyBatis的全局配置文件 -->
<configuration > 
<!-- 1.配置环境,可配置多个环境(比如:develop开发、test测试) -->
<environments default="develop"> 
<environment id="develop"> 
<!-- 1.1.配置事务管理方式:JDBC/MANAGED JDBC:将事务交给JDBC管理(推荐) MANAGED:自己管理事务
-->
<transactionManager type="JDBC"></transactionManager> <!-- 1.2.配置数据源,即连接池 JNDI/POOLED/UNPOOLED JNDI:已过时 POOLED:使用连接池(推荐) UNPOOLED:不使用连接池
-->
<dataSource type="POOLED"> 
<property name="driver" value="com.mysql.jdbc.Driver"/> 
<property name="url" value="jdbc:mysql://localhost:3306/yonghedb?characterEncoding=utf-8"/> 
<property name="username" value="root"/> 
<property name="password" value="root"/> </dataSource> 
</environment> 
</environments> 
<!-- 2.导入Mapper配置文件,如果mapper文件有多个,可以通过多个mapper标 签导入 -->
<mappers>
 <mapper resource="EmpMapper.xml"/> 
 </mappers>
</configuration>

environments 标签:该标签内部可以配置多个environment,即多种环境,每种环境可以做不同配置或连接不同数据库。例如,开发、测试、生产环境可能需要不同的配置,连接的数据库可能也不相同,因此我们可以配置三个environment,分别对应上面三种不同的环境。
但是要记住,environment可以配置多个,但是最终要使用的只能是其中一个!
environment 标签:内部可以配置多种配置信息,下面介绍事务管理配置和数据源配置。
transactionManage 标签:事务管理配置,mybatis中有两种事务管理方式,也就是
type="[JDBC|MANAGED]。
**JDBC:这个配置就是直接使用了 JDBC的提交和回滚设置,它依赖于从数据源得到的连 接来管理事务范围。推荐使用。 **
MANAGED:这个配置几乎没做什么。它从来不提交或回滚一个连接。需要自己手动添加并 管理。不推荐使用
JNDI:已过时,不推荐使用!
POOLED:使用连接池,mybatis会创建连接池,并从连接池中获取连接访问数据库,在 操作完成后,将会把连接返回连池。
UNPOOLED:不使用连接池,该方式适用于只有小规模数量并发用户的简单应用程序上。

mappers 标签:用于导入mapper文件的位置,其中可以配置多个mapper,即可以导入多个mapper文件。

3.2. EmpMapper.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值应该保证唯一 在程序中通过[ namespace + id ]定位到要执行哪一条SQL语句
-->
<mapper namespace="EmpMapper"> 
<!-- 通过select、insert、update、delete标签声明要执行的SQL -->
<!-- 练习1: 查询emp表中的所有员工信息 resultType指定查询的结果将会封装到什么类型中 即使最终返回的结果是集合(List<Emp>),resultType也只需要指定集合中的泛 型即可!
-->
<select id="findAll" resultType="com.tedu.pojo.Emp"> select * from emp 
</select>
</mapper>

(1)第1行是xml的文档声明,用于声明xml的版本和编码
(2)第2、3、4行,引入了xml约束文档,当前xml文档将会按照mybatis-3- mapper.dtd文件所要求的规则进行书写。
(3)Mapper标签:根标签,其中namespace(名称空间,也叫命名空间),要求不能重复。在程序中通过【namespace + id 】定位到要执行哪一条SQL语句
(4)select标签:用于指定将来要执行的各种SQL语句。标签上可以声明属性,下面介绍常用的属性:id、resultType、resultMap
id属性:要求值不能重复。将来在执行SQL时,可以通过【namespace + id】找到指定SQL并执行。
resultType属性:从这条SQL语句中返回所期望类型的类的完全限定名称(包名+类名)。注意如果是集合情形,那应该是集合可以包含的类型,而不能是集合本身。
简而言之,resultType控制查询SQL执行后返回值的类型或集合中的泛型,例如查询emp表中的单条记录,返回值是一个Emp对象,因此,
resultType=“com.xxx.pojo.Emp”;
如果查询emp表中的多条记录,返回值是一个List,此时resultType的值应该集合中的泛型,因此resultType=“com.xxx.pojo.Emp”;
resultMap属性:复杂对象结构(例如多表关联查询等)。 使用 resultType 或 resultMap,但不能同时使用。

4. MyBatis增删改查

1、编辑EmpMapper.xml文件

<!-- 练习02: 新增员工信息: 赵云 保安 6000 -->
	<insert id="testInsert02" >
		insert into emp value( null, '赵云', '保安', 6000 )
	</insert>
	
	<!-- 练习03: 修改员工信息:赵云 保镖 20000 -->
	<update id="testUpdate03">
		update emp set job='保镖', salary=20000 where name='赵云'
	</update>
	
	<!-- 练习04: 删除name为'赵云'的记录 -->
	<delete id="testDelete04">
		delete from emp where name='赵云'
	</delete>

2、编写TestMybatis类

//1.读取mybatis的核心配置文件(mybatis-config.xml)
		InputStream in = Resources.getResourceAsStream( "mybatis-config.xml" );
		//2.根据配置信息获取SqlSession工厂对象
		SqlSessionFactory factory = new SqlSessionFactoryBuilder().build( in );
		//3.通过SqlSession工厂获取一个SqlSession对象(打开与数据库的连接)
		SqlSession session = factory.openSession( true );  //true表示自动提交事务
		//4.1.新增员工信息: 赵云 保安 6000
		//int rows = session.insert( "EmpMapper.testInsert02" );
		//4.2.修改员工信息:赵云 保镖 20000
		//int rows  = session.update( "EmpMapper.testUpdate03" );
		//4.3.删除name为'赵云'的记录
		int rows = session.delete( "EmpMapper.testDelete04" );
		
		//session.commit(); //提交事务

5. mybatis中的占位符

5.1. #{}占位符
在上面的增删改查操作中,SQL语句中的值是写死在SQL语句中的,而在实际开发中,此处的值往往是用户提交过来的值,因此这里我们需要将SQL中写死的值替换为占位符。
在mybatis中占位符有两个,分别是 #{} 占位符 和 KaTeX parse error: Expected 'EOF', got '#' at position 12: {} 占位符: **#̲{}: 相当于JDBC中的问号…{} :是为SQL片段(字符串)进行占位,将传过来的SQL片段直接拼接在${} 占位符所在的位置,不会进行任何的转义处理。(由于是直接将参数拼接在SQL语句中,因此可能会引发SQL注入攻击问题)
需要注意的是:使用 ${} 占位符为SQL语句中的片段占位时,即使只有一个占位符,需要传的也只有一个参数,也需要将参数先封装再传递!**

练习:
在mapper文件中编写SQL语句:

<!-- 练习05: 查询emp表中指定id的员工信息 -->
	<select id="findById05"  resultType="com.tedu.pojo.Emp">
		select * from emp where id=#{id}
	</select>
	
	<!--练习06:新增员工信息:张飞Java工程师 15000  -->
	<insert id="insert06">
	insert into emp value(null,#{name},#{job},#{salary})
	</insert>
	
	<!-- 练习07:修改员工信息:张飞架构师 25000 -->
	<update id="update07">
	update emp set job=#{job},salary=#{salary}
	where name=#{name}
	</update>
	
	<!--练习08:根据条件查询emp表中的员工信息  -->
	<select id="findAll08" resultType="com.xxx.pojo.Emp">
	select * from emp ${sqlStr}
	</select>
	
	<delete id="delete09">
	delete from emp where id=#{id}   <!--如果id=${id} 会出现注入攻击,删除全部  -->
	</delete>

Java代码实现:

SqlSession session;
	@Before
	public void init() throws IOException {
		//1.读取mybatis的核心配置文件(mybatis-config.xml)
		InputStream in = Resources.getResourceAsStream( "mybatis-config.xml" );
		//2.根据配置信息获取SqlSession工厂对象
		SqlSessionFactory factory = new SqlSessionFactoryBuilder().build( in );
		//3.通过SqlSession工厂获取一个SqlSession对象(打开与数据库的连接)
		session = factory.openSession( true );  //true
	}
	
	//=============== mybatis中的占位符 ===============
	
	/** 练习05: 查询emp表中指定id的员工信息 */
	@Test
	public void testFindById05() {
		Integer id = 8;
		Emp emp = session.selectOne( "EmpMapper.findById05", id );
		System.out.println( emp );
	}
	
	/* 练习06:新增员工信息:张飞Java工程师 15000
	 * 如果SQL语句有多个占位符,就需要传递多个参数,多参数如何传递
	 * 1》通过map集合进行封装(通用)
	 * 2》通过PoJo对象进行封装
	 *  */
	@Test
	public void testInsert06() {
		/*
		 * //声明一个Map集合,将SQL参数封装到Map集合中
		 *  Map<String,Object> map = new HashMap<>();
		 * map.put("name","张飞"); 
		 * map.put("job","Java开发工程师"); 
		 * map.put("salary",15000);
		 * session.insert("EmpMapper.insert06",map);
		 */
		
		//声明一个Emp对象,将参数封装到Emp对象中
		Emp emp =new Emp();
		emp.setName("关羽");
		emp.setJob("士兵");
		emp.setSalary(20000d);
		session.insert("EmpMapper.insert06",emp);
		
	}
	
	/* 练习07:修改员工信息:张飞架构师 25000 */
	@Test
	public void testUpdate07() {
		//声明一个map集合,将SQL参数封装到map集合中
		Map<String,Object> map=new HashMap<>();
		map.put("name","张飞");
		map.put("job","架构师");
		map.put("salary",25000d);
		session.update("EmpMapper.update07",map);
	}
	
	
	/*练习08:根据条件查询emp表中的员工信息*/
	@Test
	public void testFindAll08() {
		Map<String,Object> map=new HashMap<String,Object>();
//		map.put("sqlStr","where salary>3500");
		map.put("sqlStr","where name like '刘%'");
		
		List<Emp> list = session.selectList("EmpMapper.findAll08",map);
		for(Emp emp:list) {
			System.out.println(emp);
		}
	}
	
	@Test
	public void testDelete09() {
		Map map=new HashMap();
		map.put("id","1 or 1=1");
		session.delete("EmpMapper.delete09",map);
	}

在上面的增删改查练习中,当SQL语句中包含的参数值是传递过来的,在SQL语句中我们会通过 #{} 占位符进行占位,在SQL语句真正执行时,再将传递过来的值替换SQL语句中的占位符。
其实, #{} 就是JDBC中的问号(?)占位符,因此为了安全考虑,在执行时会对传递过来的字符串和日期类型高的值进行转译处理。
例如:查询指定name的员工信息,SQL语句为:
select * from emp where name=#{name}
其实就等价于JDBC中: select * from emp where name=? ,
如果传过来的参数值为:张三,那么最终执行的SQL语句为:
– 在参数替换占位符的同时进行了转义处理(在值的两边加上了单引号)
select * from emp where name=‘张三’

5.2. ${}占位符
那么如果我们在传递的时候不是一个参数值,而是一个SQL片段呢?
例如:在查询时,我们想动态的传递查询的列:select #{columns} from emp
此时传递过来的应该是一个SQL片段,不同于上面的参数值,如果此时还用 # {} ,也会像上面一样被转译处理: select ‘id,name,job’ from emp ,这不是我们希望看到的!
如果不想让传过来的SQL片段被转译处理,而是直接拼接在SQL语句中,那么这里可以使用 ${} ,例如:
select ${columns} from emp
拼接之后: select id,name,job from emp
练习:
在mapper文件中编写SQL语句:

<!-- 练习9: 动态指定要显示的列 -->
<select id="findAll2" resultType="com.tedu.pojo.Emp"> 
select ${cols} from emp
 </select>

java代码示例:

@Test
 public void testFindAll2() { 
 Map map = new HashMap(); 
 //map.put("cols", "id, name"); //map.put("cols", "id, name, salary");
  map.put("cols", "id,name,job,salary");
   //执行sql语句, 返回结果 List<Emp> list = 
   session.selectList("EmpMapper.findAll2", map); 
   //输出结果
for ( Emp e : list ) { System.out.println( e ); }

6. Mapper接口开发

6.1. Mapper接口开发介绍
在上面的Mybatis案例中, 通过SqlSession对象调用方法进行增删改查操作时,方法中需要传入的第一个参数是一个字符串值,该值对应的内容为: (Mapper文的)namespace + id, 通过这种方式,找到Mapper文件中映射的SQL语句并执行!!
这种方式由于传入的是字符串值, 很容易发生字符串拼写错误且编译时期不会提示。
这里我们将会讲解比上面更加简单的方式,也是我们企业开发中最常用的方式,即使用mapper接口开发。使用mapper接口开发需要注意以下几点:
1、创建一个接口,接口的全限定类名和mapper文件的namespace值要相同
2、mapper文件中每条要执行的SQL语句,在接口中要添加一个对应的方法,并且接口中 的方法名和SQL标签上的id值相同
3、Mapper接口中方法接收的参数类型,和mapper.xml中定义的sql的接收的参数类型 要相同
4、接口中方法的返回值类型和SQL标签上的resultType即返回值类型相同(如果方法返 回值是集合,resultType只需要指定集合中的泛型)

6.2. Mapper接口开发实现
下面将使用mapper接口开发的方式,实现根据id查询指定的员工信息
1、创建com.xxx.dao.EmpMapper接口
由于接口的全路径名(com.xxx.dao.EmpMapper)要和EmpMapper.xml的 namespace值保持一致,因此, 这里将namespace的值改为
com.xxx.dao.EmpMapper:

<?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">
	<!--第一步: 接口全类名等于namespace值 -->
<mapper namespace="com.xxx.dao.EmpMapper">
	
</mapper>

2、在接口中提供方法

//com.tedu.dao.EmpMapper  namespace等于全类名  
public interface EmpMapper {
//	第二步:SQL标签上的id值等于方法名
//	第三步:因为查询的不止一条记录,因此返回值是一个List集合,集合中是Emp类型
//	第四步:SQL语句中,没有占位符,所以方法上也不需要声明参数
	public List<Emp> findAll();
	
	//增删改返回int类型,可以不反回
	public int insert(Map map);
	
	public void update(Emp emp);
	
	public void delete(Integer id);
}

注意:方法的名字要和映射的sql标签的id值保持一致
方法的返回值类型和resultType的类型要一致

<?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">
	<!--第一步: 接口全类名等于namespace值 -->
<mapper namespace="com.xxx.dao.EmpMapper">
	<!-- 练习10: 查询emp表中的所有员工信息
		其中id属性: 定义一个独一无二的编号, 将来通过这个编号, 可以定位并执行SQL
		resultType属性: 指定使用哪个类型来封装SQL语句查询的结果
		只能用在select标签(查询SQL), 新增/修改/删除的SQL则不需要该属性。
		检查resultType的值: 按住ctrl键, 鼠标移到全路径上, 有下划线说明正确。
	 -->
	<select id="findAll"  resultType="com.tedu.pojo.Emp">
		select * from emp
	</select>
	
	<!-- 练习05: 查询emp表中指定id的员工信息 -->
	<select id="findById"  resultType="com.tedu.pojo.Emp">
		select * from emp where id=#{id}
	</select>
	
	<!--练习11:新增员工信息:赵云 保安 8000  -->
	<insert id="insert">
	insert into emp value(null,#{name1},#{job1},#{salary1})
	</insert>
	
	<!-- 练习12:修改员工信息:赵云 保镖 30000 -->
	<update id="update">
	update emp set job=#{job},salary=#{salary}
	where name=#{name}
	</update>
	
	<!--练习13:删除指定id的员工信息  -->
	<delete id="delete">
	delete from emp where id=#{id}
	</delete>
	
</mapper>

3、提供实现类并测试

SqlSession session;
	@Before
	public void init() throws IOException {
		//1.读取mybatis的核心配置文件(mybatis-config.xml)
		InputStream in = Resources.getResourceAsStream( "mybatis-config.xml" );
		//2.根据配置信息获取SqlSession工厂对象
		SqlSessionFactory factory = new SqlSessionFactoryBuilder().build( in );
		//3.通过SqlSession工厂获取一个SqlSession对象(打开与数据库的连接)
		session = factory.openSession( true );  //true
	}

	//=============== mybatis的接口开发 ===============

	/*练习10:查询所有的员工信息*/
	@Test
	public void testFindAll10() {
		/*1、获取EmpMapper接口的子类实例
		 * EmpMapper接口由我们负责提供,而接口的实现类(子类)由框架负责提供,并实现
		 * 接口中的所有方法,并根据实现类创建一个实例
		 */

		EmpMapper mapper=session.getMapper(EmpMapper.class);
		/*2、调用Mapper接口子类实例 上的findAll方法,查询所有员工信息
		 * findAll方法由框架实现。底层一定会根据namespace+id去定位SQL语句,并执行SQL语句
		 * 1)namespace=接口的全类名
		 * 2)SQL标签的id值=接口中的方法名
		 * 只要在findAll方法中获取到接口的全类名和当前方法名,就可以定位执行SQL
		 */
		List<Emp> list = mapper.findAll();
		for(Emp emp:list) {
			System.out.println(emp);
		}
	}

	/* 练习11:新增员工信息,赵云,保安,8000*/
	@Test
	public void testInsert11() {
		//获取EmpMapper接口实现类的实例
		EmpMapper mapper = session.getMapper(EmpMapper.class);
		//调用EmpMapper接口实现类实例的insert方法,新增员工信息
		Map<String,Object> map=new HashMap<>();
		map.put("name1","赵云");
		map.put("job1","保安");
		map.put("salary1",8000);
		//这里也可以使用pojo对象封装SQL中的参数值
		mapper.insert(map);
	}

	/*练习12:修改员工信息:赵云 保镖 30000*/
	@Test
	public void testUpdate() {
		//获取EmpMapper接口的实现类实例
		EmpMapper mapper = session.getMapper(EmpMapper.class);
		//调用EmpMapper接口实现类实例的insert方法,修改员工信息
		Emp emp =new Emp();
		emp.setName("赵云");
		emp.setJob("保镖");
		emp.setSalary(30000d);
		mapper.update(emp);
	}


	/*练习13:删除指定id的员工信息*/
	@Test
	public void testInsert13() {
		//获取EmpMapper接口的实现类的实例
		EmpMapper mapper = session.getMapper(EmpMapper.class);
		//调用mapper接口实现类实例的delete方法,根据id删除员工信息
		mapper.delete(1);
	}

Mybatis接口开发sql不用写在xml配置文件
1.mybatis-config.xml配置文件配置

<!-- 2.使用注解方式配置SQL语句,指定注解所在接口的 包路径 -->
	<mappers>
		<!-- <mapper resource="EmpMapper.xml"/> -->
		
		<!--SQL语句通过注解进行配置,注解在mapper接口中,mapper接口在dao包下  -->
		<package name="com.tedu.dao"/>
	</mappers>

2.mapper接口

//com.tedu.dao.EmpMapper  namespace等于全类名  
public interface EmpMapper {
//	第二步:SQL标签上的id值等于方法名
//	第三步:因为查询的不止一条记录,因此返回值是一个List集合,集合中是Emp类型
//	第四步:SQL语句中,没有占位符,所以方法上也不需要声明参数
	@Select("select * from emp")
	public List<Emp> findAll();
	
	
	//增删改返回int类型,可以不反回
	@Insert("insert into emp value(null,#{name1},#{job1},#{salary1})")
	public int insert(Map map);
	
	@Update("update emp set job=#{job},salary=#{salary}")
	public void update(Emp emp);
	
	@Delete("delete from emp where id=#{id}")
	public void delete(Integer id);
}

其余配置pojo(用于封装结果类)一样
3.测试结果

SqlSession session;
	@Before
	public void init() throws IOException {
		//1.读取mybatis的核心配置文件(mybatis-config.xml)
		InputStream in = Resources.getResourceAsStream( "mybatis-config.xml" );
		//2.根据配置信息获取SqlSession工厂对象
		SqlSessionFactory factory = new SqlSessionFactoryBuilder().build( in );
		//3.通过SqlSession工厂获取一个SqlSession对象(打开与数据库的连接)
		session = factory.openSession( true );  //true
	}

	//=============== mybatis的接口开发 ===============

	/*练习10:查询所有的员工信息*/
	@Test
	public void testFindAll10() {
		/*1、获取EmpMapper接口的子类实例
		 * EmpMapper接口由我们负责提供,而接口的实现类(子类)由框架负责提供,并实现
		 * 接口中的所有方法,并根据实现类创建一个实例
		 */

		EmpMapper mapper=session.getMapper(EmpMapper.class);
		/*2、调用Mapper接口子类实例 上的findAll方法,查询所有员工信息
		 * findAll方法由框架实现。底层一定会根据namespace+id去定位SQL语句,并执行SQL语句
		 * 1)namespace=接口的全类名
		 * 2)SQL标签的id值=接口中的方法名
		 * 只要在findAll方法中获取到接口的全类名和当前方法名,就可以定位执行SQL
		 */
		List<Emp> list = mapper.findAll();
		for(Emp emp:list) {
			System.out.println(emp);
		}
	}

	/* 练习11:新增员工信息,赵云22,保安,8000*/
	@Test
	public void testInsert11() {
		//获取EmpMapper接口实现类的实例
		EmpMapper mapper = session.getMapper(EmpMapper.class);
		//调用EmpMapper接口实现类实例的insert方法,新增员工信息
		Map<String,Object> map=new HashMap<>();
		map.put("name1","赵云22");
		map.put("job1","保安");
		map.put("salary1",8000);
		//这里也可以使用pojo对象封装SQL中的参数值
		mapper.insert(map);
	}

	/*练习12:修改员工信息:赵云22 保镖 30000*/
	@Test
	public void testUpdate() {
		//获取EmpMapper接口的实现类实例
		EmpMapper mapper = session.getMapper(EmpMapper.class);
		//调用EmpMapper接口实现类实例的insert方法,修改员工信息
		Emp emp =new Emp();
		emp.setName("赵云22");
		emp.setJob("保镖");
		emp.setSalary(30000d);
		mapper.update(emp);
	}


	/*练习13:删除指定id的员工信息*/
	@Test
	public void testInsert13() {
		//获取EmpMapper接口的实现类的实例
		EmpMapper mapper = session.getMapper(EmpMapper.class);
		//调用mapper接口实现类实例的delete方法,根据id删除员工信息
		mapper.delete(10);
	}

7. 扩展内容

7.1. Jdbc回顾
通过JDBC查询Emp表中的所有记录,并封装到一个List集合中返回
1、创建TestJdbc类,完成查询所有员工:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException; 
import java.sql.Statement; 
import java.util.ArrayList; 
import java.util.List; 
import com.tedu.pojo.Emp; 
/** Jdbc回顾 */ 
public class TestJdbc { 
public static void main(String[] args) { 
/* 查询emp表中的所有员工信息,将每个员工信息的封装到一个 * Emp对象中,再将封装了员工信息所有Emp对象存入List集合 * 中,并遍历输出所有的员工信息 */ List<Emp> empList = findAll();
for(Emp emp : empList){ 
System.out.println(emp); 
} }
/*** 查询emp表中的所有员工信息,封装到List集合并返回 */ private static List<Emp> findAll() { 
Connection conn = null; 
Statement stat = null; 
ResultSet rs = null;
 try { 
 //1.注册数据库驱动 
 Class.forName("com.mysql.jdbc.Driver"); 
 //2.获取数据库连接(Connection) 
 conn = DriverManager.getConnection( "jdbc:mysql:///yonghedb", "root", "root"); 
 //3.获取传输器 
 stat = conn.createStatement(); 
 //4.利用传输器发送sql到数据库执行,并返回执行结果 
 String sql = "select * from emp";
 rs = stat.executeQuery(sql); 
 //5.处理结果 
 //5.1.声明List集合,用于封装所有的员工信息 
 List<Emp> empList = new ArrayList(); 
 //5.2.遍历ResultSet结果集 
 while(rs.next()) { 
 //5.3.获取结果集中的每一条员工信息 
 int id = rs.getInt("id"); 
 String name = rs.getString("name"); 
 String job = rs.getString("job"); 
 double salary = rs.getDouble("salary"); 
 //5.4.将每一条员工信息封装到一个Emp对象中 
 Emp emp = new Emp(); emp.setId(id); emp.setName(name); emp.setJob(job); emp.setSalary(salary); 
 //5.5.将Emp对象存入List集合中 
 empList.add(emp); 
 }
 return empList; 
 } catch (Exception e) { 
 e.printStackTrace(); System.out.println("查询失败!"); 
 } finally{ 
//6.释放资源 
if(rs != null){ 
try { rs.close(); 
} catch (SQLException e) {
e.printStackTrace(); 
}finally{ rs = null; } }
if(stat != null){ 
try { stat.close(); 
} catch (SQLException e) {
 e.printStackTrace(); 
 }finally{ stat = null; } }
 if(conn != null){ 
 try { conn.close();
 } catch (SQLException e) { 
 e.printStackTrace(); 
 }finally{
  conn = null; } } 
  }return null; } }

2、声明Emp实体类,用于封装员工信息:

package com.tedu.pojo; /*** 实体类,用于封装Emp表中的一条用户信息 */ public class Emp {
 //1.声明实体类中的属性 
 private Integer id; 
 private String name; private String job; private Double salary; 
 //2.提供对应的getter和setter方法 
 public Integer getId() { return id; }
 public void setId(Integer id) { this.id = id; }
 public String getName() { return name; }public void setName(String name) { this.name = name; }
 public String getJob() { return job; }public void setJob(String job) { this.job = job; }
 public Double getSalary() { return salary; }
 public void setSalary(Double salary) {this.salary = salary; }
 //3.重写toString方法 
 @Override
  public String toString() { return "Emp [id=" + id + ", name=" + name + ", job=" + job + ", salary=" + salary + "]"; } }

7.2. mybatis-config没有提示的解决办法
如果在没有网络(外网)的情况下,编写mybatis-config.xml文件没有提示,可以按照下面的步骤进行配置:
1、创建mybatis-3-config.dtd文件,复制下面内容到文件

<?xml version="1.0" encoding="UTF-8" ?>
<!--

       Copyright 2009-2012 The MyBatis Team

       Licensed under the Apache License, Version 2.0 (the "License");
       you may not use this file except in compliance with the License.
       You may obtain a copy of the License at

          http://www.apache.org/licenses/LICENSE-2.0

       Unless required by applicable law or agreed to in writing, software
       distributed under the License is distributed on an "AS IS" BASIS,
       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
       See the License for the specific language governing permissions and
       limitations under the License.

-->

<!ELEMENT configuration (properties?, settings?, typeAliases?, typeHandlers?, objectFactory?, objectWrapperFactory?, plugins?, environments?, databaseIdProvider?, mappers?)>

<!ELEMENT databaseIdProvider (property*)>
<!ATTLIST databaseIdProvider
type CDATA #REQUIRED
>

<!ELEMENT properties (property*)>
<!ATTLIST properties
resource CDATA #IMPLIED
url CDATA #IMPLIED
>

<!ELEMENT property EMPTY>
<!ATTLIST property
name CDATA #REQUIRED
value CDATA #REQUIRED
>

<!ELEMENT settings (setting+)>

<!ELEMENT setting EMPTY>
<!ATTLIST setting
name CDATA #REQUIRED
value CDATA #REQUIRED
>

<!ELEMENT typeAliases (typeAlias*,package*)>

<!ELEMENT typeAlias EMPTY>
<!ATTLIST typeAlias
type CDATA #REQUIRED
alias CDATA #IMPLIED
>

<!ELEMENT typeHandlers (typeHandler*,package*)>

<!ELEMENT typeHandler EMPTY>
<!ATTLIST typeHandler
javaType CDATA #IMPLIED
jdbcType CDATA #IMPLIED
handler CDATA #REQUIRED
>

<!ELEMENT objectFactory (property*)>
<!ATTLIST objectFactory
type CDATA #REQUIRED
>

<!ELEMENT objectWrapperFactory (property*)>
<!ATTLIST objectWrapperFactory
type CDATA #REQUIRED
>

<!ELEMENT plugins (plugin+)>

<!ELEMENT plugin (property*)>
<!ATTLIST plugin
interceptor CDATA #REQUIRED
>

<!ELEMENT environments (environment+)>
<!ATTLIST environments
default CDATA #REQUIRED
>

<!ELEMENT environment (transactionManager,dataSource)>
<!ATTLIST environment
id CDATA #REQUIRED
>

<!ELEMENT transactionManager (property*)>
<!ATTLIST transactionManager
type CDATA #REQUIRED
>

<!ELEMENT dataSource (property*)>
<!ATTLIST dataSource
type CDATA #REQUIRED
>

<!ELEMENT mappers (mapper*,package*)>

<!ELEMENT mapper EMPTY>
<!ATTLIST mapper
resource CDATA #IMPLIED
url CDATA #IMPLIED
class CDATA #IMPLIED
>

<!ELEMENT package EMPTY>
<!ATTLIST package
name CDATA #REQUIRED
>

2、复制下面的url地址:
http://mybatis.org/dtd/mybatis-3-config.dtd
3、在eclipse菜单栏中: window --> Preferences --> 在搜索框中搜索 [ xml ] XML --> XML Catalog --> User Specified Entries --> Add…
在这里插入图片描述

4、在弹出的窗口中:
在这里插入图片描述
配置完成后,记得重新打开mybatis-config.xml文件!
7.3. Mapper文件没有提示的解决办法
如果在没有网络(外网)的情况下,编写XxxMapper.xml文件没有提示,可以按照下面的步骤进行配置:
1、创建mapper.dtd文件,复制下面内容到文件:
2、复制上面的url地址,即:
http://mybatis.org/dtd/mybatis-3-mapper.dtd
3、在eclipse菜单栏中: window --> Preferences --> 在搜索框中搜索 [ xml ] XML --> XML Catalog --> User Specified Entries --> Add…
在这里插入图片描述
4、在弹出的窗口中:
在这里插入图片描述

二、Spring框架

1. Spring简介

1.1. 什么是Spring?
Spring框架(简称Spring)是一个用于简化Java企业级应用开发的开源应用程序框架,以 IoC (Inverse Of Control: 控制反转/反转控制)和 AOP (Aspact Oriented Programming:面向切面编程)为核心,提供了表现层SpringMVC和持久层Spring JDBC以及业务层事务管理等众多模块的企业级应用技术,还能整合开源世界中众多
著名的第三方框架和类库,逐渐成为使用最多的JavaEE企业应用开源框架。
SSH(struts2 Spring hibernate) SSM(Springmvc Spring mybatis) Spring的本质是管理软件中的对象,即创建对象和维护对象之间的关系
1.2. Spring的发展历程
1997 年 IBM提出了EJB 的思想
1998 年,SUN制定开发标准规范 EJB1.0
1999 年,EJB1.1 发布
2001 年,EJB2.0 发布
2003 年,EJB2.1 发布
2006 年,EJB3.0 发布
Rod Johnson (罗德·约翰逊,Spring 之父)
Expert One-to-One J2EE Development without EJB(2004)
阐述了 J2EE 开发不使用 EJB的解决方式(Spring 雏形)
2017年9月份发布了Spring的最新版本Spring 5.0通用版
1.3. Spring的优势
1)方便解耦,简化开发
通过 Spring提供的 IoC容器,可以将对象间的依赖关系交由Spring进行控制,避免硬编码所造成的过度程序耦合。用户也不必再为较为底层的需求编写代码,可以更专注于上层的应用。
2)AOP 编程的支持
通过 Spring的 AOP 功能,方便进行面向切面的编程,许多不容易用传统OOP(Object Oriented Programming:面向对象编程) 实现的功能可以通过 AOP 轻松应付。
3)声明式事务的支持
可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活的进行事务的管理,提高开发效率和质量。
4)方便程序的测试
可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可做的事情。
5)方便集成各种优秀框架
Spring可以降低各种框架的使用难度,提供了对各种优秀框架(Struts、 Hibernate、Hessian、Quartz等)的直接支持。
6)降低 JavaEE API 的使用难度。
Spring对 JavaEE API(如 JDBC、JavaMail、远程调用等)进行了薄薄的封装层,使这些API 的使用难度大为降低。
。。。
1.4. Spring的架构
Spring 的目标就是要整合一切优秀资源,然后对外提供一个统一的服务。Spring模块构建在核心容器之上,核心容器定义了创建、配置和管理 bean 的方式,如下图所示:
在这里插入图片描述
Spring框架由多个模块组成,它们根据应用开发功能进行分组。下面列出了Spring框架中的各个模块组,并描述了其中一些重要模块组所提供的功能。
在这里插入图片描述
Spring模块之间的依赖关系如下图所示:
在这里插入图片描述

2. Spring IoC容器

2.1. 什么是控制反转
IoC(Inverse Of Control)控制反转:即,把创建对象的权利交给框架,也就是指将对象的创建、对象的初始化、对象的存储、对象的管理交给了Spring容器。
IoC 是一种通过描述来生成或者获取对象的技术,对于Java初学者 更多时候所熟悉的是使用 new 关键字来创建对象,而在spring中则不是,它是通过描述(XML或注
解)来创建对象。
在 Spring 中把每一个需要管理的对象称之为 Spring Bean(简称为Bean),而Spring中管理这些 Bean 的容器,被我们称之为 Spring IoC 容器(简称 IoC容器)
在这里插入图片描述
在此之前,当需要对象时,通常是利用new关键字创建一个对象:

/* 创建一个User对象——这里使用new对象的方式造成了程序之间的耦合性提升 */ 
User u = new User();

但由于new对象,会提高类和类之间的依赖关系,即代码之间的耦合性。
而现在我们可以将对象的创建交给框架来做:

// 获取Spring IoC容器对象 
ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml"); 
// 从spring的IoC容器中获取User对象 
private EmpService service = (EmpService)ctx.getBean("user");

将需要的POJO提前在配置文件(XML或注解)中进行描述,Spring会根据配置文件将其中所描述生成 IoC容器并装配Bean,在需要使用时,再次获取即可
这样一来,当需要对象时不用自己创建,而是通过框架获取即可,不用硬编码去new对象,自然就降低类和类之间的依赖关系,也就是耦合性。
2.2. Spring IoC入门
在Spring中允许我们通过XML或注解方式装配Bean,下面先来介绍Spring中通过XML方法如何装配Bean。
2.2.1. 创建工程,引入依赖
1、创建Maven的Java工程
2、引入junit、Spring的jar包:在maven工程的pom.xml文件的根标签(project)内添加如下配置:

<dependencies>
		<!-- 添加junit依赖 -->
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.10</version>
		</dependency>


		<!-- 添加spring的依赖 -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>4.3.12.RELEASE</version>
		</dependency>
	</dependencies>

导入后保存pom文件,项目如图所示:
在这里插入图片描述

2.2.2. 创建Spring配置文件
1、首先定义一个User类:com.xxx.pojo.User.java,代码如下:
public class User { }
2、在工程的src/main/resources源码目录下,创建Spring核心配置文件— beans.xml,打开文件并添加如下文件头信息:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
	
</beans>

3、在 beans.xml 文件中添加如下配置:将User类作为Bean装配到Spring IoC容器中

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
	<!--User类作为bean装配到Spring IoC容器中(User类的实例由Spring创 建) -->
<bean id="user" class="com.tedu.pojo.User"></bean>
</beans>

以下是关于spring应用程序XML文件的要点:
每个 元素配置一个由Spring IoC容器所管理的应用程序对象。在Spring框架的术语中,一个 元素代表一个Bean。id属性指定bean的唯一名称,class属性指定bean的完全限定类名。
2.2.3. 创建类进行测试
1、创建测试类—— IoCTest
2、测试步骤及代码如下:

public class IoCTest {
 @Test 
 public void testIoC() { 
 /* 获取Spring的IoC容器: * 将XML配置文件 bean.xml 传递给 ClassPathXmlApplicationContext * 的构造方法,Spring会读取其中的配置,将里面配置的Bean对象装配到IoC 容器中*/ 
 ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); 
 // 使用getBean方法获取对应的POJO并输出 
 User user = (User)ctx.getBean( "user" ); System.out.println( user ); } }

3、运行结果:
在这里插入图片描述
4、Spring小结:
在这里插入图片描述
1)在程序运行后,Spring会到配置文件中读取 Bean 配置信息,根据配置信息生产 Spring IoC容器对象
2)Spring IoC 容器根据读取到的Bean配置信息,到硬盘上加载相应的 Class 字节码文件,根据反射创建 Bean 实例。
3)Spring会将创建的Bean存储到 Spring 容器中,也就是所谓的 Bean Pool(Bean池,底层是一个Map)中
4) 根据 Spring 提供的 getBean 方法获取Bean实例并应用到我们的应用程序中。
2.2.4. 几个方法
下面列出了BeanFactory接口的几个常用方法,可以在程序中试一试。

/* 根据bean的名称,返回指定bean的一个实例(如果有) */ 
Object getBean(String name) throws BeansException; 
/* 根据Bean的类型(也可以是父类或父接口),返回唯一匹配给定对象类型的bean实 例(如果有) */ 
<T> T getBean(Class<T> requiredType) throws BeansException; 
/* 检索这个Bean工厂是否包含一个给定名称的bean实例 */ 
boolean containsBean(String name); 
/* 获取Bean的类型 */
 Class<?> getType(String name) throws NoSuchBeanDefinitionException; 
 /* 根据Bean的名称,检索当前Bean是不是单例,如果是则返回true */
  boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
   /* 根据Bean的名称,检索当前Bean是不是多例,如果是则返回true */ 
  boolean isPrototype(String name) throws NoSuchBeanDefinitionException;

3. Bean的作用范围

3.1. Bean对象的作用范围
在Spring容器中管理的Bean对象的作用域,可以通过scope属性或用相关注解指定其作用范围。

<bean id="user" scope="singleton" class="com.tedu.pojo.User"> 
</bean>

1、Bean的scope属性常用取值:
(1)scope=“singleton”:单实例(默认值)
(2)scope=“prototype”:多实例

singleton (单例)或 prototype (多例)其含义如下:

  1. singleton:单实例(默认),这个作用域标识的对象具备全局唯一性。
    a)当把一个 bean 定义设置 scope 为singleton作用域时,那么Spring IoC容器 只会创建该bean定义的唯一实例。也就是说,整个Spring IoC容器中只会创建当前类的 唯一一个对象。
    b)这个单一实例会被存储到 bean池中,并且所有针对该 bean 的后续请求和引用都将 返回被缓存的、唯一的这个对象实例。
    c)单例的bean对象由spring负责创建、初始化、存储、销毁(在spring容器销毁时销 毁)
  2. prototype:多实例。这个作用域标识的对象每次获取都会创建新的对象。
    当把一个 bean 定义设置 scope 为prototype作用域时,Spring IoC容器会在每一 次获取当前Bean时,都会产生一个新的Bean实例(相当于new的操作),该实例不会被 存储到bean池中,spring也不负责销毁,当程序不再使用这个对象时,由GC负责销毁。
    2、什么时候使用单例和多例?
    1)从使用频次上考虑,如果一个对象使用的频率非常高,建议使用单例,这样会将bean对象存储到bean池中,从始至终只有一个实例,可以减少对象创建,减少对资源的消耗。
    2)在没有线程安全问题的前提下,没必要每次获取都创建一个对象,这样子既浪费CPU又浪费内存;
    3)从使用频次上考虑,如果一个对象使用的频率非常低,没有必要将对象存储到map中(存储到bean池中的对象会一直存在bean池中,在spring容器销毁时销毁),建议使用多例。
    4)如果一个对象被多个线程所共享,并且对象中存在共享数据,一旦这个数据被多个线程所操作,就可能会产生线程不安全问题,可以考虑使用多例

在Spring中配置Bean实例是单例还是多例方法是:
单例:

<bean id="user" scope="singleton" class="com.tedu.pojo.User"> 
</bean>

多例:

<bean id="user" scope="prototype" class="com.tedu.pojo.User">
 </bean>

3、Bean对象的生命周期
生命周期:指一个对象何时创建、何时销毁以及从创建之后到销毁之前的所处的状态
单实例对象(singleton):
出生:当spring容器对象创建时,bean对象就会被创建
活着:只要容器没有销毁,bean对象就会一直存活
死亡:当spring容器销毁,bean对象也会跟着消亡
总结:单例对象的生命周期和容器相同,spring容器负责singleton对象的创建、存 储、销毁(随着spring容器销毁而销毁)。
多实例对象(prototype):
出生:当获取bean对象时,spring框架才会为我们创建bean对象
活着:只要对象是在使用过程中,就会一直存活
死亡:当对象长时间不用,且没有别的对象引用时,由Java垃圾回收机制负责回收
总结:spring容器只负责prototype对象的创建和初始化,不负责存储和销毁。当对象 长时间不时,由Java垃圾回收机制负责回收

4. Spring DI依赖注入

4.1. 两种注入方式
DI(Dependency Injection)依赖注入 。
依赖注入,即组件之间的依赖关系由容器在应用系统运行期来决定,也就是由容器动态地将某种依赖关系的目标对象实例注入到应用系统中的各个关联的组件之中。
简单来说,所谓的依赖注入其实就是,在创建对象的同时或之后,如何给对象的属性赋值。
如果对象由我们自己创建,这一切都变得很简单,例如:

User user = new User(); 
user.setName("张三"); //通过setName方法为name属性赋值 
user.setAge(18); //通过setAge方法为age属性赋值

或者:

User user = new User("张三", 18); //在创建对象的同时,通过有参构造函数 为对象的属性赋值

如果对象由Spring IoC容器创建,那么Spring是怎么给属性赋值的?Spring提供两种方式为属性赋值:
1)set方法注入
2)构造方法注入

4.2. set方法注入
顾名思义,就是在类中提供需要注入成员的 set 方法。spring框架底层会调用set方法为成员变量赋值。
例如:Spring框架负责创建User的Bean对象之后,会通过调用User对象的setName方法为name属性赋值

4.2.1. 普通属性注入
需求:通过Spring IoC容器获取User实例,并为User对象的name和age属性(普通属性)赋值
1、在User类中声明name和age属性,并添加对应的setter和getter方法,以及重写toString方法

public class User { 
//提供私有成员变量
 private String name;
  private Integer age;
   private UserInfo info;
    //提供getter和setter方法 ... 
    //重写toString方法 ...
    }

2、在beans.xml中声明User类的bean实例

<!--User类作为bean装配到Spring IoC容器中(User类的实例由Spring创建)
-->
<bean id="user" class="com.tedu.pojo.User">
</bean>

3、创建测试类—DITest

import org.Springframework.context.support.ClassPathXmlApplicationContext ;
public class DITest { 
@Test 
public void testDI() { 
// 获取Spring的IoC容器
 ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); 
 // 使用getBean方法获取对应的POJO并输出 
 User user = (User)ctx.getBean( "user" ); System.out.println( user ); }

由于这里没有为User对象的属性赋值,所以此时运行测试,结果为:
在这里插入图片描述
4、修改beans.xml中User Bean标签,为User Bean的name和age属性注入属性值

<!--User类作为bean装配到Spring IoC容器中(User类的实例由Spring创建)
-->
<bean id="user" class="com.tedu.pojo.User"> <!-- 通过set方法为User Bean的name、age属性赋值 -->
<property name="name" value="张三"/>
 <property name="age" value="20"/>
</bean>

上面的bean定义表明,User bean通过 标签为User对象的 name 和 age 属性赋值。
之所以称之为是set方法注入,是因为在框架底层会调用User对象的setter方法为属性赋值。
例如:下面User bean标签中的 标签是为User对象的属性赋值。

<property name="name" value="张三"></property>

因为 name=" name “,因此需要User对象中有setName方法才可以完成注入,否则将会注入失败。
value=” 张三",表示为(User对象的) setName 方法对应的属性赋值为" 张三 "。
普通属性(基本数据类型和String类型)直接通过value注入即可。
5、运行测试类DITest,结果为:
上面通过Spring提供的set方法对User对象的属性进行了赋值,所以此时运行测试,结果为:
User[name=张三,age=20,info=null]
4.2.2. 对象属性注入
需求:通过Spring IoC容器获取User实例,并为User对象的userInfo属性(对象属性)赋值
1、在beans.xml中,将UserInfo类作为bean装配到Spring IoC容器中(如果已有可以忽略)

<!--UserInfo类作为bean装配到Spring IoC容器中(UserInfo类的实例由 Spring创建) -->
<bean id="userInfo" class="com.tedu.pojo.UserInfo">
</bean>

2、在beans.xml中,将通过 Spring 容器获取的UserInfo对象作为值,注入到User对象的userInfo属性

<!--User类作为bean装配到Spring IoC容器中(User类的实例由Spring创建)
-->
<bean id="user" class="com.tedu.pojo.User"> 
<!-- 通过set方法为User Bean的name、age属性赋值 -->
<property name="name" value="张三"/> 
<property name="age" value="20"/> 
<!-- 通过set方法为User Beaninfo(对象)属性赋值 -->
<property name="info" ref="userInfo"/> 
</bean> 


<!--UserInfo类作为bean装配到Spring IoC容器中(UserInfo类的实例由 Spring创建) -->
<bean id="userInfo" class="com.tedu.pojo.UserInfo">
</bean>

由于此处是将UserInfo对象的引用作为值设置给info属性,因此 ref 属性指定为UserInfo对象bean标签的id值。将通过 Spring 容器获取的UserInfo对象传递给 setUserInfo方法,从而对User对象的属性赋值。
对象属性通过ref属性注入。
3、运行测试类DITest,结果为:
User[name=张三,age=20,info=com.tedu.pojo,UserInfo@65e2dbf3]

4.3. 构造方法注入
顾名思义,就是使用类中的构造函数,为成员变量赋值——即构造方法注入。
需要注意的是:为成员变量赋值是通过配置的方式,让spring框架在创建bean对象的同时,为成员变量赋值。而不是我们自己去实现。
需求:通过Spring IoC容器获取User实例,并为User对象的name、age、info属性赋值
1、为User类添加无参构造和有参构造函数

//提供无参构造 
public User() { } 
//提供有参构造 
public User(String name, Integer age, UserInfo info) { 
this.name = name; 
this.age = age; 
this.info = info; 
}

注意:如果不添加有参构造函数,可以无参、有参构造函数都不添加;
如果要添加有参构造函数,强烈建议同时添加无参构造函数,否则在很多地方使用时都可能会抛出异常!

2、修改beans.xml文件,将set方法修改为构造方法注入。

<!--User类作为bean装配到Spring IoC容器中(User类的实例由Spring创建)
-->
<bean id="user" class="com.tedu.pojo.User"> 
<!-- 通过set方法为User Bean的name、age属性赋值 <property name="name" value="韩少云"/> 
<property name="age" value="20"/> -->
<!-- 通过set方法为User Beaninfo(对象)属性赋值 
<property name="info" ref="userInfo"/> -->


<!-- 通过构造方法为User Bean的name、age、info属性赋值 -->
<constructor-arg name="name" value="马云"/> 
<constructor-arg name="age" value="30"/> 
<constructor-arg name="info" ref="userInfo"/> </bean> 

<!--UserInfo类作为bean装配到Spring IoC容器中(UserInfo类的实例由 Spring创建) -->
<bean id="userInfo" class="com.tedu.pojo.UserInfo"></bean>

其中,constructor-arg标签name属性的值必须和构造函数中参数的名字相同!
同样的,普通属性直接通过value注入即可;对象属性通过ref属性注入。
3、运行测试类DITest,结果为:
在这里插入图片描述

5. Spring全注解开发

5.1. Spring注解开发入门
1、创建Maven的Java工程
2、首先定义一个User类:com.xxx.pojo.User.java,代码如下:

public class User { 
//提供私有成员变量 
private String name; 
private Integer age; 
//提供getter和setter方法 ... 
//重写toString方法 
... 
}

3、再定义一个Java配置文件:com.tedu.AppConfig.java,代码如下:

/* @Configuration注解: 
* 告诉spring这是一个java配置文件(类),spring会根据这个文件生成IoC容器 
* AppConfig(java配置类)== beans.xml配置文件 
*/ 
@Configuration 
public class AppConfig { 
/* @Bean注解:将当前方法返回的POJO装配到IoC容器中(作用等同于bean标签) * name属性用于定义这个bean的名称,如果没有配置将会默认使用方法名作为名 字 * 类型为方法的返回值类型 */ 
@Bean(name="user")
 public User getUser() { 
 User user = new User(); 
 user.setName("刘德华"); 
 user.setAge(20);
  return user; 
  } }

这里需要注意以下两个注解的作用:
@Configuration 注解:告诉spring这是一个java配置文件(类),spring会根据这个java文件生成IoC容器
@Bean 注解:将当前方法返回的POJO装配到IoC容器中(作用等同于bean标签),括号中的name属性用于定义这个bean的名称,如果没有配置name,将会默认使用这个方法的名字作为bean的名称保存到Spring的IoC容器中。
4、基于AnnotationConfigApplicationContext获取Spring的IoC容器
提供测试类:com.xxx.IoCTest,代码如下:

public class IoCTest {
 @Test 
 public void testAnnoTation01() { 
 /* 获取Spring的IoC容器: * 将Java配置文件AppConfig传递给 AnnotationConfigApplicationContext * 的构造方法,读取其中的配置,将里面配置的Bean对象装配到IoC容器中 */ 
 ApplicationContext ctx = new AnnotationConfigApplicationContext( AppConfig.class ); 
 // 使用getBean方法获取对应的POJO并输出 
 User user = (User)ctx.getBean( "user" ); System.out.println( user ); 
 } }

运行输出结果如下:
在这里插入图片描述
显然,在 AppConfig.java 文件中名称为User的Bean已经被装配到IoC容器中,并且可以通过 getBean 方法获取到该 Bean,并将 Bean 的属性信息打印出来。
当然这只是一个很简单的方法,@Bean 注解也不是唯一创建 Bean 的方法,还有其他的方式可以让 IoC 容器装配 Bean,并且 Bean 之间还有依赖关系需要进一步处理,这些会在下面进行讲解。
5.2. 通过扫描装配Bean
如果所有的bean都是通过@Bean注解装配到 Spring IoC容器中,那将是一件很麻烦的事情。在Spring中允许我们进行扫描装配Bean到IoC容器。扫描装配Bean使用的注解是 @Component 和 ComponentScan。
@Component 注解:标记哪个类需要被扫描装配到 Spring IoC容器
@ComponentScan 注解:标记采用何种策略去扫描装配Bean(默认扫描当前类所在的包及其子包)

扫描装配Bean示例:
1、定义一个 com.xxx.UserDao 和一个 com.xxx.UserService,在类上添加@Component注解

package com.tedu.dao; import org.springframework.stereotype.Component; /* @Component注解:用于标记此类会被Spring IoC容器扫描配置为Bean * @Component("userDao"): 其中配置的“userDao”将作为Bean的名称,也可以 不指定 * 如果不指定,IoC容器会将类名第一个字母小写,其余不变作为bean的名称 */ @Component("userDao")
 public class UserDao { 
 }
package com.tedu.service; import org.springframework.stereotype.Component; 
@Component 
public class UserService { 
}

@Component 注解:用于标记此类会被Spring IoC容器扫描配置为Bean,其中配置的“userDao”将作为Bean的名称,也可以不指定。如果不指定,IoC容器会将类名第一个字母小写,其余不变作为bean的名称
2、扫描装配Bean:为了让Spring IoC容器装配这个UserDao,改造AppConfig

/** @ComponentScan注解:让Spring进行扫描,默认只扫描此类所在的包及其子包 
* @ComponentScan("com.xxx.*"):也可以指定所扫描的包(com.xxx包及其子 包)* @ComponentScan(basePackages = {"com.xxx.dao","com.xxx.service"}): 
* * 当需要扫描的包有多个时,可以指定多个包。 */ 
@Configuration 
@ComponentScan 
//@ComponentScan(basePackages=" com.xxx.dao")
//@ComponentScan(basePackages={" com.xxx.dao","com.xxx.service"})
 public class AppConfig { }

@ComponentScan 注解:让Spring进行扫描,默认只扫描此类所在的包及其子包,也可以指定所扫描的包和类,例如:
1) @ComponentScan( “com.xxx." ) :其中的 "com.xxx.” 表示扫描com.xxx包及其所有子包,例如:com.xxx.dao、com.xxx.service、com.xxx.pojo都 是com.xxx的子包,都会被扫描到。如果需要扫描的包有多个,可以按照下面的方式指定。
2) @ComponentScan( basePackages= {“com.xxx.dao”,“com.xxx.service”} ) :basePackages指定一个数组,数组中定义了所需要扫描的包。{“com.xxx.dao”,“com.xxx.service”} 表示扫描com.xxx.dao和com.xxx.service这两个包 。
3) @ComponentScan( basePackageClasses= {UserDao.class,UserService.class} ) :basePackageClasses指定一个数组,数组中定义了所要扫描类的字节码对象

3、添加测试方法:

/* 2、扫描装配Bean */ 
@Test public void testAnnoTation02() { 
/* 获取Spring的IoC容器: * 将Java配置文件AppConfig传递给 AnnotationConfigApplicationContext * 的构造方法,读取其中的配置,将里面配置的Bean对象装配到IoC容器中 */ 
ApplicationContext ctx = new AnnotationConfigApplicationContext( AppConfig.class ); 

//从spring容器中获取UserService类的实例
UserService service =(UserService)ctx.getBean("userService");
System.out.println(service);

// 获取IoC容器中的所有bean的名称,并输出
String[] beanNames = ctx.getBeanDefinitionNames();
for (String beanName : beanNames) { System.out.println( beanName ); 

//判断是否包含
System.out.println("是否包含User类的bean:"+ctx.containsBean("user"));
} }

5.3. @Scope注解
上面在通过XML方式配置时说过,可以通过bean标签上的 scope 属性 指定bean的作用域

<bean id="user" scope="singleton" class="com.tedu.pojo.User"> 
</bean>

在使用注解开发中,可以通过@Scope注解达到相同的效果
其中 @Scope 注解:指定当前Bean是单例作用域还是多例作用域,默认是单例
示例:

/* @Component注解:用于标记此类会被Spring IoC容器扫描配置为Bean * @Component("userDao"): 其中配置的“userDao”将作为Bean的名称,也可以不 指定* 如果不指定,IoC容器会将类名第一个字母小写,其余不变作为bean的名称 * @Scope注解:指定bean的作用域,如果不指定,默认是单例,可以指定为 prototype */ 
@Component("userDao")
 @Scope("prototype") 
 public class UserDao { }

5.4. 属性赋值(@Value)
1、通过@Value注解可以为Bean的属性赋值
1)例如:为User对象的name属性和age属性赋值

@Component public class User { 
//提供私有成员变量
 @Value("赵云") 
private String name;
 @Value("24") 
private Integer age; 
... }

2)测试:获取User的Bean实例并输出到控制台查看

/** 1、测试@Value注解 
* @Value注解:为Bean的属性赋值(值:数值,字符串,布尔值) */ 
 @Test 
public void testValue(){
 // 获取Spring的IoC容器 
 ApplicationContext ctx = new AnnotationConfigApplicationContext( AppConfig.class ); 
 // 使用getBean方法获取对应的POJO并输出 
 User user = (User)ctx.getBean( "user" ); System.out.println( user ); 
 }

3)运行结果为:可以看出已经成功为User对象的name、age属性赋了值
User [name=赵云, age=24]

**2、也可以将属性值写到配置文件中,再通过@Value注解及${}符为Bean的属性赋值,**例如:
1)在项目src/main/resource/下提供config.properties文件,添加两行配置:
config.name=马云
config.age=28
2)在AppConfig配置类中,通过@PropertySource注解读取config.properties文件中的key/value到运行环境中

/** @PropertySource注解:读取外部配置文件中的key/value,并保存到运行环境中 */ 
@PropertySource("classpath:/config.properties") 
@Configuration @ComponentScan public class AppConfig {
 ... 
 }

3)修改User类中的代码,通过@Value注解及${}符为Bean的属性赋值

@Component public class User { 
//提供私有成员变量 
@Value("${config.name}") 
private String name; 
@Value("${config.age}") private Integer age; 
...
 }

4)测试:可以看出已经成功为User对象的name、age属性赋了值
User [name=马云, age=28]

5.5. 自动装配(@Autowired)
@Autowired是我们使用得最多的注解之一,可以将定义好的Bean作为属性值注入到其他Bean的属性上,而这一过程是自动完成的。
@Autowired自动注入的原则:
1)默认优先按照类型从Spring容器中进行查找bean,如果找到一个则直接注入,如果没有找到,则抛出NoSuchBeanDefinition异常。
2)但如果该类型的bean对象在spring容器中有多个,此时还会基于属性名进行匹配,如果属性名和spring中bean的名字相同,则直接注入,如果都不匹配则直接抛出NoUniqueBeanDefinition异常。
3)当然,我们可以通过@Qualifier注解,显式的为属性指定要注入哪一个名字的bean(此注解必须配合@AutoWired注解使用)。
1、下面测试@Autowired注解的使用
1)修改User类中的代码:添加UserInfo属性(通过@Autowired注解为注入值),并提供对应的getter和setter方法,最后重写toString方法

@Component 
public class User { 
//提供私有成员变量 ... 
@Autowired 
private Animal animal; 
//提供getter和setter方法 .... 
public Animal getAnimal() { return animal; } public void setAnimal(Animal animal) { this.animal = animal; } 
//重写toString方法 
@Override public String toString() { return "User [name=" + name + ", age=" + age + ", animal=" + animal + "]"; } }

2)添加Animal接口和Dog类,让Dog实现Animal接口,再通过@Component注解将Dog作为bean装配到spring容器中:

package com.tedu.pojo; 
//Animal接口 
public interface Animal { }
package com.tedu.pojo; import org.springframework.stereotype.Component; 
//Dog实现类
 @Component("dog")
  public class Dog implements Animal{
   }

3)测试:

/** 2、测试@Autowired注解 * @Autowired注解:将定义好的bean作为属性值注入到其他Bean的属性上 */ @Test 
public void testAutowired(){ 
// 获取Spring的IoC容器 
ApplicationContext ctx = new AnnotationConfigApplicationContext( AppConfig.class ); 
// 使用getBean方法获取对应的POJO并输出
 User user = (User)ctx.getBean( "user" ); System.out.println( user ); 
 }

测试结果如下:
User [name=马云, age=28, animal=com.tedu.pojo.Dog@55040f2f]
从打印结果可以看出,@Autowired注解成功为Animal类型的animal属性注入了一个Dog类型的Bean。底层是根据animal属性的类型(Animal)到Spring容器中寻找该类型的bean,如果刚好找到一个,就可以完成注入。

2、那么,如果UserInfo类型的bean对象在spring容器中有多个,@AutoWired注解该如何注入?
1)再添加一个Cat类并实现Animal接口,再通过@Component注解将Cat作为 bean装配到spring容器中:

package com.xxx.pojo; 
import org.springframework.stereotype.Component; @Component("cat") 
public class Cat implements Animal{ 
}

2)此时Animal类型的bean在Spring容器中有两份(bean的name分别为:dog和 cat),再次运行测试,程序会抛出如下异常:
… Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type ‘com.xxx.pojo.Animal’ available: expected single matching bean but found 2: cat,dog …
解决方法1:可以将User类中的animal属性名改为其中一个bean的名字(dog或 cat);或者将其中一个bean的名字改为User类中的animal属性名,再运行测试。
解决方法2:也可以通过 @Qualifier注解显式的为属性指定要注入哪一个名字的bean

@Autowired
 @Qualifier("dog") 
 private Animal animal;
 

解决方法3:还可以通过@Primary注解指定默认首选的Bean。即注入依赖的过程
中,当有多个候选者的时候,可以指定哪个候选者为主要的候选者。

spring依赖注入方式:
xml配置、@Bean:set方法注入,构造方法注入;
注解方式:@Autowired:根据bean的类型注入、可配合@Qualifier(“dog”) 使用。@Resource(name=“dog”):根据bean的名称进行注入。@Value(""):普通属性注入。
还可以创建IOC容器创建对象:// 获取Spring的IoC容器
ApplicationContext ctx = new AnnotationConfigApplicationContext( AppConfig.class );
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext(“beans.xml”);
// 使用getBean方法获取对应的POJO并输出
User user = (User)ctx.getBean( “user” );

常用注解
@Configuration 标识当前类是配置类
@ComponentScan 包扫描注解 扫描注解
@Bean 标识该方法的返回值交给Spring容器管理
@Scope 控制多例和单例
@Lazy 懒加载
@PostConstruct 初始化方法
@PreDestroy 销毁方法
@Component 将当前类未来的对象交给容器管理
@Autowired 按照类型进行注入
@Qualifier 按照名称进行注入
@Repository 标识持久层注解
@Service 标识Service层
@Controller 标识Controller层
@Value 为属性赋值 @Value("${key}")
@PropertySource 加载指定路径的配置文件properties
@Aspect 标识当前类是一个切面类
@Pointcut 用于定义切入点表达式 表达式写法4种
@EnableAspectJAutoProxy 让AOP的注解有效果
@Before AOP-前置通知
@AfterReturning AOP-后置通知
@AfterThrowing AOP-异常通知
@After AOP-最终通知
@Around AOP-环绕通知
@Order(1) //可以利用order关键字 实现AOP的排序 数字越小越先执行.
@ResponseBody 将返回的数据转化为JSON串, 如果是字符串本身 原数据返回
@RequestMapping("/hello") 实现浏览器的请求路径与方法的映射
@PathVariable restFul结构,接收参数的注解.
@GetMapping("") 只能接收GET请求类型
@DeleteMapping("") 只能接收DELETE请求类型
@PostMapping("") 只能接收POST请求类型
@PutMapping("") 只能接收PUT请求类型
@RestController 表示Controller类,同时要求返回值为JSON
@CrossOrigin 允许跨域访问
@RequestBody 参数接收时,将JSON串转化为java对象 json中的key与对象的属性一致.
@Data lombok动态生成get/set/toString/equals/hashcode等方法
@Accessors 控制是否开启链式加载结构
@NoArgsConstructor 生成无参构造方法
@AllArgsConstructor 生成全参构造方法
@Mapper mybatis将当前的接口交给Spring容器管理. Map<类名小写,JDK动态代理对象>
@SpringBootTest 该注解的作用在进行代码测试时启动spring容器,之后动态的获取对象 注意包路径 主启动类的同包及子包中.
@Param Mybatis中将参数封装为Map集合. @Param(“maxAge”) int maxAge
@Alias Mybatis中定义对象的别名 @Alias(“User”)
@MapperScan Mybatis中扫描指定包路径的接口 为其创建代理对象.
@Insert Mybatis 新增操作注解
@Update Mybatis 修改操作注解
@Delete Mybatis 删除操作注解
@Select Mybatis 查询操作注解
@Transactional Spring中用来控制事务
@RestControllerAdvice Controller层的全局异常处理
@ExceptionHandler 按照某种异常类型进行拦截
@TableName(“item_cat”) MybatisPlus注解POJO与数据表绑定 注意表名
@TableId(type = IdType.AUTO) MybatisPlus注解 标识主键/主键自增
@TableField(exist = false) MybatisPlus注解 标识属性是否存在,及名称是否一致

三、Springmvc框架

1. MVC设计模式

1.1. 什么是设计模式
设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。
使用设计模式的目的:为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。
设计模式使代码编写真正工程化;
设计模式是软件工程的基石脉络,如同大厦的结构一样。
设计模式就是一种模子,经过多年实践锤炼形成一套行之有效的完成某个特定任务的步骤和方式。
例如:西凤酒的酿造过程,酿造工序,前后不能变,温差不能变,这样做就是好喝,稍微改动就变味道了。
再如,北京烤鸭,就是那样做,就是那些调料腌制,变量配比,味道口感就是不如人家。
1.2. MVC设计模式
MVC设计模式是一种通用的软件编程思想
在MVC设计模式中认为, 任何软件都可以分为三部分组成:
(1)控制程序流转的控制器(Controller)
(2)封装数据处理数据的模型(Model)
(3)负责展示数据的视图(view)
并且在MVC设计思想中要求一个符合MVC设计思想的软件应该保证上面这三部分相互独立,互不干扰,每一个部分只负责自己擅长的部分。
如果某一个模块发生变化,应该尽量做到不影响其他两个模块。这样做的好处是,软件的结构会变得更加的清晰,可读性强。有利于后期的扩展和维护,并且代码可以实现复用
在这里插入图片描述

2. 初识SpringMVC

2.1. Servlet的缺点
1、通常情况下,一个Servlet类只负责处理一个请求,若项目中有成百上千个请求需要处理,就需要有成百上千个Servlet类,这样会使得项目中Servlet类的个数暴增;
2、在Servlet3.0版本之前,每一个Servlet都需要在web.xml文件中至少做八行配置信息,配置内容多且繁琐。当Servlet特别多时,web.xml配置量太多,不利于团队开发;
3、当通过客户端提交参数到服务器,通过Servlet进行接收时,无论数据本身是什么格式,在Servlet中一律按照字符串进行接收,后期需要进行类型转换,复杂类型还需要特殊处理,特别麻烦!
String value = request.getParameter(String name);
2.2. SpringMVC简介
Springmvc是Spring框架的一个模块,Spring和Springmvc无需中间整合层整合
Springmvc是一个基于mvc的web框架
2.3. Springmvc执行原理
在这里插入图片描述
(1) .用户发送请求 至 前端控制器(DispatcherServlet);
提示:DispatcherServlet的作用:接收请求,调用其它组件处理请求,响应结果,相当于转发器、中央处理器,是整个流程控制的中心
(2) .前端控制器(DispatcherServlet)收到请求后调用处理器映射器(HandlerMapping)处理器映射器(HandlerMapping)找到具体的Controller(可以根据xml配置、注解进行查找),并将Controller返回给DispatcherServlet;
(3) .前端控制器(DispatcherServlet)调用处理器适配器(HandlerAdapter)。处理器适配器经过适配调用具体的Controller;(Controller–> service --> Dao --> 数据库)
Controller执行完成后返回ModelAndView,
提示:Model(模型数据,即Controller处理的结果,Map) View(逻辑视图名,即负责展示结果的JSP页面的名字)
处理器适配器(HandlerAdapter)将controller执行的结果(ModelAndView)返回给前
端控制器(DispatcherServlet);
(4) .前端控制器(DispatcherServlet)将执行的结果(ModelAndView)传给视图解析器(ViewReslover)
视图解析器(ViewReslover)根据View(逻辑视图名)解析后返回具体JSP页面
(5) .前端控制器(DispatcherServlet)根据Model对View进行渲染(即将模型数据填充至视图中);
前端控制器(DispatcherServlet)将填充了数据的网页响应给用户。
其中整个过程中需要开发人员编写的部分有Controller、Service、Dao、View;

3. Springmvc入门案例

需求:
1)通过浏览器访问 http://localhost:8080/项目名称/hello 地址,在控制台输出 “hello Springmvc” 2)将请求转向(跳转到) /WEB-INF/pages/test.jsp 页面

3.1. 创建Maven—web工程
1、通过Maven创建web工程
在这里插入图片描述
2、在pom.xml中引入springmvc所需jar包:将下面的配置直接拷贝到pom.xml中的根标签内

<dependencies> 
<!-- 单元测试 -->
<dependency> 
<groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.10</version> 
</dependency> 
<!-- springMVC的jar包 -->
<dependency> 
<groupId>org.springframework</groupId> 
<artifactId>spring-webmvc</artifactId> 
<version>4.1.3.RELEASE</version> 
</dependency> 
<!-- servlet 和 jsp的jar包 -->
<dependency>
 <groupId>javax.servlet</groupId> 
 <artifactId>servlet-api</artifactId> 
 <version>2.5</version> 
 </dependency> 
 <dependency>
  <groupId>javax.servlet</groupId> 
  <artifactId>jsp-api</artifactId> 
  <version>2.0</version> 
  </dependency> 
  <!-- java对象转换json的工具类 
  <dependency> 
  <groupId>com.fasterxml.jackson.core</groupId> 
  <artifactId>jackson-databind</artifactId> 
  <version>2.5.1</version> 
  </dependency>
-->
</dependencies>

3.2. 在web.xml中配置前端控制器

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
  <display-name>CGB-SPRINGMVC-01</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
  
  <!--1、 配置springmvc的前端控制器(DispatcherServlet-->
  <servlet>
  <servlet-name>springmvc</servlet-name>
  <!--这里配置DispatcherServlet类的全类名,ctrl shift + t  -->
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  
  <!--2、这里配置springmvc-confing文件的位置  -->
  <init-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>classpath:springmvc-config.xml</param-value>
  </init-param>
  
  </servlet>
  
  <servlet-mapping>
  <servlet-name>springmvc</servlet-name>
  <!-- 这里配置的' / '表示拦截 除了访问JSP以外的所有请求 
        servlet           不拦截    
        jsp               不拦截     
        html/css/js/图片  会拦截
  -->
  <url-pattern>/</url-pattern>
  </servlet-mapping>
  
  <!-- 配置过滤器,解决POST提交的中文参数乱码问题 -->
<filter>
	<filter-name>encodingFilter</filter-name>
	<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
	<init-param>
		<param-name>encoding</param-name>
		<param-value>UTF8</param-value>
	</init-param>
</filter>
<filter-mapping>
	<filter-name>encodingFilter</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>
  
</web-app>

3.3. 创建并配置springmvc-config.xml
直接复制下面配置文件的内容即可!

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/mvc
						http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
						http://www.springframework.org/schema/beans
						http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
						http://www.springframework.org/schema/context
          				http://www.springframework.org/schema/context/spring-context-4.0.xsd">
	
	<!-- 1.配置前端控制器放行静态资源(html/css/js等,否则静态资源将无法访问) -->
	<mvc:default-servlet-handler/>
	
	<!-- 2.配置注解驱动,用于识别注解(比如@Controller-->
	<mvc:annotation-driven></mvc:annotation-driven>
	
	<!-- 3.配置需要扫描的包:spring自动去扫描 base-package 下的类,
		如果扫描到的类上有 @Controller@Service@Component等注解,
		将会自动将类注册为bean 
	 -->
	<context:component-scan base-package="com.xxx.controller">
	</context:component-scan>
	
	<!-- 4.配置内部资源视图解析器
		prefix:配置路径前缀
		suffix:配置文件后缀
	 -->
	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/WEB-INF/pages/"/>
		<property name="suffix" value=".jsp"/>
	</bean>
	
	
</beans>

3.4. 创建并实现HelloController类
1、创建com.xxx.controller.HelloController类
2、实现HelloController类

package com.tedu.controller; 
import org.springframework.stereotype.Controller; 
import org.springframework.web.bind.annotation.RequestMapping; 
/* @Controller作用: 表示当前类属于controller层 
* 同时标识将当前类的对象的创建交给spring容器负责
* http://localhost/day16-springmvc/hello */ 

@Controller 
public class HelloController { 
/* 
@RequestMapping("/hello") 用于配置当前方法的访问路径,不能省略,且 不能重复! * @RequestMapping注解在当前方法上声明的访问路径, 在当前controller类 中不能重复! * 如果controller类上没有访问路径,当前方法上的访问路径在所有 controller类中都不能重复! */ 
@RequestMapping("/hello") 
public String testHello() { 
System.out.println( "hello springmvc...." ); 
//跳转到哪一个路径 /WEB-INF/pages/home.jsp 
return "home"; 
} }

3.5. 创建并实现home.jsp
在WEB-INF/pages/目录下,创建home.jsp页面。
WEB-INF/pages/home.jsp

<%@ page pageEncoding="UTF-8"%> 
<!DOCTYPE html> 
<html>
 <head>
  <meta charset="UTF-8"> 
  </head>
 <body>
  <h1>day16-springmvc...home.jsp....</h1> </body> 
 </html>

3.6. 访问测试
打开浏览器,输入url地址:http://localhost:8080/day16-springmv/hello 地址,
访问结果如下

4. springmvc参数绑定

当项目中引入springmvc框架后,所有的请求流转将由springmvc进行控制,当客户端发送的请求中包含数据(也就是请求参数)时,那么该如何在controller层获取这些参数呢?
springmvc会自动的将请求中包含的参数和方法的参数进行匹配,也就是说只要保证,请求中的参数名称和方法中的参数名称相对应(另,参数的格式也要正确),在方法中就可以使用这些参数—即请求中的参数。
4.1. 基本类型参数绑定
当需要获取客户端发送过来的少量数据时,可以在Controller中声明的方法上,通过声明方法参数对这些参数一个一个进行接收,具体示例如下:
需求:通过浏览器发请求访问Controller,并在请求中携带name、age数据访问服务器,在服务器端的
Controller中获取这些数据。
1、在HelloController类中添加testParam1方法,用于接收基本类型的参数,代码实现如下:

package com.xxx.controller; 
import org.springframework.stereotype.Controller; 
import org.springframework.web.bind.annotation.RequestMapping; 
@Controller 
/* 这个注解表示当前类是属于控制层 */ 
public class HelloController { 
/* 1、测试springmvc的简单类型参数绑定 
* ../testParam1?name=张飞&age=20&addr=河北 
* 如何获取请求中name、age、addr的参数值? 
* request.getParameter("name") -- 张飞 
*  在方法上添加三个同类型、同名的形参分别为 name、age、addr 
* 用于接收请求中name、age、addr参数的值 */ 

@RequestMapping("/testParam1") 
public String testParam1(String name, Integer age, String addr) {
System.out.println( "name="+name ); System.out.println( "age="+age ); System.out.println( "addr="+addr ); 
return "home"; } }

2、访问HelloController中的testParam1方法,在访问时,注意将name、age、 addr参数一起发送给服务器:
在这里插入图片描述
控制台输出结果为:
在这里插入图片描述
4.2. 包装类型参数绑定
当需要获取客户端发送过来的多个数据时,可以在Controller中声明的方法上,通过声明方法参数对这些数据一个一个进行接收较麻烦,可以在方法上声明对象类型的参数,通过对这些数据统一进行接受,springmvc会自动将接收过来的参数封装在对象中,具体示例如下:
1、在HelloController类中添加param2方法,用于接收对象类型的参数,代码实现如下:

package com.tedu.controller; 
import org.springframework.stereotype.Controller; 
import org.springframework.web.bind.annotation.RequestMapping; 
@Controller /* 这个注解表示当前类是属于控制层 */ 
public class HelloController { 
/* 2、测试springmvc的包装类型参数绑定 
* ../testParam2?name=关羽&age=30&addr=北京 
*  如何获取请求中name、age、addr的参数值? 
*  提供一个User类,在类中添加和请求参数同名的属性
* 底层通过调用setName、setAge、setAddr方法将参数值封装到User对象中 */

@RequestMapping("/testParam2") 
public String testParam2(User user) {
 System.out.println( "user.name="+user.getName() ); 
 System.out.println( "user.age="+user.getAge() ); 
 System.out.println( "user.addr="+user.getAddr() ); 
 return "home"; 
 } }

3、创建User类,声明name、age、addr属性,提供对应的set和get方法

package com.tedu.pojo; 
/*** 封装用户信息 */ 
public class User { 
private String name; private Integer age; private String addr; 
public User() {} //无参构造函数 
public User(String name, Integer age, String addr) { 
super(); 
this.name = name; this.age = age; this.addr = addr; 
}
//get、set方法 
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Integer getAge() { return age; }
public void setAge(Integer age) { this.age = age; }
public String getAddr() { return addr; }
public void setAddr(String addr) { this.addr = addr; } }

4、访问HelloController中的param2方法,在访问时,注意将name和age参数一起发送给服务器:
在这里插入图片描述
控制台输出结果为:
在这里插入图片描述
4.3. 日期类型参数绑定
1、在HelloController类中添加testParam3方法,代码实现如下:

@Controller /* 这个注解表示当前类是属于控制层 */ 
public class HelloController { 
/* 3、测试springmvc的日期类型参数绑定 * ../testParam3?date=2020-4-10 16:40:39 报400错误,表示参数类型不 匹配* ../testParam3?date=2020/4/10 16:40:39 * 如何获取请求中date参数的值? * springmvc默认是以斜杠接收日期类型的参数, 如果提交的日期就是以斜杠 * 分隔的, springmvc就可以接收这个日期类型的参数, 否则将无法接收 * 如果提交的日期参数就是以横杠分隔, 也可以修改springmvc默认的接收格式 * 改为以横杠分隔!! */ 
@RequestMapping("/testParam3") 
public String testParam3(Date date) { 
System.out.println( "date="+date ); 
return "home"; 
} }

2、访问HelloController中的testParam3方法,在访问时,注意将date参数一起发送给服务器:
在这里插入图片描述

控制台输出结果为:
在这里插入图片描述
常见问题:
1、当访问HelloController中的testParam3方法,如果传递给服务器的日期数据是如下格式:
在这里插入图片描述
从图中可以看出,如果日期参数是 yyyy-MM-dd格式(以横杠分隔)就会出现400错误,其实是因为参数格式匹配错误,由于springmvc默认的日期格式是yyyy/MM/dd(以斜杠分隔),因此如果日期参数不是yyyy/MM/dd 格式,就会出现400错误!!
2、解决方案:
在springmvc中,提供了@InitBinder注解,用于指定自定义的日期转换格式,因此,我们只需要在Controller类中添加下面的代码即可,在接受日期类型的参数时,会自动按照自定义的日期格式进行转换。

/* 自定义日期格式转换器 * 将springmvc默认以斜杠(/)分隔日期改为以横杠分隔(-)
*/
 @InitBinder 
 public void InitBinder (ServletRequestDataBinder binder){ 
binder.registerCustomEditor(java.util.Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"), true) ); 
}

3、再次测试:
在这里插入图片描述
控制台输出结果为:
在这里插入图片描述

5. 跳转和乱码处理

5.1. 实现转发(forward)
在前面request对象的学习中,通过request对象可以实现请求转发(即资源的跳转)。同样的,springmvc也提供了请求转发的方式,具体实现如下:
需求:通过浏览器访问 testForward方法,执行testForward方法后,将请求转发到(HelloController)hello, 也就是home.jsp页面。
1、在HelloController中,提供testForward方法,代码实现如下:

/* 测试请求转发(forward) */ 
@RequestMapping("testForward") 
public String testForward(){ System.out.println("测试请求转发(forward)..."); 
return "forward:hello"; 
}

2、打开浏览器,在浏览器中输入:http://localhost/day16-springmvc/testForwa rd地址,访问效果如下:
在这里插入图片描述
forward方式相当于:
request.getRequestDispatcher(“url”).forward(request,response);
转发是一次请求,一次响应;
转发后地址栏地址没有发生变化(还是访问testForward的地址);
转发前后的request和response对象也是同一个。

5.2. 实现重定向(redirect)
在前面response对象的学习中,通过response对象可以实现请求重定向(即资源的跳转)。
同样的,springmvc也提供了请求重定向的方式,具体实现如下:
需求:通过浏览器访问 testRedirect方法,执行testRedirect方法后,将请求重定向到
(HelloController)hello, 也就是home.jsp页面。
1、在HelloController中,提供testRedirect方法,代码实现如下:

/* 测试请求重定向(redirect) */ @RequestMapping("testRedirect") 
public String testRedirect(){ 
System.out.println("测试请求重定向(redirect)...");
 return "redirect:hello"; 
 }

2、打开浏览器,在浏览器中输入:http://localhost/day16-springmvc/testRedire ct地址,访问效果如下:
在这里插入图片描述
redirect方式相当于:
response.sendRedirect(url);
重定向是两次请求,两次响应;
重定向后地址栏地址发生了变化(变为转发后的地址);
并且在重定向前后,request和response对象不是同一个。

5.3. 乱码处理
在前面的Servlet学习中,我们学习了GET和POST请求参数乱码如何解决。
springmvc也提供了解决请求参数乱码的方案,就是在web.xml中加入如下代码(配置请求参数乱码过滤器),可以解决POST提交的中文参数乱码问题!

<!-- 配置过滤器,解决POST提交的中文参数乱码问题 -->
<filter> 
<filter-name>encodingFilter</filter-name> <filter- class>org.springframework.web.filter.CharacterEncodingFilter</filt er-class> 
<init-param> 
<param-name>encoding</param-name> 
<param-value>UTF8</param-value> 
</init-param> 
</filter>
 <filter-mapping> 
 <filter-name>encodingFilter</filter-name> 
 <url-pattern>/*</url-pattern> 
</filter-mapping>

Servlet中,两种请求方式乱码解决方案回顾:
(1)如果请求方式为POST提交,必然会出现乱码,解决方式是在任何获取参数的代码之前,添加如下代码:
request.setCharacterEncoding(“utf-8”);
(2)如果请求方式为GET提交,tomcat8及之后的版本已经解决了GET提交的中文参数乱码问题,因此不需要处理;在 tomcat7 及之前的版本中,获取GET提交的中文参数仍有乱码,解决方法是:只需要在[tomcat]/conf/server.xml中添加如下配置也可
以解决乱码问题。
在这里插入图片描述

6. springmvc响应数据

当请求发起访问Controller中的方法时,可以通过参数声明,在方法内使用Model。

@RequestMapping("/doorList") 
public String doorList(Model model){}

Model对象实际上是一个Map集合,例如:往model中添加一个属性

model.addAttribute(String name, Object value);

其中,addAttribute方法会将属性保存到request域中,再通过转发将属性数据带到相应的JSP中,通过${}取出并显示。
示例,往Model中添加属性:

@RequestMapping("/testModel") 
public String testModel(Model model){ 
/* 往Model添加属性 */ 
model.addAttribute("name", "马云"); model.addAttribute("age", 20); 
return "home"; 
}

在home.jsp中取出属性并显示:

<body>
 <h1>hello springmvc~~~</h1>
  ${ name } <br/>
   ${ age } 
   </body>

7. 扩展内容

7.1. springmvc前端控制器放行静态资源的解决办法
在配置springMVC开发环境时,会在web.xml文件中配置springMVC的前端控制器,将所有请求交给前端控制器处理,因此在url-pattern中配置了斜杠(/):

<!-- 1.配置springmvc前端控制器, 并将所有请求交给springmvc处理 -->
<servlet>
 <servlet-name>springmvc</servlet-name> <servlet- class>org.springframework.web.servlet.DispatcherServlet</servlet- class> 
 <!-- 配置springmvc核心配置文件的位置,默认springmvc的配置文件是在WEB- INF目录下,默认的名字为springmvc-servlet.xml,如果要放在其他目录,则需要 指定如下配置:
-->
<init-param> 
<param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc-config.xml</param-value> 
</init-param> 
</servlet> 
<servlet-mapping> 
<servlet-name>springmvc</servlet-name> 
<!-- 斜杠表示拦截所有请求(除JSP以外) -->
<url-pattern>/</url-pattern>
</servlet-mapping>

url-pattern中配置的斜杠(/)表示将除了JSP以外的其他请求都拦截下来,交给spring的前端控制器来处理。
但是这样配置,会将对静态资源的访问也拦截下来,导致访问静态资源时,出现404(资源找不到),因为spring的前端控制器将对静态资源的访问也当成了一个controller请求,去配置对应的映射路径,这当然找不到。
比如访问:http://localhost/day15-springmvc/home.html,由于配置的是斜杠
(/),所以此时会拦截静态资源,到controller中匹配路径为/home.html的方法,此时自然是匹配不到的。
如果需要访问到静态资源,让前端控制器对静态资源的请求放行。此时可以在
springmvc-config.xml文件中添加放行静态资源的配置:

<!-- 1.配置前端控制器放行静态资源(html/css/js等,否则静态资源将无法访问) -
->
<mvc:default-servlet-handler/>

四、Maven项目整合MyBatis、Spring、Springmvc

1、创建Maven web项目

pom.xml插入以下依赖

<dependencies>
		<!-- 单元测试 -->
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.10</version>
		</dependency>
		<!-- 整合log4j -->
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
			<version>1.6.4</version>
		</dependency>
		<!-- Servlet/JSP/JSTL -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>servlet-api</artifactId>
			<version>2.5</version>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jsp-api</artifactId>
			<version>2.0</version>
		</dependency>
		<dependency>
			<groupId>jstl</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
		</dependency>
		<!-- 整合spring框架(包含springmvc) 这个jar文件包含springmvc开发时的核心类, 同时也会将依赖的相关jar文件引入进来(spring的核心jar文件也包含在内) -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>4.1.3.RELEASE</version>
		</dependency>
		<!--这个jar文件包含对Spring对JDBC数据访问进行封装的所有类 -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-jdbc</artifactId>
			<version>4.1.3.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-aspects</artifactId>
			<version>4.1.3.RELEASE</version>
		</dependency>
		<!-- 整合mybatis框架 -->
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis</artifactId>
			<version>3.2.8</version>
		</dependency>
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis-spring</artifactId>
			<version>1.2.2</version>
		</dependency>
		<!-- mysql驱动 -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.32</version>
		</dependency>
		<!-- druid连接池 -->
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>druid</artifactId>
			<version>1.1.6</version>
		</dependency>

	</dependencies>

2、web.xml配置mvc

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	version="2.5">
	<display-name>day17-springmvc</display-name>
	<welcome-file-list>
		<welcome-file>index.html</welcome-file>
		<welcome-file>index.htm</welcome-file>
		<welcome-file>index.jsp</welcome-file>
		<welcome-file>default.html</welcome-file>
		<welcome-file>default.htm</welcome-file>
		<welcome-file>default.jsp</welcome-file>
	</welcome-file-list>
	
	<!-- 配置全站500异常跳转页面 -->
	<error-page>
		<error-code>500</error-code>
		<location>/WEB-INF/pages/500.jsp</location>
	</error-page>
	
	<!-- 配置全站404跳转页面 -->
	<error-page>
		<error-code>404</error-code>
		<location>/WEB-INF/pages/404.jsp</location>
	</error-page>
	
	
	<!-- 配置springmvc的前端控制器(DispatcherServlet) ctrl+shift+T -->
	<servlet>
		<servlet-name>springmvc</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<!-- 配置springmvc核心配置文件的位置(contextConfigLocation是固定的名字,不能写错!) -->
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:spring/*.xml</param-value>
		</init-param>
	</servlet>
	<servlet-mapping>
		<servlet-name>springmvc</servlet-name>
		<!-- 配置前端控制器拦截对当前应用下的所有资源的访问(除了JSP) -->
		<url-pattern>/</url-pattern>
	</servlet-mapping>

	<!-- 配置(启用)springmvc的乱码处理过滤器,用于处理POST提交中文参数的乱码问题! -->
	<filter>
		<filter-name>encodingFilter</filter-name>
		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
		<init-param>
			<param-name>encoding</param-name>
			<param-value>UTF8</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>encodingFilter</filter-name>
		<!-- /*表示拦截所有的请求,对所有请求都进行过滤 -->
		<url-pattern>/*</url-pattern>
	</filter-mapping>

</web-app>




3、配置数据库、log4j

在这里插入图片描述
jdbc.properties

db.driverClassName=com.mysql.jdbc.Driver
db.url=jdbc:mysql:///yonghedb?characterEncoding=utf-8
db.username=root
db.password=123456

log4j.properties
# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# 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

4、配置mybatis
在这里插入图片描述
mapper目录写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">
<!-- 门店表的映射文件	namespace值为对应接口的全路径 -->
<mapper namespace="">
	
</mapper>

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">
    
<!-- MyBatis的全局配置文件 -->
<configuration >
	<!-- 将开发环境配置和导入mapper文件的配置移除,在spring的配置文件中进行配置,所以下面的配置注释起来了 -->
	<!-- 1.配置开发环境 -->
	<!--<environments default="develop">
		<!-- 这里可以配置多个环境,比如develop,test等 -->
		<environment id="develop">
			<!-- 1.1.配置事务管理方式:JDBC:将事务交给JDBC管理(推荐) -->
			<transactionManager type="JDBC"></transactionManager>
			<!-- 1.2.配置数据源,即连接池方式:JNDI/POOLED/UNPOOLED -->
			<dataSource type="POOLED">
				<property name="driver" value="com.mysql.jdbc.Driver"/>
				<property name="url" value="jdbc:mysql://localhost:3306/yonghedb?characterEncoding=utf-8"/>
				<property name="username" value="root"/>
				<property name="password" value="root"/>
			</dataSource>
		</environment>
	</environments>
	
	<!-- 2.加载Mapper配置文件,路径以斜杠间隔: xx/xx/../xx.xml -->
	<mappers>
		<mapper resource="mybatis/mapper/DoorMapper.xml"/>
	</mappers>-->
</configuration>

5、配置spring、springmvc配置文件
在这里插入图片描述
applicationContext.xml

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
	http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
	http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">
	
	<!-- 1.加载jdbc.properties文件的位置 -->
	<context:property-placeholder location="classpath:jdbc.properties"/>
	
	<!-- 2.配置druid连接池 ,id是固定值,class是druid连接池类的全路径 -->
	<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
		<!-- 配置连接数据库的基本信息 -->
		<property name="driverClassName" value="${db.driverClassName}"></property>
		<property name="url" value="${db.url}"></property>
		<property name="username" value="${db.username}"></property>
		<property name="password" value="${db.password}"></property>
	</bean>
	
	<!-- 3.整合spring和mybatis框架	
		将SqlSession等对象的创建交给Spring容器
		id值(sqlSessionFactory)是固定值
	 -->
	<bean id="sqlSessionFactory" 
		class="org.mybatis.spring.SqlSessionFactoryBean">
		<!-- 3.1.指定mybatis核心配置文件的位置 -->
		<property name="configLocation" 
				value="classpath:mybatis/mybatis-config.xml"></property>
		<!-- 3.2.配置连接池(数据源) ref指向连接池bean对象的id值 -->
		<property name="dataSource" ref="dataSource"></property>
		<!-- 3.3、扫描所有的 XxxMapper.xml映射文件,读取其中配置的SQL语句 -->
		<property name="mapperLocations" value="classpath:mybatis/mapper/*.xml"/>
	</bean>
	
	<!-- 4、定义mapper接口扫描器
		如果bean标签上没有id,默认id值是当前类的类名,首字母小写
		扫描 com.tedu.dao包下的所有接口,由框架负责提供接口的子类
		由spring容器负责创建接口的子类实例(spring会将创建好的接口子类实例存到map中)
	 -->
	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
		<!-- 扫描所有XxxMapper接口,将接口实例的创建交给spring容器 -->
		<property name="basePackage" 
			value="com.tedu.dao"/>
	</bean>
	
	
</beans>

springmvc-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/mvc
						http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
						http://www.springframework.org/schema/beans
						http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
						http://www.springframework.org/schema/context
          				http://www.springframework.org/schema/context/spring-context-4.0.xsd">
	
	<!-- 1.配置前端控制器放行静态资源(html/css/js等,否则静态资源将无法访问) -->
	<mvc:default-servlet-handler/>
	
	<!-- 2.启用默认配置, 配置注解驱动,用于识别注解(比如@Controller-->
	<mvc:annotation-driven></mvc:annotation-driven>
	
	<!-- 3.配置需要扫描的包:spring自动去扫描 base-package 下的类,
		如果扫描到的类上有 @Controller@Service@Component等注解,
		将会自动将类注册为bean 
	 -->
	<context:component-scan base-package="com.tedu.controller">
	</context:component-scan>
	
	<!-- 4.配置内部资源视图解析器
		prefix:配置路径前缀
		suffix:配置文件后缀
	 -->
	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/WEB-INF/pages/"/>
		<property name="suffix" value=".jsp"/>
	</bean>
	
	
</beans>

6、可以写Java代码了
在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值