Mybatis
代理模式浅析
mybatis是如何通过代理模式实现查询的
这条语句的底层使用了动态代理模式,动态创建一个EmployeeMapper的一个代理对象并赋给接口引用。所以在MyBatis中不需要显式提供Mapper接口的实现类,这也是简单的地方。
代理模式下开发各种功能
1_多种参数传递问题
1单个基本数据类型
2多个基本数据类型
3单个引用数据类型
4map集合数据类型
5多个引用数据类型
接口
1. package com.msb.mapper;
2.
3. import com.msb.pojo.Emp;
4. import org.apache.ibatis.annotations.Param;
5.
6. import java.util.List;
7. import java.util.Map;
8.
9. /**
10. * @Author: Ma HaiYang
11. * @Description: MircoMessage:Mark_7001
12. */
13. public interface EmpMapper {
14. /**
15. * 该方法用于查询全部的员工信息
16. * @return 全部员工信息封装的Emp对象的List集合
17. */
18. List<Emp> findAll();
19.
20. /**
21. * 根据员工编号查询单个员工信息的方法
22. * @param empno 员工编号
23. * @return 如果找到了返回Emp对象,找不到返回null
24. */
25. Emp findByEmpno(int empno);
26.
27.
28. /**
29. * 根据员工编号和薪资下限去查询员工信息
30. * @param empno 员工编号
31. * @param sal 薪资下限
32. * @return 多个Emp对象的List集合
33. */
34. List<Emp> findByDeptnoAndSal(@Param("deptno") int deptno,@Param("sal") double sal);
35.
36. List<Emp> findByDeptnoAndSal2(Map<String,Object> map);
37.
38. List<Emp> findByDeptnoAndSal3(Emp emp);
39.
40. List<Emp> findByDeptnoAndSal4(@Param("empa") Emp empa,@Param("empb") Emp empb);
41.
42.
43. }
44.
45.
mapper映射文件
1. <?xml version="1.0" encoding="UTF-8" ?>
2. <!DOCTYPE mapper
3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
5.
6. <mapper namespace="com.msb.mapper.EmpMapper">
7.
8. <!--
9. 1 接口的名字和Mapper映射为文件名字必须保持一致(不包含拓展名)
10. 2 Mapper映射文件的namespace必须是接口的全路径名
11. 3 sql语句的id必须是对应方法的名
12. 4 DeptMapper映射文件应该和接口编译之后放在同一个目录下
13. -->
14. <!--List<Emp> findAll();-->
15. <select id="findAll" resultType="emp" >
16. select * from emp
17. </select>
18.
19.
20. <!--
21. 单个基本数据类型作为方法参数
22. #{}中可以随便写,遵循见名知意
23. Emp findByEmpno(int empno);
24. -->
25. <select id="findByEmpno" resultType="emp" >
26. select * from emp where empno =#{empno}
27. </select>
28.
29.
30. <!--
31. 多个基本数据类型作为方法参数
32. List<Emp> findByDeptnoAndSal(@Param("detpno") int deptno,@Param("sal") double sal);
33. 方式1 arg* arg0 arg1 arg2 数字是索引,从0开始
34. 方式2 param* param1 param2 param3 数字是编号,从1开始
35. 使用别名
36. List<Emp> findByDeptnoAndSal(@Param("detpno") int deptno,@Param("sal") double sal);
37. 通过@Param注解使用别名之后,就不能再使用arg* 但是可以继续使用param*
38. -->
39. <select id="findByDeptnoAndSal" resultType="emp">
40. <!--select * from emp where deptno =#{arg0} and sal >= #{arg1}-->
41. <!-- select * from emp where deptno =#{param1} and sal >= #{param2}-->
42. <!-- select * from emp where deptno =#{deptno} and sal >= #{sal}-->
43. </select>
44.
45.
46. <!--
47. 参数是map,{}写键的名字
48. -->
49. <select id="findByDeptnoAndSal2" resultType="emp" parameterType="map" >
50. <!--select * from emp where deptno =#{arg0} and sal >= #{arg1}-->
51. <!-- select * from emp where deptno =#{param1} and sal >= #{param2}-->
52. select * from emp where deptno =#{deptno} and sal >= #{sal}
53. </select>
54.
55. <!--单个引用类型,{}中写的使用对象的属性名-->
56. <select id="findByDeptnoAndSal3" resultType="emp" parameterType="emp" >
57.
58. select * from emp where deptno =#{deptno} and sal >= #{sal}
59. </select>
60.
61. <!--
62. 多个引用类型作为方法参数
63. List<Emp> findByDeptnoAndSal4(@Param("empa") Emp empa,@Param("empb") Emp empb);
64. 如果用@Param定义了别名,那么就不能使用arg*.属性名,但是可以使用param*.属性名和别名.属性名
65. -->
66. <select id="findByDeptnoAndSal4" resultType="emp" >
67. <!-- select * from emp where deptno =#{arg0.deptno} and sal >= #{arg1.sal} -->
68. select * from emp where deptno =#{param1.deptno} and sal >= #{param2.sal}
69. <!-- select * from emp where deptno =#{empa.deptno} and sal >= #{empb.sal}-->
70. </select>
71.
72. </mapper>
73.
测试 代码
1. package com.msb.testDemo;
2.
3. import com.msb.mapper.EmpMapper;
4. import com.msb.pojo.Emp;
5. import com.msb.util.SqlSessionUtil;
6. import org.apache.ibatis.session.SqlSession;
7.
8. import java.util.List;
9.
10. public class Test1 {
11. public static void main(String[] args) {
12. SqlSession sqlSession = SqlSessionUtil.getSqlSession(true);
13. /*
14. * 帮助我们生成一个接口下的实现类对象的
15. *
16. * */
17. EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
18. List<Emp> emps = mapper.getAllEmp();
19. for(Emp emp:emps) {
20. System.out.println(emp);
21. }
22. // 1单个基本数据类型作为方法参数
23. Emp emp = mapper.getByEmpno(7902);
24. System.out.println(emp);
25. // 2多个基本数据类型作为方法参数
26. List<Emp> emps2 = mapper.getByDeptnoAndSal(10, 1500);
27. for(Emp em:emps2) {
28. System.out.println(em);
29. }
30. // 3单个引用类型作为方法参数
31. Emp condition=new Emp();
32. condition.setDeptno(10);
33. condition.setSal(1500.0);
34. List<Emp> emps3 = mapper.getByDeptnoAndSal2(condition);
35. for(Emp em:emps3) {
36. System.out.println(em);
37. }
38.
39. // 4多个引用类型作为方法参数
40. Emp condition1=new Emp();
41. condition1.setDeptno(10);
42. Emp condition2=new Emp();
43. condition2.setSal(1500.0);
44. List<Emp> emps4 = mapper.getByDeptnoAndSal3(condition1,condition2);
45. for(Emp em:emps4) {
46. System.out.println(em);
47. }
48. sqlSession.close();
49. }
50. }
51.
2_模糊查询功能
在进行模糊查询时,在映射文件中可以使用concat()函数来连接参数和通配符。另外注意对于特殊字符,比如<,不能直接书写,应该使用字符实体替换。
接口
1. /**
2. * 根据名字做模糊查询
3. * @param name 模糊查询的文字
4. * @return Emp对象List集合
5. */
6. List<Emp> findByEname( String name);
mapper映射文件
1. <!--List<Emp> getByName(String name);-->
2. <select id="findByEname" resultType="emp" >
3. select * from emp where ename like concat('%',#{name},'%')
4. </select>
5.
3_主键自增回填
MySQL支持主键自增。有时候完成添加后需要立刻获取刚刚自增的主键,由下一个操作来使用。比如结算构造车后,主订单的主键确定后,需要作为后续订单明细项的外键存在。如何拿到主键呢,MyBatis提供了支持,可以非常简单的获取。
接口
1. public interface DeptMapper {
2. int addDept(Dept dept);
3. int addDept2(Dept dept);
4. }
mapper映射文件
1. <mapper namespace="com.msb.mapper.DeptMapper">
2. <!-- int addDept(Dept dept);
3. useGeneratedKeys="true" 返回数据库帮我们生成的主键
4. keyProperty="deptno" 生成的主键值用我们dept对象那个属性存储
5. -->
6. <insert id="addDept" parameterType="dept" useGeneratedKeys="true" keyProperty="deptno">
7. insert into dept values(null,#{dname},#{loc})
8. </insert>
9.
10. <insert id="addDept2" parameterType="dept">
11. <selectKey order="AFTER" keyProperty="deptno" resultType="int">
12. select @@identity
13. </selectKey>
14. insert into dept values(null,#{dname},#{loc})
15. </insert>
16. </mapper>
17.
测试代码
1. SqlSession sqlSession = SqlSessionUtil.getSqlSession(true);
2. DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
3. Dept dept =new Dept(null,"AI学院","北京");
4. int i = mapper.addDept2(dept);
5. System.out.println(i);
6. System.out.println(dept.getDeptno());
7. sqlSession.close();
方式1
useGeneratedKeys:表示要使用自增的主键
keyProperty:表示把自增的主键赋给JavaBean的哪个成员变量。
以添加Dept对象为例,添加前Dept对象的deptno是空的,添加完毕后可以通过getDeptno() 获取自增的主键。
方式2
order:取值AFTER|BEFORE,表示在新增之后|之前执行<selectKey>中的SQL命令
keyProperty:执行select @@identity后结果填充到哪个属性中
resultType:结果类型。
- 技术扩展
- 在很多应用场景中需要新增数据后获取到新增数据的主键值,针对这样的需求一般由三种解决方式:
- 主键自定义,用户通过UUID或时间戳等方式生成唯一主键,把这个值当做主键值。在分布式场景中应用较多。
- 查询后通过select max(主键) from 表获取主键最大值。这种方式在多线程访问情况下可能出现问题。
- 查询后通过select @@identity获取最新生成主键。要求这条SQL必须在insert操作之后,且数据库连接没有关闭。
4_实现DML操作
EmpMapper接口
1. /**
2. * 增加员工信息
3. * @param emp 存储新增员工信息的Emp对象
4. * @return 对数据库数据产生影响的行数
5. */
6. int addEmp(Emp emp);
7.
8. /**
9. * 根据员工编号修改员工姓名的方法
10. * @param empno 要修改的员工编号
11. * @param ename 修改之后的新的员工名字
12. * @return 对数据库数据产生影响的行数
13. */
14. int updateEnameByEmpno(@Param("empno") int empno,@Param("ename") String ename);
15.
16. /**
17. * 根据员工编号删除员工信息
18. * @param empno 要删除的员工编号
19. * @return 对数据库数据产生影响的行数
20. */
21. int deleteByEmpno(int empno);
EmpMapper映射 文件
1. <!--int addEmp(Emp emp);-->
2. <insert id="addEmp" >
3. insert into emp values(DEFAULT ,#{ename},#{job},#{mgr},#{hiredate},#{sal},#{comm},#{deptno})
4. </insert>
5. <!--int updateEnameByEmpno(@Param("empno") int empno,@Param("ename") String ename);-->
6. <update id="updateEnameByEmpno" >
7. update emp set ename =#{ename} where empno =#{empno}
8. </update>
9. <!--int deleteByEmpno(int empno);-->
10. <update id="deleteByEmpno" >
11. delete from emp where empno =#{empno}
12. </update>
测试代码
1. package com.msb.test;
2.
3. import com.msb.mapper.DeptMapper;
4. import com.msb.mapper.EmpMapper;
5. import com.msb.pojo.Dept;
6. import com.msb.pojo.Emp;
7. import org.apache.ibatis.io.Resources;
8. import org.apache.ibatis.session.SqlSession;
9. import org.apache.ibatis.session.SqlSessionFactory;
10. import org.apache.ibatis.session.SqlSessionFactoryBuilder;
11. import org.junit.After;
12. import org.junit.Before;
13. import org.junit.Test;
14.
15. import java.io.IOException;
16. import java.io.InputStream;
17. import java.util.Date;
18.
19. /**
20. * @Author: Ma HaiYang
21. * @Description: MircoMessage:Mark_7001
22. */
23. public class Test3 {
24.
25. private SqlSession sqlSession;
26. @Before
27. public void init(){
28. SqlSessionFactoryBuilder ssfb =new SqlSessionFactoryBuilder();
29. InputStream resourceAsStream = null;
30. try {
31. resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
32. } catch (IOException e) {
33. e.printStackTrace();
34. }
35. SqlSessionFactory factory=ssfb.build(resourceAsStream) ;
36. sqlSession=factory.openSession();
37. }
38.
39.
40. @Test
41. public void testAddEmp(){
42. EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
43. mapper.addEmp(new Emp(null, "TOM", "SALESMAN", 7521, new Date(), 2314.0, 100.0, 10));
44. sqlSession.commit();
45. }
46.
47. @Test
48. public void testUpdateEnameByEmpno(){
49. EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
50. mapper.updateEnameByEmpno(7938, "TOM");
51. sqlSession.commit();
52. }
53.
54. @Test
55. public void testDeletByEmpno(){
56. EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
57. mapper.deleteByEmpno(7938);
58. sqlSession.commit();
59. }
60.
61.
62.
63.
64. @After
65. public void release(){
66. // 关闭SQLSession
67. sqlSession.close();
68. }
69.
70. }
7_动态SQL
经常遇到很多按照很多查询条件进行查询的情况,比如京东根据不同的条件筛选商品。其中经常出现很多条件不取值的情况,在后台应该如何完成最终的SQL语句呢?
如果采用JDBC进行处理,需要根据条件是否取值进行SQL语句的拼接,一般情况下是使用StringBuilder类及其append方法实现,还是有些繁琐的。如果你有使用 JDBC 或其它类似框架的经验,你就能体会到根据不同条件拼接 SQL语句的痛苦。例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL 这一特性可以彻底摆脱这种痛苦。
MyBatis在简化操作方法提出了动态SQL功能,将使用Java代码拼接SQL语句,改变为在XML映射文件中截止标签拼接SQL语句。相比而言,大大减少了代码量,更灵活、高度可配置、利于后期维护。
MyBatis中动态SQL是编写在mapper.xml中的,其语法和JSTL类似,但是却是基于强大的OGNL表达式实现的。
MyBatis也可以在注解中配置SQL,但是由于注解功能受限,尤其是对于复杂的SQL语句,可读性很差,所以较少使用。
1_if标签
接口
1. public interface EmpMapper2 {
2. List<Emp> findByCondition(Emp emp);
3. }
4.
映射文件
1. <?xml version="1.0" encoding="UTF-8" ?>
2. <!DOCTYPE mapper
3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
5.
6. <mapper namespace="com.msb.mapper.EmpMapper2">
7.
8. <!--List<Emp> findByCondition(Emp emp);-->
9. <select id="findByCondition" resultType="emp">
10. select * from emp where 1=1
11. <if test="empno != null">
12. and empno =#{empno}
13. </if>
14. <if test="ename != null and ename != ''">
15. and ename like concat('%',#{ename},'%')
16. </if>
17. <if test="job != null and job != ''">
18. and job =#{job}
19. </if>
20. <if test="mgr != null">
21. and mgr =#{mgr}
22. </if>
23. <if test="hiredate != null">
24. and hiredate =#{hiredate}
25. </if>
26. <if test="sal != null">
27. and sal =#{sal}
28. </if>
29. <if test="comm != null">
30. and comm =#{comm}
31. </if>
32. <if test="deptno != null">
33. and deptno =#{deptno}
34. </if>
35.
36. </select>
37.
38. </mapper>
39.
测试代码
1. public static void main(String[] args) {
2. SqlSession sqlSession = MyBatisUtil.getSqlSession(false);
3. EmpMapper2 mapper = sqlSession.getMapper(EmpMapper2.class);
4. Emp condition =new Emp();
5. /* condition.setDeptno(20);*/
6. /* condition.setSal(3000.0);*/
7. /*condition.setHiredate(new java.sql.Date(81,1,22));*/
8. condition.setComm(0.0);
9. condition.setDeptno(20);
10. List<Emp> emps = mapper.findEmpByCondition(condition);
11. for (Emp e:emps
12. ) {
13. System.out.println(e);
14. }
15. }
16.
17.
2_where标签
用于处理where关键字和and
1. <select id="findEmpByCondition" resultType="emp">
2. select * from emp
3. <where>
4. <if test="empno != null">
5. and empno= #{empno}
6. </if>
7. <if test="ename != null and ename != ''">
8. and ename= #{ename}
9. </if>
10. <if test="job != null and job != ''">
11. and job= #{job}
12. </if>
13. <if test="mgr != null ">
14. and mgr= #{mgr}
15. </if>
16. <if test="hiredate != null ">
17. and hiredate= #{hiredate}
18. </if>
19. <if test="sal != null">
20. and sal= #{sal}
21. </if>
22. <if test="comm != null ">
23. and comm =#{comm}
24. </if>
25. <if test="deptno != null ">
26. and deptno= #{deptno}
27. </if>
28. </where>
29. </select>
30.
3_choose标签
前面的when条件成立 后面的 when就不再判断了
1. <select id="findEmpByCondition2" resultType="emp">
2. select * from emp
3. <where>
4. <choose>
5. <when test="empno != null">
6. and empno= #{empno}
7. </when>
8. <when test="ename != null and ename != ''">
9. and ename= #{ename}
10. </when>
11. <when test="job != null and job != ''">
12. and job= #{job}
13. </when>
14. <when test="mgr != null ">
15. and mgr= #{mgr}
16. </when>
17. <when test="hiredate != null ">
18. and hiredate= #{hiredate}
19. </when>
20. <when test="sal != null">
21. and sal= #{sal}
22. </when>
23. <when test="comm != null ">
24. and comm =#{comm}
25. </when>
26. <when test="deptno != null ">
27. and deptno= #{deptno}
28. </when>
29. </choose>
30. </where>
31. </select>
32.
4_set标签
接口
1. int updateEmpByCondtion(Emp emp);
映射文件
1. <!--int updateEmpByCondtion(Emp emp);-->
2. <update id="updateEmpByCondtion" >
3. update emp
4. <set>
5. <if test="ename != null and ename != '' ">
6. , ename =#{ename}
7. </if>
8. <if test="job != null and ename != '' ">
9. , job =#{job}
10. </if>
11. <if test="mgr != null ">
12. , mgr =#{mgr}
13. </if>
14. <if test="hiredate != null ">
15. , hiredate =#{hiredate}
16. </if>
17. <if test="sal != null ">
18. , sal =#{sal}
19. </if>
20. <if test="comm != null ">
21. , comm =#{comm}
22. </if>
23. <if test="deptno != null ">
24. , deptno =#{deptno}
25. </if>
26. </set>
27. where empno =#{empno}
28. </update>
5_trim标签
- Trim 标签处理 set
1.
2. <update id="updateEmpByCondition2" >
3. update emp
4. <!--prefix 要增加什么前缀
5. prefixOverrides 要去除什么前缀
6. suffix 要增加什么后缀
7. suffixOverrides 要去除什么后缀
8. set 是trim的一种特殊情况
9. -->
10. <trim prefix="set" suffixOverrides="," >
11. <if test="ename != null and ename != ''">
12. ename= #{ename},
13. </if>
14. <if test="job != null and job != ''">
15. job= #{job},
16. </if>
17. <if test="mgr != null ">
18. mgr= #{mgr},
19. </if>
20. <if test="hiredate != null ">
21. hiredate= #{hiredate},
22. </if>
23. <if test="sal != null">
24. sal= #{sal},
25. </if>
26. <if test="comm != null ">
27. comm =#{comm},
28. </if>
29. <if test="deptno != null ">
30. deptno= #{deptno},
31. </if>
32. </trim>
33. where empno = #{empno}
34. </update>
35.
Trim标签 处理where
2. <select id="findEmpByCondition" resultType="emp">
3. select * from emp
4. <trim prefix="where" prefixOverrides="and">
5. <if test="empno != null">
6. and empno= #{empno}
7. </if>
8. <if test="ename != null and ename != ''">
9. and ename= #{ename}
10. </if>
11. <if test="job != null and job != ''">
12. and job= #{job}
13. </if>
14. <if test="mgr != null ">
15. and mgr= #{mgr}
16. </if>
17. <if test="hiredate != null ">
18. and hiredate= #{hiredate}
19. </if>
20. <if test="sal != null">
21. and sal= #{sal}
22. </if>
23. <if test="comm != null ">
24. and comm =#{comm}
25. </if>
26. <if test="deptno != null ">
27. and deptno= #{deptno}
28. </if>
29. </trim>
30.
31.
32. </select>
6_bind标签
一般用于处理模糊查询的模板
接口
List<Emp> findEmpByEname(@param("a")String name);
SQL语句
7_sql标签
1. <sql id="empColumn">empno,ename,job,mgr,hiredate,sal,comm,deptno</sql>
2. <sql id="baseSelect">select <include refid="empColumn"></include> from emp</sql>
3.
4. <!--List<Emp> findByCondition(Emp emp);-->
5. <select id="findByCondition" resultType="emp">
6. <include refid="baseSelect"></include>
7. <trim prefix="where" prefixOverrides="and">
8. <if test="empno != null">
9. and empno =#{empno}
10. </if>
11. <if test="ename != null and ename != ''">
12. <bind name="likePattern" value="'%'+ename+'%'"/>
13. and ename like #{likePattern}
14. </if>
15. <if test="job != null and job != ''">
16. and job =#{job}
17. </if>
18. <if test="mgr != null">
19. and mgr =#{mgr}
20. </if>
21. <if test="hiredate != null">
22. and hiredate =#{hiredate}
23. </if>
24. <if test="sal != null">
25. and sal =#{sal}
26. </if>
27. <if test="comm != null">
28. and comm =#{comm}
29. </if>
30. <if test="deptno != null">
31. and deptno =#{deptno}
32. </if>
33. </trim>
34.
35. </select>
36.
8_foreach标签
1. <!--List<Emp> findByEmpnos1(int[] empnos);
2. collection="" 遍历的集合或者是数组
3. 参数是数组,collection中名字指定为array
4. 参数是List集合,collection中名字指定为list
5. separator="" 多个元素取出的时候 用什么文字分隔
6. open="" 以什么开头
7. close="" 以什么结尾
8. item="" 中间变量名
9. for(Person per:PersonList)
10.
11. -->
12. <select id="findByEmpnos1" resultType="emp">
13. select * from emp where empno in
14. <foreach collection="array" separator="," open="(" close=")" item="deptno">
15. #{deptno}
16. </foreach>
17. </select>
18.
19.
20. <!-- List<Emp> findByEmpnos2(List<Integer> empnos);-->
21. <select id="findByEmpnos2" resultType="emp">
22. select * from emp where empno in
23. <foreach collection="list" separator="," open="(" close=")" item="deptno">
24. #{deptno}
25. </foreach>
26. </select>
27.
8_MyBatis实现多表查询
前面已经使用MyBatis完成了对Emp表的CRUD操作,不管是使用SqlSession直接操作,还是使用Mapper代理方式,都只是完成了对单个数据库表的操作。这肯定是远远不够的。
在实际开发中,经常会将来自多张表的数据在一个位置显示。比如查询并显示的员工信息中会有来自部门表、岗位表的数据,而后台一般是定义一个方法
1_关联查询
1_手动处理映射关系
1. 实体类
2. public class Emp implements Serializable {
3. private Integer empno;
4. private String name;
5. private String job;
6. private Integer mgr;
7. private Date hiredate;
8. private Double sal;
9. private Double comm;
10. private Integer deptno;
11.
12.
13. 映射文件
14. <mapper namespace="com.msb.mapper.EmpMapper">
15. <!--手动处理数据库查询字段和封装实体类属性之间的映射关系
16. 1 主键一般使用id属性
17. 2 当属性名和查询出的数据表字段名相同 可以不写映射关系
18. -->
19. <resultMap id="empMap" type="emp">
20. <!--<id property="empno" column="empno"></id>-->
21. <result property="name" column="ename"></result>
22. <!--<result property="job" column="job"></result>
23. <result property="sal" column="sal"></result>
24. <result property="hiredate" column="hiredate"></result>
25. <result property="mgr" column="mgr"></result>
26. <result property="comm" column="comm"></result>
27. <result property="deptno" column="deptno"></result>-->
28. </resultMap>
29. <select id="findByEmpno" resultMap="empMap" >
30. select * from emp where empno =#{empno}
31. </select>
32. </mapper>
2_一对一关联查询
数据准备: 创建项目表和项目记录表
1.
2. CREATE TABLE `projects` (
3. `pid` int(2) NOT NULL AUTO_INCREMENT,
4. `pname` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
5. `money` int(11) NULL DEFAULT NULL,
6. PRIMARY KEY (`pid`) USING BTREE
7. ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
8.
9. INSERT INTO `projects` VALUES (1, ' ***大学OA', 500000);
10. INSERT INTO `projects` VALUES (2, '学生选课系统', 100000);
11. INSERT INTO `projects` VALUES (3, '讲师测评系统', 20000);
12. INSERT INTO `projects` VALUES (4, '线上问答系统 ', 20000);
13.
14. CREATE TABLE `projectrecord` (
15. `empno` int(4) NOT NULL,
16. `pid` int(2) NOT NULL,
17. PRIMARY KEY (`empno`, `pid`) USING BTREE,
18. INDEX `fk_project_pro`(`pid`) USING BTREE,
19. CONSTRAINT `fk_emp_pro` FOREIGN KEY (`empno`) REFERENCES `emp` (`EMPNO`) ON DELETE CASCADE ON UPDATE CASCADE,
20. CONSTRAINT `fk_project_pro` FOREIGN KEY (`pid`) REFERENCES `projects` (`pid`) ON DELETE CASCADE ON UPDATE CASCADE
21. ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
22.
23.
24. INSERT INTO `projectrecord` VALUES (7369, 1);
25. INSERT INTO `projectrecord` VALUES (7521, 1);
26. INSERT INTO `projectrecord` VALUES (7369, 2);
27. INSERT INTO `projectrecord` VALUES (7499, 2);
28. INSERT INTO `projectrecord` VALUES (7521, 2);
29. INSERT INTO `projectrecord` VALUES (7369, 3);
30. INSERT INTO `projectrecord` VALUES (7499, 3);
31. INSERT INTO `projectrecord` VALUES (7521, 3);
32. INSERT INTO `projectrecord` VALUES (7369, 4);
33. INSERT INTO `projectrecord` VALUES (7499, 4);
需求:根据编号查询员工信息及所在的部门信息
实体类添加一个部门作为属性
实体类
1. @AllArgsConstructor
2. @NoArgsConstructor
3. @Data
4. public class Emp implements Serializable {
5. private Integer empno;
6. private String ename;
7. private String job;
8. private Integer mgr;
9. private Date hiredate;
10. private Double sal;
11. private Double comm;
12. private Integer deptno;
13. // 组合一个Dept对象作为自己的属性
14. private Dept dept;
15.
16. }
17.
接口
1. public interface EmpMapper {
2.
3. /**
4. * 根据员工编号查询员工的所有信息并携带所在的部门信息
5. * @param empno 要查询的员工编号
6. * @return Emp对象,组合了Dept对象作为属性,对部门信息进行存储
7. */
8. Emp findEmpJoinDeptByEmpno(int empno);
9.
10.
11. }
12.
映射文件
1. <?xml version="1.0" encoding="UTF-8" ?>
2. <!DOCTYPE mapper
3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
5.
6. <mapper namespace="com.msb.mapper.EmpMapper">
7.
8.
9. <!--Emp findEmpJoinDeptByEmpno(int empno);-->
10. <resultMap id="empJoinDept" type="emp">
11. <!--设置emp本身的八个属性的映射关系-->
12. <id property="empno" column="empno"></id>
13. <result property="ename" column="ename"></result>
14. <result property="job" column="job"></result>
15. <result property="sal" column="sal"></result>
16. <result property="hiredate" column="hiredate"></result>
17. <result property="mgr" column="mgr"></result>
18. <result property="comm" column="comm"></result>
19. <result property="deptno" column="deptno"></result>
20. <!--
21. association 处理一对一
22. 封装一对一信息关系的标签
23. property emp类的属性名
24. javaType 用哪个类的对象给属性赋值
25. -->
26. <association property="dept" javaType="dept">
27. <id column="deptno" property="deptno"></id>
28. <result column="dname" property="dname"></result>
29. <result column="loc" property="loc"></result>
30. </association>
31.
32. </resultMap>
33.
34. <select id="findEmpJoinDeptByEmpno" resultMap="empJoinDept" >
35. select * from
36. emp e
37. left join dept d
38. on e.deptno =d.deptno
39. where empno = #{empno}
40. </select>
41.
42.
43. </mapper>
测试代码
1. @Test
2. public void testOneToOne() throws ParseException {
3. EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
4. Emp emp = mapper.findEmpJoinDeptByEmpno(7499);
5. System.out.println(emp);
6.
7.
8. }
3_一对多关联查询
根据部门号查询部门信息及该部门的所有员工信息
实体类
1. @Data
2. @AllArgsConstructor
3. @NoArgsConstructor
4. public class Dept implements Serializable {
5. private Integer deptno;
6. private String dname;
7. private String loc;
8.
9. // 组合一个Emp的List集合作为属性
10. private List<Emp> empList;
11. }
12.
接口
1. package com.msb.mapper;
2.
3. import com.msb.pojo.Dept;
4.
5. /**
6. * @Author: Ma HaiYang
7. * @Description: MircoMessage:Mark_7001
8. */
9. public interface DeptMapper {
10. /**
11. * 根据部门编号查询部门信息及该部分的所有员工信息
12. * @param deptno 要查询的部门编号
13. * @return Dept对象,内部组合了一个Emp的List属性用于封装部门的所有员工信息
14. */
15. Dept findDeptJoinEmpsByDeptno(int deptno);
16.
17. }
18.
映射文件
1. <?xml version="1.0" encoding="UTF-8" ?>
2. <!DOCTYPE mapper
3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
5.
6. <mapper namespace="com.msb.mapper.DeptMapper">
7. <!--Dept findDeptJoinEmpsByDeptno(int deptno);-->
8. <resultMap id="deptJoinEmps" type="dept">
9. <id column="deptno" property="deptno"></id>
10. <result column="dname" property="dname"></result>
11. <result column="loc" property="loc"></result>
12. <!--处理一对多关系的标签-->
13. <collection property="empList" ofType="emp" >
14. <!--设置emp本身的八个属性的映射关系-->
15. <id property="empno" column="empno"></id>
16. <result property="ename" column="ename"></result>
17. <result property="job" column="job"></result>
18. <result property="sal" column="sal"></result>
19. <result property="hiredate" column="hiredate"></result>
20. <result property="mgr" column="mgr"></result>
21. <result property="comm" column="comm"></result>
22. <result property="deptno" column="deptno"></result>
23. </collection>
24. </resultMap>
25.
26.
27. <select id="findDeptJoinEmpsByDeptno" resultMap="deptJoinEmps">
28. select * from dept d left join emp e on d.deptno =e.deptno where d.deptno =#{deptno}
29. </select>
30.
31.
32. </mapper>
测试代码
1. @Test
2. public void testOneToMany() throws ParseException {
3. DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
4. Dept dept = mapper.findDeptJoinEmpsByDeptno(20);
5. System.out.println(dept);
6. System.out.println("---------");
7. List<Emp> empList = dept.getEmpList();
8. empList.forEach(System.out::println);
9.
10. }
11.
4_多对多关联查询
根据项目编号查询项目信息,以及参与到该项目之中的所有的员工信息
实体类
1. @NoArgsConstructor
2. @AllArgsConstructor
3. @Data
4. public class Project implements Serializable {
5. private Integer pid;
6. private String pname;
7. private Integer money;
8.
9. // 组合一个ProjectRecord对象集合作为属性
10. private List<ProjectRecord> projectRecords;
11.
12. }
13.
14.
15.
16. @Data
17. @AllArgsConstructor
18. @NoArgsConstructor
19. public class ProjectRecord implements Serializable {
20. private Integer empno;
21. private Integer pid;
22.
23. // 组合一个Emp对象作为属性
24. private Emp emp;
25. }
接口
1. package com.msb.mapper;
2.
3. import com.msb.pojo.Emp;
4. import com.msb.pojo.Project;
5.
6. /**
7. * @Author: Ma HaiYang
8. * @Description: MircoMessage:Mark_7001
9. */
10. public interface ProjectMapper {
11.
12. /**
13. * 根据项目编号查询一个项目信息及参与该项目的所有员工信息
14. * @param pid 项目编号
15. * @return 所有信息封装的Project对象
16. */
17. Project findProjectJoinEmpsByPid(int pid);
18.
19.
20. }
21.
映射文件
1. <?xml version="1.0" encoding="UTF-8" ?>
2. <!DOCTYPE mapper
3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
5.
6. <mapper namespace="com.msb.mapper.ProjectMapper">
7.
8. <!--Project findProjectJoinEmpsByPid(int pid);-->
9. <resultMap id="projectJoinEmps" type="project">
10. <id column="pid" property="pid"></id>
11. <result column="pname" property="pname"></result>
12. <result column="money" property="money"></result>
13. <!--一对多 集合属性 collection-->
14. <collection property="projectRecords" ofType="projectRecord">
15. <id column="empno" property="empno"></id>
16. <id column="pid" property="pid"></id>
17. <!--一对一 -->
18. <association property="emp" javaType="emp">
19. <id property="empno" column="empno"></id>
20. <result property="ename" column="ename"></result>
21. <result property="job" column="job"></result>
22. <result property="sal" column="sal"></result>
23. <result property="hiredate" column="hiredate"></result>
24. <result property="mgr" column="mgr"></result>
25. <result property="comm" column="comm"></result>
26. <result property="deptno" column="deptno"></result>
27. </association>
28. </collection>
29.
30. </resultMap>
31.
32. <select id="findProjectJoinEmpsByPid" resultMap="projectJoinEmps">
33. select * from
34. project p
35. left join projectrecord pr
36. on p.pid = pr.pid
37. left join emp e
38. on e.empno = pr.empno
39. where p.pid= #{pid}
40. </select>
41.
42. </mapper>
测试代码
1. @Test
2. public void testManyToMany() throws ParseException {
3. ProjectMapper mapper = sqlSession.getMapper(ProjectMapper.class);
4. Project project = mapper.findProjectJoinEmpsByPid(2);
5. System.out.println(project.getPid());
6. System.out.println(project.getPname());
7. System.out.println(project.getMoney());
8.
9. List<ProjectRecord> projectRecords = project.getProjectRecords();
10. for (ProjectRecord projectRecord : projectRecords) {
11. Emp emp = projectRecord.getEmp();
12. System.out.println(emp);
13. }
14.
15. }
16.
2_级联查询
级联查询,顾名思义,就是利于数据库表间的外键关联关系进行自动的级联查询操作。使用MyBatis实现级联查询,除了实体类增加关联属性外,还需要在映射文件中进行配置。
1_立即加载
功能1:查询所有员工的信息(多对一关联)
经过对比,发现经过在映射文件中配置,测试类的代码大大简化了,无序手动进行关联查询和组装数据了。
功能2:查询10号部门及其该部门员工信息。
Dept和Emp实体类
1. @Data
2. @AllArgsConstructor
3. @NoArgsConstructor
4. public class Dept implements Serializable {
5. private Integer deptno;
6. private String dname;
7. private String loc;
8. // 当前部门下的所有员工对象的List集合
9. private List<Emp> empList;
10. }
11.
12.
13. @Data
14. @AllArgsConstructor
15. @NoArgsConstructor
16. public class Emp implements Serializable {
17. private Integer empno;
18. private String ename;
19. private String job;
20. private Integer mgr;
21. private Date hiredate;
22. private Double sal;
23. private Double comm;
24. private Integer deptno;
25.
26.
27. }
DeptMapper和 EmpMapper接口
1. package com.msb.mapper;
2.
3. import com.msb.pojo.Dept;
4.
5. /**
6. * @Author: Ma HaiYang
7. * @Description: MircoMessage:Mark_7001
8. */
9. public interface DeptMapper {
10. Dept findDeptByDeptno(int deptno);
11.
12. }
13.
14.
15. package com.msb.mapper;
16.
17. import com.msb.pojo.Emp;
18.
19. import java.util.List;
20.
21. /**
22. * @Author: Ma HaiYang
23. * @Description: MircoMessage:Mark_7001
24. */
25. public interface EmpMapper {
26.
27. List<Emp> findEmpsByDeptno(int deptno);
28.
29.
30. }
DeptMapper和EmpMapper映射文件
1. <?xml version="1.0" encoding="UTF-8" ?>
2. <!DOCTYPE mapper
3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
5.
6. <mapper namespace="com.msb.mapper.DeptMapper">
7. <!--Dept findDeptByDeptno(int deptno);
8. select="com.msb.mapper.EmpMapper.findEmpsByDeptno" 调用的另一个SQL语句
9. javaType="list" 实体类的属性数据类型
10. column="deptno" 给另一个SQL语句传入的参数列
11. jdbcType="INTEGER" 参数对应JDBC的数据类型
12. fetchType="eager" 加载方式 eager 积极加载 lazy延迟加载-->
13. <resultMap id="deptJoinEmps" type="dept">
14. <id property="deptno" column="deptno"></id>
15. <result property="dname" column="dname"></result>
16. <result property="loc" column="loc"></result>
17.
18. <collection property="empList"
19. select="com.msb.mapper.EmpMapper.findEmpsByDeptno"
20. javaType="list"
21. column="deptno"
22. jdbcType="INTEGER"
23. fetchType="eager"
24. >
25. </collection>
26.
27. </resultMap>
28.
29. <select id="findDeptByDeptno" resultMap="deptJoinEmps">
30. select * from dept where deptno =#{deptno}
31. </select>
32. </mapper>
33.
34.
35.
36. <?xml version="1.0" encoding="UTF-8" ?>
37. <!DOCTYPE mapper
38. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
39. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
40.
41. <mapper namespace="com.msb.mapper.EmpMapper">
42. <!--List<Emp> findEmpsByDeptno(int deptno);-->
43. <select id="findEmpsByDeptno" resultType="emp">
44. select * from emp where deptno =#{deptno}
45. </select>
46.
47. </mapper>
48.
49.
测试 代码
1. @Test
2. public void testFindByDetpno() {
3. DeptMapper deptMapper = sqlSession.getMapper(DeptMapper.class);
4. Dept dept = deptMapper.findDeptByDeptno(20);
5. System.out.println(dept.getDname());
6. System.out.println(dept.getDeptno());
7. System.out.println(dept.getLoc());
8. List<Emp> empList = dept.getEmpList();
9. empList.forEach(System.out::println);
10.
11.
12. }
13.
14.
2_延迟加载
延迟加载,又称按需加载。延迟加载的内容等到真正使用时才去进行加载(查询)。多用在关联对象或集合中。
延迟加载的好处:先从单表查询、需要时再从关联表去关联查询,大大降低数据库在单位时间内的查询工作量,将工作在时间上的分配更加均匀,而且单表要比关联查询多张表速度要快。
延迟加载的设置
第一步:全局开关:在sqlMapConfig.xml中打开延迟加载的开关。配置完成后所有的association和collection元素都生效
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="true"/>
</settings>
lazyLoadingEnabled:是否开启延迟加载。是Mybatis是否启用懒加载的全局开关。当开启时,所有关联对象都会延迟加载。特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态
aggressiveLazyLoading:当开启时,任何方法的调用都会懒加载对象的所有属性。否则,每个属性会按需加载,
第二步:分开关:指定的association和collection元素中配置fetchType属性。eager:表示立刻加载;lazy:表示延迟加载。将覆盖全局延迟设置。
3_总结
resultMap中的常见属性
属性
|
描述
|
property
|
需要映射到JavaBean 的属性名称。
|
javaType
|
property的类型,一个完整的类名,或者是一个类型别名。如果你匹配的是一个JavaBean,那MyBatis 通常会自行检测到。
|
column
|
数据表的列名或者列别名。
|
jdbcType
|
column在数据库表中的类型。这个属性只在insert,update 或delete 的时候针对允许空的列有用。JDBC 需要这项,但MyBatis 不需要。
|
typeHandler
|
使用这个属性可以覆写类型处理器,实现javaType、jdbcType之间的相互转换。一般可以省略,会探测到使用的什么类型的typeHandler进行处理
|
fetchType
|
自动延迟加载
|
select
|
association、collection的属性,使用哪个查询查询属性的值,要求指定namespace+id的全名称
|
ofType
|
collection的属性,指明集合中元素的类型(即泛型类型)
|
级联查询和多表查询的比较及其选择
|
级联查询
|
多表查询
|
SQL语句数量
|
多条
|
一条
|
性能
|
性能低
|
性能高
|
延迟加载
|
立即加载、延迟加载
|
只有立即加载
|
灵活性
|
更灵活
|
不灵活
|
SQL难易度
|
简单
|
复杂
|
选择依据
|
简单、灵活
|
高性能
|
ResultType和ResultMap使用场景
1) 如果你做的是单表的查询并且封装的实体和数据库的字段一一对应 resultType
2) 如果实体封装的属性和数据库的字段不一致 resultMap
3) 使用N+1级联查询的时候 resultMap
4) 使用的是多表的连接查询 resultMap
一对一关联映射的实现
1) 实例:学生和学生证、雇员和工牌
2) 数据库层次:主键关联或者外键关联(参看之前内容)
3) MyBatis层次:在映射文件的设置双方均使用association即可,用法相同
多对多映射的实现
1) 实例:学生和课程、用户和角色
2) 数据库层次:引入一个中间表将一个多对多转为两个一对多
3) MyBatis层次
方法1:在映射文件的设置双方均使用collection即可,不用引入中间类
方法2:引入中间类和中间类的映射文件,按照两个一对多处理
自关联映射
1) 实例:Emp表中的员工和上级。一般是一对多关联
2) 数据库层次:外键参考当前表的主键(比如mgr参考empno)
3) MyBatis层次:按照一对多处理,但是增加的属性都写到一个实体类中,增加的映射也都写到一个映射文件中
9_MyBatis注解开发
1. public interface DeptMapper {
2. Dept findDeptByDeptno(int deptno);
3.
4.
5. @Select("select * from dept where deptno =#{deptno}")
6. Dept findByDeptno(int deptno);
7.
8. @Update("update dept set dname =#{dname}, loc =#{loc} where deptno =#{deptno}")
9. int updateDept(Dept dept);
10.
11. @Insert("insert into dept values(DEFAULT,#{dname},#{loc})")
12. int addDept(Dept dept);
13.
14. @Delete("delete from dept where deptno =#{deptno}")
15. int removeDept(int deptno);
16.
17. }
18.
19.
1.使用注解没有实现Java代码和SQL语句的解耦
2.无法实现SQL语句的动态拼接
3.进行多表的查询时定制ResultMap比较麻烦
注解和XML的优缺点
|
XML
|
注解
|
优点
|
1.类和类之间的解耦
2.利于修改。直接修改XML文件,无需到源代码中修改。
3.配置集中在XML中,对象间关系一目了然,利于快速了解项目和维护
4.容易和其他系统进行数据交交换
|
1.简化配置
2.使用起来直观且容易,提升开发效率
3.类型安全,编译器进行校验,不用等到运行期才会发现错误。
4.注解的解析可以不依赖于第三方库,可以直接使用Java自带的反射
|
10_缓存
是一种临时存储少量数据至内存或者是磁盘的一种技术.减少数据的加载次数,可以降低工作量,提高程序响应速度
缓存的重要性是不言而喻的。mybatis的缓存将相同查询条件的SQL语句执行一遍后所得到的结果存在内存或者某种缓存介质当中,当下次遇到一模一样的查询SQL时候不在执行SQL与数据库交互,而是直接从缓存中获取结果,减少服务器的压力;尤其是在查询越多、缓存命中率越高的情况下,使用缓存对性能的提高更明显。
MyBatis允许使用缓存,缓存一般放置在高速读/写的存储器上,比如服务器的内存,能够有效的提供系统性能。MyBatis分为一级缓存和二级缓存,同时也可配置关于缓存设置。
一级存储是SqlSession上的缓存,二级缓存是在SqlSessionFactory(namespace)上的缓存。默认情况下,MyBatis开启一级缓存,没有开启二级缓存。当数据量大的时候可以借助一些第三方缓存框架或Redis缓存来协助保存Mybatis的二级缓存数据。
1_一级缓存
一级存储是SqlSession上的缓存,默认开启,是一种内存型缓存,不要求实体类对象实现Serializable接口。
缓存中的数据使用键值对形式存储数据
namespace+sqlid+args+offset>>> hash值作为键,查询出的结果作为值
测试 代码
1. @Test
2. public void testFindDeptByDetpno() {
3. EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
4. Emp emp = mapper.findByEmpno(7521);
5. System.out.println(emp);
6.
7.
8. // 中间发生了增删改或者是调用了SqlSession调用了commit,会自动清空缓存
9. sqlSession.commit();// 增删改的时候调用
10.
11. EmpMapper mapper2 = sqlSession.getMapper(EmpMapper.class);
12. Emp emp2 = mapper2.findByEmpno(7521);
13. System.out.println(emp2);
14.
15. System.out.println(emp==emp2);
16. System.out.println(mapper==mapper2);
17.
18. }
2_二级缓存
二级缓存是以namespace为标记的缓存,可以是由一个SqlSessionFactory创建的SqlSession之间共享缓存数据。默认并不开启。下面的代码中创建了两个SqlSession,执行相同的SQL语句,尝试让第二个SqlSession使用第一个SqlSession查询后缓存的数据。要求实体类必须实现序列化接口
接口
1. public interface EmpMapper {
2. Emp findByEmpno(int empno);
3. }
映射文件
1. <mapper namespace="com.msb.mapper.EmpMapper">
2. <cache/>
3.
4. <select id="findByEmpno" resultType="emp" useCache="true" flushCache="false">
5. select * from emp where empno =#{empno}
6. </select>
7.
8. </mapper>
9.
测试 代码
1. package com.msb.test;
2.
3. import com.msb.mapper.EmpMapper;
4. import com.msb.pojo.Emp;
5. import org.apache.ibatis.io.Resources;
6. import org.apache.ibatis.session.SqlSession;
7. import org.apache.ibatis.session.SqlSessionFactory;
8. import org.apache.ibatis.session.SqlSessionFactoryBuilder;
9. import org.junit.After;
10. import org.junit.Before;
11. import org.junit.Test;
12.
13. import java.io.IOException;
14. import java.io.InputStream;
15.
16. /**
17. * @Author: Ma HaiYang
18. * @Description: MircoMessage:Mark_7001
19. */
20. public class Test3 {
21.
22. private SqlSession sqlSession;
23. private SqlSession sqlSession2;
24. @Before
25. public void init(){
26. SqlSessionFactoryBuilder ssfb =new SqlSessionFactoryBuilder();
27. InputStream resourceAsStream = null;
28. try {
29. resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
30. } catch (IOException e) {
31. e.printStackTrace();
32. }
33. SqlSessionFactory factory=ssfb.build(resourceAsStream) ;
34. sqlSession=factory.openSession();
35. sqlSession2=factory.openSession();
36. }
37.
38.
39. @Test
40. public void testFindDeptByDetpno() {
41. EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
42. Emp emp = mapper.findByEmpno(7521);
43. System.out.println(emp);
44. // SqlSession提交之后,才会将查询的结果放入二级缓存
45. sqlSession.commit();
46.
47.
48. EmpMapper mapper2 = sqlSession2.getMapper(EmpMapper.class);
49. Emp emp2 = mapper2.findByEmpno(7521);
50. System.out.println(emp2);
51.
52.
53. }
54.
55.
56.
57. @After
58. public void release(){
59. // 关闭SQLSession
60. sqlSession.close();
61. sqlSession2.close();
62. }
63.
64. }
65.
注意其中的commit(),执行该命令后才会将该SqlSession的查询结果从一级缓存中放入二级缓存,供其他SqlSession使用。另外执行SqlSession的close()也会将该SqlSession的查询结果从一级缓存中放入二级缓存。两种方式区别在当前SqlSession是否关闭了。
执行结果显示进行了两次对数据库的SQL查询,说明二级缓存并没有开启。需要进行如下步骤完成开启。
1) 全局开关:在sqlMapConfig.xml文件中的<settings>标签配置开启二级缓存
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
cacheEnabled的默认值就是true,所以这步的设置可以省略。
2) 分开关:在要开启二级缓存的mapper文件中开启缓存:
<mapper namespace="com.msb.mapper.EmployeeMapper">
<cache/>
</mapper>
3) 二级缓存未必完全使用内存,有可能占用硬盘存储,缓存中存储的JavaBean对象必须实现序列化接口,
public class Emp implements Serializable { }
经过设置后,查询结果如图所示。发现第一个SqlSession会首先去二级缓存中查找,如果不存在,就查询数据库,在commit()或者close()的时候将数据放入到二级缓存。第二个SqlSession执行相同SQL语句查询时就直接从二级缓存中获取了。
注意:
1) MyBatis的二级缓存的缓存介质有多种多样,而并不一定是在内存中,所以需要对JavaBean对象实现序列化接口。
2) 二级缓存是以 namespace 为单位的,不同 namespace 下的操作互不影响
3) 加入Cache元素后,会对相应命名空间所有的select元素查询结果进行缓存,而其中的insert、update、delete在操作是会清空整个namespace的缓存。
4) cache 有一些可选的属性 type, eviction, flushInterval, size, readOnly, blocking。
<cache type="" readOnly="" eviction=""flushInterval=""size=""blocking=""/>
属性
|
含义
|
默认值
|
type
|
自定义缓存类,要求实现org.apache.ibatis.cache.Cache接口
|
null
|
readOnly
|
是否只读
true:给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。
false:会返回缓存对象的拷贝(通过序列化) 。这会慢一些,但是安全
|
false
|
eviction
|
缓存策略
LRU(默认) – 最近最少使用:移除最长时间不被使用的对象。
FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。
WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。
|
LRU
|
flushInterval
|
刷新间隔,毫秒为单位。默认为null,也就是没有刷新间隔,只有执行update、insert、delete语句才会刷新
|
null
|
size
|
缓存对象个数
|
1024
|
blocking
|
是否使用阻塞性缓存BlockingCache
true:在查询缓存时锁住对应的Key,如果缓存命中了则会释放对应的锁,否则会在查询数据库以后再释放锁,保证只有一个线程到数据库中查找指定key对应的数据
false:不使用阻塞性缓存,性能更好
|
false
|
5) 如果在加入Cache元素的前提下让个别select 元素不使用缓存,可以使用useCache属性,设置为false。useCache控制当前sql语句是否启用缓存 flushCache控制当前sql执行一次后是否刷新缓存
<select id="findByEmpno" resultType="emp" useCache="true" flushCache="false">
3_三方缓存
分布式缓存框架:我们系统为了提高系统并发和性能,一般对系统进行分布式部署(集群部署方式)不适用分布缓存, 缓存的数据在各个服务单独存储,不方便系统开发。所以要使用分布式缓存对缓存数据进行集中管理.ehcache,redis ,memcache缓存框架。
Ehcache:是一种广泛使用的开源java分布式缓存。主要面向通用缓存,javaEE 和 轻量级容器。它具有内存和磁盘存储功能。被用于大型复杂分布式web application的
这里的三方缓存是作为二级缓存使用的
导入依赖的jar文件
1. <dependency>
2. <groupId>org.mybatis.caches</groupId>
3. <artifactId>mybatis-ehcache</artifactId>
4. <version>1.0.2</version>
5. </dependency>
6. <dependency>
7. <groupId>net.sf.ehcache</groupId>
8. <artifactId>ehcache</artifactId>
9. <version>2.10.1</version>
10. </dependency>
11.
12. <dependency>
13. <groupId>org.slf4j</groupId>
14. <artifactId>slf4j-nop</artifactId>
15. <version>1.7.2</version>
16. </dependency>
17.
去各自的sql映射文件里,开启二级缓存,并把缓存类型指定为EhcacheCache
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
在资源目录下放置一个缓存配置文件,文件名为: ehcache.xml 内容如下
1. <?xml version="1.0" encoding="UTF-8"?>
2. <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3. xsi:noNamespaceSchemaLocation="ehcache.xsd"
4. updateCheck="true" monitoring="autodetect"
5. dynamicConfig="true">
6.
7. <diskStore path="D:\msb\ehcache" />
8. <defaultCache
9. maxElementsInMemory="1000"
10. maxElementsOnDisk="10000000"
11. eternal="false"
12. overflowToDisk="true"
13. timeToIdleSeconds="120"
14. timeToLiveSeconds="120"
15. diskExpiryThreadIntervalSeconds="120"
16. memoryStoreEvictionPolicy="LRU">
17. </defaultCache>
18.
19.
20. </ehcache>
21.
22. <!-- Cache配置
23. · name:Cache的唯一标识
24. · maxElementsInMemory:内存中最大缓存对象数。
25. · maxElementsOnDisk:磁盘中最大缓存对象数,若是0表示无穷大。
26. · eternal:Element是否永久有效,一但设置了,timeout将不起作用。
27. · overflowToDisk:配置此属性,当内存中Element数量达到maxElementsInMemory时,Ehcache将会Element写到磁盘中。
28. · timeToIdleSeconds:设置Element在失效前的允许闲置时间。仅当element不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
29. · timeToLiveSeconds:设置Element在失效前允许存活时间。最大时间介于创建时间和失效时间之间。仅当element不是永久有效时使用,默认是0.,也就是element存活时间无穷大。
30. · diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
31. · diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
32. · memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。 -->
33.
34.
11_逆向工程
MyBatis的一个主要的特点就是需要程序员自己编写SQL,那么如果表太多的话,难免会很麻烦,所以MyBatis官方提供了一个逆向工程,可以针对单表自动生成MyBatis执行所需要的代码(包括mapper.xml,mapper.java,pojo)。一般在开发中,常用的逆向工程方式是通过数据库的表生成代码。
创建maven项目导入逆向工程依赖
1. <dependencies>
2.
3. <!-- mysql驱动包 -->
4. <dependency>
5. <groupId>mysql</groupId>
6. <artifactId>mysql-connector-java</artifactId>
7. <version>8.0.16</version>
8. </dependency>
9.
10. <!-- 日志包,方便查看执行信息-->
11. <dependency>
12. <groupId>org.slf4j</groupId>
13. <artifactId>slf4j-log4j12</artifactId>
14. <version>1.6.1</version>
15. </dependency>
16.
17. <!-- 代码生成工具jar -->
18. <dependency>
19. <groupId>org.mybatis.generator</groupId>
20. <artifactId>mybatis-generator-core</artifactId>
21. <version>1.3.2</version>
22. </dependency>
23.
24.
25. </dependencies>
26.
27.
配置逆向工程配置文件 在resources目录下放置一个名为generatorConfig.xml的配置文件,文件内容如下
1. <?xml version="1.0" encoding="UTF-8"?>
2. <!DOCTYPE generatorConfiguration
3. PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
4. "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
5.
6. <generatorConfiguration>
7. <context id="testTables" targetRuntime="MyBatis3">
8. <commentGenerator>
9. <!-- 是否去除自动生成的注释 true:是 : false:否 -->
10. <property name="suppressAllComments" value="true" />
11. </commentGenerator>
12. <!--数据库连接的信息:驱动类、连接地址、用户名、密码 -->
13. <!-- <jdbcConnection driverClass="com.mysql.jdbc.Driver"
14. connectionURL="jdbc:mysql://localhost:3306/mybatis" userId="root"
15. password="123">
16. </jdbcConnection> -->
17. <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
18. connectionURL="jdbc:mysql://127.0.0.1:3306/mydb?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true"
19. userId="root"
20. password="root">
21. </jdbcConnection>
22.
23. <!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,为 true时把JDBC DECIMAL 和
24. NUMERIC 类型解析为java.math.BigDecimal -->
25. <javaTypeResolver>
26. <property name="forceBigDecimals" value="false" />
27. </javaTypeResolver>
28.
29. <!-- targetProject:生成PO类的位置 -->
30. <javaModelGenerator targetPackage="com.msb.pojo"
31. targetProject=".\src">
32. <!-- enableSubPackages:是否让schema作为包的后缀 -->
33. <property name="enableSubPackages" value="false" />
34. <!-- 从数据库返回的值被清理前后的空格 -->
35. <property name="trimStrings" value="true" />
36. </javaModelGenerator>
37. <!-- targetProject:mapper映射文件生成的位置 -->
38. <sqlMapGenerator targetPackage="com.msb.mapper"
39. targetProject=".\src">
40. <!-- enableSubPackages:是否让schema作为包的后缀 -->
41. <property name="enableSubPackages" value="false" />
42. </sqlMapGenerator>
43. <!-- targetPackage:mapper接口生成的位置 -->
44. <javaClientGenerator type="XMLMAPPER"
45. targetPackage="com.msb.mapper"
46. targetProject=".\src">
47. <!-- enableSubPackages:是否让schema作为包的后缀 -->
48. <property name="enableSubPackages" value="false" />
49. </javaClientGenerator>
50. <!-- 指定数据库表 -->
51.
52. <table tableName="dept" domainObjectName="Dept"
53. enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false"
54. enableSelectByExample="false" selectByExampleQueryId="false" >
55. <columnOverride column="id" javaType="Integer" />
56. </table>
57.
58. </context>
59. </generatorConfiguration>
60.
在resources目录下放置一个名为log4j.properties的配置文件,文件内容如下
1. log4j.rootLogger=debug,stdout
2.
3. log4j.appender.stdout=org.apache.log4j.ConsoleAppender
4. log4j.appender.stdout.Target=System.err
5. log4j.appender.stdout.layout=org.apache.log4j.SimpleLayout
6.
7. log4j.appender.logfile=org.apache.log4j.FileAppender
8. log4j.appender.logfile.File=d:/msb.log
9. log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
10. log4j.appender.logfile.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %l %F %p %m%n
11.
12.
运行逆向工程代码
1. package com.msb.gennerator;
2.
3. import org.mybatis.generator.api.MyBatisGenerator;
4. import org.mybatis.generator.config.Configuration;
5. import org.mybatis.generator.config.xml.ConfigurationParser;
6. import org.mybatis.generator.internal.DefaultShellCallback;
7.
8. import java.io.File;
9. import java.util.ArrayList;
10. import java.util.List;
11.
12. /**
13. *
14. */
15. public class GeneratorSqlmap {
16. public void generator() throws Exception{
17. List<String> warnings = new ArrayList<String>();
18. boolean overwrite = true;
19.
20. File configFile = new File("D:\\ideaProjects\\reverse\\target\\classes\\generatorConfig.xml");
21. ConfigurationParser cp = new ConfigurationParser(warnings);
22. Configuration config = cp.parseConfiguration(configFile);
23. DefaultShellCallback callback = new DefaultShellCallback(overwrite);
24. MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config,
25. callback, warnings);
26. myBatisGenerator.generate(null);
27.
28. }
29. public static void main(String[] args) throws Exception {
30. try {
31. GeneratorSqlmap generatorSqlmap = new GeneratorSqlmap();
32. generatorSqlmap.generator();
33. } catch (Exception e) {
34. e.printStackTrace();
35. }
36. }
37. }
38.
生成的实体类和Mapper接口和Mapper映射文件,包含了基本的CURD功能,哪里需要文件就放哪里
12_main目录下配置文件打包问题
1. <build>
2. <!--告诉maven将项目源码中的xml文件也进行编译,并放到编译目录中-->
3. <resources>
4. <resource>
5. <directory>src/main/java</directory>
6. <includes>
7. <include>**/*.xml</include>
8. </includes>
9. <filtering>true</filtering>
10. </resource>
11. <resource>
12. <directory>src/main/resources</directory>
13. <filtering>true</filtering>
14. </resource>
15. </resources>
16. </build>
17.
18.
第30章_Spring
1_Spring_概念介绍_了解
Spring是分层的全栈式的轻量级开发框架,以IOC和AOP为核心,官网是https://spring.io
Spring版本情况
Spring优势
1 方便解耦,简化开发
Spring通过容器,将对象的创建从代码中剥离出来,交给Spring控制,避免直接编码造成模块之间的耦合度高,用户也不必自己编码处理对象的单例和多例控制,主要关注接口功能即可,不用关注具体使用哪个实现类和实现细节问题
2 AOP切面编程
AOP切面编程是程序设计的一种概念,Spring对该概念实现的比较好,通过切面编程我们可以在不修改原有代码的情况下实现功能的增加,通常用于 事务控制,日志记录,性能检测,权限控制等等
3 声明式事务
事务的控制可以托管给Spring,我们通过注解或者配置文件声明事务的处理方式即可,不用我们自己去编码处理
4 整合JUNIT,方便测试
spring整合JUNIT单元测试,对于项目的功能都可以进行轻松快速的测试,便于我们调试程序
5方便整合各种优秀的框架
SSM> Spring+SpringMVC +MyBatis
SSH> Spring+Hibernate +Strust
各种其他框架
6 丰富的功能封装
spring对JAVAEE(JDBC ,JAVAMail,)都进行了一系列的封装,简化我们对于API的使用,提高程序的开发效率
7 规范的源码学习样本
spring的源码设计巧妙,结构清晰,大量使用了设计模式,是java代码规范编写的典范,也是高级程序员面试中经常会问到的源码
Spring的体系结构
1. Data Access/Integration(数据访问/集成)
数据访问/集成层包括 JDBC、ORM、OXM、JMS 和 Transactions 模块,具体介绍如下。
- JDBC 模块:提供了一个 JDBC 的抽象层,大幅度减少了在开发过程中对数据库操作的编码。
- ORM 模块:对流行的对象关系映射 API,包括 JPA、JDO、Hibernate 和 iBatis 提供了的集成层。
- OXM 模块:提供了一个支持对象/XML 映射的抽象层实现,如 JAXB、Castor、XMLBeans、JiBX 和 XStream。
- JMS 模块:指 Java 消息服务,包含的功能为生产和消费的信息。
- Transactions 事务模块:支持编程和声明式事务管理实现特殊接口类,并为所有的 POJO。
2. Web 模块
Spring 的 Web 层包括 Web、Servlet、Struts 和 Portlet 组件,具体介绍如下。
- Web 模块:提供了基本的 Web 开发集成特性,例如多文件上传功能、使用的 Servlet 监听器的 IoC 容器初始化以及 Web 应用上下文。
- Servlet模块:包括 Spring 模型—视图—控制器(MVC)实现 Web 应用程序。
- Struts 模块:包含支持类内的 Spring 应用程序,集成了经典的 Struts Web 层。
- Portlet 模块:提供了在 Portlet 环境中使用 MV C实现,类似 Web-Servlet 模块的功能。
3. Core Container(核心容器)
Spring 的核心容器是其他模块建立的基础,由 Beans 模块、Core 核心模块、Context 上下文模块和 Expression Language 表达式语言模块组成,具体介绍如下。
- Beans 模块:提供了 BeanFactory,是工厂模式的经典实现,Spring 将管理对象称为 Bean。
- Core 核心模块:提供了 Spring 框架的基本组成部分,包括 IoC 和 DI 功能。
- Context 上下文模块:建立在核心和 Beans 模块的基础之上,它是访问定义和配置任何对象的媒介。ApplicationContext 接口是上下文模块的焦点。
- Expression Language 模块:是运行时查询和操作对象图的强大的表达式语言。
4. 其他模块
Spring的其他模块还有 AOP、Aspects、Instrumentation 以及 Test 模块,具体介绍如下。
- AOP 模块:提供了面向切面编程实现,允许定义方法拦截器和切入点,将代码按照功能进行分离,以降低耦合性。
- Aspects 模块:提供与 AspectJ 的集成,是一个功能强大且成熟的面向切面编程(AOP)框架。
- Instrumentation 模块:提供了类工具的支持和类加载器的实现,可以在特定的应用服务器中使用。
- Test 模块:支持 Spring 组件,使用 JUnit 或 TestNG 框架的测试。
2_Spring_IOC概念引入_重要
简单的说就是,创建对象的权利,或者是控制的位置,由JAVA代码转移到spring容器,由spring的容器控制对象的创建,就是控制反转,spring创建对象时,会读取配置文件中的信息,然后使用反射给我们创建好对象之后在容器中存储起来,当我们需要某个对象时,通过id获取对象即可,不需要我们自己去new.
一句话:创建对象交给容器
Spring解耦合的原理
图解
创建maven项目,设置maven
先创建一个空项目
名字可以是spring_all
在项目下创建模块 名字可以是spring_test_01
pom.xml中导入spring依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>3.5</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.3.5</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.5</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>5.3.5</version>
</dependency>
</dependencies>
四个依赖介绍
spring-context 上下文,容器
spring-beans 创建对象
spring-core 核心jar
spring-expression 表达式jar
但是事实上,我们导入spring-context的时候,会自动导入其他依赖的jar,自动进行了依赖传递
所以,导入一个spring-context 依赖也可以
依赖传递关系图如下
为了方便测试,我们导入Junit测试依赖
<dependency>
2. <groupId>junit</groupId>
3. <artifactId>junit</artifactId>
4. <version>4.13.1</version>
5. <scope>test</scope>
6. </dependency>
在项目中定义一个接口和实现类
EmpDao接口和实现类
接口中定义一个方法并在实现类中实现
接口
实现类
在spring配置容器中的对象
创建spring配置文件
文件名没有明确要求,暂时可以叫spring
在spring.xml中配置一个需要由容器初始化的对象
测试通过容器获取对象
3_Spring_IOC原理分析_重要
IOC底层原理
1 XML解析技术读取配置文件
1. <bean id="empDao" class="com.msb.dao.impl.EmpDaoImpl"></bean>
将上面的信息读取进入程序 对象的ID ,一个是对象的类的全路径名
2 反射技术实例化对象,放到容器中
获得类的字节码
Class clazz =Class.forName("com.msb.dao.impl.EmpDaoImpl")
通过字节码实例化对象
Object obj = clazz.newInstance();
将对象放到一个map集合中
map.put("empDao",obj)
3工厂模式返回Bean对象 getBean方法
public Object getBean(String name){
Object obj =map.get(name);
return obj;
}
IOC接口
BeanFactory 接口: IOC容器基本功能接口,是spring内部使用的接口,我们在处理业务时一般不直接使用该接口
ApplicationContext 接口: BeanFactory的子接口,提供更多更强大的功能,研发人员一般使用的接口
4_Spring_XML方式实现DI
spring中的Bean的管理:
Bean(汉译咖啡豆). 又称JAVABean.其实就是JAVA程序程序中的一个个对象,所以Bean的管理其实就是spring对于JAVA程序中的对象的管理
管理的内容是什么
1 对象的创建 IOC
IOC 叫做控制反转,就是Spring给我们创建对象,然后我们直接用,不用自己NEW,前面已经解释过
IOC处理的是对象如何创建的问题
2 属性的赋值 DI
DI Dependency Injection,即“依赖注入” 就是创建属性时给对象属性赋值
对象功能的实现往往要依赖属性的值,那么给对象属性赋值就可以说成是依赖注入
由于对象属性不仅仅是基本数据类型,还可能是其他类,或者引用类型
那么依赖注入将会把更多的对象之间的关系整理到一起,可以行程一个庞大的依赖关系
DI处理的是对象的属性赋值和互相依赖的关系
spring给我们提供了两种关于bean的方式
1 基于XML方式的Bean管理
2 基于注解方式的Bean管理
创建新的模块并导入以下依赖
依赖
<dependency>
2. <groupId>org.springframework</groupId>
3. <artifactId>spring-context</artifactId>
4. <version>5.3.5</version>
5. </dependency>
6.
7. <dependency>
8. <groupId>junit</groupId>
9. <artifactId>junit</artifactId>
10. <version>4.13.1</version>
11. <scope>test</scope>
12. </dependency>
13.
创建spring配置文件,一般spring的配置文件很多人约定俗称为application****.xml
准备一个要实例化的类
1. public class User {
2. private Integer userid;
3. private String username;
4. private String password;
5.
6. @Override
7. public String toString() {
8. return "User{" +
9. "userid=" + userid +
10. ", username='" + username + '\'' +
11. ", password='" + password + '\'' +
12. '}';
13. }
14.
15. public User() {
16. System.out.println("noArgConstructor");
17. }
18.
19. public User(Integer userid, String username, String password) {
20. System.out.println("allArgConstructor");
21. this.userid = userid;
22. this.username = username;
23. this.password = password;
24. }
25.
26. public void setUserid(Integer userid) {
27. System.out.println("setUserid");
28. this.userid = userid;
29. }
30.
31. public void setUsername(String username) {
32. System.out.println("setUsername");
33. this.username = username;
34. }
35.
36. public void setPassword(String password) {
37. System.out.println("setpassword");
38. this.password = password;
39. }
40. }
IOC创建对象
通过无参构造方法构造对象
1. <bean id="user1" class="com.msb.bean.User"></bean>
<bean> 标签的常见属性
1. <bean id="user1" class="com.msb.bean.User" name="user1" scope="prototype" lazy-init="true" ></bean>
id 对象的id
class 类的全路径名
name 和id类似,一般不用
scope 控制对象单例多例和使用范围
singleton作用域(scope 默认值), Spring IOC容器中只会存在一个共享的bean实例
prototype作用域部署的bean,每一次获取都会产生一个新的bean实例,相当与一个new的操作
request表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP request内有效
session作用域表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP session内有效
global session作用域类似于标准的HTTP Session作用域,不过它仅仅在基于portlet的web应用中才有意义
lazy-init 懒加载 调用getBean的时候再去实例化对象
DI给对象属性赋值
1 通过set方法给对象属性赋值
1. <!--property 就是在使用set方法实现依赖注入-->
2. <bean id="user1" class="com.msb.bean.User">
3. <property name="userid" value="1"></property>
4. <property name="username" value="张三"></property>
5. <property name="password" value="abcdefg"></property>
6. </bean>
2 通过有参构造给对象属性赋值
1. <!--
2. constructor-arg 就是在使用构造方法实现依赖注入
3. constructor-arg 的个数必须和某个构造方法的参数个数向对应
4. name指的是参数名
5. index指的是参数的索引
6. value指的是参数值
7. -->
8. <bean id="user2" class="com.msb.bean.User">
9. <constructor-arg name="userid" value="2"></constructor-arg>
10. <constructor-arg name="username" value="小明"></constructor-arg>
11. <constructor-arg name="password" value="123456789"></constructor-arg>
12. </bean>
13.
14. <bean id="user3" class="com.msb.bean.User">
15. <constructor-arg index="0" value="3"></constructor-arg>
16. <constructor-arg index="1" value="小黑"></constructor-arg>
17. <constructor-arg index="2" value="987654321"></constructor-arg>
18. </bean>
3 通过p名称空间和c名称空间给对象属性赋值
添加约束
1. <?xml version="1.0" encoding="UTF-8"?>
2. <beans xmlns="http://www.springframework.org/schema/beans"
3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4. xmlns:p="http://www.springframework.org/schema/p"
5. xmlns:c="http://www.springframework.org/schema/c"
6. xsi:schemaLocation="http://www.springframework.org/schema/beans
7. http://www.springframework.org/schema/beans/spring-beans.xsd">
配置对象
<!--p名称空间,就是对property的简化处理-->
2. <bean id="user4" class="com.msb.bean.User" p:userid="4" p:username="小东" p:password="111111" ></bean>
3.
4. <!--c名称空间,就是对constructor-arg的简化-->
5. <bean id="user5" class="com.msb.bean.User" c:userid="5" c:username="小西" c:password="222222" ></bean>
测试代码
1. public class Test1 {
2.
3. @Test
4. public void testGetBean(){
5. ApplicationContext context =new ClassPathXmlApplicationContext("applicationContext.xml");
6. User user1 = context.getBean("user1", User.class);
7. User user2 = context.getBean("user2", User.class);
8. User user3 = context.getBean("user3", User.class);
9. User user4 = context.getBean("user4", User.class);
10. User user5 = context.getBean("user5", User.class);
11. System.out.println(user1);
12. System.out.println(user2);
13. System.out.println(user3);
14. System.out.println(user4);
15. System.out.println(user5);
16. }
17.
18. }
4 注入空值和特殊符号
1. <bean id="user1" class="com.msb.bean.User">
2. <!--null值-->
3. <property name="userid">
4. <null></null>
5. </property>
6. <!--特殊符号 转译字符 < < >> & & -->
7. <property name="username" value="&xiaoming<>"></property>
8. <!-- 特殊符号 <![CDATA[内容]]> -->
9. <property name="password">
10. <value><![CDATA[&<123456>]]></value>
11. </property>
12. </bean>
5 关于bean引用
实体类
1. package com.msb.bean;
2.
3. import java.util.Date;
4.
5. /**
6. * @Author: Ma HaiYang
7. * @Description: MircoMessage:Mark_7001
8. */
9. public class Mouse {
10. private String name;
11. private Date birthdate;
12.
13. @Override
14. public String toString() {
15. return "Mouse{" +
16. "name='" + name + '\'' +
17. ", birthdate=" + birthdate +
18. '}';
19. }
20.
21. public Mouse() {
22. }
23.
24. public Mouse(String name, Date birthdate) {
25. this.name = name;
26. this.birthdate = birthdate;
27. }
28.
29. public String getName() {
30. return name;
31. }
32.
33. public void setName(String name) {
34. this.name = name;
35. }
36.
37. public Date getBirthdate() {
38. return birthdate;
39. }
40.
41. public void setBirthdate(Date birthdate) {
42. this.birthdate = birthdate;
43. }
44. }
45.
46.
47.
48.
49. package com.msb.bean;
50.
51. /**
52. * @Author: Ma HaiYang
53. * @Description: MircoMessage:Mark_7001
54. */
55. public class Cat {
56. private String name;
57. private Mouse mouse1;
58.
59. @Override
60. public String toString() {
61. return "Cat{" +
62. "name='" + name + '\'' +
63. ", mouse1=" + mouse1 +
64. '}';
65. }
66.
67. public Cat() {
68. }
69.
70. public Cat(String name, Mouse mouse1) {
71. this.name = name;
72. this.mouse1 = mouse1;
73. }
74.
75. public String getName() {
76. return name;
77. }
78.
79. public void setName(String name) {
80. this.name = name;
81. }
82.
83. public Mouse getMouse1() {
84. return mouse1;
85. }
86.
87. public void setMouse1(Mouse mouse1) {
88. this.mouse1 = mouse1;
89. }
90. }
91.
92.
xml 配置
<!--告诉容器准备一个Date对象-->
2. <bean id="date1" class="java.util.Date"></bean>
3.
4. <bean id="mouse1" class="com.msb.bean.Mouse">
5. <property name="name" value="Jerry"></property>
6. <!--bean引用引用外部bean-->
7. <property name="birthdate" ref="date1"></property>
8. </bean>
9.
10.
11. <bean id="cat1" class="com.msb.bean.Cat">
12. <property name="name" value="Tom"></property>
13. <!--引用内部bean-->
14. <property name="mouse1" >
15. <bean class="com.msb.bean.Mouse">
16. <property name="name" value="Jerry2"></property>
17. <property name="birthdate">
18. <bean class="java.util.Date"></bean>
19. </property>
20. </bean>
21. </property>
22. </bean>
23.
24.
25. <bean id="mouse2" class="com.msb.bean.Mouse"></bean>
26. <bean id="cat2" class="com.msb.bean.Cat">
27. <property name="name" value="Tom2"></property>
28. <!--级联引入bean-->
29. <property name="mouse1" ref="mouse2"></property>
30. <!--用反射调用get*** 方法,获得对象之后,再赋值-->
31. <property name="mouse1.name" value="Jerry3"></property>
32. <property name="mouse1.birthdate">
33. <bean class="java.util.Date"></bean>
34. </property>
35. </bean>
6 关于集合注入
实体类
1. package com.msb.bean;
2.
3. /**
4. * @Author: Ma HaiYang
5. * @Description: MircoMessage:Mark_7001
6. */
7. public class Book {
8. private String bname;
9. private String author;
10.
11. @Override
12. public String toString() {
13. return "Book{" +
14. "bname='" + bname + '\'' +
15. ", author='" + author + '\'' +
16. '}';
17. }
18.
19. public Book() {
20. }
21.
22. public Book(String bname, String author) {
23. this.bname = bname;
24. this.author = author;
25. }
26.
27. public String getBname() {
28. return bname;
29. }
30.
31. public void setBname(String bname) {
32. this.bname = bname;
33. }
34.
35. public String getAuthor() {
36. return author;
37. }
38.
39. public void setAuthor(String author) {
40. this.author = author;
41. }
42. }
43.
44.
45.
46. package com.msb.bean;
47.
48. import java.util.Arrays;
49. import java.util.List;
50. import java.util.Map;
51. import java.util.Set;
52.
53. /**
54. * @Author: Ma HaiYang
55. * @Description: MircoMessage:Mark_7001
56. */
57. public class Student {
58. private String[] books;
59. private Set<String> bookSet;
60. private List<String> bookList;
61. private Map<String,String> bookMap;
62. private List<Book> bookList2;
63.
64. @Override
65. public String toString() {
66. return "Student{" +
67. "books=" + Arrays.toString(books) +
68. ", bookSet=" + bookSet +
69. ", bookList=" + bookList +
70. ", bookMap=" + bookMap +
71. ", bookList2=" + bookList2 +
72. '}';
73. }
74.
75. public String[] getBooks() {
76. return books;
77. }
78.
79. public void setBooks(String[] books) {
80. this.books = books;
81. }
82.
83. public Set<String> getBookSet() {
84. return bookSet;
85. }
86.
87. public void setBookSet(Set<String> bookSet) {
88. this.bookSet = bookSet;
89. }
90.
91. public List<String> getBookList() {
92. return bookList;
93. }
94.
95. public void setBookList(List<String> bookList) {
96. this.bookList = bookList;
97. }
98.
99. public Map<String, String> getBookMap() {
100. return bookMap;
101. }
102.
103. public void setBookMap(Map<String, String> bookMap) {
104. this.bookMap = bookMap;
105. }
106.
107. public List<Book> getBookList2() {
108. return bookList2;
109. }
110.
111. public void setBookList2(List<Book> bookList2) {
112. this.bookList2 = bookList2;
113. }
114.
115. public Student() {
116. }
117.
118. public Student(String[] books, Set<String> bookSet, List<String> bookList, Map<String, String> bookMap, List<Book> bookList2) {
119.
120. this.books = books;
121. this.bookSet = bookSet;
122. this.bookList = bookList;
123. this.bookMap = bookMap;
124. this.bookList2 = bookList2;
125. }
126. }
127.
128.
配置文件
1. <?xml version="1.0" encoding="UTF-8"?>
2. <beans xmlns="http://www.springframework.org/schema/beans"
3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4. xmlns:p="http://www.springframework.org/schema/p"
5. xmlns:c="http://www.springframework.org/schema/c"
6. xmlns:util="http://www.springframework.org/schema/util"
7. xsi:schemaLocation="
8. http://www.springframework.org/schema/beans
9. http://www.springframework.org/schema/beans/spring-beans.xsd
10. http://www.springframework.org/schema/util
11. http://www.springframework.org/schema/util/spring-util.xsd
12. ">
13.
14. <!--定义公共集合-->
15. <util:list id="outerbookList">
16. <!--声明多个Book对象-->
17. <bean id="b1" class="com.msb.bean.Book" p:bname="JAVA" p:author="马士兵"></bean>
18. <bean id="b2" class="com.msb.bean.Book" p:bname="Go" p:author="马士兵"></bean>
19. <bean id="b3" class="com.msb.bean.Book" p:bname="JVM" p:author="马士兵"></bean>
20. </util:list>
21.
22. <bean id="student1" class="com.msb.bean.Student">
23. <!--数组属性注入-->
24. <property name="books">
25. <array>
26. <value>JAVA</value>
27. <value>MySQL</value>
28. <value>Spring</value>
29. </array>
30. </property>
31.
32. <!--set集合注入-->
33. <property name="bookSet">
34. <set>
35. <value>JAVA</value>
36. <value>MySQL</value>
37. <value>Spring</value>
38. </set>
39. </property>
40.
41. <!--list集合注入-->
42. <property name="bookList">
43. <list>
44. <value>JAVA</value>
45. <value>MySQL</value>
46. <value>Spring</value>
47. </list>
48. </property>
49.
50. <!--map集合注入-->
51. <property name="bookMap">
52. <map>
53. <entry key="JAVA" value="马士兵"></entry>
54. <entry key="Go" value="马士兵"></entry>
55. <entry key="JVM" value="马士兵"></entry>
56. </map>
57. </property>
58.
59. <!--List对象集合注入-->
60. <property name="bookList2" ref="outerbookList"></property>
61. </bean>
62. </beans>
7 工厂方式获取bean
特点 : bean标签中定义的class类不是返回的对象的类,而是生产该对象的工厂
工厂模式:GOF 设计模式
spring中给我们定义好了一个工厂接口,可以生产对象的接口,我们可以通过工厂来获取bean
定义工厂对象 实现 FactoryBean接口
1. package com.msb.bean;
2.
3.
4. import org.springframework.beans.factory.FactoryBean;
5.
6. /**
7. * @Author: Ma HaiYang
8. * @Description: MircoMessage:Mark_7001
9. */
10. public class BookFactory implements FactoryBean<Book> {
11. @Override
12. public Book getObject() throws Exception {
13. Book book=new Book();
14. book.setBname("JAVA");
15. book.setAuthor("马士兵");
16. return book;
17. }
18.
19. @Override
20. public Class<?> getObjectType() {
21. return null;
22. }
23. }
1. <bean id="book" class="com.msb.bean.BookFactory"></bean>
测试代码
@Test
2. public void testGetBean(){
3. ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext5.xml");
4. Book book = applicationContext.getBean("book", Book.class);
5. System.out.println(book);
6. }
5_Spring_Bean的生命周期
bean从创建到销毁经历的各个阶段以及每个阶段所调用的方法
1 通过构造器创建bean实例 执行构造器
2 为bean属性赋值 执行set方法
3 初始化bean 调用bean的初始化方法,需要配置指定调用的方法
4 bean的获取 容器对象 getBean方法
5 容器关闭销毁bean 调用销毁方法,需要配置指定调用的方法
测试生命周期
准备bean
1. package com.msb.bean;
2.
3. /**
4. * @Author: Ma HaiYang
5. * @Description: MircoMessage:Mark_7001
6. */
7. public class User {
8. private Integer userid;
9. private String username;
10. private String password;
11.
12. public void initUser(){
13. System.out.println("第三步:User初始化");
14. }
15. public User() {
16. System.out.println("第一步:User构造");
17. }
18. public void destoryUser(){
19. System.out.println("第五步:User销毁");
20. }
21.
22. @Override
23. public String toString() {
24. return "User{" +
25. "userid=" + userid +
26. ", username='" + username + '\'' +
27. ", password='" + password + '\'' +
28. '}';
29. }
30. public User(Integer userid, String username, String password) {
31. this.userid = userid;
32. this.username = username;
33. this.password = password;
34. }
35.
36. public void setUserid(Integer userid) {
37. System.out.println("setUserid");
38. this.userid = userid;
39. }
40.
41. public void setUsername(String username) {
42. System.out.println("第二步:User属性赋值");
43. this.username = username;
44. }
45. public void setPassword(String password) {
46. this.password = password;
47. }
48. }
配置bean
1. <?xml version="1.0" encoding="UTF-8"?>
2. <beans xmlns="http://www.springframework.org/schema/beans"
3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4. xmlns:p="http://www.springframework.org/schema/p"
5. xmlns:c="http://www.springframework.org/schema/c"
6. xsi:schemaLocation="http://www.springframework.org/schema/beans
7. http://www.springframework.org/schema/beans/spring-beans.xsd">
8.
9. <bean id="user" class="com.msb.bean.User" init-method="initUser" destroy-method="destoryUser">
10. <property name="username" value="xiaoming"></property>
11. </bean>
12.
13. </beans>
测试bean
1. package com.msb.test;
2.
3. import com.msb.bean.User;
4. import org.junit.Test;
5. import org.springframework.context.ApplicationContext;
6. import org.springframework.context.support.ClassPathXmlApplicationContext;
7.
8. /**
9. * @Author: Ma HaiYang
10. * @Description: MircoMessage:Mark_7001
11. */
12. public class Test1 {
13. @Test
14. public void testGetBean(){
15. ClassPathXmlApplicationContext context =new ClassPathXmlApplicationContext("applicationContext.xml");
16. User user = context.getBean("user",User.class);
17. System.out.println("第四步:User对象从容器中获取");
18. // 关闭容器
19. context.close();
20. }
21. }
22.
关于后置处理器
1 通过构造器创建bean实例 执行构造器
2 为bean属性赋值 执行set方法
3 把bean实例传递给bean的后置处理器的方法
4 初始化bean 调用bean的初始化方法,需要配置指定调用的方法
5 把bean实例传递给bean的后置处理器的方法
6 bean的获取 容器对象 getBean方法
7 容器关闭销毁bean 调用销毁方法,需要配置指定调用的方法
1 创建后置处理器 实现 BeanPostProcesser 重写两个方法
1. package com.msb.beanProcesser;
2.
3. import org.springframework.beans.BeansException;
4. import org.springframework.beans.factory.config.BeanPostProcessor;
5.
6. /**
7. * @Author: Ma HaiYang
8. * @Description: MircoMessage:Mark_7001
9. */
10. public class MyBeanProcesser implements BeanPostProcessor {
11. @Override
12. public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
13. //Object bean 实例化的bean
14. //String beanName bean的id
15. System.out.println("bean:初始化方法之前");
16. return bean;// 这里必须return bean
17. }
18.
19. @Override
20. public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
21. System.out.println("bean:初始化方法之后");
22. return bean;// 这里必须returnbean
23. }
24. }
25.
2 配置后置处理器,对容器中的所有bean添加后置处理器的生命周期
1. <?xml version="1.0" encoding="UTF-8"?>
2. <beans xmlns="http://www.springframework.org/schema/beans"
3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4. xmlns:p="http://www.springframework.org/schema/p"
5. xmlns:c="http://www.springframework.org/schema/c"
6. xsi:schemaLocation="http://www.springframework.org/schema/beans
7. http://www.springframework.org/schema/beans/spring-beans.xsd">
8.
9. <bean id="user" class="com.msb.bean.User" init-method="initUser" destroy-method="destoryUser">
10. <property name="username" value="xiaoming"></property>
11. </bean>
12.
13. <bean id="myBeanProcesser" class="com.msb.beanProcesser.MyBeanProcesser"></bean>
14.
15. </beans>
BeanPostProcessor接口作用:
如果我们想在Spring容器中完成bean实例化、配置以及其他初始化方法前后要添加一些自己逻辑处理。我们需要定义一个或多个BeanPostProcessor接口实现类,然后注册到Spring IoC容器中。
1、接口中的两个方法都要将传入的bean返回,而不能返回null,如果返回的是null那么我们通过getBean方法将得不到目标。
2、ApplicationContext会自动检测在配置文件中实现了BeanPostProcessor接口的所有bean,并把它们注册为后置处理器,然后在容器创建bean的适当时候调用它,因此部署一个后置处理器同部署其他的bean并没有什么区别。而使用BeanFactory实现的时候,bean 后置处理器必须通过代码显式地去注册,在IoC容器继承体系中的ConfigurableBeanFactory接口中定义了注册方法
6_Spring_Bean的自动装配
bean自动装配
通过property标签可以手动指定给属性进行注入
我们也可以通过自动转配,完成属性的自动注入,就是自动装配,可以简化DI的配置
准备实体类
1. package com.msb.bean;
2.
3. /**
4. * @Author: Ma HaiYang
5. * @Description: MircoMessage:Mark_7001
6. */
7. public class Dept {
8. }
1. package com.msb.bean;
2.
3. /**
4. * @Author: Ma HaiYang
5. * @Description: MircoMessage:Mark_7001
6. */
7. public class Emp {
8. private Dept dept;
9.
10. @Override
11. public String toString() {
12. return "Emp{" +
13. "dept=" + dept +
14. '}';
15. }
16.
17. public Dept getDept() {
18. return dept;
19. }
20.
21. public void setDept(Dept dept) {
22. this.dept = dept;
23. }
24.
25. public Emp() {
26. }
27.
28. public Emp(Dept dept) {
29. this.dept = dept;
30. }
31. }
32.
配置文件
1. <?xml version="1.0" encoding="UTF-8"?>
2. <beans xmlns="http://www.springframework.org/schema/beans"
3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4. xmlns:p="http://www.springframework.org/schema/p"
5. xmlns:c="http://www.springframework.org/schema/c"
6. xsi:schemaLocation="http://www.springframework.org/schema/beans
7. http://www.springframework.org/schema/beans/spring-beans.xsd">
8.
9.
10. <bean id="dept" class="com.msb.bean.Dept"></bean>
11. <!--
12. autowire 属性控制自动将容器中的对象注入到当前对象的属性上
13. byName 根据目标id值和属性值注入,要保证当前对象的属性值和目标对象的id值一致
14. byType 根据类型注入,要保证相同类型的目标对象在容器中只有一个实例
15. -->
16.
17. <bean id="emp" class="com.msb.bean.Emp" autowire="byName"></bean>
18.
19. </beans>
测试代码
1. package com.msb.test;
2.
3. import com.msb.bean.Emp;
4. import com.msb.bean.User;
5. import org.junit.Test;
6. import org.springframework.context.support.ClassPathXmlApplicationContext;
7.
8. /**
9. * @Author: Ma HaiYang
10. * @Description: MircoMessage:Mark_7001
11. */
12. public class Test2 {
13. @Test
14. public void testGetBean(){
15. ClassPathXmlApplicationContext context =new ClassPathXmlApplicationContext("applicationContext2.xml");
16. Emp emp = context.getBean("emp", Emp.class);
17. System.out.println(emp);
18.
19. }
20. }
7_Spring_使用外部属性配置文件
spring容器可以读取.properties属性配置文件,可以将文件中的信息注入给bean
例如,引入Druid数据源,配置连接池信息
1 导入Druid依赖和mysql-connector依赖
<dependency>
2. <groupId>com.alibaba</groupId>
3. <artifactId>druid</artifactId>
4. <version>1.1.10</version>
5. </dependency>
6.
7. <dependency>
8. <groupId>mysql</groupId>
9. <artifactId>mysql-connector-java</artifactId>
10. <version>8.0.22</version>
11. </dependency>
2 准备属性配置文件
resources目录下准备一个jdbc.properties属性配置文件
配置文件内容
1. jdbc_driver=com.mysql.cj.jdbc.Driver
2. jdbc_url=jdbc:mysql://127.0.0.1:3306/mydb?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
3. jdbc_username=root
4. jdbc_password=root
applicationContext中添加context名称空间 并读取属性配置文件
配置druid数据源将属性配置文件中的信息注入到连接池中
1. <?xml version="1.0" encoding="UTF-8"?>
2. <beans xmlns="http://www.springframework.org/schema/beans"
3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4. xmlns:p="http://www.springframework.org/schema/p"
5. xmlns:c="http://www.springframework.org/schema/c"
6. xmlns:context="http://www.springframework.org/schema/context"
7. xsi:schemaLocation="http://www.springframework.org/schema/beans
8. http://www.springframework.org/schema/beans/spring-beans.xsd
9. http://www.springframework.org/schema/context
10. http://www.springframework.org/schema/context/spring-context.xsd
11. ">
12.
13. <context:property-placeholder location="classpath:jdbc.properties"/>
14.
15. <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
16. <property name="username" value="${jdbc_username}"></property>
17. <property name="password" value="${jdbc_password}"></property>
18. <property name="url" value="${jdbc_url}"></property>
19. <property name="driverClassName" value="${jdbc_driver}"></property>
20. </bean>
21.
22. </beans>
3 DEBUG测试代码
8_Spring_注解方式管理bean
1注解方式创建对象IOC
导入依赖 aop
@Component 放在类上,用于标记,告诉spring当前类需要由容器实例化bean并放入容器中
该注解有三个子注解
@Controller 用于实例化controller层bean
@Service 用于实例化service层bean
@Repository 用于实例化持久层bean
当不确定是哪一层,就用Component
这几个注解互相混用其实也可以,但是不推荐
第一步:在applicationContext.xml中配置开启注解扫描
1. <?xml version="1.0" encoding="UTF-8"?>
2. <beans xmlns="http://www.springframework.org/schema/beans"
3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4. xmlns:p="http://www.springframework.org/schema/p"
5. xmlns:c="http://www.springframework.org/schema/c"
6. xmlns:context="http://www.springframework.org/schema/context"
7. xsi:schemaLocation="http://www.springframework.org/schema/beans
8. http://www.springframework.org/schema/beans/spring-beans.xsd
9. http://www.springframework.org/schema/context
10. http://www.springframework.org/schema/context/spring-context.xsd
11. ">
12.
13. <!--添加注解扫描,扫描指定的包,将包中的所有有注解的类实例化
14. base-package 后面放要扫描的包
15. 如果有多个包需要扫描,可以使用逗号隔开 com.msb.bean,com.msb.service
16. 或者可以写上一层包路径 com.msb
17. 可以通过注解指定bean的id@Component("user1")
18. 如果不指定,则id默认是 类名首字母小写
19. -->
20. <context:component-scan base-package="com.msb.bean"></context:component-scan>
21.
22.
23. </beans>
第二步:在类上添加注解,让spring容器给我们创建bean实例并存储于容器中
1. package com.msb.bean;
2.
3. import org.springframework.stereotype.Component;
4.
5. /**
6. * @Author: Ma HaiYang
7. * @Description: MircoMessage:Mark_7001
8. */
9. @Component(value = "user1")
10. public class User {
11. }
测试代码
1. package com.msb.test;
2.
3. import com.msb.bean.User;
4. import org.junit.Test;
5. import org.springframework.context.ApplicationContext;
6. import org.springframework.context.support.ClassPathXmlApplicationContext;
7.
8. /**
9. * @Author: Ma HaiYang
10. * @Description: MircoMessage:Mark_7001
11. */
12. public class Test1 {
13.
14. @Test
15. public void testGetBean(){
16. ApplicationContext context =new ClassPathXmlApplicationContext("applicationContext.xml");
17. User user = context.getBean("user1", User.class);
18. System.out.println(user);
19. }
20. }
组件扫描配置注解识别
1. <!--
2. use-default-filters="false"
3. 默认值为true 代表使用默认的扫描过滤器
4. 默认的扫描过滤器会识别并包含 @Component @Controller @Service @Repository 四个注解
5. 不使用默认的filter,使用我们自己的filter
6. -->
7. <!--控制只扫描Controller注解-->
8. <context:component-scan base-package="com.msb" use-default-filters="false">
9. <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
10. </context:component-scan>
11.
12. <!--控制不扫描Controller注解-->
13. <context:component-scan base-package="com.msb" use-default-filters="true">
14. <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
15. </context:component-scan>
16.
2注解方式依赖注入DI
@Autowired 根据属性数据类型自动装配
@Qualifier 根据属性名称注入依赖
@Resources 可以根据类型,也可以根据名称注入
@Value 注入普通数据类型(8+String)
项目结构如下
applicationContext.xml中配置包扫描和读取属性配置文件
Dao层
接口
实现类
让容器扫描 Service层,实例化对象
接口
实现类
1. package com.msb.service.impl;
2.
3. import com.msb.dao.UserDao;
4. import com.msb.service.UserService;
5. import org.springframework.beans.factory.annotation.Autowired;
6. import org.springframework.beans.factory.annotation.Qualifier;
7. import org.springframework.beans.factory.annotation.Value;
8. import org.springframework.stereotype.Service;
9.
10. import javax.annotation.Resource;
11.
12. /**
13. * @Author: Ma HaiYang
14. * @Description: MircoMessage:Mark_7001
15. */
16. @Service
17. public class UserServiceImpl implements UserService {
18. /*
19. *@Autowired
20. * 根据类型到容器中去寻找对应的对象,找到后给当前属性赋值
21. * 不需要依赖 set方法
22. * 属性类型可以是接口,会自动匹配对应的实现类对象
23. * @Autowired配合 @Qualifier,可以通过名称指定注入的对象
24. *
25. * @Resource 如果不配置name 那么就是根据类型注入
26. * @Resource(name="userDaoImplB") 配置name,就是根据名称注入
27. *
28. *
29. * @Resource 是JDK中javax包的注解
30. * @Autowired 和 @Qualifier 是spring中的注解
31. *
32. * @Value 可以个普通属性赋值
33. * @Value 可以使用${}这种表达式获取系统的变量值
34. * 或者是.properties属性配置文件中的值
35. *
36. * */
37. //@Autowired
38. //@Qualifier("userDaoImplA")
39. //@Qualifier("userDaoImplB")
40. //private UserDao userDao ;
41.
42. @Resource(name="userDaoImplB")
43. private UserDao userDao ;
44. @Value("${username}")
45. private String sname;
46. @Value("boy")
47. private String sgender;
48. @Value("${age}")
49. private Integer sage;
50. @Override
51. public void add() {
52. System.out.println("userServiceImpl add ... ... ");
53. System.out.println(sname);
54. System.out.println(sgender);
55. System.out.println(sage);
56. userDao.add();
57. }
58. }
59.
测试代码
1. package com.msb.test;
2.
3. import com.msb.service.UserService;
4. import org.junit.Test;
5. import org.springframework.context.ApplicationContext;
6. import org.springframework.context.support.ClassPathXmlApplicationContext;
7.
8. /**
9. * @Author: Ma HaiYang
10. * @Description: MircoMessage:Mark_7001
11. */
12. public class Test1 {
13.
14. @Test
15. public void testGetBean(){
16. ApplicationContext context =new ClassPathXmlApplicationContext("applicationContext.xml");
17. UserService userService = context.getBean("userServiceImpl", UserService.class);
18. userService.add();
19. }
20. }
21.
3完全使用注解(了解)
创建配置类,替代XML配置文件
测试代码
1. @Test
2. public void testGetBean2(){
3. ApplicationContext context=new AnnotationConfigApplicationContext(SpringConfig.class);
4. UserServiceImpl userService = context.getBean("userServiceImpl", UserServiceImpl.class);
5. userService.add();
6. }
9_Spring_JDK动态代理
代理模式
是通过代理对象访问目标对象,这样可以在目标对象基础上增强额外的功能,如添加权限,访问控制和审计等功能。
房产中介代替业主卖房
静态代理
静态代理中代理类与被代理类都需要实现同一个接口,这就说明我们的一个静态代理类只能代理一个类,并且还要事先知道我们要代理哪个类才能写代理类,如果我们有其他类还想使用代理那就必须再写一个代理类。然而在实际开发中我们是可能是有非常多的类是需要被代理的,并且事先我们可能并不知道我们要代理哪个类。所以如果继续使用静态代理反而会增加许多的工作量,并且效率低下,代码复用率也不好。
1. package com.msb.test;
2.
3. /**
4. * @Author: Ma HaiYang
5. * @Description: MircoMessage:Mark_7001
6. */
7. public class Test1 {
8.
9. public static void main(String[] args) {
10. Person person =new Person("张三");
11. Court court=new Lawyer(person);
12. court.doCourt();
13. }
14. }
15.
16. // 接口
17. interface Court{
18. void doCourt();
19. }
20. // 代理类
21. class Lawyer implements Court{
22. private Person person;
23.
24. public Lawyer(Person person) {
25. this.person = person;
26. }
27.
28. @Override
29. public void doCourt() {
30. System.out.println("律师取证:视频证明张三当时正在旅游,不在案发现场");
31. System.out.println("律师总结:张三不可能去杀人");
32. person.doCourt();
33. }
34. }
35.
36. // 被代理的类
37. class Person implements Court{
38. private String name;
39.
40. public Person(String name) {
41. this.name = name;
42. }
43.
44. @Override
45. public void doCourt() {
46. System.out.println(name+"说:我没有杀人");
47. }
48. }
49.
50.
51.
动态代理
动态代理可以针对于一些不特定的类或者一些不特定的方法进行代理,我们可以在程序运行时动态的变化代理的规则,代理类在程序运行时才创建的代理模式成为动态代理。这种情况下,代理类并不是在Java代码中定义好的,而是在程序运行时根据我们的在Java代码中的“指示”动态生成的
Proxy 动态代理 JDK动态代理 面向接口
cglib 动态代理 第三方动态代理 面向父类
张三吃饭
1. package com.msb.testProxy;
2.
3. import java.lang.reflect.InvocationHandler;
4. import java.lang.reflect.Method;
5. import java.lang.reflect.Proxy;
6. import java.util.Arrays;
7.
8. /**
9. * @Author: Ma HaiYang
10. * @Description: MircoMessage:Mark_7001
11. */
12. public class Test1 {
13. public static void main(String[] args) {
14. Dinner dinner=new Person("张三");
15.
16. // 通过Porxy动态代理获得一个代理对象,在代理对象中,对某个方法进行增强
17. // ClassLoader loader,被代理的对象的类加载器
18. ClassLoader classLoader = dinner.getClass().getClassLoader();
19. // Class<?>[] interfaces,被代理对象所实现的所有接口
20. Class[] interaces= dinner.getClass().getInterfaces();
21. // InvocationHandler h,执行处理器对象,专门用于定义增强的规则
22. InvocationHandler handler = new InvocationHandler(){
23. // invoke 当我们让代理对象调用任何方法时,都会触发invoke方法的执行
24. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
25. // Object proxy, 代理对象
26. // Method method,被代理的方法
27. // Object[] args,被代理方法运行时的实参
28. Object res=null;
29. if(method.getName().equals("eat")){
30. System.out.println("饭前洗手");
31. // 让原有的eat的方法去运行
32. res =method.invoke(dinner, args);
33. System.out.println("饭后刷碗");
34. }else{
35. // 如果是其他方法,那么正常执行就可以了
36. res =method.invoke(dinner, args);
37. }
38. return res;
39. }
40. };
41.
42.
43. Dinner dinnerProxy =(Dinner) Proxy.newProxyInstance(classLoader,interaces,handler);
44. //dinnerProxy.eat("包子");
45. dinnerProxy.drink();
46. }
47. }
48.
49.
50.
51.
52.
53. interface Dinner{
54. void eat(String foodName);
55. void drink();
56. }
57.
58.
59. class Person implements Dinner{
60. private String name;
61.
62. public Person(String name) {
63. this.name = name;
64. }
65.
66. @Override
67. public void eat(String foodName) {
68. System.out.println(name+"正在吃"+foodName);
69. }
70. @Override
71. public void drink( ) {
72. System.out.println(name+"正在喝茶");
73. }
74. }
75.
76. class Student implements Dinner{
77. private String name;
78.
79. public Student(String name) {
80. this.name = name;
81. }
82.
83. @Override
84. public void eat(String foodName) {
85. System.out.println(name+"正在食堂吃"+foodName);
86. }
87. @Override
88. public void drink( ) {
89. System.out.println(name+"正在喝可乐");
90. }
91. }
使用代理技术 获得代理对象 代替张三 增强打官司的方法
总结
1在不修改原有代码的 或者没有办法修改原有代码的情况下 增强对象功能 使用代理对象 代替原来的对象去完成功能
进而达到拓展功能的目的
2JDK Proxy 动态代理面向接口的动态代理 一定要有接口和实现类的存在 代理对象增强的是实现类 在实现接口的方法重写的方法
生成的代理对象只能转换成 接口的不能转换成 被代理类
代理对象只能增强接口中定义的方法 实现类中其他和接口无关的方法是无法增强的
代理对象只能读取到接口中方法上的注解 不能读取到实现类方法上的注解
10_Spring_CGLIB动态代理
proxy 动态代理
面向接口
1必须有接口和实现类
2增强接口中定义的方法
3只能读取接口中方法的上注解
cglib动态代理模式
面向父类
1. package com.msb.testCglib;
2.
3.
4. import org.junit.Test;
5. import org.springframework.cglib.proxy.Enhancer;
6. import org.springframework.cglib.proxy.MethodInterceptor;
7. import org.springframework.cglib.proxy.MethodProxy;
8.
9. import java.lang.reflect.Method;
10.
11. /**
12. * @Author: Ma HaiYang
13. * @Description: MircoMessage:Mark_7001
14. */
15. public class Test1 {
16. @Test
17. public void testCglib(){
18. Person person =new Person();
19. // 获取一个Person的代理对象
20. // 1 获得一个Enhancer对象
21. Enhancer enhancer=new Enhancer();
22. // 2 设置父类字节码
23. enhancer.setSuperclass(person.getClass());
24. // 3 获取MethodIntercepter对象 用于定义增强规则
25. MethodInterceptor methodInterceptor=new MethodInterceptor() {
26. @Override
27. public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
28. /*Object o, 生成之后的代理对象 personProxy
29. Method method, 父类中原本要执行的方法 Person>>> eat()
30. Object[] objects, 方法在调用时传入的实参数组
31. MethodProxy methodProxy 子类中重写父类的方法 personProxy >>> eat()
32. */
33. Object res =null;
34. if(method.getName().equals("eat")){
35. // 如果是eat方法 则增强并运行
36. System.out.println("饭前洗手");
37. res=methodProxy.invokeSuper(o,objects);
38. System.out.println("饭后刷碗");
39. }else{
40. // 如果是其他方法 不增强运行
41. res=methodProxy.invokeSuper(o,objects); // 子类对象方法在执行,默认会调用父类对应被重写的方法
42. }
43. return res;
44. }
45. };
46. // 4 设置methodInterceptor
47. enhancer.setCallback(methodInterceptor);
48. // 5 获得代理对象
49. Person personProxy = (Person)enhancer.create();
50. // 6 使用代理对象完成功能
51. personProxy.eat("包子");
52. }
53.
54. }
55.
56. class Person {
57.
58.
59. public Person( ) {
60.
61. }
62.
63. public void eat(String foodName) {
64. System.out.println("张三正在吃"+foodName);
65. }
66.
67. }
68.
69.
11_Spring_AOP概念和原理
AOP切面编程一般可以帮助我们在不修改现有代码的情况下,对程序的功能进行拓展,往往用于实现 日志处理,权限控制,性能检测,事务控制等
AOP实现的原理就是动态代理,在有接口的情况下,使用JDK动态代理,在没有接口的情况下使用cglib动态代理
为Dao层所有的add方法添加一个性能记录功能
AOP中的术语辨析
1 连接点 Joint point:
类里面那些可以被增强的方法,这些方法称之为连接点
表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point
2 切入点 Pointcut:
实际被增强的方法,称之为切入点
表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方
3 通知 Advice:
实际增强的逻辑部分称为通知 (增加的功能)
Advice 定义了在 Pointcut 里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个 joint point 之前、之后还是代替执行的代码。
通知类型: 1 前置通知 2 后置通知 3 环绕通知 4 异常通知 5 最终通知
4 目标对象 Target:被增强功能的对象(被代理的对象)
织入 Advice 的目标对象
5 切面Aspect: 表现为功能相关的一些advice方法放在一起声明成的一个Java类
Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。
6 织入 Weaving:
创建代理对象并实现功能增强的声明并运行过程
将 Aspect 和其他对象连接起来, 并创建 Adviced object 的过程
12_Spring_AOP注解方式实现_掌握
AspectJ本身并不是spring框架中的组成部分, 是一个独立的AOP框架,一般把AspectJ和Spring框架的AOP依赖一起使用,所以要导入一个独立的依赖
实现的两种方式
1 基于注解方式实现 (熟练)
2 基于XML配置方式 (了解)
准备工作1
导入依赖
1. <dependencies>
2. <!--spring核心容器包-->
3. <dependency>
4. <groupId>org.springframework</groupId>
5. <artifactId>spring-context</artifactId>
6. <version>5.3.5</version>
7. </dependency>
8.
9. <!--spring切面包-->
10. <dependency>
11. <groupId>org.springframework</groupId>
12. <artifactId>spring-aspects</artifactId>
13. <version>5.3.5</version>
14. </dependency>
15. <!--织入包 spring-aspects 已经导入该包,这里可以不导入-->
16. <!--<dependency>
17. <groupId>org.aspectj</groupId>
18. <artifactId>aspectjweaver</artifactId>
19. <version>1.9.6</version>
20. </dependency>-->
21.
22. <!--aop联盟包-->
23. <dependency>
24. <groupId>aopalliance</groupId>
25. <artifactId>aopalliance</artifactId>
26. <version>1.0</version>
27. </dependency>
28.
29. <!--Apache Commons日志包-->
30. <dependency>
31. <groupId>commons-logging</groupId>
32. <artifactId>commons-logging</artifactId>
33. <version>1.2</version>
34. </dependency>
35.
36. <!--德鲁伊连接池-->
37. <dependency>
38. <groupId>com.alibaba</groupId>
39. <artifactId>druid</artifactId>
40. <version>1.1.10</version>
41. </dependency>
42.
43. <!--mysql驱动-->
44. <dependency>
45. <groupId>mysql</groupId>
46. <artifactId>mysql-connector-java</artifactId>
47. <version>8.0.22</version>
48. </dependency>
49.
50. <!--Junit单元测试-->
51. <dependency>
52. <groupId>junit</groupId>
53. <artifactId>junit</artifactId>
54. <version>4.13.1</version>
55. <scope>test</scope>
56. </dependency>
57.
58.
59. <!--lombok -->
60. <dependency>
61. <groupId>org.projectlombok</groupId>
62. <artifactId>lombok</artifactId>
63. <version>1.18.12</version>
64. <scope>provided</scope>
65. </dependency>
66.
67.
68. </dependencies>
69.
准备工作2
切入点表达式: 通过一个表达式来确定AOP要增强的是哪个或者那些方法
语法结构:execution([权限修饰符][返回值类型][类的全路径名][方法名](参数 列表) )
例子1
execution(* com.msb.dao.UserDaoImpl.add(..)) //指定切点为UserDaoImpl.add方法
execution(* com.msb.dao.UserDaoImpl.*(..)) //指定切点为UserDaoImpl.所有的方法
execution(* com.msb.dao.*.*(..)) //指定切点为dao包下所有的类中的所有的方法
execution(* com.msb.dao.*.add(..)) // 指定切点为dao包下所有的类中的add的方法
execution(* com.msb.dao.*.add*(..)) // 指定切点为dao包下所有的类中的add开头的方法
基于注解方式实现
项目结构
开启注解扫描和AOP切面编程自动生成代理对象配置
1. <?xml version="1.0" encoding="UTF-8"?>
2. <beans xmlns="http://www.springframework.org/schema/beans"
3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4. xmlns:p="http://www.springframework.org/schema/p"
5. xmlns:c="http://www.springframework.org/schema/c"
6. xmlns:util="http://www.springframework.org/schema/util"
7. xmlns:context="http://www.springframework.org/schema/context"
8. xmlns:aop="http://www.springframework.org/schema/aop"
9. xsi:schemaLocation="
10. http://www.springframework.org/schema/beans
11. http://www.springframework.org/schema/beans/spring-beans.xsd
12. http://www.springframework.org/schema/util
13. http://www.springframework.org/schema/util/spring-util.xsd
14. http://www.springframework.org/schema/context
15. http://www.springframework.org/schema/context/spring-context.xsd
16. http://www.springframework.org/schema/aop
17. http://www.springframework.org/schema/aop/spring-aop.xsd
18. ">
19.
20. <!--spring 包扫描 -->
21. <context:component-scan base-package="com.msb"/>
22. <!--aop autoProxy 自动生成代理对象 -->
23. <aop:aspectj-autoproxy/>
24.
25. </beans>
准备接口
UserDao和EmpDao
1. package com.msb.dao;
2.
3. /**
4. * @Author: Ma HaiYang
5. * @Description: MircoMessage:Mark_7001
6. */
7. public interface EmpDao {
8. int addEmp(Integer empno,String ename,String job);
9. }
10.
1. package com.msb.dao;
2.
3. /**
4. * @Author: Ma HaiYang
5. * @Description: MircoMessage:Mark_7001
6. */
7. public interface UserDao {
8. int addUser(Integer userid,String username);
9. }
接口实现类
1. package com.msb.dao.impl;
2.
3. import com.msb.dao.UserDao;
4. import org.springframework.stereotype.Repository;
5.
6. /**
7. * @Author: Ma HaiYang
8. * @Description: MircoMessage:Mark_7001
9. */
10. @Repository
11. public class UserDaoImpl implements UserDao {
12. public int addUser(Integer userid,String username){
13. System.out.println("userdao add ... ...");
14. //int i =1/0;
15. return 1;
16. }
17. }
1. package com.msb.dao.impl;
2.
3. import com.msb.dao.EmpDao;
4. import com.msb.dao.UserDao;
5. import org.springframework.stereotype.Repository;
6.
7. /**
8. * @Author: Ma HaiYang
9. * @Description: MircoMessage:Mark_7001
10. */
11. @Repository
12. public class EmpDaoImpl implements EmpDao {
13. public int addEmp(Integer empno,String ename,String job){
14. System.out.println("empDao add ... ...");
15. return 1;
16. }
17. }
18.
准备切面
1. package com.msb.aspect;
2.
3. import org.aspectj.lang.JoinPoint;
4. import org.aspectj.lang.ProceedingJoinPoint;
5. import org.aspectj.lang.annotation.*;
6. import org.springframework.core.annotation.Order;
7. import org.springframework.stereotype.Component;
8.
9. import java.util.Arrays;
10.
11. /**
12. * @Author: Ma HaiYang
13. * @Description: MircoMessage:Mark_7001
14. */
15.
16. @Component
17. @Aspect
18. public class DaoAspect {
19. //定义公共切点
20. @Pointcut("execution(* com.msb.dao.*.add*(..))")
21. public void addPointCut(){}
22.
23.
24. /*
25. * 前置通知: 切点方法执行之前先执行的功能
26. * 参数列表可以用JoinPoint接收切点对象
27. * 可以获取方法执行的参数
28. * */
29. @Before("addPointCut()")
30. public void methodBefore(JoinPoint joinPoint){
31. System.out.println("Before invoked");
32. }
33.
34. /*
35. * 后置通知:方法执行之后要增强的功能
36. * 无论切点方法是否出现异常都会执行的方法
37. * 参数列表可以用JoinPoint接收切点对象
38. * */
39. @After("addPointCut()")
40. public void methodAfter(JoinPoint joinPoint){
41. System.out.println("After invoked");
42.
43. }
44. /*
45. * 返回通知:切点方法正常运行结束后增强的功能
46. * 如果方法运行过程中出现异常,则该功能不运行
47. * 参数列表可以用 JoinPoint joinPoint接收切点对象
48. * 可以用Object res接收方法返回值,需要用returning指定返回值名称
49. * */
50. @AfterReturning( value = "addPointCut()",returning = "res")
51. public void methodAfterReturning(JoinPoint joinPoint,Object res){
52. System.out.println("AfterReturning invoked");
53.
54. }
55. /*
56. * 异常通知:切点方法出现异常时运行的增强功能
57. * 如果方法运行没有出现异常,则该功能不运行
58. * 参数列表可以用Exception ex接收异常对象 需要通过throwing指定异常名称
59. * */
60. @AfterThrowing( value = "addPointCut()",throwing = "ex")
61. public void methodAfterThrowing(Exception ex){
62. System.out.println("AfterThrowing invoked");
63. }
64.
65. /*环绕通知:在切点方法之前和之后都进行功能的增强
66. * 需要在通知中定义方法执行的位置,并在执行位置之前和之后自定义增强的功能
67. * 方法列表可以通过ProceedingJoinPoint获取执行的切点
68. * 通过proceedingJoinPoint.proceed()方法控制切点方法的执行位置
69. * proceedingJoinPoint.proceed()方法会将切点方法的返回值获取到,并交给我们,可以做后续处理
70. * 我们在环绕通知的最后需要将切点方法的返回值继续向上返回,否则切点方法在执行时接收不到返回值
71. * */
72. @Around("addPointCut()")
73. public Object methodAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
74. System.out.println("aroundA invoked");
75. Object proceed = proceedingJoinPoint.proceed();
76. System.out.println("aroundB invoked");
77. return proceed;
78. }
79. }
80.
测试代码
@Test
2. public void test1(){
3.
4. ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
5. UserDao userDao = context.getBean( UserDao.class);
6. int add = userDao.addUser(10,"晓明");
7.
8. }
有多个增强类对同一个方法进行增强,通过@Order注解设置增强类优先级
数字越小,优先级越高
数字越小,其代理位置越靠近注入位置
完全使用注解开发
创建配置类
1. package com.msb.config;
2.
3. import org.springframework.context.annotation.ComponentScan;
4. import org.springframework.context.annotation.Configuration;
5. import org.springframework.context.annotation.EnableAspectJAutoProxy;
6.
7. /**
8. * @Author: Ma HaiYang
9. * @Description: MircoMessage:Mark_7001
10. */
11. @Configuration
12. @ComponentScan(basePackages = "com.msb")
13. @EnableAspectJAutoProxy(proxyTargetClass = true)
14. public class SpringConfig {
15. }
16.
测试代码
1. @Test
2. public void test2(){
3.
4. ApplicationContext context=new AnnotationConfigApplicationContext(SpringConfig.class);
5. UserDao userDao = context.getBean( UserDao.class);
6. int add = userDao.addUser(10,"晓明");
7.
8. }
13_Spring_AOPXML方式实现_了解
1、创建两个类,增强类和被增强类,创建方法
见之前的代码
2、在spring配置文件中创建两个类对象
1. <!--创建对象-->
2. <bean id="userDao" class="com.com.msb.UserDaoImpl"></bean>
3. <bean id="daoAspect" class="com.com.aspect.DaoAspect"></bean>
3、在spring配置文件中配置切入点
1. <!--配置aop增强-->
2. <aop:config>
3. <!--切入点-->
4. <aop:pointcut id="pointCutAdd" expression="execution(* com.msb.dao.UserDao.add*(..))"/>
5. <!--配置切面-->
6. <aop:aspect ref="daoAspect">
7. <!--增强作用在具体的方法上-->
8. <aop:before method="methodBefore" pointcut-ref="pointCutAdd"/>
9. <aop:after method="methodAfter" pointcut-ref="pointCutAdd"/>
10. <aop:around method="methodAround" pointcut-ref="pointCutAdd"/>
11. <aop:after-returning method="methodAfterReturning" pointcut-ref="pointCutAdd" returning="res"/>
12. <aop:after-throwing method="methodAfterThrowing" pointcut-ref="pointCutAdd" throwing="ex"/>
13. </aop:aspect>
14. </aop:config>
14_Spring_JDBCTemplate的使用
JdbcTemplate概述
JdbcTemplate是spring框架中提供的一个对象,是对原始繁琐的Jdbc API对象的简单封装。spring框架为我们提供了很多的操作模板类。例如:操作关系型数据的JdbcTemplate和,操作nosql数据库的RedisTemplate,操作消息队列的JmsTemplate等等。
按如下项目结构准备 maven jar项目即可
1 导入依赖
<dependencies>
3. <!--spring核心容器包-->
4. <dependency>
5. <groupId>org.springframework</groupId>
6. <artifactId>spring-context</artifactId>
7. <version>5.3.5</version>
8. </dependency>
9.
10.
11. <!--spring切面包-->
12. <dependency>
13. <groupId>org.springframework</groupId>
14. <artifactId>spring-aspects</artifactId>
15. <version>5.3.5</version>
16. </dependency>
17.
18. <!--aop联盟包-->
19. <dependency>
20. <groupId>aopalliance</groupId>
21. <artifactId>aopalliance</artifactId>
22. <version>1.0</version>
23. </dependency>
24.
25. <!--德鲁伊连接池-->
26. <dependency>
27. <groupId>com.alibaba</groupId>
28. <artifactId>druid</artifactId>
29. <version>1.1.10</version>
30. </dependency>
31. <!--mysql驱动-->
32. <dependency>
33. <groupId>mysql</groupId>
34. <artifactId>mysql-connector-java</artifactId>
35. <version>8.0.22</version>
36. </dependency>
37. <!--springJDBC包-->
38. <dependency>
39. <groupId>org.springframework</groupId>
40. <artifactId>spring-jdbc</artifactId>
41. <version>5.3.5</version>
42. </dependency>
43. <!--spring事务控制包-->
44. <dependency>
45. <groupId>org.springframework</groupId>
46. <artifactId>spring-tx</artifactId>
47. <version>5.3.5</version>
48. </dependency>
49. <!--spring orm 映射依赖-->
50. <dependency>
51. <groupId>org.springframework</groupId>
52. <artifactId>spring-orm</artifactId>
53. <version>5.3.5</version>
54. </dependency>
55.
56.
57. <!--Apache Commons日志包-->
58. <dependency>
59. <groupId>commons-logging</groupId>
60. <artifactId>commons-logging</artifactId>
61. <version>1.2</version>
62. </dependency>
63. <!--Junit单元测试-->
64. <dependency>
65. <groupId>junit</groupId>
66. <artifactId>junit</artifactId>
67. <version>4.13.1</version>
68. <scope>test</scope>
69. </dependency>
70. <!--lombok -->
71. <dependency>
72. <groupId>org.projectlombok</groupId>
73. <artifactId>lombok</artifactId>
74. <version>1.18.12</version>
75. <scope>provided</scope>
76. </dependency>
77. </dependencies>
2 准备JDBC.properties
1. jdbc_username=root
2. jdbc_password=root
3. jdbc_driver=com.mysql.cj.jdbc.Driver
4. jdbc_url=jdbc:mysql://127.0.0.1:3306/mydb?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
3 准备applicationContext.xml
1. <?xml version="1.0" encoding="UTF-8"?>
2. <beans xmlns="http://www.springframework.org/schema/beans"
3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4. xmlns:p="http://www.springframework.org/schema/p"
5. xmlns:c="http://www.springframework.org/schema/c"
6. xmlns:util="http://www.springframework.org/schema/util"
7. xmlns:context="http://www.springframework.org/schema/context"
8. xmlns:aop="http://www.springframework.org/schema/aop"
9. xsi:schemaLocation="
10. http://www.springframework.org/schema/beans
11. http://www.springframework.org/schema/beans/spring-beans.xsd
12. http://www.springframework.org/schema/util
13. http://www.springframework.org/schema/util/spring-util.xsd
14. http://www.springframework.org/schema/context
15. http://www.springframework.org/schema/context/spring-context.xsd
16. http://www.springframework.org/schema/aop
17. http://www.springframework.org/schema/aop/spring-aop.xsd
18. ">
19.
20. <!--spring 注解扫描-->
21. <context:component-scan base-package="com.msb"/>
22. <!--读取jdbc配置文件-->
23. <context:property-placeholder location="classpath:jdbc.properties"/>
24.
25. <!--配置德鲁伊连接池-->
26. <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
27. <property name="username" value="${jdbc_username}"></property>
28. <property name="password" value="${jdbc_password}"></property>
29. <property name="url" value="${jdbc_url}"></property>
30. <property name="driverClassName" value="${jdbc_driver}"></property>
31. </bean>
32.
33.
34. <!--配置JDBCTemplate对象,并向里面注入DataSource-->
35. <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
36. <!--通过set方法注入连接池-->
37. <property name="dataSource" ref="dataSource"></property>
38. </bean>
39.
40.
41.
42. </beans>
3 准备实体类
1. package com.msb.pojo;
2.
3. import lombok.AllArgsConstructor;
4. import lombok.Data;
5. import lombok.NoArgsConstructor;
6.
7. import java.util.Date;
8.
9. /**
10. * @Author: Ma HaiYang
11. * @Description: MircoMessage:Mark_7001
12. */
13. @AllArgsConstructor
14. @NoArgsConstructor
15. @Data
16. public class Emp implements Serializable{
17. private Integer empno;
18. private String ename;
19. private String job;
20. private Integer mgr;
21. private Date hiredate;
22. private Double sal;
23. private Double comm;
24. private Integer deptno;
25. }
26.
4 准备service层接口和实现类
1. package com.msb.service;
2.
3. import com.msb.pojo.Emp;
4.
5. import java.util.List;
6.
7. /**
8. * @Author: Ma HaiYang
9. * @Description: MircoMessage:Mark_7001
10. */
11. public interface EmpService {
12. int findEmpCount();
13.
14. Emp findByEmpno(int empno);
15.
16. List<Emp> findByDeptno(int deptno);
17.
18. int addEmp(Emp emp);
19.
20. int updateEmp(Emp emp);
21.
22. int deleteEmp( int empno);
23. }
24.
1. package com.msb.service.impl;
2.
3. import com.msb.dao.EmpDao;
4. import com.msb.pojo.Emp;
5. import com.msb.service.EmpService;
6. import org.springframework.beans.factory.annotation.Autowired;
7. import org.springframework.stereotype.Service;
8.
9. import java.util.List;
10.
11. /**
12. * @Author: Ma HaiYang
13. * @Description: MircoMessage:Mark_7001
14. */
15. @Service
16. public class EmpServiceImpl implements EmpService {
17. @Autowired
18. private EmpDao empDao;
19.
20. @Override
21. public int findEmpCount() {
22. return empDao.findEmpCount();
23. }
24.
25. @Override
26. public Emp findByEmpno(int empno) {
27. return empDao.findByEmpno( empno);
28. }
29.
30. @Override
31. public List<Emp> findByDeptno(int deptno) {
32. return empDao.findByDeptno( deptno);
33. }
34.
35. @Override
36. public int addEmp(Emp emp) {
37. return empDao.addEmp(emp);
38. }
39.
40. @Override
41. public int updateEmp(Emp emp) {
42.
43. return empDao.updateEmp(emp);
44. }
45.
46. @Override
47. public int deleteEmp(int empno) {
48. return empDao.deleteEmp(empno);
49. }
50. }
51.
5 准备dao层接口和实现类
1. package com.msb.dao;
2.
3. import com.msb.pojo.Emp;
4.
5. import java.util.List;
6.
7. /**
8. * @Author: Ma HaiYang
9. * @Description: MircoMessage:Mark_7001
10. */
11. public interface EmpDao {
12. int findEmpCount();
13.
14. Emp findByEmpno(int empno);
15.
16. List<Emp> findByDeptno(int deptno);
17.
18. int addEmp(Emp emp);
19.
20. int updateEmp(Emp emp);
21.
22. int deleteEmp(int empno);
23. }
24.
1. package com.msb.dao.impl;
2.
3. import com.msb.dao.EmpDao;
4. import com.msb.pojo.Emp;
5. import org.springframework.beans.factory.annotation.Autowired;
6. import org.springframework.jdbc.core.BeanPropertyRowMapper;
7. import org.springframework.jdbc.core.JdbcTemplate;
8. import org.springframework.jdbc.core.RowMapper;
9. import org.springframework.stereotype.Repository;
10.
11. import java.sql.ResultSet;
12. import java.sql.SQLException;
13. import java.util.List;
14.
15. /**
16. * @Author: Ma HaiYang
17. * @Description: MircoMessage:Mark_7001
18. */
19.
20. @Repository
21. public class EmpDaoImpl implements EmpDao {
22. @Autowired
23. private JdbcTemplate jdbcTemplate;
24.
25. @Override
26. public int findEmpCount() {
27. /*查询员工个数
28. * queryForObject 两个参数
29. * 1 SQL语句
30. * 2 返回值类型
31. *
32. * */
33. Integer empCount = jdbcTemplate.queryForObject("select count(1) from emp", Integer.class);
34. return empCount;
35. }
36.
37. @Override
38. public Emp findByEmpno(int empno) {
39. /*
40. * 查询单个员工对象
41. * queryForObject三个参数
42. * 1 SQL语句
43. * 2 RowMapper接口的实现类对象,用于执行返回的结果用哪个类来进行封装 ,实现类为BeanPropertyRowMapper
44. * 3 SQL语句中需要的参数 (可变参数)
45. * */
46. BeanPropertyRowMapper<Emp> rowMapper =new BeanPropertyRowMapper<>(Emp.class);
47. Emp emp = jdbcTemplate.queryForObject("select * from emp where empno =?", rowMapper, empno);
48. return emp;
49. }
50.
51. @Override
52. public List<Emp> findByDeptno(int deptno) {
53. /*
54. * 查询单个员工对象
55. * query三个参数
56. * 1 SQL语句
57. * 2 RowMapper接口的实现类对象,用于执行返回的结果用哪个类来进行封装 ,实现类为BeanPropertyRowMapper
58. * 3 SQL语句中需要的参数 (可变参数)
59. * */
60. BeanPropertyRowMapper<Emp> rowMapper =new BeanPropertyRowMapper<>(Emp.class);
61. List<Emp> emps = jdbcTemplate.query("select * from emp where deptno =?", rowMapper, deptno);
62. return emps;
63. }
64.
65. @Override
66. public int addEmp(Emp emp) {
67. /*增删改
68. * 统统用update方法 两个参数
69. * 1 SQL语句
70. * 2 SQL语句需要的参数 (可变参数)
71. *
72. * */
73. String sql ="insert into emp values(DEFAULT ,?,?,?,?,?,?,?)";
74. Object[] args ={emp.getEname(),emp.getJob(),emp.getMgr(),emp.getHiredate(),emp.getSal(),emp.getComm(),emp.getDeptno()};
75. return jdbcTemplate.update(sql,args);
76. }
77.
78. @Override
79. public int updateEmp(Emp emp) {
80. String sql ="update emp set ename =? , job =?, mgr=? , hiredate =?, sal=?, comm=?, deptno =? where empno =?";
81. Object[] args ={emp.getEname(),emp.getJob(),emp.getMgr(),emp.getHiredate(),emp.getSal(),emp.getComm(),emp.getDeptno(),emp.getEmpno()};
82. return jdbcTemplate.update(sql,args);
83. }
84.
85. @Override
86. public int deleteEmp(int empno) {
87. String sql ="delete from emp where empno =?";
88. return jdbcTemplate.update(sql, empno);
89. }
90. }
91.
6 测试代码
1. package com.msb.test;
2.
3. import com.msb.pojo.Emp;
4. import com.msb.service.EmpService;
5. import org.junit.Test;
6. import org.springframework.context.ApplicationContext;
7. import org.springframework.context.support.ClassPathXmlApplicationContext;
8.
9. import java.util.Date;
10. import java.util.List;
11.
12. /**
13. * @Author: Ma HaiYang
14. * @Description: MircoMessage:Mark_7001
15. */
16. public class Test1 {
17.
18. @Test
19. public void testEmpService(){
20. ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
21. EmpService empService = context.getBean(EmpService.class);
22. // 查询员工个数
23. /*int empCount = empService.findEmpCount();
24. System.out.println(empCount);*/
25.
26. // 根据员工编号查询员工对象
27.
28. /* Emp byEmpno = empService.findByEmpno(7521);
29. System.out.println(byEmpno);*/
30.
31.
32. /*根据部门编号查询多个员工对象集合*/
33. /*List<Emp> emps = empService.findByDeptno(20);
34. emps.forEach(System.out::println);*/
35.
36. /*增加员工信息*/
37. /*int rows = empService.addEmp(new Emp(null, "TOM", "SALESMAN", 7521, new Date(), 2000.0, 100.0, 10));
38. System.out.println(rows);*/
39.
40.
41. /*根据员工编号修改员工信息*/
42. /*int rows = empService.updateEmp(new Emp(7939, "JERRY", "MANAGER", 7839, new Date(), 3000.0, 0.0, 20));
43. System.out.println(rows);*/
44.
45.
46. /*根据员工编号删除员工信息*/
47. /*int rows = empService.deleteEmp(7939);
48. System.out.println(rows);*/
49.
50.
51. }
52. }
53.
15_Spring_JDBCTemplate批操作
一次连接,操作表格里的多条数据,就是批量操作
1 批量增加
2 批量修改
3 批量删除
实体类
1. package com.msb.pojo;
2.
3. import lombok.AllArgsConstructor;
4. import lombok.Data;
5. import lombok.NoArgsConstructor;
6.
7. import java.io.Serializable;
8.
9. /**
10. * @Author: Ma HaiYang
11. * @Description: MircoMessage:Mark_7001
12. */
13. @AllArgsConstructor
14. @NoArgsConstructor
15. @Data
16. public class Dept implements Serializable {
17. private Integer deptno;
18. private String dname;
19. private String loc;
20. }
21.
DeptService
1. package com.msb.service;
2.
3. import com.msb.pojo.Dept;
4.
5. import java.util.List;
6.
7. /**
8. * @Author: Ma HaiYang
9. * @Description: MircoMessage:Mark_7001
10. */
11. public interface DeptService {
12. int[] deptBatchAdd(List<Dept> depts);
13.
14. int[] deptBatchUpdate(List<Dept> depts);
15.
16. int[] deptBatchDelete(List<Integer> deptnos);
17.
18.
19. }
20.
1. package com.msb.service.impl;
2.
3. import com.msb.dao.DeptDao;
4. import com.msb.pojo.Dept;
5. import com.msb.service.DeptService;
6. import org.springframework.beans.factory.annotation.Autowired;
7. import org.springframework.stereotype.Service;
8.
9. import java.util.List;
10.
11. /**
12. * @Author: Ma HaiYang
13. * @Description: MircoMessage:Mark_7001
14. */
15. @Service
16. public class DeptServiceImpl implements DeptService {
17. @Autowired
18. private DeptDao deptDao;
19. @Override
20. public int[] deptBatchAdd(List<Dept> depts) {
21.
22. return deptDao.deptBatchAdd(depts);
23. }
24.
25. @Override
26. public int[] deptBatchUpdate(List<Dept> depts) {
27. return deptDao.deptBatchUpdate(depts);
28. }
29.
30. @Override
31. public int[] deptBatchDelete(List<Integer> deptnos) {
32. return deptDao.deptBatchDelete(deptnos);
33. }
34. }
35.
DeptDao
1. package com.msb.dao;
2.
3. import com.msb.pojo.Dept;
4.
5. import java.util.List;
6.
7. /**
8. * @Author: Ma HaiYang
9. * @Description: MircoMessage:Mark_7001
10. */
11. public interface DeptDao {
12. int[] deptBatchAdd(List<Dept> depts);
13.
14. int[] deptBatchUpdate(List<Dept> depts);
15.
16. int[] deptBatchDelete(List<Integer> deptnos);
17. }
18.
19.
1. package com.msb.dao.impl;
2.
3. import com.msb.dao.DeptDao;
4. import com.msb.pojo.Dept;
5. import org.springframework.beans.factory.annotation.Autowired;
6. import org.springframework.jdbc.core.JdbcTemplate;
7. import org.springframework.stereotype.Repository;
8.
9. import java.util.LinkedList;
10. import java.util.List;
11.
12. /**
13. * @Author: Ma HaiYang
14. * @Description: MircoMessage:Mark_7001
15. */
16. @Repository
17. public class DeptDaoImpl implements DeptDao {
18. @Autowired
19. private JdbcTemplate jdbcTemplate;
20. @Override
21. public int[] deptBatchAdd(List<Dept> depts) {
22. String sql ="insert into dept values(DEFAULT,?,?)";
23. List<Object[]> args =new LinkedList<>();
24. for (Dept dept : depts) {
25. Object[] arg ={dept.getDname(),dept.getLoc()};
26. args.add(arg);
27. }
28. return jdbcTemplate.batchUpdate(sql, args);
29. }
30.
31. @Override
32. public int[] deptBatchUpdate(List<Dept> depts) {
33. String sql ="update dept set dname =? ,loc =? where deptno=?";
34. List<Object[]> args =new LinkedList<>();
35. for (Dept dept : depts) {
36. Object[] arg ={dept.getDname(),dept.getLoc(),dept.getDeptno()};
37. args.add(arg);
38. }
39. return jdbcTemplate.batchUpdate(sql, args);
40. }
41.
42. @Override
43. public int[] deptBatchDelete(List<Integer> deptnos) {
44. String sql ="delete from dept where deptno =?";
45. List<Object[]> args =new LinkedList<>();
46. for (Integer deptno : deptnos) {
47. Object[] arg ={deptno};
48. args.add(arg);
49. }
50. return jdbcTemplate.batchUpdate(sql, args);
51. }
52. }
测试
1. package com.msb.test;
2.
3. import com.msb.pojo.Dept;
4. import com.msb.service.DeptService;
5. import com.msb.service.EmpService;
6. import org.junit.Test;
7. import org.springframework.context.ApplicationContext;
8. import org.springframework.context.support.ClassPathXmlApplicationContext;
9.
10. import java.util.ArrayList;
11. import java.util.Arrays;
12. import java.util.List;
13.
14. /**
15. * @Author: Ma HaiYang
16. * @Description: MircoMessage:Mark_7001
17. */
18. public class Test2 {
19.
20. @Test
21. public void testBatchAdd(){
22. ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
23. DeptService deptService = context.getBean(DeptService.class);
24. List<Dept> depts =new ArrayList<>();
25. for (int i = 0; i < 10; i++) {
26. depts.add(new Dept(null,"name"+i,"loc"+i));
27. }
28.
29. int[] ints = deptService.deptBatchAdd(depts);
30. System.out.println(Arrays.toString(ints));
31.
32.
33. }
34.
35. @Test
36. public void testBatchUpdate(){
37. ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
38. DeptService deptService = context.getBean(DeptService.class);
39. List<Dept> depts =new ArrayList<>();
40. for (int i = 51; i <=60; i++) {
41. depts.add(new Dept(i,"newname","newLoc"));
42. }
43.
44. int[] ints = deptService.deptBatchUpdate(depts);
45. System.out.println(Arrays.toString(ints));
46. }
47.
48. @Test
49. public void testBatchDelete(){
50. ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
51. DeptService deptService = context.getBean(DeptService.class);
52. List<Integer> deptnos =new ArrayList<>();
53. for (int i = 51; i <=69; i++) {
54. deptnos.add(i);
55. }
56. int[] ints = deptService.deptBatchDelete(deptnos);
57. System.out.println(Arrays.toString(ints));
58. }
59. }
16_Spring_事务回顾
1. 事务的概念
事务(Transaction)指的是一个操作序列,该操作序列中的多个操作要么都做,要么都不做,是一个不可分割的工作单位,是数据库环境中的逻辑工作单位,由DBMS中的事务管理子系统负责事务的处理。
目前常用的存储引擎有InnoDB(MySQL5.5以后默认的存储引擎)和MyISAM(MySQL5.5之前默认的存储引擎),其中InnoDB支持事务处理机制,而MyISAM不支持。
2. 事务的特性
事务处理可以确保除非事务性序列内的所有操作都成功完成,否则不会永久更新面向数据的资源。通过将一组相关操作组合为一个要么全部成功要么全部失败的序列,可以简化错误恢复并使应用程序更加可靠。
但并不是所有的操作序列都可以称为事务,这是因为一个操作序列要成为事务,必须满足事务的原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)。这四个特性简称为ACID特性。
1) 原子性
原子是自然界最小的颗粒,具有不可再分的特性。事务中的所有操作可以看做一个原子,事务是应用中不可再分的最小的逻辑执行体。
使用事务对数据进行修改的操作序列,要么全部执行,要么全不执行。通常,某个事务中的操作都具有共同的目标,并且是相互依赖的。如果数据库系统只执行这些操作中的一部分,则可能会破坏事务的总体目标,而原子性消除了系统只处理部分操作的可能性。
2) 一致性
一致性是指事务执行的结果必须使数据库从一个一致性状态,变到另一个一致性状态。当数据库中只包含事务成功提交的结果时,数据库处于一致性状态。一致性是通过原子性来保证的。
例如:在转账时,只有保证转出和转入的金额一致才能构成事务。也就是说事务发生前和发生后,数据的总额依然匹配。
3) 隔离性
隔离性是指各个事务的执行互不干扰,任意一个事务的内部操作对其他并发的事务,都是隔离的。也就是说:并发执行的事务之间既不能看到对方的中间状态,也不能相互影响。
例如:在转账时,只有当A账户中的转出和B账户中转入操作都执行成功后才能看到A账户中的金额减少以及B账户中的金额增多。并且其他的事务对于转账操作的事务是不能产生任何影响的。
4) 持久性
持久性指事务一旦提交,对数据所做的任何改变,都要记录到永久存储器中,通常是保存进物理数据库,即使数据库出现故障,提交的数据也应该能够恢复。但如果是由于外部原因导致的数据库故障,如硬盘被损坏,那么之前提交的数据则有可能会丢失。
3. 事务的并发问题
脏读(Dirty read)
当一个事务正在访问数据并且对数据进行了修改,而这种修改还没有提交到数据库中,这时另外一个事务也访问了这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是“脏数据”,依据“脏数据”所做的操作可能是不正确的。
不可重复读
(Unrepeatableread): 指在一个事务内多次读同一数据。在这个事务还没有结束时,另一个事务也访问该数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改导致第一个事务两次读取的数据可能不太一样。这就发生了在一个事务内两次读到的数据是不一样的情况,因此称为不可重复读。
幻读
(Phantom read): 幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。
不可重复度和幻读区别:
不可重复读的重点是修改,幻读的重点在于新增或者删除。
解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表
例1(同样的条件, 你读取过的数据, 再次读取出来发现值不一样了 ):事务1中的A先生读取自己的工资为 1000的操作还没完成,事务2中的B先生就修改了A的工资为2000,导 致A再读自己的工资时工资变为 2000;这就是不可重复读。
例2(同样的条件, 第1次和第2次读出来的记录数不一样 ):假某工资单表中工资大于3000的有4人,事务1读取了所有工资大于3000的人,共查到4条记录,这时事务2又插入了一条工资大于3000的记录,事务1再次读取时查到的记录就变为了5条,这样就导致了幻读
4.事务的隔离级别
事务的隔离级别用于决定如何控制并发用户读写数据的操作。数据库是允许多用户并发访问的,如果多个用户同时开启事务并对同一数据进行读写操作的话,有可能会出现脏读、不可重复读和幻读问题,所以MySQL中提供了四种隔离级别来解决上述问题。
事务的隔离级别从低到高依次为READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ以及SERIALIZABLE,隔离级别越低,越能支持高并发的数据库操作。
隔离级别
|
脏读
|
不可重复读
|
幻读
|
READ UNCOMMITTED
|
√
|
√
|
√
|
READ COMMITTED
|
×
|
√
|
√
|
REPEATABLE READ
|
×
|
×
|
√
|
SERIALIZABLE
|
×
|
×
|
×
|
spring中可以使用如下方式实现事务的控制
1 编程式(不推荐)
2 声明式(掌握)
1) 注解(简单,必会)
2) XML配置(繁琐,了解)
17_Spring_事务环境搭建
通过张三给李四转账案例演示事务的控制
1 数据库中准备表格
applicationContext.xml
jdbc.properties
见上述
2 项目中准备实体类
1. package com.msb.pojo;
2.
3. import lombok.AllArgsConstructor;
4. import lombok.Data;
5. import lombok.NoArgsConstructor;
6.
7. import java.io.Serializable;
8.
9. /**
10. * @Author: Ma HaiYang
11. * @Description: MircoMessage:Mark_7001
12. */
13. @AllArgsConstructor
14. @NoArgsConstructor
15. @Data
16. public class Account implements Serializable {
17. private Integer id;
18. private String name;
19. private Integer money;
20. }
3 准备DAO层,创建一个根据id修改money的方法
1. package com.msb.dao;
2.
3. /**
4. * @Author: Ma HaiYang
5. * @Description: MircoMessage:Mark_7001
6. */
7. public interface AccountDao {
8. int transMoney(int id,int money);
9. }
10.
1. package com.msb.dao.impl;
2.
3. import com.msb.dao.AccountDao;
4. import org.springframework.beans.factory.annotation.Autowired;
5. import org.springframework.jdbc.core.JdbcTemplate;
6. import org.springframework.stereotype.Repository;
7.
8. /**
9. * @Author: Ma HaiYang
10. * @Description: MircoMessage:Mark_7001
11. */
12. @Repository
13. public class AccountDaoImpl implements AccountDao {
14. @Autowired
15. private JdbcTemplate jdbcTemplate;
16. @Override
17. public int transMoney(int id, int money) {
18. String sql ="update account set money =money +? where id =?";
19. return jdbcTemplate.update(sql,money,id);
20. }
21. }
4 准备Service,创建一个转账的业务方法
1. package com.msb.service;
2.
3. /**
4. * @Author: Ma HaiYang
5. * @Description: MircoMessage:Mark_7001
6. */
7. public interface AccountService {
8.
9. int transMoney(int from ,int to,int money);
10.
11. }
12.
1. package com.msb.service.impl;
2.
3. import com.msb.dao.AccountDao;
4. import com.msb.service.AccountService;
5. import org.springframework.beans.factory.annotation.Autowired;
6. import org.springframework.stereotype.Service;
7.
8. /**
9. * @Author: Ma HaiYang
10. * @Description: MircoMessage:Mark_7001
11. */
12. @Service
13. public class AccountServiceImpl implements AccountService {
14. @Autowired
15. private AccountDao accountDao;
16.
17. @Override
18. public int transMoney(int from, int to, int money) {
19. int rows=0;
20. rows+=accountDao.transMoney(from, 0 - money);
21. rows+=accountDao.transMoney(to, money);
22. return rows;
23. }
24. }
25.
5 测试代码,测试转账
1. package com.msb.test;
2.
3. import com.msb.service.AccountService;
4. import org.junit.Test;
5. import org.springframework.context.ApplicationContext;
6. import org.springframework.context.support.ClassPathXmlApplicationContext;
7.
8. /**
9. * @Author: Ma HaiYang
10. * @Description: MircoMessage:Mark_7001
11. */
12. public class TestTx {
13.
14. @Test()
15. public void testTransaction(){
16. ApplicationContext context =new ClassPathXmlApplicationContext("applicationContext.xml");
17. AccountService accountService = context.getBean(AccountService.class);
18. int rows = accountService.transMoney(1, 2, 100);
19. System.out.println(rows);
20.
21. }
22.
23. }
24.
PROPAGATION_REQUIRED
如果add方法有事务,那么addDept和addEmp就加入到add方法里的事务
如果add方法没有事务,那么就新建一个事务,将addDept和addEmp加入到这个新的事务中
PROPAGATION_REQUIRES_NEW
无论add是否有事务,都建立一个新的事务,所有的方法都加入到新的事务中,add原来的事务就不用了
isolation 事务的隔离级别
1) DEFAULT (默认)
这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别。另外四个与JDBC的隔离级别相对应。
MySQL默认REPEATABLE_READ
Oracle默认READ_COMMITTED
2) READ_UNCOMMITTED (读未提交)
这是事务最低的隔离级别,它允许另外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻像读。
3) READ_COMMITTED (读已提交)
保证一个事务修改的数据提交后才能被另外一个事务读取,另外一个事务不能读取该事务未提交的数据。这种事务隔离级别可以避免脏读出现,但是可能会出现不可重复读和幻像读。
4) REPEATABLE_READ (可重复读)
这种事务隔离级别可以防止脏读、不可重复读,但是可能出现幻像读。它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了不可重复读。
5) SERIALIZABLE(串行化)
这是花费最高代价但是最可靠的事务隔离级别,事务被处理为顺序执行。除了防止脏读、不可重复读外,还避免了幻像读。
timeout 超时时间
事务一定要在多长时间之内提交,如果不提交就会回滚
readOnly 只读事务
事务是否只能读取数据库的数据,如果为true,则不允许进行增删改
rollbackFor 指定发生回滚的异常
当方法发生哪些异常时才会回滚
noRollbackFor 指定不发生回滚的异常
当方法发生哪些异常时,不会回滚
19_Spring_事务管理XML配置方式
applicationContext中,通过AOP实现事务的控制
1. <?xml version="1.0" encoding="UTF-8"?>
2. <beans xmlns="http://www.springframework.org/schema/beans"
3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4. xmlns:p="http://www.springframework.org/schema/p"
5. xmlns:c="http://www.springframework.org/schema/c"
6. xmlns:util="http://www.springframework.org/schema/util"
7. xmlns:context="http://www.springframework.org/schema/context"
8. xmlns:aop="http://www.springframework.org/schema/aop"
9. xmlns:tx="http://www.springframework.org/schema/tx"
10. xsi:schemaLocation="
11. http://www.springframework.org/schema/beans
12. http://www.springframework.org/schema/beans/spring-beans.xsd
13. http://www.springframework.org/schema/util
14. http://www.springframework.org/schema/util/spring-util.xsd
15. http://www.springframework.org/schema/context
16. http://www.springframework.org/schema/context/spring-context.xsd
17. http://www.springframework.org/schema/aop
18. http://www.springframework.org/schema/aop/spring-aop.xsd
19. http://www.springframework.org/schema/tx
20. http://www.springframework.org/schema/tx/spring-tx.xsd
21. ">
22.
23.
24.
25. <context:component-scan base-package="com.msb"/>
26.
27. <!--读取jdbc配置文件-->
28. <context:property-placeholder location="classpath:jdbc.properties"/>
29. <!--配置德鲁伊连接池-->
30. <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
31. <property name="username" value="${jdbc_username}"></property>
32. <property name="password" value="${jdbc_password}"></property>
33. <property name="url" value="${jdbc_url}"></property>
34. <property name="driverClassName" value="${jdbc_driver}"></property>
35. </bean>
36. <!--配置JDBCTemplate对象,并向里面注入DataSource-->
37. <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
38. <!--通过set方法注入连接池-->
39. <property name="dataSource" ref="dataSource"></property>
40. </bean>
41. <!--配置一个事务管理器-->
42. <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
43. <!--将数据源注入事务管理器-->
44. <property name="dataSource" ref="dataSource"></property>
45. </bean>
46. <!--配置通知-->
47. <tx:advice id="txAdvice">
48. <!--配置事务参数-->
49. <tx:attributes>
50. <tx:method name="transMoney" isolation="DEFAULT" propagation="REQUIRED"/>
51. </tx:attributes>
52. </tx:advice>
53. <!--配置AOP-->
54. <aop:config>
55. <!--配置切入点-->
56. <aop:pointcut id="pt" expression="execution(* com.msb.service.AccountService.transMoney(..))"/>
57. <!--配置切面-->
58. <aop:advisor advice-ref="txAdvice" pointcut-ref="pt"></aop:advisor>
59. </aop:config>
60.
61.
62.
63.
64.
65.
66. </beans>
18_Spring_事务管理注解方式
事务的管理应该放在我们的service层进行处理
spring中有两种事务的管理方式
1 编程式事务管理(了解)
2 声明式事务管理(掌握)
基于注解方式实现(掌握)
XML方式实现(了解)
Spring声明式事务的实现方式,底层就是AOP,AOP的底层就是动态代理
Spring事务管理相关的API
事务管理器接口: PlatformTransactionManager 针对不同的框架,提供了不同的实现类
注解方式实现事务控制
在applicationContext.xml配置事务相关的配置
1. <?xml version="1.0" encoding="UTF-8"?>
2. <beans xmlns="http://www.springframework.org/schema/beans"
3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4. xmlns:p="http://www.springframework.org/schema/p"
5. xmlns:c="http://www.springframework.org/schema/c"
6. xmlns:util="http://www.springframework.org/schema/util"
7. xmlns:context="http://www.springframework.org/schema/context"
8. xmlns:aop="http://www.springframework.org/schema/aop"
9. xmlns:tx="http://www.springframework.org/schema/tx"
10. xsi:schemaLocation="
11. http://www.springframework.org/schema/beans
12. http://www.springframework.org/schema/beans/spring-beans.xsd
13. http://www.springframework.org/schema/util
14. http://www.springframework.org/schema/util/spring-util.xsd
15. http://www.springframework.org/schema/context
16. http://www.springframework.org/schema/context/spring-context.xsd
17. http://www.springframework.org/schema/aop
18. http://www.springframework.org/schema/aop/spring-aop.xsd
19. http://www.springframework.org/schema/tx
20. http://www.springframework.org/schema/tx/spring-tx.xsd
21. ">
22. <!--spring 注解扫描-->
23. <context:component-scan base-package="com.msb"/>
24. <!--读取jdbc配置文件-->
25. <context:property-placeholder location="classpath:jdbc.properties"/>
26. <!--配置德鲁伊连接池-->
27. <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
28. <property name="username" value="${jdbc_username}"></property>
29. <property name="password" value="${jdbc_password}"></property>
30. <property name="url" value="${jdbc_url}"></property>
31. <property name="driverClassName" value="${jdbc_driver}"></property>
32. </bean>
33. <!--配置JDBCTemplate对象,并向里面注入DataSource-->
34. <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
35. <!--通过set方法注入连接池-->
36. <property name="dataSource" ref="dataSource"></property>
37. </bean>
38.
39. <!--配置一个事务管理器-->
40. <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
41. <!--将数据源注入事务管理器-->
42. <property name="dataSource" ref="dataSource"></property>
43. </bean>
44.
45. <!--开启事务注解-->
46. <tx:annotation-driven transaction-manager="transactionManager"/>
47.
48.
49. </beans>
在Service层中添加事务的注解
1. package com.msb.service.impl;
2.
3. import com.msb.dao.AccountDao;
4. import com.msb.service.AccountService;
5. import org.springframework.beans.factory.annotation.Autowired;
6. import org.springframework.stereotype.Service;
7. import org.springframework.transaction.annotation.Transactional;
8.
9. /**
10. * @Author: Ma HaiYang
11. * @Description: MircoMessage:Mark_7001
12. */
13. @Service
14. //@Transactional //加在类上,代表类中的所有方法都添加了事务控制
15. public class AccountServiceImpl implements AccountService {
16. @Autowired
17. private AccountDao accountDao;
18.
19. @Override
20. @Transactional// 放在方法上,就是仅仅对当前方法增加了事务控制
21. public int transMoney(int from, int to, int money) {
22. int rows=0;
23. rows+=accountDao.transMoney(from, 0 - money);
24. int i =1/0;
25. rows+=accountDao.transMoney(to, money);
26. return rows;
27. }
28. }
29.
再次测试,就算是service方法运行出现异常,自动会回滚,如果没有,那么自动提交
@Transactional 注解的一些参数和参数的含义
@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.READ_UNCOMMITTED,readOnly = true,rollbackFor = ClassCastException.class,noRollbackFor = NullPointerException.class,timeout = 10)
1.
2. @Target({ElementType.TYPE, ElementType.METHOD})
3. @Retention(RetentionPolicy.RUNTIME)
4. @Inherited
5. @Documented
6. public @interface Transactional {
7. @AliasFor("transactionManager")
8. String value() default "";
9.
10. @AliasFor("value")
11. String transactionManager() default "";
12.
13. String[] label() default {};
14.
15. Propagation propagation() default Propagation.REQUIRED;
16.
17. Isolation isolation() default Isolation.DEFAULT;
18.
19. int timeout() default -1;
20.
21. String timeoutString() default "";
22.
23. boolean readOnly() default false;
24.
25. Class<? extends Throwable>[] rollbackFor() default {};
26.
27. String[] rollbackForClassName() default {};
28.
29. Class<? extends Throwable>[] noRollbackFor() default {};
30.
31. String[] noRollbackForClassName() default {};
32. }
33.
propagation 事务的传播行为(面试)
多事务方法之间调用,事务是如何管理的
事务传播行为类型 | 说明 |
PROPAGATION_REQUIRED | 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择(默认)。 |
PROPAGATION_SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行。 |
PROPAGATION_MANDATORY | 使用当前的事务,如果当前没有事务,就抛出异常。 |
PROPAGATION_REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起。 |
PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
PROPAGATION_NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常。 |
PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。 |
如果service层 add方法调用了 addDept和addEmp两个方法
20_Spring_零XML事务控制
创建配置类
1. package com.msb.config;
2.
3. import com.alibaba.druid.pool.DruidDataSource;
4. import org.springframework.beans.factory.annotation.Value;
5. import org.springframework.context.annotation.Bean;
6. import org.springframework.context.annotation.ComponentScan;
7. import org.springframework.context.annotation.Configuration;
8. import org.springframework.context.annotation.PropertySource;
9. import org.springframework.jdbc.core.JdbcTemplate;
10. import org.springframework.jdbc.datasource.DataSourceTransactionManager;
11. import org.springframework.transaction.PlatformTransactionManager;
12. import org.springframework.transaction.annotation.EnableTransactionManagement;
13.
14. import javax.sql.DataSource;
15.
16. /**
17. * @Author: Ma HaiYang
18. * @Description: MircoMessage:Mark_7001
19. */
20. @Configuration // 配置类标志注解
21. @ComponentScan(basePackages = "com.msb") // spring包扫描
22. @PropertySource("classpath:jdbc.properties") // 读取属性配置文件
23. @EnableTransactionManagement // 开启事务注解
24. public class SpringConfig {
25. @Value("${jdbc_driver}")
26. private String driver;
27. @Value("${jdbc_url}")
28. private String url;
29. @Value("${jdbc_username}")
30. private String username;
31. @Value("${jdbc_password}")
32. private String password;
33.
34.
35.
36. /*创建数据库连接池*/
37. @Bean
38. public DruidDataSource getDruidDataSource(){
39. DruidDataSource dataSource=new DruidDataSource();
40. dataSource.setDriverClassName(driver);
41. dataSource.setUrl(url);
42. dataSource.setUsername(username);
43. dataSource.setPassword(password);
44. return dataSource;
45. }
46. /*创建JdbcTemplate对象*/
47. @Bean
48. public JdbcTemplate getJdbcTemplate(DataSource dataSource){
49. JdbcTemplate jdbcTemplate=new JdbcTemplate();
50. jdbcTemplate.setDataSource(dataSource);
51. return jdbcTemplate;
52. }
53.
54. /*创建事务管理器*/
55. @Bean
56. public PlatformTransactionManager getPlatformTransactionManager(DataSource dataSource){
57. DataSourceTransactionManager transactionManager =new DataSourceTransactionManager();
58. transactionManager.setDataSource(dataSource);
59. return transactionManager;
60. }
61.
62. }
63.
测试代码
1. @Test()
2. public void testTransaction3(){
3. ApplicationContext context =new AnnotationConfigApplicationContext(SpringConfig.class);
4. AccountService accountService = context.getBean(AccountService.class);
5. int rows = accountService.transMoney(1, 2, 100);
6. System.out.println(rows);
7.
8. }
21_Spring_日志框架和测试支持
spring5框架自带了通用的日志封装,也可以整合自己的日志
1)spring移除了 LOG4jConfigListener,官方建议使用log4j2
2)spring5整合log4j2
导入log4j2依赖
<!--log4j2 依赖-->
2. <!--<dependency>
3. <groupId>org.apache.logging.log4j</groupId>
4. <artifactId>log4j-core</artifactId>
5. <version>2.14.0</version>
6. </dependency>-->
7.
8. <!--slf4-impl 包含了log4j2 依赖-->
9. <dependency>
10. <groupId>org.apache.logging.log4j</groupId>
11. <artifactId>log4j-slf4j-impl</artifactId>
12. <version>2.14.0</version>
13. <scope>test</scope>
14. </dependency>
在resources目录下准备log4j2.xml的配置文件
1. <?xml version="1.0" encoding="UTF-8"?>
2. <Configuration status="DEBUG">
3. <Appenders>
4. <Console name="Console" target="SYSTEM_OUT">
5. <PatternLayout pattern="%d{YYYY-MM-dd HH:mm:ss} [%t] %-5p %c{1}:%L - %msg%n" />
6. </Console>
7. </Appenders>
8. <Loggers>
9. <Root level="debug">
10. <AppenderRef ref="Console" />
11. </Root>
12. </Loggers>
13. </Configuration>
spring5关于测试工具的支持
整合junit4
依赖的jar
<!--Junit4单元测试-->
2. <dependency>
3. <groupId>junit</groupId>
4. <artifactId>junit</artifactId>
5. <version>4.13.1</version>
6. <scope>test</scope>
7. </dependency>
8. <!--spring test测试支持包-->
9. <dependency>
10. <groupId>org.springframework</groupId>
11. <artifactId>spring-test</artifactId>
12. <version>5.3.5</version>
13. <scope>test</scope>
14. </dependency>
15.
测试代码编写方式
1. package com.msb.test;
2.
3. import com.msb.config.SpringConfig;
4. import com.msb.service.AccountService;
5. import org.junit.Test;
6. import org.junit.runner.RunWith;
7. import org.springframework.beans.factory.annotation.Autowired;
8. import org.springframework.context.ApplicationContext;
9. import org.springframework.context.annotation.AnnotationConfigApplicationContext;
10. import org.springframework.context.support.ClassPathXmlApplicationContext;
11. import org.springframework.lang.Nullable;
12. import org.springframework.test.context.ContextConfiguration;
13. import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
14.
15. /**
16. * @Author: Ma HaiYang
17. * @Description: MircoMessage:Mark_7001
18. */
19. @RunWith(SpringJUnit4ClassRunner.class)// 指定测试支持类
20. @ContextConfiguration("classpath:applicationContext.xml")// 指定核心配置文件位置
21. public class Test2 {
22.
23. @Autowired // 注入要获取的bean
24. private AccountService accountService;
25. @Test()
26. public void testTransaction(){
27. int rows = accountService.transMoney(1, 2, 100);
28. System.out.println(rows);
29. }
30. }
31.
整合junit5
依赖的jar
<!--junit5单元测试-->
2. <dependency>
3. <groupId>org.junit.jupiter</groupId>
4. <artifactId>junit-jupiter-api</artifactId>
5. <version>5.7.0</version>
6. <scope>test</scope>
7. </dependency>
测试代码编写方式
1. package com.msb.test;
2.
3. import com.msb.service.AccountService;
4. import org.junit.jupiter.api.Test;
5. import org.junit.jupiter.api.extension.ExtendWith;
6. import org.springframework.beans.factory.annotation.Autowired;
7. import org.springframework.test.context.ContextConfiguration;
8. import org.springframework.test.context.junit.jupiter.SpringExtension;
9. import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
10.
11. /**
12. * @Author: Ma HaiYang
13. * @Description: MircoMessage:Mark_7001
14. */
15. /*使用ExtentWith和ContextConfiguration注解*/
16. /*@ExtendWith(SpringExtension.class)
17. @ContextConfiguration("classpath:applicationContext.xml")*/
18. // 使用复合注解
19. @SpringJUnitConfig(locations = "classpath:applicationContext.xml")
20. public class Test3 {
21. @Autowired // 注入要获取的bean
22. private AccountService accountService;
23. @Test
24. public void testTransaction(){
25. int rows = accountService.transMoney(1, 2, 100);
26. System.out.println(rows);
27. }
28. }
29.
第31章_SpringMVC
1_SpringMVC_概述
M model 模型层 DAO封装 >>> Mybatis
V view 视图层 html css js jsp
C controller 控制层 Servlet封装 >>> springMVC
SpringMVC是spring为展现层提供的基于MVC设计理念的优秀WEB框架,是目前最主流的MVC框架之一
SpringMVC通过一套注解,可以让普通的JAVA类成为contrllor控制器,无需继承Servlet,实现了控制层和Servlet之间的解耦
SpringMVC支持Rest风格的URL写法
SpringMVC采用了松耦合,可热插的主键结构,比其他的框架更具扩展性和灵活性
2_SpringMVC_项目搭建
1创建空项目 项目和maven web模块
设置maven和 lombok
创建maven web module
注意选择骨架为maven-archetype-webapp
键入GroupID和 artfactid
补充项目结构文件夹并标记文件夹
创建好目录后,选中目录,右击 mark directory as 选择对应目录类型即可
修改web.xml 中的版本约束
可以创建一个javaEE项目,然后复制web.xml文件中的内容即可
1. <?xml version="1.0" encoding="UTF-8"?>
2. <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4. xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
5. version="4.0">
6. </web-app>
创建普通Servlet,然后跳转至JSP
导入依赖
<!--servlet jsp 依赖-->
2. <dependency>
3. <groupId>javax.servlet</groupId>
4. <artifactId>javax.servlet-api</artifactId>
5. <version>4.0.1</version>
6. <scope>provided</scope>
7. </dependency>
8. <dependency>
9. <groupId>javax.servlet.jsp</groupId>
10. <artifactId>javax.servlet.jsp-api</artifactId>
11. <version>2.3.3</version>
12. <scope>provided</scope>
13. </dependency>
创建servlet
1. package com.msb.controller;
2.
3. import javax.servlet.ServletException;
4. import javax.servlet.annotation.WebServlet;
5. import javax.servlet.http.HttpServlet;
6. import javax.servlet.http.HttpServletRequest;
7. import javax.servlet.http.HttpServletResponse;
8. import java.io.IOException;
9.
10. /**
11. * @Author: Ma HaiYang
12. * @Description: MircoMessage:Mark_7001
13. */
14. @WebServlet("/myServlet.do")
15. public class MyServlet extends HttpServlet {
16. @Override
17. protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
18. req.getRequestDispatcher("first.jsp").forward(req,resp);
19. }
20. }
21.
准备一个first.jsp(略)
配置Tomcat启动运行项目
添加运行的外部Tomcat环境
将当前模块放入Tomcat
启动测试: 略
2导入jar依赖
1. <dependencies>
2. <!--spring核心容器包-->
3. <dependency>
4. <groupId>org.springframework</groupId>
5. <artifactId>spring-context</artifactId>
6. <version>5.3.5</version>
7. </dependency>
8. <!--spring切面包-->
9. <dependency>
10. <groupId>org.springframework</groupId>
11. <artifactId>spring-aspects</artifactId>
12. <version>5.3.5</version>
13. </dependency>
14. <!--aop联盟包-->
15. <dependency>
16. <groupId>aopalliance</groupId>
17. <artifactId>aopalliance</artifactId>
18. <version>1.0</version>
19. </dependency>
20. <!--德鲁伊连接池-->
21. <dependency>
22. <groupId>com.alibaba</groupId>
23. <artifactId>druid</artifactId>
24. <version>1.1.10</version>
25. </dependency>
26. <!--mysql驱动-->
27. <dependency>
28. <groupId>mysql</groupId>
29. <artifactId>mysql-connector-java</artifactId>
30. <version>8.0.22</version>
31. </dependency>
32. <!--springJDBC包-->
33. <dependency>
34. <groupId>org.springframework</groupId>
35. <artifactId>spring-jdbc</artifactId>
36. <version>5.3.5</version>
37. </dependency>
38. <!--spring事务控制包-->
39. <dependency>
40. <groupId>org.springframework</groupId>
41. <artifactId>spring-tx</artifactId>
42. <version>5.3.5</version>
43. </dependency>
44. <!--spring orm 映射依赖-->
45. <dependency>
46. <groupId>org.springframework</groupId>
47. <artifactId>spring-orm</artifactId>
48. <version>5.3.5</version>
49. </dependency>
50. <!--Apache Commons日志包-->
51. <dependency>
52. <groupId>commons-logging</groupId>
53. <artifactId>commons-logging</artifactId>
54. <version>1.2</version>
55. </dependency>
56.
57. <!--log4j2 日志-->
58. <dependency>
59. <groupId>org.apache.logging.log4j</groupId>
60. <artifactId>log4j-slf4j-impl</artifactId>
61. <version>2.14.0</version>
62. <scope>test</scope>
63. </dependency>
64.
65. <!--lombok -->
66. <dependency>
67. <groupId>org.projectlombok</groupId>
68. <artifactId>lombok</artifactId>
69. <version>1.18.12</version>
70. <scope>provided</scope>
71. </dependency>
72.
73. <!--spring test测试支持包-->
74. <dependency>
75. <groupId>org.springframework</groupId>
76. <artifactId>spring-test</artifactId>
77. <version>5.3.5</version>
78. <scope>test</scope>
79. </dependency>
80.
81. <!--junit5单元测试-->
82. <dependency>
83. <groupId>org.junit.jupiter</groupId>
84. <artifactId>junit-jupiter-api</artifactId>
85. <version>5.7.0</version>
86. <scope>test</scope>
87. </dependency>
88.
89.
90. <!--springMVC支持包-->
91. <dependency>
92. <groupId>org.springframework</groupId>
93. <artifactId>spring-web</artifactId>
94. <version>5.3.5</version>
95. </dependency>
96.
97. <dependency>
98. <groupId>org.springframework</groupId>
99. <artifactId>spring-webmvc</artifactId>
100. <version>5.3.5</version>
101. </dependency>
102.
103. <!--jsp 和Servlet 可选-->
104. <dependency>
105. <groupId>javax.servlet</groupId>
106. <artifactId>javax.servlet-api</artifactId>
107. <version>4.0.1</version>
108. <scope>provided</scope>
109. </dependency>
110.
111. <dependency>
112. <groupId>javax.servlet.jsp</groupId>
113. <artifactId>javax.servlet.jsp-api</artifactId>
114. <version>2.3.3</version>
115. <scope>provided</scope>
116. </dependency>
117.
118.
119. </dependencies>
3在web.xml中配置DispatcherServlet
1. <?xml version="1.0" encoding="UTF-8"?>
2. <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4. xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
5. version="4.0">
6.
7. <!--配置DispatcherServlet -->
8. <servlet>
9. <servlet-name>dispatcherServlet</servlet-name>
10. <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
11. <!--配置初始化参数,读取springMVC的核心配置文件的位置和名称-->
12. <!--
13. 当然,不使用initparam,springMVC会到一个默认路径下读取默认名称的.xml配置文件
14. 默认路径为/WEB-INF/
15. 默认配置文件名为:<servlet-name>-servlet.xml
16. 我们暂时不推荐这种方式
17. -->
18. <!-- <init-param>
19. <param-name>contextConfigLocation</param-name>
20. <param-value>classpath:dispatcherServlet-servlet.xml</param-value>
21. </init-param>-->
22. <load-on-startup>1</load-on-startup>
23. </servlet>
24. <!--配置dispatcherServlet的映射路径为 / 包含全部的servlet, JSP除外-->
25. <servlet-mapping>
26. <servlet-name>dispatcherServlet</servlet-name>
27. <url-pattern>/</url-pattern>
28. </servlet-mapping>
29.
30. </web-app>
4加入SpringMVC的配置文件
在resources下添加 springmvc.xml
1. <?xml version="1.0" encoding="UTF-8"?>
2. <beans xmlns="http://www.springframework.org/schema/beans"
3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4. xmlns:p="http://www.springframework.org/schema/p"
5. xmlns:c="http://www.springframework.org/schema/c"
6. xmlns:util="http://www.springframework.org/schema/util"
7. xmlns:context="http://www.springframework.org/schema/context"
8. xmlns:aop="http://www.springframework.org/schema/aop"
9. xmlns:tx="http://www.springframework.org/schema/tx"
10. xsi:schemaLocation="
11. http://www.springframework.org/schema/beans
12. http://www.springframework.org/schema/beans/spring-beans.xsd
13. http://www.springframework.org/schema/util
14. http://www.springframework.org/schema/util/spring-util.xsd
15. http://www.springframework.org/schema/context
16. http://www.springframework.org/schema/context/spring-context.xsd
17. http://www.springframework.org/schema/aop
18. http://www.springframework.org/schema/aop/spring-aop.xsd
19. http://www.springframework.org/schema/tx
20. http://www.springframework.org/schema/tx/spring-tx.xsd
21. ">
22.
23. <!--配置spring包扫描-->
24. <context:component-scan base-package="com.msb"></context:component-scan>
25.
26. <!--配置视图解析器
27. <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
28. <property name="prefix" value="/WEB-INF/view/" ></property>
29. <property name="suffix" value=".jsp" ></property>
30. </bean>-->
31.
32. </beans>
添加log4j2.xml
1. <?xml version="1.0" encoding="UTF-8"?>
2. <Configuration status="DEBUG">
3. <Appenders>
4. <Console name="Console" target="SYSTEM_OUT">
5. <PatternLayout pattern="%d{YYYY-MM-dd HH:mm:ss} [%t] %-5p %c{1}:%L - %msg%n" />
6. </Console>
7. </Appenders>
8. <Loggers>
9. <Root level="debug">
10. <AppenderRef ref="Console" />
11. </Root>
12. </Loggers>
13. </Configuration>
5编写controller层处理器
1. package com.msb.controller;
2.
3. import org.springframework.stereotype.Controller;
4. import org.springframework.web.bind.annotation.RequestMapping;
5. import org.springframework.web.bind.annotation.ResponseBody;
6.
7. /**
8. * @Author: Ma HaiYang
9. * @Description: MircoMessage:Mark_7001
10. */
11. @Controller
12. @RequestMapping("/msb")
13. public class FirstController {
14. @RequestMapping("/firstController.do")
15. public String firstController(){
16. System.out.println("this is firstController");
17. return "/first.jsp";
18. }
19. }
20.
6编写视图层
1. <%--
2. Created by IntelliJ IDEA.
3. User: Mark70
4. Date: 2021/4/12
5. Time: 12:28
6. To change this template use File | Settings | File Templates.
7. --%>
8. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
9. <html>
10. <head>
11. <title>Title</title>
12. </head>
13. <body>
14. this is first Jsp
15. </body>
16. </html>
17.
启动测试:略
3_SpringMVC_执行流程
1DispatcherServlet:前端控制器
用户请求到达前端控制器,它就相当于 mvc 模式中的 c,dispatcherServlet 是整个流程控制的中心,由 它调用其它组件处理用户的请求,dispatcherServlet 的存在降低了组件之间的耦合性。
2 HandlerMapping:处理器映射器
HandlerMapping 负责根据用户请求找到 Handler 即处理器,SpringMVC 提供了不同的映射器实现不同的 映射方式,例如:配置文件方式,实现接口方式,注解方式等。
3 Handler:处理器 (自己定义的Controller处理单元)
它就是我们开发中要编写的具体业务控制器。由 DispatcherServlet 把用户请求转发到 Handler。由 Handler 对具体的用户请求进行处理。
4 HandlAdapter:处理器适配器
通过 HandlerAdapter 对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行
5 View Resolver:视图解析器
View Resolver 负责将处理结果生成 View 视图,View Resolver 首先根据逻辑视图名解析成物理视图名 即具体的页面地址,再生成 View 视图对象,最后对 View 进行渲染将处理结果通过页面展示给用户。
6 View:视图
SpringMVC 框架提供了很多的 View 视图类型的支持,包括:jstlView、freemarkerView、pdfView等。我们最常用的视图就是 jsp。 一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开 发具体的页面。
7 <mvc:annotation-driven>说明
在 SpringMVC 的各个组件中,处理器映射器、处理器适配器、视图解析器称为 SpringMVC 的三大组件。
使 用 <mvc:annotation-driven> 自动加载 RequestMappingHandlerMapping (处理映射器) 和 RequestMappingHandlerAdapter ( 处 理 适 配 器 ) , 可 用 在 SpringMVC.xml 配 置 文 件 中 使 用 <mvc:annotation-driven>替代注解处理器和适配器的配置。
1. protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
2. HttpServletRequest processedRequest = request;
3. HandlerExecutionChain mappedHandler = null;
4. boolean multipartRequestParsed = false;
5. WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
6.
7. try {
8. try {
9. ModelAndView mv = null;
10. Object dispatchException = null;
11.
12. try {
13. processedRequest = this.checkMultipart(request);
14. multipartRequestParsed = processedRequest != request;
15. mappedHandler = this.getHandler(processedRequest);
16. if (mappedHandler == null) {
17. this.noHandlerFound(processedRequest, response);
18. return;
19. }
20.
21. HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
22. String method = request.getMethod();
23. boolean isGet = "GET".equals(method);
24. if (isGet || "HEAD".equals(method)) {
25. long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
26. if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
27. return;
28. }
29. }
30.
31. if (!mappedHandler.applyPreHandle(processedRequest, response)) {
32. return;
33. }
34.
35. mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
36. if (asyncManager.isConcurrentHandlingStarted()) {
37. return;
38. }
39.
40. this.applyDefaultViewName(processedRequest, mv);
41. mappedHandler.applyPostHandle(processedRequest, response, mv);
42. } catch (Exception var20) {
43. dispatchException = var20;
44. } catch (Throwable var21) {
45. dispatchException = new NestedServletException("Handler dispatch failed", var21);
46. }
47.
48. this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
49. } catch (Exception var22) {
50. this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
51. } catch (Throwable var23) {
52. this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
53. }
54.
55. } finally {
56. if (asyncManager.isConcurrentHandlingStarted()) {
57. if (mappedHandler != null) {
58. mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
59. }
60. } else if (multipartRequestParsed) {
61. this.cleanupMultipart(processedRequest);
62. }
63.
64. }
65. }
66.
67. private void applyDefaultViewName(HttpServletRequest request, @Nullable ModelAndView mv) throws Exception {
68. if (mv != null && !mv.hasView()) {
69. String defaultViewName = this.getDefaultViewName(request);
70. if (defaultViewName != null) {
71. mv.setViewName(defaultViewName);
72. }
73. }
74.
75. }
HandlerMapping的实现类的作用
实现类RequestMappingHandlerMapping,它会处理@RequestMapping 注解,并将其注册到请求映射表中。
HandlerAdapter的实现类的作用
实现类RequestMappingHandlerAdapter,则是处理请求的适配器,确定调用哪个类的哪个方法,并且构造方法参数,返回值。
当配置了mvc:annotation-driven/后,Spring就知道了我们启用注解驱动。然后Spring通过context:component-scan/标签的配置,会自动为我们将扫描到的@Component,@Controller,@Service,@Repository等注解标记的组件注册到工厂中,来处理我们的请求,这个时候接收返回json数据、参数验证、统一异常等功能。
1. <?xml version="1.0" encoding="UTF-8"?>
2. <beans xmlns="http://www.springframework.org/schema/beans"
3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4. xmlns:p="http://www.springframework.org/schema/p"
5. xmlns:c="http://www.springframework.org/schema/c"
6. xmlns:util="http://www.springframework.org/schema/util"
7. xmlns:context="http://www.springframework.org/schema/context"
8. xmlns:aop="http://www.springframework.org/schema/aop"
9. xmlns:tx="http://www.springframework.org/schema/tx"
10. xmlns:mvc="http://www.springframework.org/schema/mvc"
11. xsi:schemaLocation="
12. http://www.springframework.org/schema/beans
13. http://www.springframework.org/schema/beans/spring-beans.xsd
14. http://www.springframework.org/schema/util
15. http://www.springframework.org/schema/util/spring-util.xsd
16. http://www.springframework.org/schema/context
17. http://www.springframework.org/schema/context/spring-context.xsd
18. http://www.springframework.org/schema/aop
19. http://www.springframework.org/schema/aop/spring-aop.xsd
20. http://www.springframework.org/schema/tx
21. http://www.springframework.org/schema/tx/spring-tx.xsd
22. http://www.springframework.org/schema/mvc
23. http://www.springframework.org/schema/mvc/spring-mvc.xsd
24. ">
25.
26. <!--配置spring包扫描-->
27. <context:component-scan base-package="com.msb"></context:component-scan>
28.
29. <!--配置处理器映射器-->
30. <!-- <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"></bean>-->
31. <!--配置处理器适配器-->
32. <!-- <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"></bean>-->
33. <!--一个注解替换上面的两个配置-->
34. <!--<mvc:annotation-driven>会自动注册RequestMappingHandlerMapping与RequestMappingHandlerAdapter两个Bean-->
35. <mvc:annotation-driven/>
36. <!--配置视图解析器-->
37. <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
38. <property name="prefix" value="/WEB-INF/view/" ></property>
39. <property name="suffix" value=".jsp" ></property>
40. </bean>
41.
42. <!--静态资源放行-->
43. <!--<mvc:resources mapping="/js/**" location="/js/"></mvc:resources>
44. <mvc:resources mapping="/img/**" location="/img/"></mvc:resources>
45. <mvc:resources mapping="/css/**" location="/css/"></mvc:resources>-->
46. <mvc:resources mapping="/static/**" location="/static/"></mvc:resources>
47.
48. </beans>
4_SpringMVC_@RequestMapping注解
1@RequestMapping控制请求方式
method属性可以控制请求的方式,值为RequestMethod的枚举值
1. @RequestMapping( value = "/***" ,method = RequestMethod.GET)
2@RequestMapping控制请求参数params和请求头headers
param:表示请求中必须包含名为param的参数
!param:表示请求中不能包含名为param的参数
param = value 表示请求中包含名为param的参数,但是值必须是value
param != value 表示请求中包含名为param的参数,但是值不能是value
{"param1","param2=value"},可以将对于多个参数的要求写入数组
1. @RequestMapping( value = "/***" ,params = {"username!=root","password"})
1. @RequestMapping( value = "/***",headers = {"Accept-Encoding=gzip, deflate"})
3@PathVariable注解和RESTful风格的支持
普通形式的url
*****/contextPath/aaa.do
*****/contextPath/aaa.jsp
*****/contextPath/aaa.html
*****/contextPath/css/aaa.css
*****/contextPath/js/aaa.js
*****/contextPath/aaa.do?id=10&username=root
restFul风格的url
*****/contextPath/aaa/10/root
*****/contextPath/aaa
controller 处理单元
1. @Controller
2. public class PathController {
3. @RequestMapping("/testPathVariable/{id}/{username}")
4. public String testPathVariable(@PathVariable("id") Integer id, @PathVariable("username") String username){
5. System.out.println("id:"+id);
6. System.out.println("username:"+username);
7. System.out.println("testPathVariable1");
8. return "success";
9. }
10. }
请求测试
Http协议中,四个表示操作方式的动词"GET POST PUT DELETE",他们对应四种基本操作,GET用来获取资源,POST用来新建资源,PUT用来更新资源,DELETE用来删除资源
简单的说,就是我们在访问资源时,可以通过这四个状态来表示对于资源的不同操作,这四个状态表现为我们请求的四种方式
/controller/1 HTTP GET :得到id为1 的资源
/controller/1 HTTP DELETE :删除id为1的资源
/controller/1 HTTP PUT :更新id为1 的资源
/controller/1 HTTP POST :增加id为1 的资源
在访问同一个url的时候,通过不同的请求方式,对应到不同的controller处理单元
1 配置hiddenHttpMethodFilter
1. <!--配置hiddenHttpMethodFilter ,将POST请求转换为PUT或者DELETE请求-->
2. <filter>
3. <filter-name>hiddenHttpMethodFilter</filter-name>
4. <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
5. </filter>
6. <filter-mapping>
7. <filter-name>hiddenHttpMethodFilter</filter-name>
8. <url-pattern>/*</url-pattern>
9. </filter-mapping>
转换原理
1. protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
2. HttpServletRequest requestToUse = request;
3. if ("POST".equals(request.getMethod()) && request.getAttribute("javax.servlet.error.exception") == null) {
4. String paramValue = request.getParameter(this.methodParam);// "_method"
5. if (StringUtils.hasLength(paramValue)) {
6. String method = paramValue.toUpperCase(Locale.ENGLISH);
7. if (ALLOWED_METHODS.contains(method)) {
8. requestToUse = new HiddenHttpMethodFilter.HttpMethodRequestWrapper(request, method);
9. }
10. }
11. }
12.
13. filterChain.doFilter((ServletRequest)requestToUse, response);
14. }
2 准备Controller层代码
1. package com.msb.controller;
2.
3. import org.springframework.stereotype.Controller;
4. import org.springframework.web.bind.annotation.PathVariable;
5. import org.springframework.web.bind.annotation.RequestMapping;
6. import org.springframework.web.bind.annotation.RequestMethod;
7. import org.springframework.web.bind.annotation.RestController;
8.
9.
10. /**
11. * @Author: Ma HaiYang
12. * @Description: MircoMessage:Mark_7001
13. */
14.
15. @RequestMapping(value = "/myController")
16. @RestController
17. public class MyController {
18. @RequestMapping(value = "/testRest/{id}",method = RequestMethod.PUT)
19. public String testPut(@PathVariable(value = "id") Integer id){
20. System.out.println("testPut, id:"+id);
21. return "show";
22. }
23. @RequestMapping(value = "/testRest/{id}",method = RequestMethod.DELETE)
24. public String testDelete(@PathVariable(value = "id") Integer id){
25. System.out.println("testDelete, id:"+id);
26. return "show";
27. }
28.
29. @RequestMapping(value = "/testRest/{id}",method = RequestMethod.POST)
30. public String testPOST(@PathVariable(value = "id") Integer id){
31. System.out.println("testPOST, id:"+id);
32. return "show";
33. }
34. @RequestMapping(value = "/testRest/{id}",method = RequestMethod.GET)
35. public String testGET(@PathVariable(value = "id") Integer id){
36. System.out.println("testGET, id:"+id);
37. return "show";
38. }
39.
40. }
3 准备页面代码
1. <form action="myController/testRest/10" method="POST">
2. <input type="hidden" name="_method" value="PUT">
3. <input type="submit" value="testPUT">
4. </form>
5. <br/>
6. <form action="myController/testRest/10" method="POST">
7. <input type="hidden" name="_method" value="DELETE">
8. <input type="submit" value="testDELETE">
9. </form>
10. <br/>
11. <form action="myController/testRest/10" method="POST">
12. <input type="submit" value="testPOST">
13. </form>
14. <br/>
15. <form action="myController/testRest/10" method="GET">
16. <input type="submit" value="testGET">
17. </form>
5_SpringMVC_获取请求参数
紧耦合方式(了解)
DispatcherServlet中的service方法直接将此次请求的request对象传递给调用的单元方法即可。同时在单元方法上声明形参HttpServletRequest来接收request实参即可。
解耦合方式(熟练)
DispatcherServlet在其service方法中将请求数据根据需求从request对象中获取出来后,将数据直接传递给对应的单元方法使用。同时在单元方法上直接声明对应的形参接收请求数据即可。在单元方法上声明形参来接收请求数据时,形参名必须和请求数据的键名一致,DispatcherServlet会将调用单元方法的形参名作为请求数据的键名获取请求数据,然后传递给单元方法。
1. package com.msb.controller;
2.
3. import org.springframework.web.bind.annotation.RequestMapping;
4. import org.springframework.web.bind.annotation.RequestParam;
5. import org.springframework.web.bind.annotation.RestController;
6.
7. import javax.servlet.http.HttpServletRequest;
8. import javax.servlet.http.HttpServletResponse;
9.
10. /**
11. * @Author: Ma HaiYang
12. * @Description: MircoMessage:Mark_7001
13. */
14. @RestController
15. public class TestDataController {
16.
17. /*紧耦合方式参数注入
18. * 使用传统的HttpServletRequest对象获取参数 javax.servlet
19. * */
20. @RequestMapping("/getParamByRequest.do")
21. public String getParam1(HttpServletRequest req, HttpServletResponse resp){
22. String username = req.getParameter("username");
23. String password = req.getParameter("password");
24. System.out.println("username:"+username+" password:"+password);
25. return "getParamSuccess";
26. }
27. /*解耦合方式参数注入
28. * HttpServletRequest对象获取参数 通过SpringMVC框架功能,自动转换参数
29. * 处理单元参数列表中参数名必须和请求中的参数名一致
30. * 如不一致,可以通过@RequestParma注解进行转换
31. * */
32. @RequestMapping("/getParamByArgName.do")
33. public String getParam2(String username,@RequestParam("pwd") int password){
34. System.out.println("username:"+username+" password:"+password);
35. return "getParamSuccess";
36. }
37.
38. }
1POJO接收参数
form表单
1. <form action="getDataByPojo">
2. <p>姓名<input type="text" name="pname"></p>
3. <p>年龄<input type="text" name="page"></p>
4. <p>性别:
5. <input type="radio" name="gender" value="1" >男
6. <input type="radio" name="gender" value="0" >女
7. </p>
8. <p>爱好
9. <input type="checkbox" name="hobby" value="1"> 篮球
10. <input type="checkbox" name="hobby" value="2"> 足球
11. <input type="checkbox" name="hobby" value="3"> 羽毛球
12. </p>生日
13. <p>
14. <input type="text" name="birthdate">
15. </p>
16.
17. <input type="submit">
18. </form>
controller处理单元
1. package com.msb.controller;
2.
3. import com.msb.pojo.Person;
4. import org.springframework.web.bind.annotation.RequestMapping;
5. import org.springframework.web.bind.annotation.RestController;
6.
7. import java.util.Arrays;
8.
9. /**
10. * @Author: Ma HaiYang
11. * @Description: MircoMessage:Mark_7001
12. */
13. @RestController
14. public class ReceiveDataController {
15.
16. /*
17. * 使用POJO接收参数时,注意事项
18. * 提交的参数名必须和POJO的属性名保持一致
19. * springmvc底层通过反射给参数列表的属性赋值
20. * 通过set方法设置属性值的,不是直接通过操作属性
21. * POJO的属性一定要有set方法,要不然就会接收失败
22. * */
23. @RequestMapping("/getDataByPojo")
24. public String getDataByPojo(Person p){
25. System.out.println(p);
26. return "success";
27. }
28. }
29.
POJO实体类
1. package com.msb.pojo;
2.
3. import org.springframework.format.annotation.DateTimeFormat;
4.
5. import java.io.Serializable;
6. import java.util.Arrays;
7.
8. /**
9. * @Author: Ma HaiYang
10. * @Description: MircoMessage:Mark_7001
11. */
12. public class Person implements Serializable {
13. private String pname;
14. private String page;
15. private String gender;
16. private String[] hobby;
17. private String birthdate;
18. }
2日期类型转换
方式1 : 注解方式 推荐该方式
@DateTimeFormat(pattern = "yyyy-MM-dd") 可以用于方法参数列表和 类的属性上
方式2: 配置转换器
第一步定义转换器
1. package com.msb.util;
2.
3. import org.springframework.core.convert.converter.Converter;
4.
5. import java.text.ParseException;
6. import java.text.SimpleDateFormat;
7. import java.util.Date;
8.
9. /**
10. * @Author: Ma HaiYang
11. * @Description: MircoMessage:Mark_7001
12. */
13. public class StringToDateConverter implements Converter<String, Date> {
14. private SimpleDateFormat dateFormat=new SimpleDateFormat("yyyy-MM-dd");
15. @Override
16. public Date convert(String source) {
17. Date date =null;
18. try {
19. date = dateFormat.parse(source);
20. } catch (ParseException e) {
21. throw new RuntimeException("日期转换异常");
22. }
23.
24. return date;
25. }
26. }
27.
第二步,springmvc.xml 中配置转换器
1. <!--数据转换工厂-->
2. <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
3. <!--配置类型转换器-->
4. <property name="converters">
5. <array>
6. <!--注入自定义转换器对象-->
7. <bean class="com.msb.util.StringToDateConverter"></bean>
8. </array>
9. </property>
10. </bean>
11.
12.
13. <!--这里配置转换服务工厂-->
14. <mvc:annotation-driven conversion-service="conversionService"/>
3List集合接收参数
实体类
1. @AllArgsConstructor
2. @NoArgsConstructor
3. @Data
4. public class Person implements Serializable {
5. private String pname;
6. private String page;
7. private String gender;
8. private String[] hobby;
9. @DateTimeFormat(pattern = "yyyy-MM-dd")
10. private Date birthdate;
11. private List<Pet> pets;
12. }
controller
1. @RequestMapping("/getDataByPojo")
2. public String getDataByPojo(Person p){
3. System.out.println(p);
4. System.out.println(p.getPets());
5. return "success";
6. }
form表单
1. <form action="getDataByPojo">
2. <p>姓名<input type="text" name="pname"></p>
3. <p>年龄<input type="text" name="page"></p>
4. <p>性别:
5. <input type="radio" name="gender" value="1" >男
6. <input type="radio" name="gender" value="0" >女
7. </p>
8. <p>爱好
9. <input type="checkbox" name="hobby" value="1"> 篮球
10. <input type="checkbox" name="hobby" value="2"> 足球
11. <input type="checkbox" name="hobby" value="3"> 羽毛球
12. </p>生日
13. <p>
14. <input type="text" name="birthdate">
15. </p>
16. 宠物:
17. <p>
18. 宠物1: 名字:<input type="text" name="pets[0].petName" >类型:<input type="text" name="pets[0].petType">
19. </p>
20. <p>
21. 宠物2: 名字:<input type="text" name="pets[1].petName" >类型:<input type="text" name="pets[1].petType">
22. </p>
23. <input type="submit">
24. </form>
25.
4Map集合接收参数
实体类
1. @AllArgsConstructor
2. @NoArgsConstructor
3. @Data
4. public class Person implements Serializable {
5. private String pname;
6. private String page;
7. private String gender;
8. private String[] hobby;
9. @DateTimeFormat(pattern = "yyyy-MM-dd")
10. private Date birthdate;
11. private List<Pet> pets;
12. private Map<String,Pet> petMap;
13. }
form 表单
1.
2. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
3. <html>
4. <head>
5. <title>Title</title>
6. </head>
7. <body>
8.
9. <form action="getDataByPojo" method="post">
10. <p>姓名<input type="text" name="pname"></p>
11. <p>年龄<input type="text" name="page"></p>
12. <p>性别:
13. <input type="radio" name="gender" value="1" >男
14. <input type="radio" name="gender" value="0" >女
15. </p>
16. <p>爱好
17. <input type="checkbox" name="hobby" value="1"> 篮球
18. <input type="checkbox" name="hobby" value="2"> 足球
19. <input type="checkbox" name="hobby" value="3"> 羽毛球
20. </p>生日
21. <p>
22. <input type="text" name="birthdate">
23. </p>
24. 宠物List:
25. <p>
26. 宠物1: 名字:<input type="text" name="pets[0].petName" >类型:<input type="text" name="pets[0].petType">
27. </p>
28. <p>
29. 宠物2: 名字:<input type="text" name="pets[1].petName" >类型:<input type="text" name="pets[1].petType">
30. </p>
31. 宠物Map:
32. <p>
33. 宠物1: 名字:<input type="text" name="petMap['a'].petName" >类型:<input type="text" name="petMap['a'].petType">
34. </p>
35. <p>
36. 宠物2: 名字:<input type="text" name="petMap['b'].petName" >类型:<input type="text" name="petMap['b'].petType">
37. </p>
38. <input type="submit">
39. </form>
40.
41.
42.
43. </body>
44. </html>
45.
controller
@RequestMapping("/getDataByPojo")
2. public String getDataByPojo(Person p){
3. System.out.println(p);
4. return "success";
5. }
5编码问题
GET乱码问题
POST乱码问题
web.xml中配置编码过滤器
1. <!--Spring 中提供的字符编码过滤器-->
2. <filter>
3. <filter-name>encFilter</filter-name>
4. <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
5. <init-param>
6. <param-name>encoding</param-name>
7. <param-value>utf-8</param-value>
8. </init-param>
9. </filter>
10. <filter-mapping>
11. <filter-name>encFilter</filter-name>
12. <url-pattern>/*</url-pattern>
13. </filter-mapping>
6_SpringMVC_常见注解
1、@RequestMapping
作用:
用于建立请求 URL 和处理请求方法之间的对应关系
出现位置:
类上: 请求 URL 的第一级访问目录。此处不写的话,就相当于应用的根目录。写的话需要以/开头
方法上: 请求 URL 的第二级访问目录
属性:
value:用于指定请求的 URL。它和 path 属性的作用是一样的。
method:用于指定请求的方式。
params(了解):用于指定限制请求参数的条件。它支持简单的表达式。要求请求参数的 key 和 value 必须和 配置的一模一样。
headers(了解):用于指定限制请求消息头的条件。
2 @RequestParam
作用:
把请求中指定名称的参数给控制器中的形参赋值。
属性:
value:请求参数中的名称。
required:请求参数中是否必须提供此参数。默认值:true。表示必须提供,如果不提供将报错。
1. @RequestMapping("/getRequestParam")
2. public String getRequestParam(@RequestParam("name")String uname, @RequestParam(value="age",required=false)Integer age){
3. System.out.println(username+","+age);
4. return "success";
5. }
3、@PathVariable
Restful的简介 :
REST(英文:Representational State Transfer,简称 REST)restful是一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。
restful 的优点
它结构清晰、符合标准、易于理解、扩展方便,所以正得到越来越多网站的采用。
作用:
用于绑定 url 中的占位符。例如:请求 url 中 /delete/{id},这个{id}就是 url 占位符。 url 支持占位符是 spring3.0 之后加入的。是 springmvc 支持 rest 风格 URL 的一个重要标志。
属性:
value:用于指定 url 中占位符名称。
required:是否必须提供占位符。
1. @Controller
2. public class PathController {
3. @RequestMapping("/testPathVariable/{id}/{username}")
4. public String testPathVariable(@PathVariable("id") Integer id, @PathVariable("username") String username){
5. System.out.println("id:"+id);
6. System.out.println("username:"+username);
7. System.out.println("testPathVariable1");
8. return "success";
9. }
10. }
4、@RequestHeader(了解)
作用:
用于获取请求消息头。
属性:
value:提供消息头名称
required:是否必须有此消息头
1. @RequestMapping("/getRequestHeader")
2. public String getRequestHeader(@RequestHeader(value="Accept", required=false)String requestHeader){
3. System.out.println(requestHeader);
4. return "success";
5. }
6.
5、@CookieValue(了解)
作用:
用于把指定 cookie 名称的值传入控制器方法参数。
属性:
value:指定 cookie 的名称。
required:是否必须有此 cookie
实现
1. @RequestMapping("/getCookie")
2. public String getCookie(@CookieValue(value="JSESSIONID",required=false) String cookieValue){
3. System.out.println(cookieValue);
4. return "success";
5. }
7_SpringMVC_响应处理
在学习了SpringMVC的配置流程以及单元方法请求数据的获取后,我们可以使用SpringMVC搭建一个项目,在单元方法中使用SpringMVC提供的方式来获取请求信息,然后根据功能需求,声明请求处理的逻辑代码,进行请求的处理。当请求处理完成后,我们需要将此次请求的处理结果响应给浏览器,以前我们是自己在Servlet中使用response对象来完成响应的,那么在SpringMVC中如何响应请求的处理结果呢?
0单元方法返回值为void
1. @RequestMapping("/testReturnVoid")
2. public void testReturnVoid() throws Exception {
3. System.out.println("AccountController 的 testForward 方法执行了。。。。");
4. }
在SpringMVC中如果对于当前的控制单元,没有写对应的返回值,这个时候SpringMVC就会找和自己控制单元名称一致的页面展示,如果没有配置视图解析器的前缀和后缀是没有产生404,需要注意控制单元仍然可以进。
1转发和重定向ServletAPI 实现
1. @RequestMapping("demo1")
2. public void testDemo1(HttpServletRequest request, HttpServletResponse response) throws Exception {
3. // 请求转发
4. //request.getRequestDispatcher("/forward.jsp").forward(request,response);
5. // 响应重定向
6. response.sendRedirect(request.getContextPath()+"/redirect.jsp");
7.
8. }
单元方法的返回值类型设置void。因为使用response对象在单元方法中直接对此次请求进行了响应,不再通过DispatcherServlet了,既然已经响应了,就不需要再给DispatcherServlet返回值了。在单元方法上声明HttpServletResponse形参,来接收此次请求的response对象。
2使用forward关键字完成响应
/*
2. * 返回字符串告诉DispatcherServlet跳转的路径
3. * 在路径之前放上一个forward: 关键字,就是请求转发
4. * 如果路径前的关键字是forward,那么可以省略不写
5. * */
6. @RequestMapping("demo2")
7. public String testDemo2() throws Exception {
8. //return "forward:/forwardPage.jsp";
9. return "/forwardPage.jsp";
10. }
使用通过单元方法的返回值来告诉DispatcherServlet请求转发指定的资源,如果是请求转发,forward关键字可以省略不写的
3使用redirect关键字完成响应
/*
2. * 返回字符串告诉DispatcherServlet跳转的路径
3. * 在路径之前放上一个redirect: 关键字,就是重定向
4. * 如果路径前的关键字是redirect,那么不可以省略
5. * /表示当前项目下.这里不需要项目的上下文路径
6. * */
7. @RequestMapping("demo3")
8. public String testDemo3() throws Exception {
9. return "redirect:/redirectPage.jsp";
10. }
使用通过单元方法的返回值来告诉DispatcherServlet重定向指定的资源,注意这个redirect关键字不可以省去
4使用View视图转发和重定向
1. @RequestMapping("demo4")
2. public View testDemo4(HttpServletRequest req) {
3.
4. View view =null;
5. // 请求转发
6. //view =new InternalResourceView("/forwardPage.jsp");
7. // 重定向
8. view=new RedirectView(req.getContextPath()+"/redirectPage.jsp");
9. return view;
10. }
RedirectView中所做的操作,最终的实现是在renderMergedOutputModel中完成实现的,简单来说RedirectView实现了链接的重定向,并且将数据保存到FlashMap中,这样在跳转后的链接中可以获取一些数据.
5使用ModelAndView转发重定向
@RequestMapping("demo5")
2. public ModelAndView testDemo5(HttpServletRequest req) {
3. ModelAndView mv=new ModelAndView();
4. // 请求转发
5. //mv.setViewName("forward:/forwardPage.jsp");
6. //mv.setView(new InternalResourceView("/forwardPage.jsp"));
7. // 重定向
8. //mv.setViewName("redirect:/redirectPage.jsp");
9. mv.setView(new RedirectView(req.getContextPath()+"/redirectPage.jsp"));
10. return mv;
11. }
ModelAndView中的Model代表模型,View代表视图,这个名字就很好地解释了该类的作用。业务处理器调用模型层处理完用户请求后,把结果数据存储在该类的model属性中,把要返回的视图信息存储在该类的view属性中,然后让该ModelAndView返回该Spring MVC框架。
6ResponseBody 响应 json 数据
当浏览器发起一个ajax请求给服务器,服务器调用对应的单元方法处理ajax请求。而ajax的请求在被处理完成后,其处理结果需要直接响应。而目前我们在单元方 法中响应ajax请求,使用的是response对象,需要我们自己将要响应的数据转换 为json字符串响应,比较麻烦,而我们一直希望在单元方法中无论是否是ajax请求,都使用return语句来完成资源的响应,怎么办?
既然我们希望使用单元方法的返回值来响应ajax请求的处理结果,而目前DispatcherServlet的底层会将单元方法的返回值按照请求转发或者重定向来处理,所以就需要我们告诉DispatcherServlet,单元方法的返回值不要按照请求转发或者重定向处理,而是按照直接响应处理,将单元方法的返回值直接响应给浏览器。
第一步 导入jackson的jar
<dependency>
2. <groupId>com.fasterxml.jackson.core</groupId>
3. <artifactId>jackson-databind</artifactId>
4. <version>2.12.1</version>
5. </dependency>
第二步 声明单元方法处理ajax请求,并在单元方法上新增注解@ResponseBody
/*
2. * @ResponseBody
3. * 1方法的返回值不在作为界面跳转依据,而已直接作为返回的数据
4. * 2将方法的返回的数据自动使用ObjectMapper转换为JSON
5. */
6. @ResponseBody
7. @RequestMapping("testAjax")
8. public Pet testAjax(Person p) throws JsonProcessingException {
9. System.out.println(p);
10. Pet pet =new Pet("Tom","cat");
11. return pet;
12. }
注意:把我们要响应的数据直接return即可,返回值类型为要return的数据类型。
第三步: 在ajax的回调函数中,无需再次使用eval函数将响应数据转换为json对象
直接使用即可。
1. <head>
2. <meta charset="UTF-8">
3. <title>Title</title>
4. <script src="js/jquery.min.js"></script>
5. <script>
6. $(function(){
7. $("#btn").click(function(){
8. $.get("testAjax",{pname:"晓明",page:"10"},function(data){
9. console.log(data.petName)
10. console.log(data.petType)
11. })
12. })
13. })
14. </script>
15. </head>
16. <body>
17. <input id="btn" type="button" value="testJSON">
18. </body>
19. </html>
关于 @RestController
相当于@Controller+@ResponseBody两个注解的结合,返回json数据不需要在方法前面加@ResponseBody注解了,但使用@RestController这个注解,就不能返回jsp,html页面,视图解析器无法解析jsp,html页面
1. @RestController
2. public class AjaxController {
3. /*
4. * @ResponseBody
5. * 1方法的返回值不在作为界面跳转依据,而已直接作为返回的数据
6. * 2将方法的返回的数据自动使用ObjectMapper转换为JSON
7. */
8. @RequestMapping("testAjax")
9. public Pet testAjax(Person p) throws JsonProcessingException {
10. System.out.println(p);
11. Pet pet =new Pet("Tom","cat");
12. return pet;
13. }
14. }
15.
8_SpringMVC_SSM整合
SSM整合
登录案例开发
1准备数据库表格
2创建maven web项目并补充项目结构,准备好MVC模式下的主要目录
3更新web.xml 文件和准备包结构
1. <?xml version="1.0" encoding="UTF-8"?>
2. <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4. xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
5. version="4.0">
6.
7. </web-app>
4导入依赖
1.
2. <dependencies>
3. <!--spring核心容器包-->
4. <dependency>
5. <groupId>org.springframework</groupId>
6. <artifactId>spring-context</artifactId>
7. <version>5.3.5</version>
8. </dependency>
9. <!--spring切面包-->
10. <dependency>
11. <groupId>org.springframework</groupId>
12. <artifactId>spring-aspects</artifactId>
13. <version>5.3.5</version>
14. </dependency>
15. <!--aop联盟包-->
16. <dependency>
17. <groupId>aopalliance</groupId>
18. <artifactId>aopalliance</artifactId>
19. <version>1.0</version>
20. </dependency>
21. <!--德鲁伊连接池-->
22. <dependency>
23. <groupId>com.alibaba</groupId>
24. <artifactId>druid</artifactId>
25. <version>1.1.10</version>
26. </dependency>
27. <!--mysql驱动-->
28. <dependency>
29. <groupId>mysql</groupId>
30. <artifactId>mysql-connector-java</artifactId>
31. <version>8.0.22</version>
32. </dependency>
33. <!--springJDBC包-->
34. <dependency>
35. <groupId>org.springframework</groupId>
36. <artifactId>spring-jdbc</artifactId>
37. <version>5.3.5</version>
38. </dependency>
39. <!--spring事务控制包-->
40. <dependency>
41. <groupId>org.springframework</groupId>
42. <artifactId>spring-tx</artifactId>
43. <version>5.3.5</version>
44. </dependency>
45. <!--spring orm 映射依赖-->
46. <dependency>
47. <groupId>org.springframework</groupId>
48. <artifactId>spring-orm</artifactId>
49. <version>5.3.5</version>
50. </dependency>
51. <!--Apache Commons日志包-->
52. <dependency>
53. <groupId>commons-logging</groupId>
54. <artifactId>commons-logging</artifactId>
55. <version>1.2</version>
56. </dependency>
57. <!--log4j2 日志-->
58. <dependency>
59. <groupId>org.apache.logging.log4j</groupId>
60. <artifactId>log4j-slf4j-impl</artifactId>
61. <version>2.14.0</version>
62. <scope>test</scope>
63. </dependency>
64. <!--lombok -->
65. <dependency>
66. <groupId>org.projectlombok</groupId>
67. <artifactId>lombok</artifactId>
68. <version>1.18.12</version>
69. <scope>provided</scope>
70. </dependency>
71. <!--spring test测试支持包-->
72. <dependency>
73. <groupId>org.springframework</groupId>
74. <artifactId>spring-test</artifactId>
75. <version>5.3.5</version>
76. <scope>test</scope>
77. </dependency>
78. <!--junit5单元测试-->
79. <dependency>
80. <groupId>org.junit.jupiter</groupId>
81. <artifactId>junit-jupiter-api</artifactId>
82. <version>5.7.0</version>
83. <scope>test</scope>
84. </dependency>
85. <!--springMVC支持包-->
86. <dependency>
87. <groupId>org.springframework</groupId>
88. <artifactId>spring-web</artifactId>
89. <version>5.3.5</version>
90. </dependency>
91. <dependency>
92. <groupId>org.springframework</groupId>
93. <artifactId>spring-webmvc</artifactId>
94. <version>5.3.5</version>
95. </dependency>
96. <!--jsp 和Servlet 可选-->
97. <dependency>
98. <groupId>javax.servlet</groupId>
99. <artifactId>javax.servlet-api</artifactId>
100. <version>4.0.1</version>
101. <scope>provided</scope>
102. </dependency>
103. <dependency>
104. <groupId>javax.servlet.jsp</groupId>
105. <artifactId>javax.servlet.jsp-api</artifactId>
106. <version>2.3.3</version>
107. <scope>provided</scope>
108. </dependency>
109.
110. <!--json依赖-->
111. <dependency>
112. <groupId>com.fasterxml.jackson.core</groupId>
113. <artifactId>jackson-databind</artifactId>
114. <version>2.12.1</version>
115. </dependency>
116.
117. <!--mybatis 核心jar包-->
118. <dependency>
119. <groupId>org.mybatis</groupId>
120. <artifactId>mybatis</artifactId>
121. <version>3.5.3</version>
122. </dependency>
123. <!--spring mybatis整合包-->
124. <dependency>
125. <groupId>org.mybatis</groupId>
126. <artifactId>mybatis-spring</artifactId>
127. <version>2.0.3</version>
128. </dependency>
129.
130.
131. </dependencies>
接下来准备配置文件(重要!!!)
5 log4j2.xml
1. <?xml version="1.0" encoding="UTF-8"?>
2. <Configuration status="DEBUG">
3. <Appenders>
4. <Console name="Console" target="SYSTEM_ERR">
5. <PatternLayout pattern="%d{YYYY-MM-dd HH:mm:ss} [%t] %-5p %c{1}:%L - %msg%n" />
6. </Console>
7. </Appenders>
8. <Loggers>
9. <Root level="DEBUG">
10. <AppenderRef ref="Console" />
11. </Root>
12. </Loggers>
13. </Configuration>
6 jdbc.properties
1. jdbc_driver=com.mysql.cj.jdbc.Driver
2. jdbc_url=jdbc:mysql://127.0.0.1:3306/mydb?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
3. jdbc_username=root
4. jdbc_password=root
7 springMVC.xml配置文件
1. <?xml version="1.0" encoding="UTF-8"?>
2. <beans xmlns="http://www.springframework.org/schema/beans"
3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4. xmlns:p="http://www.springframework.org/schema/p"
5. xmlns:c="http://www.springframework.org/schema/c"
6. xmlns:util="http://www.springframework.org/schema/util"
7. xmlns:context="http://www.springframework.org/schema/context"
8. xmlns:aop="http://www.springframework.org/schema/aop"
9. xmlns:tx="http://www.springframework.org/schema/tx"
10. xmlns:mvc="http://www.springframework.org/schema/mvc"
11. xsi:schemaLocation="
12. http://www.springframework.org/schema/beans
13. http://www.springframework.org/schema/beans/spring-beans.xsd
14. http://www.springframework.org/schema/util
15. http://www.springframework.org/schema/util/spring-util.xsd
16. http://www.springframework.org/schema/context
17. http://www.springframework.org/schema/context/spring-context.xsd
18. http://www.springframework.org/schema/aop
19. http://www.springframework.org/schema/aop/spring-aop.xsd
20. http://www.springframework.org/schema/tx
21. http://www.springframework.org/schema/tx/spring-tx.xsd
22. http://www.springframework.org/schema/mvc
23. http://www.springframework.org/schema/mvc/spring-mvc.xsd
24. ">
25. <!--扫描controller-->
26. <context:component-scan base-package="com.msb.controller"></context:component-scan>
27.
28. <!--这里配置三大组件-->
29. <mvc:annotation-driven />
30.
31. <!--视图解析器-->
32. <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
33. </bean>
34.
35. <!--配置静态资源放行-->
36. <!--<mvc:resources mapping="/js/**" location="/js/"></mvc:resources>-->
37. <!--<mvc:resources mapping="/css/**" location="/css/"></mvc:resources>-->
38. <!--<mvc:resources mapping="/img/**" location="/img/"></mvc:resources>-->
39. <!--<mvc:resources mapping="/static/**" location="/static/"></mvc:resources>-->
40.
41. </beans>
8 applicationContext.xml
1. <?xml version="1.0" encoding="UTF-8"?>
2. <beans xmlns="http://www.springframework.org/schema/beans"
3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4. xmlns:p="http://www.springframework.org/schema/p"
5. xmlns:c="http://www.springframework.org/schema/c"
6. xmlns:util="http://www.springframework.org/schema/util"
7. xmlns:context="http://www.springframework.org/schema/context"
8. xmlns:aop="http://www.springframework.org/schema/aop"
9. xmlns:tx="http://www.springframework.org/schema/tx"
10. xmlns:mvc="http://www.springframework.org/schema/mvc"
11. xsi:schemaLocation="
12. http://www.springframework.org/schema/beans
13. http://www.springframework.org/schema/beans/spring-beans.xsd
14. http://www.springframework.org/schema/util
15. http://www.springframework.org/schema/util/spring-util.xsd
16. http://www.springframework.org/schema/context
17. http://www.springframework.org/schema/context/spring-context.xsd
18. http://www.springframework.org/schema/aop
19. http://www.springframework.org/schema/aop/spring-aop.xsd
20. http://www.springframework.org/schema/tx
21. http://www.springframework.org/schema/tx/spring-tx.xsd
22. http://www.springframework.org/schema/mvc
23. http://www.springframework.org/schema/mvc/spring-mvc.xsd
24. ">
25. <!--加载外部属性文件-->
26. <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
27. <!--扫描service层-->
28. <context:component-scan base-package="com.msb.service"></context:component-scan>
29. <!--配置德鲁伊数据源-->
30. <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
31. <property name="username" value="${jdbc_username}"></property>
32. <property name="password" value="${jdbc_password}"></property>
33. <property name="url" value="${jdbc_url}"></property>
34. <property name="driverClassName" value="${jdbc_driver}"></property>
35. </bean>
36. <!--配置sqlSessionFactory-->
37. <bean id="factory" class="org.mybatis.spring.SqlSessionFactoryBean">
38. <property name="dataSource" ref="dataSource"></property>
39. <property name="typeAliasesPackage" value="com.msb.pojo"></property>
40. </bean>
41. <!--配置MapperScanner 扫描mapper.xml 和接口-->
42. <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
43. <!--配置SQLSessionFactory-->
44. <property name="sqlSessionFactoryBeanName" value="factory"></property>
45. <!--扫描mapper-->
46. <property name="basePackage" value="com.msb.mapper"></property>
47. </bean>
48. <!--配置事务管理器-->
49. <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
50. <property name="dataSource" ref="dataSource"></property>
51. </bean>
52. <!--开启事务注解-->
53. <tx:annotation-driven transaction-manager="transactionManager"/>
54.
55.
56. </beans>
9 web.xml
1. <?xml version="1.0" encoding="UTF-8"?>
2. <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4. xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
5. version="4.0">
6.
7. <!--spring核心配置文件位置-->
8. <context-param>
9. <param-name>contextConfigLocation</param-name>
10. <param-value>classpath:applictionContext.xml</param-value>
11. </context-param>
12.
13. <!--spring Listener-->
14. <listener>
15. <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
16. </listener>
17.
18. <!--编码过滤器-->
19. <filter>
20. <filter-name>encFilter</filter-name>
21. <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
22. <init-param>
23. <param-name>encoding</param-name>
24. <param-value>utf-8</param-value>
25. </init-param>
26. </filter>
27. <filter-mapping>
28. <filter-name>encFilter</filter-name>
29. <url-pattern>/*</url-pattern>
30. </filter-mapping>
31.
32. <!--DispatcherServlet-->
33. <servlet>
34. <servlet-name>dispatcherServlet</servlet-name>
35. <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
36. <init-param>
37. <param-name>contextConfigLocation</param-name>
38. <param-value>classpath:springmvc.xml</param-value>
39. </init-param>
40. <load-on-startup>1</load-on-startup>
41. </servlet>
42.
43. <servlet-mapping>
44. <servlet-name>dispatcherServlet</servlet-name>
45. <url-pattern>/</url-pattern>
46. </servlet-mapping>
47.
48. </web-app>
49.
10 开发业务代码
index.jsp
1. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
2. <html>
3. <head>
4. <title>Title</title>
5. </head>
6. <body>
7. <form action="login.do" method="post">
8. <input type="text" name="uname">
9. <input type="password" name="password">
10. <input type="submit" value="登录">
11. </form>
12. </body>
13. </html>
success.jsp
1.
2. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
3. <html>
4. <head>
5. <title>Title</title>
6. </head>
7. <body>
8. 登录成功
9. </body>
10. </html>
fail.jsp
1.
2. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
3. <html>
4. <head>
5. <title>Title</title>
6. </head>
7. <body>
8. 登录失败
9. </body>
10. </html>
User实体类
1. package com.msb.pojo;
2.
3. import lombok.AllArgsConstructor;
4. import lombok.Data;
5. import lombok.NoArgsConstructor;
6.
7. import java.io.Serializable;
8.
9. /**
10. * @Author: Ma HaiYang
11. * @Description: MircoMessage:Mark_7001
12. */
13.
14. @AllArgsConstructor
15. @NoArgsConstructor
16. @Data
17. public class User implements Serializable {
18. private Integer uid;
19. private String uname;
20. private String password;
21. }
22.
UserController
1. package com.msb.controller;
2.
3. import com.msb.pojo.User;
4. import com.msb.service.UserService;
5. import org.springframework.beans.factory.annotation.Autowired;
6. import org.springframework.stereotype.Controller;
7. import org.springframework.web.bind.annotation.RequestMapping;
8. import org.springframework.web.bind.annotation.ResponseBody;
9.
10. import java.util.List;
11.
12. /**
13. * @Author: Ma HaiYang
14. * @Description: MircoMessage:Mark_7001
15. */
16. @Controller
17. public class UserController {
18. @Autowired
19. private UserService userService;
20.
21. @RequestMapping("login.do")
22. public String login(String uname,String password){
23. String view =null;
24. User user = userService.findUser(uname, password);
25. if(null != user){
26. view ="/success.jsp";
27. }else{
28. view="/fail.jsp";
29. }
30. return view;
31.
32. }
33.
34. @ResponseBody
35. @RequestMapping("findAllUser.do")
36. public List<User> findAllUser(){
37. return userService.findAllUser();
38. }
39.
40. }
41.
UserService
1. package com.msb.service.impl;
2.
3. import com.msb.mapper.UserMapper;
4. import com.msb.pojo.User;
5. import com.msb.service.UserService;
6. import org.springframework.beans.factory.annotation.Autowired;
7. import org.springframework.stereotype.Service;
8. import org.springframework.transaction.annotation.Transactional;
9.
10. import java.util.List;
11.
12. /**
13. * @Author: Ma HaiYang
14. * @Description: MircoMessage:Mark_7001
15. */
16. @Service
17. @Transactional
18. public class UserServiceImpl implements UserService {
19. @Autowired
20. private UserMapper userMapper;
21.
22. @Override
23. public User findUser(String uname, String password) {
24. return userMapper.findUser(uname,password);
25. }
26.
27. @Override
28. public List<User> findAllUser() {
29. return userMapper.findAllUser();
30.
31. }
32. }
33.
UserMapper
1. package com.msb.mapper;
2.
3. import com.msb.pojo.User;
4.
5. import java.util.List;
6.
7. /**
8. * @Author: Ma HaiYang
9. * @Description: MircoMessage:Mark_7001
10. */
11. public interface UserMapper {
12. User findUser(String uname, String password);
13.
14. List<User> findAllUser();
15.
16. }
17.
UserMapper.xml
1. <?xml version="1.0" encoding="UTF-8" ?>
2. <!DOCTYPE mapper
3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
5. <mapper namespace="com.msb.mapper.UserMapper">
6.
7. <!--User findUser(String uname, String password);-->
8. <select id="findUser" resultType="user">
9. select * from user where uname =#{param1} and password =#{param2}
10. </select>
11.
12. <!--findAllUser-->
13. <select id="findAllUser" resultType="user">
14. select * from user
15. </select>
16.
17. </mapper>
9_SpringMVC_作用域传参
PageContext对象
作用域范围:当前jsp页面内有效
request对象
作用域范围:一次请求内。
作用: 解决了一次请求内的资源的数据共享问题
session对象
作用域范围:一次会话内有效。
说明:浏览器不关闭,并且后台的session不失效,在任意请求中都可以获取到同一个session对象。
作用:解决了一个用户不同请求的数据共享问题。
application(ServletContext)对象
作用域范围:整个项目内有效。
特点:一个项目只有一个,在服务器启动的时候即完成初始化创建无论如何获取都是同一个项目。
作用:解决了不同用户的数据共享问题。
1 传统方式传递数据
定义Controller
1. package com.msb.controller;
2.
3. import com.msb.pojo.User;
4. import com.msb.service.UserService;
5. import org.springframework.beans.factory.annotation.Autowired;
6. import org.springframework.stereotype.Controller;
7. import org.springframework.web.bind.annotation.RequestMapping;
8.
9. import javax.servlet.ServletContext;
10. import javax.servlet.http.HttpServletRequest;
11. import javax.servlet.http.HttpServletRequestWrapper;
12. import javax.servlet.http.HttpSession;
13. import java.util.List;
14.
15. /**
16. * @Author: Ma HaiYang
17. * @Description: MircoMessage:Mark_7001
18. */
19. @Controller
20. public class ScopeController {
21. @Autowired
22. private UserService userService;
23.
24. /*
25. * request,session 这两个域直接放在参数列表上即可,SpringMVC就可以给我们注入
26. * ServletContext对象(application域) 不能直接放在参数列表上的
27. * */
28. @RequestMapping("setData")
29. public String setData(HttpServletRequest req,HttpSession session){
30. List<User> users = userService.findAllUser();
31.
32. // 向三个域中放入数据
33. req.setAttribute("message", "reqMessage");
34. req.setAttribute("users", users);
35.
36. session.setAttribute("message", "sesssionMessage");
37. session.setAttribute("users", users);
38.
39. ServletContext application = req.getServletContext();
40. application.setAttribute("message", "applictionMessage");
41. application.setAttribute("users", users);
42. // 跳转至showDataPage
43. return "/showDataPage.jsp";
44. }
45.
46. }
定义showDataPage.jsp
1.
2. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
3. <html>
4. <head>
5. <title>Title</title>
6. </head>
7. <body>
8. <%--域中的数据--%>
9. requestScope :message:${requestScope.message} ,uname:${requestScope.users[0].uname} <br/>
10. sessionScope :message:${sessionScope.message} ,uname:${sessionScope.users[0].uname} <br/>
11. applicationScope :message:${applicationScope.message} ,uname:${applicationScope.users[0].uname} <br/>
12. <%--请求参数--%>
13. requestParam:${param.message}
14.
15.
16. </body>
17. </html>
18.
2使用Model传递数据
/*model对象addAttribute
2. * 主要是对请求域传递数据进行了API上的封装
3. * 降低controller和Servlet之间的耦合度
4. * 重定向下,没法使用model传递域中的数据
5. * model中的字符串类型的键值对信息会转换为请求参数,转发给目标组件
6. * */
7. @RequestMapping("setData2")
8. public String setData2(Model model){
9. List<User> users = userService.findAllUser();
10. // 向域中放入数据
11. model.addAttribute("message", "reqMessage");
12. model.addAttribute("users", users);
13. // 跳转至showDataPage
14. // return "forward:/showDataPage.jsp";
15. return "redirect:/showDataPage.jsp";
16. }
3使用ModelAndView传递数据
/*
2. * ModelAndView
3. * Model数据
4. * View 视图
5. * */
6. @RequestMapping("setData3")
7. public ModelAndView setData3(){
8. ModelAndView mv=new ModelAndView();
9. Map<String, Object> model = mv.getModel();
10. // 向request域中放入数据
11. List<User> users = userService.findAllUser();
12. model.put("message", "reqMessage");
13. model.put("users", users);
14. // 设置视图
15. //mv.setViewName("forward:/showDataPage.jsp");
16. mv.setViewName("redirect:/showDataPage.jsp");
17.
18. return mv;
19. }
10_SpringMVC_上传
随着我们互联网的发展,我们的用户从直接访问网站获取信息。变为希望将自己本地的资源发送给服务器,让服务器提供给其他人使用或者查看。还有部分的用户希望可以将本地的资源上传服务器存储起来,然后再其他的电脑中可以通过访问网站来获取上传的资源,这样用户就可以打破空间的局限性,再任何时候只要有网有电脑就可以对自己的资源进行操作,比如:云存储,云编辑
1如何在页面中显示一个按钮
用户可以点击该按钮后选择本地要上传的文件在页面中使用input标签,type值设置为”file”即可
2确定上传请求的发送方式
上传成功后的响应结果在当前页面显示,使用ajax请求来完成资源的发送
3上传请求的请求数据及其数据格式
请求数据:
上传的文件本身普通数据:用户名,Id,密码等,建议上传功能中不携带除上传资源以外的数据
数据格式:
传统的请求中,请求数据是以键值对的格式来发送给后台服务器的,但是在上传请求中,没有任何一个键可以描述上次的数据,因为数据本身是非常大的键就相当于一个变量,我们使用一个变量存储一个10g的电影显然是不可能 的。在上传请求中,将请求数据以二进制流的方式发送给服务器。
4在ajax中如何发送二进制流数据给服务器
① 创建FormData的对象,将请求数据存储到该对象中发送
② 将processData属性的值设置为false,告诉浏览器发送对象请求数据
③ 将contentType属性的值设置为false,设置请求数据的类型为二进制类型。
④ 正常发送ajax即可
5上传成功后后台服务器应该响应什么结果给浏览器
并且浏览器如何处理后台服务器处理完成后,响应一个json对象给浏览器,示例格式如下:
{ state:true,msg:“服务器繁忙”,url:”上传成功的资源的请求地址”}
6 文件上传依赖的jar
<!--文件上传依赖-->
2. <dependency>
3. <groupId>commons-fileupload</groupId>
4. <artifactId>commons-fileupload</artifactId>
5. <version>1.4</version>
6. </dependency>
7.
8.
9. <dependency>
10. <groupId>commons-io</groupId>
11. <artifactId>commons-io</artifactId>
12. <version>2.8.0</version>
13. </dependency>
7 配置文件上传组件
<!--文件上传解析组件
2. id必须为multipartResolver
3. springmvc默认使用该id找该组件
4. -->
5. <bean
6. id="multipartResolver"
7. class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>
准备用户表
前端代码
1. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
2. <html>
3. <head>
4. <title>Title</title>
5. <script type="text/javascript" src="js/jquery.min.js"></script>
6. <script type="text/javascript">
7. $(function(){
8. $("#uploadFile").click(function(){
9. // 获取要上传的文件
10. var photoFile =$("#photo")[0].files[0]
11.
12. if(photoFile==undefined){
13. alert("您还未选中文件")
14. return;
15. }
16. // 将文件装入FormData对象
17. var formData =new FormData();
18. formData.append("headPhoto",photoFile)
19. // ajax向后台发送文件
20. $.ajax({
21. type:"post",
22. data:formData,
23. url:"fileUpload.do",
24. processData:false,
25. contentType:false,
26. success:function(result){
27. // 接收后台响应的信息
28. console.log(result)
29. // 图片回显
30. }
31. })
32. })
33. })
34. </script>
35. </head>
36. <body>
37.
38. <form action="addPlayer" method="get">
39. <p>账号<input type="text" name="name"></p>
40. <p>密码<input type="text" name="password"></p>
41. <p>昵称<input type="text" name="nickname"></p>
42. <p>头像:
43. <br/>
44. <input id="photo" type="file">
45. <a id="uploadFile" href="javascript:void(0)">立即上传</a>
46. </p>
47. <p><input type="submit" value="注册"></p>
48. </form>
49. </body>
50. </html>
51.
controller代码
1. package com.msb.controller;
2.
3. import org.springframework.stereotype.Controller;
4. import org.springframework.web.bind.annotation.RequestMapping;
5. import org.springframework.web.bind.annotation.ResponseBody;
6. import org.springframework.web.multipart.MultipartFile;
7.
8. import java.io.File;
9. import java.io.IOException;
10.
11. /**
12. * @Author: Ma HaiYang
13. * @Description: MircoMessage:Mark_7001
14. */
15. @Controller
16. public class FileUploadController {
17.
18. @ResponseBody
19. @RequestMapping("fileUpload.do")
20. public String fileUpload(MultipartFile headPhoto) throws IOException {
21. // 指定文件存储目录
22. File dir = new File("d:/imgs");
23. // 获取文件名
24. String originalFilename = headPhoto.getOriginalFilename();
25. // 文件存储位置
26. File file =new File(dir,originalFilename);
27. // 文件保存
28. headPhoto.transferTo(file);
29.
30. return "OK";
31. }
32. }
33.
前端页面效果展现
文件上传中的几个问题
1 中文文件名编码问题:
已经通过过滤器解决
2 文件位置存储问题
放在当前项目下,作为静态资源,这样可以通过URL访问
1. package com.msb.controller;
2.
3. import org.springframework.stereotype.Controller;
4. import org.springframework.web.bind.annotation.RequestMapping;
5. import org.springframework.web.bind.annotation.ResponseBody;
6. import org.springframework.web.multipart.MultipartFile;
7.
8. import javax.servlet.http.HttpServletRequest;
9. import java.io.File;
10. import java.io.IOException;
11.
12. /**
13. * @Author: Ma HaiYang
14. * @Description: MircoMessage:Mark_7001
15. */
16. @Controller
17. public class FileUploadController {
18.
19. @ResponseBody
20. @RequestMapping("fileUpload.do")
21. public String fileUpload(MultipartFile headPhoto, HttpServletRequest req) throws IOException {
22. // 指定文件存储目录为我们项目部署环境下的upload目录
23. String realPath = req.getServletContext().getRealPath("/upload");
24. File dir = new File(realPath);
25. // 如果不存在则创建目录
26. if(!dir.exists()){
27. dir.mkdirs();
28. }
29. // 获取文件名
30. String originalFilename = headPhoto.getOriginalFilename();
31. // 文件存储位置
32. File file =new File(dir,originalFilename);
33. // 文件保存
34. headPhoto.transferTo(file);
35.
36. return "OK";
37. }
38. }
39.
在SpringMVC中配置静态资源放行
1. <mvc:resources mapping="/upload/**" location="/upload/"></mvc:resources>
3 文件名冲突问题
使用UUID对文件名进行重命名
1. package com.msb.controller;
2.
3. import org.springframework.stereotype.Controller;
4. import org.springframework.web.bind.annotation.RequestMapping;
5. import org.springframework.web.bind.annotation.ResponseBody;
6. import org.springframework.web.multipart.MultipartFile;
7.
8. import javax.servlet.http.HttpServletRequest;
9. import java.io.File;
10. import java.io.IOException;
11. import java.util.UUID;
12.
13. /**
14. * @Author: Ma HaiYang
15. * @Description: MircoMessage:Mark_7001
16. */
17. @Controller
18. public class FileUploadController {
19.
20. @ResponseBody
21. @RequestMapping("fileUpload.do")
22. public String fileUpload(MultipartFile headPhoto, HttpServletRequest req) throws IOException {
23. // 指定文件存储目录为我们项目部署环境下的upload目录
24. String realPath = req.getServletContext().getRealPath("/upload");
25. File dir = new File(realPath);
26. // 如果不存在则创建目录
27. if(!dir.exists()){
28. dir.mkdirs();
29. }
30. // 获取文件名
31. String originalFilename = headPhoto.getOriginalFilename();
32. // 避免文件名冲突,使用UUID替换文件名
33. String uuid = UUID.randomUUID().toString();
34. // 获取拓展名
35. String extendsName = originalFilename.substring(originalFilename.lastIndexOf("."));
36. // 新的文件名
37. String newFileName=uuid.concat(extendsName);
38. // 文件存储位置
39. File file =new File(dir,newFileName);
40. // 文件保存
41. headPhoto.transferTo(file);
42.
43. return "OK";
44. }
45. }
46.
4 控制文件类型
5 控制文件大小
1. package com.msb.controller;
2.
3. import org.springframework.stereotype.Controller;
4. import org.springframework.web.bind.annotation.RequestMapping;
5. import org.springframework.web.bind.annotation.ResponseBody;
6. import org.springframework.web.multipart.MultipartFile;
7.
8. import javax.servlet.http.HttpServletRequest;
9. import java.io.File;
10. import java.io.IOException;
11. import java.util.HashMap;
12. import java.util.Map;
13. import java.util.UUID;
14.
15. /**
16. * @Author: Ma HaiYang
17. * @Description: MircoMessage:Mark_7001
18. */
19. @Controller
20. public class FileUploadController {
21.
22. @ResponseBody
23. @RequestMapping("fileUpload.do")
24. public Map<String,String> fileUpload(MultipartFile headPhoto, HttpServletRequest req) throws IOException {
25. Map<String,String> map=new HashMap<>();
26. // 控制文件大小
27. if(headPhoto.getSize()>1024*1024*5){
28. map.put("message", "文件大小不能超过5M");
29. return map;
30. }
31.
32. // 指定文件存储目录为我们项目部署环境下的upload目录
33. String realPath = req.getServletContext().getRealPath("/upload");
34. File dir = new File(realPath);
35. // 如果不存在则创建目录
36. if(!dir.exists()){
37. dir.mkdirs();
38. }
39. // 获取文件名
40. String originalFilename = headPhoto.getOriginalFilename();
41. // 避免文件名冲突,使用UUID替换文件名
42. String uuid = UUID.randomUUID().toString();
43. // 获取拓展名
44. String extendsName = originalFilename.substring(originalFilename.lastIndexOf("."));
45. // 控制文件类型
46. if(!extendsName.equals(".jpg")){
47. map.put("message", "文件类型必须是.jpg");
48. return map;
49. }
50.
51. // 新的文件名
52. String newFileName=uuid.concat(extendsName);
53. // 文件存储位置
54. File file =new File(dir,newFileName);
55. // 文件保存
56. headPhoto.transferTo(file);
57.
58. // 上传成功之后,把文件的名字和文件的类型返回给浏览器
59. map.put("message", "上传成功");
60. map.put("newFileName", newFileName);
61. map.put("filetype", headPhoto.getContentType());
62. return map;
63. }
64. }
65.
通过文件上传解析组件控制
但是会出现异常,后期可以可以配置一个异常解析器解决
<!--文件上传解析组件
2. id必须为multipartResolver
3. springmvc默认使用该id找该组件
4. -->
5. <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
6. <!--设置文件大小-->
7. <property name="maxUploadSizePerFile" value="10"></property>
8. </bean>
6 上传图片回显问题
后天已经将图片的文件名响应给浏览器
前端代码
1. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
2. <html>
3. <head>
4. <title>Title</title>
5. <script type="text/javascript" src="js/jquery.min.js"></script>
6. <script type="text/javascript">
7. $(function(){
8. $("#uploadFile").click(function(){
9. // 获取要上传的文件
10. var photoFile =$("#photo")[0].files[0]
11.
12. if(photoFile==undefined){
13. alert("您还未选中文件")
14. return;
15. }
16. // 将文件装入FormData对象
17. var formData =new FormData();
18. formData.append("headPhoto",photoFile)
19. // ajax向后台发送文件
20. $.ajax({
21. type:"post",
22. data:formData,
23. url:"fileUpload.do",
24. processData:false,
25. contentType:false,
26. success:function(result){
27. // 接收后台响应的信息
28. alert(result.message)
29. // 图片回显
30. $("#headImg").attr("src","upload/"+result.newFileName);
31. }
32. })
33. })
34. })
35. </script>
36. </head>
37. <body>
38.
39. <form action="addPlayer" method="get">
40. <p>账号<input type="text" name="name"></p>
41. <p>密码<input type="text" name="password"></p>
42. <p>昵称<input type="text" name="nickname"></p>
43. <p>头像:
44. <br/>
45. <input id="photo" type="file">
46. <br/>
47. <img id="headImg" style="width: 200px;height: 200px" alt="你还未上传图片">
48. <br/>
49. <a id="uploadFile" href="javascript:void(0)">立即上传</a>
50. </p>
51. <p><input type="submit" value="注册"></p>
52. </form>
53. </body>
54. </html>
55.
7 进度条问题
1. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
2. <html>
3. <head>
4. <title>Title</title>
5. <style>
6. .progress {
7. width: 200px;
8. height: 10px;
9. border: 1px solid #ccc;
10. border-radius: 10px;
11. margin: 10px 0px;
12. overflow: hidden;
13. }
14. /* 初始状态设置进度条宽度为0px */
15. .progress > div {
16. width: 0px;
17. height: 100%;
18. background-color: yellowgreen;
19. transition: all .3s ease;
20. }
21. </style>
22.
23. <script type="text/javascript" src="js/jquery.min.js"></script>
24. <script type="text/javascript">
25. $(function(){
26. $("#uploadFile").click(function(){
27. // 获取要上传的文件
28. var photoFile =$("#photo")[0].files[0]
29.
30. if(photoFile==undefined){
31. alert("您还未选中文件")
32. return;
33. }
34. // 将文件装入FormData对象
35. var formData =new FormData();
36. formData.append("headPhoto",photoFile)
37. // ajax向后台发送文件
38. $.ajax({
39. type:"post",
40. data:formData,
41. url:"fileUpload.do",
42. processData:false,
43. contentType:false,
44. success:function(result){
45. // 接收后台响应的信息
46. alert(result.message)
47. // 图片回显
48. $("#headImg").attr("src","upload/"+result.newFileName);
49. },
50. xhr: function() {
51. var xhr = new XMLHttpRequest();
52. //使用XMLHttpRequest.upload监听上传过程,注册progress事件,打印回调函数中的event事件
53. xhr.upload.addEventListener('progress', function (e) {
54. //loaded代表上传了多少
55. //total代表总数为多少
56. var progressRate = (e.loaded / e.total) * 100 + '%';
57. //通过设置进度条的宽度达到效果
58. $('.progress > div').css('width', progressRate);
59. })
60.
61. return xhr;
62. }
63. })
64. })
65. })
66. </script>
67. </head>
68. <body>
69.
70. <form action="addPlayer" method="get">
71. <p>账号<input type="text" name="name"></p>
72. <p>密码<input type="text" name="password"></p>
73. <p>昵称<input type="text" name="nickname"></p>
74. <p>头像:
75. <br/>
76. <input id="photo" type="file">
77. <%--图片回显--%>
78. <br/>
79. <img id="headImg" style="width: 200px;height: 200px" alt="你还未上传图片">
80. <br/>
81. <%--进度条--%>
82. <div class="progress">
83. <div></div>
84. </div>
85. <a id="uploadFile" href="javascript:void(0)">立即上传</a>
86. </p>
87. <p><input type="submit" value="注册"></p>
88. </form>
89. </body>
90. </html>
91.
8 单独准备文件存储服务器
分服务器上传作用
数据库服务器:运行我们的数据库
缓存和消息服务器:负责处理大并发访问的缓存和消息
文件服务器:负责存储用户上传文件的服务器。
应用服务器:负责部署我们的应用
在实际开发中,我们会有很多处理不同功能的服务器。(注意:此处说的不是服务器集群)
总结:分服务器处理的目的是让服务器各司其职,从而提高我们项目的运行效率。
分服务器工作示意图
单独解压一个Tomcat作为文件服务器
设置远程服务器端口号
远程服务器中设置非只读
webapps下创建一个upload目录
启动测试
项目中导入依赖
1. <dependency>
2. <groupId>com.sun.jersey</groupId>
3. <artifactId>jersey-client</artifactId>
4. <version>1.19</version>
5. </dependency>
controller代码
1. package com.msb.controller;
2.
3. import com.sun.jersey.api.client.Client;
4. import com.sun.jersey.api.client.WebResource;
5. import org.springframework.stereotype.Controller;
6. import org.springframework.web.bind.annotation.RequestMapping;
7. import org.springframework.web.bind.annotation.ResponseBody;
8. import org.springframework.web.multipart.MultipartFile;
9.
10. import javax.servlet.http.HttpServletRequest;
11. import java.io.File;
12. import java.io.IOException;
13. import java.util.HashMap;
14. import java.util.Map;
15. import java.util.UUID;
16.
17. /**
18. * @Author: Ma HaiYang
19. * @Description: MircoMessage:Mark_7001
20. */
21. @Controller
22. public class FileUploadController {
23. // 文件存储位置
24. private final static String FILESERVER="http://192.168.8.109:8090/upload/";
25.
26. @ResponseBody
27. @RequestMapping("fileUpload.do")
28. public Map<String,String> fileUpload(MultipartFile headPhoto, HttpServletRequest req) throws IOException {
29. Map<String,String> map=new HashMap<>();
30. // 获取文件名
31. String originalFilename = headPhoto.getOriginalFilename();
32. // 避免文件名冲突,使用UUID替换文件名
33. String uuid = UUID.randomUUID().toString();
34. // 获取拓展名
35. String extendsName = originalFilename.substring(originalFilename.lastIndexOf("."));
36. // 新的文件名
37. String newFileName=uuid.concat(extendsName);
38. // 创建 sun公司提供的jersey包中的client对象
39. Client client=Client.create();
40. WebResource resource = client.resource(FILESERVER + newFileName);
41. // 文件保存到另一个服务器上去了
42. resource.put(String.class, headPhoto.getBytes());
43. // 上传成功之后,把文件的名字和文件的类型返回给浏览器
44. map.put("message", "上传成功");
45. map.put("newFileName",newFileName);
46. map.put("filetype", headPhoto.getContentType());
47. return map;
48. }
49. }
50.
页面代码
1. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
2. <html>
3. <head>
4. <title>Title</title>
5. <style>
6. .progress {
7. width: 200px;
8. height: 10px;
9. border: 1px solid #ccc;
10. border-radius: 10px;
11. margin: 10px 0px;
12. overflow: hidden;
13. }
14. /* 初始状态设置进度条宽度为0px */
15. .progress > div {
16. width: 0px;
17. height: 100%;
18. background-color: yellowgreen;
19. transition: all .3s ease;
20. }
21. </style>
22.
23. <script type="text/javascript" src="js/jquery.min.js"></script>
24. <script type="text/javascript">
25. $(function(){
26. $("#uploadFile").click(function(){
27. // 获取要上传的文件
28. var photoFile =$("#photo")[0].files[0]
29.
30. if(photoFile==undefined){
31. alert("您还未选中文件")
32. return;
33. }
34. // 将文件装入FormData对象
35. var formData =new FormData();
36. formData.append("headPhoto",photoFile)
37. // ajax向后台发送文件
38. $.ajax({
39. type:"post",
40. data:formData,
41. url:"fileUpload.do",
42. processData:false,
43. contentType:false,
44. success:function(result){
45. // 接收后台响应的信息
46. alert(result.message)
47. // 图片回显
48. $("#headImg").attr("src","http://192.168.8.109:8090/upload/"+result.newFileName);
49.
50. },
51. xhr: function() {
52. var xhr = new XMLHttpRequest();
53. //使用XMLHttpRequest.upload监听上传过程,注册progress事件,打印回调函数中的event事件
54. xhr.upload.addEventListener('progress', function (e) {
55. //loaded代表上传了多少
56. //total代表总数为多少
57. var progressRate = (e.loaded / e.total) * 100 + '%';
58. //通过设置进度条的宽度达到效果
59. $('.progress > div').css('width', progressRate);
60. })
61.
62. return xhr;
63. }
64. })
65. })
66. })
67. </script>
68. </head>
69. <body>
70.
71. <form action="addPlayer" method="get">
72. <p>账号<input type="text" name="name"></p>
73. <p>密码<input type="text" name="password"></p>
74. <p>昵称<input type="text" name="nickname"></p>
75. <p>头像:
76. <br/>
77. <input id="photo" type="file">
78. <%--图片回显--%>
79. <br/>
80. <img id="headImg" style="width: 200px;height: 200px" alt="你还未上传图片">
81. <br/>
82. <%--进度条--%>
83. <div class="progress">
84. <div></div>
85. </div>
86. <a id="uploadFile" href="javascript:void(0)">立即上传</a>
87.
88. </p>
89. <p><input type="submit" value="注册"></p>
90. </form>
91. </body>
92. </html>
93.
9 保存完整player信息进入数据库
index.html
1. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
2. <html>
3. <head>
4. <title>Title</title>
5. <style>
6. .progress {
7. width: 200px;
8. height: 10px;
9. border: 1px solid #ccc;
10. border-radius: 10px;
11. margin: 10px 0px;
12. overflow: hidden;
13. }
14. /* 初始状态设置进度条宽度为0px */
15. .progress > div {
16. width: 0px;
17. height: 100%;
18. background-color: yellowgreen;
19. transition: all .3s ease;
20. }
21. </style>
22.
23. <script type="text/javascript" src="js/jquery.min.js"></script>
24. <script type="text/javascript">
25. $(function(){
26. $("#uploadFile").click(function(){
27. // 获取要上传的文件
28. var photoFile =$("#photo")[0].files[0]
29.
30. if(photoFile==undefined){
31. alert("您还未选中文件")
32. return;
33. }
34. // 将文件装入FormData对象
35. var formData =new FormData();
36. formData.append("headPhoto",photoFile)
37. // ajax向后台发送文件
38. $.ajax({
39. type:"post",
40. data:formData,
41. url:"fileUpload.do",
42. processData:false,
43. contentType:false,
44. success:function(result){
45. // 接收后台响应的信息
46. alert(result.message)
47. // 图片回显
48. $("#headImg").attr("src","http://192.168.8.109:8090/upload/"+result.newFileName);
49. // 将文件类型和文件名放入form表单
50. $("#photoInput").val(result.newFileName)
51. $("#filetypeInput").val(result.filetype)
52. },
53. xhr: function() {
54. var xhr = new XMLHttpRequest();
55. //使用XMLHttpRequest.upload监听上传过程,注册progress事件,打印回调函数中的event事件
56. xhr.upload.addEventListener('progress', function (e) {
57. //loaded代表上传了多少
58. //total代表总数为多少
59. var progressRate = (e.loaded / e.total) * 100 + '%';
60. //通过设置进度条的宽度达到效果
61. $('.progress > div').css('width', progressRate);
62. })
63.
64. return xhr;
65. }
66. })
67. })
68. })
69. </script>
70. </head>
71. <body>
72.
73. <form action="addPlayer" method="get">
74. <p>账号<input type="text" name="name"></p>
75. <p>密码<input type="text" name="password"></p>
76. <p>昵称<input type="text" name="nickname"></p>
77. <p>头像:
78. <br/>
79. <input id="photo" type="file">
80. <%--图片回显--%>
81. <br/>
82. <img id="headImg" style="width: 200px;height: 200px" alt="你还未上传图片">
83. <br/>
84. <%--进度条--%>
85. <div class="progress">
86. <div></div>
87. </div>
88. <a id="uploadFile" href="javascript:void(0)">立即上传</a>
89. <%--使用隐藏的输入框存储文件名称和文件类型--%>
90. <input id="photoInput" type="hidden" name="photo" >
91. <input id="filetypeInput" type="hidden" name="filetype">
92. </p>
93. <p><input type="submit" value="注册"></p>
94. </form>
95. </body>
96. </html>
97.
FileUploadController代码
见上一步
playerController代码
1. package com.msb.controller;
2.
3. import com.msb.pojo.Player;
4. import com.msb.service.PlayerService;
5. import org.springframework.beans.factory.annotation.Autowired;
6. import org.springframework.stereotype.Controller;
7. import org.springframework.web.bind.annotation.RequestMapping;
8.
9. /**
10. * @Author: Ma HaiYang
11. * @Description: MircoMessage:Mark_7001
12. */
13. @Controller
14. public class PlayerController {
15.
16. @Autowired
17. private PlayerService playerService;
18.
19. @RequestMapping("addPlayer")
20. public String addPlayer(Player player){
21. // 调用服务层方法,将数据保存进入数据库
22. playerService.addPlayer(player);
23. // 页面跳转至player信息展示页
24. return "redirect:/showPlayer.jsp";
25. }
26.
27. }
Service层代码
1. package com.msb.service.impl;
2.
3. import com.msb.mapper.PlayerMapper;
4. import com.msb.pojo.Player;
5. import com.msb.service.PlayerService;
6. import org.springframework.beans.factory.annotation.Autowired;
7. import org.springframework.stereotype.Service;
8.
9. /**
10. * @Author: Ma HaiYang
11. * @Description: MircoMessage:Mark_7001
12. */
13. @Service
14. public class PlayerServiceImpl implements PlayerService {
15. @Autowired
16. private PlayerMapper playerMapper;
17.
18. @Override
19. public boolean addPlayer(Player player) {
20. return playerMapper.addPlayer(player)>0;
21. }
22. }
23.
mapper代码
1. <?xml version="1.0" encoding="UTF-8" ?>
2. <!DOCTYPE mapper
3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
5. <mapper namespace="com.msb.mapper.PlayerMapper">
6. <insert id="addPlayer">
7. insert into player values(DEFAULT ,#{name},#{password},#{nickname},#{photo},#{filetype})
8. </insert>
9. </mapper>
11_SpringMVC_下载
下载之前的准备 ,展示所有玩家信息,包括图片展示
showPlayer.jsp
1.
2. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
3. <html>
4. <head>
5. <title>Title</title>
6. <style>
7. #playerTable{
8. width: 50%;
9. border: 3px solid cadetblue;
10. margin: 0px auto;
11. text-align: center;
12. }
13. #playerTable th,td{
14. border: 1px solid gray;
15. }
16. #playerTable img{
17. width: 100px;
18. height: 100px;
19. }
20. </style>
21.
22. <script type="text/javascript" src="js/jquery.min.js"></script>
23. <script>
24. $(function(){
25. $.ajax({
26. type:"get",
27. url:"getAllPlayer",
28. success:function(players){
29. $.each(players,function(i,e){
30. $("#playerTable").append('<tr>\n' +
31. ' <td>'+e.id+'</td>\n' +
32. ' <td>'+e.name+'</td>\n' +
33. ' <td>'+e.password+'</td>\n' +
34. ' <td>'+e.nickname+'</td>\n' +
35. ' <td>\n' +
36. ' <img src="http://192.168.8.109:8090/upload/'+e.photo+'" alt="" src>\n' +
37. ' </td>\n' +
38. ' <td>\n' +
39. ' <a href="">下载</a>\n' +
40. ' </td>\n' +
41. ' </tr>')
42. })
43. }
44. })
45.
46. })
47. </script>
48. </head>
49. <body>
50. <table id="playerTable" cellspacing="0xp" cellpadding="0px">
51. <tr>
52. <th>编号</th>
53. <th>用户名</th>
54. <th>密码</th>
55. <th>昵称</th>
56. <th>头像</th>
57. <th>操作</th>
58. </tr>
59. </table>
60.
61.
62. </body>
63. </html>
PlayerController
1. package com.msb.controller;
2.
3. import com.msb.pojo.Player;
4. import com.msb.service.PlayerService;
5. import org.springframework.beans.factory.annotation.Autowired;
6. import org.springframework.stereotype.Controller;
7. import org.springframework.web.bind.annotation.RequestMapping;
8. import org.springframework.web.bind.annotation.ResponseBody;
9.
10. import java.util.List;
11.
12. /**
13. * @Author: Ma HaiYang
14. * @Description: MircoMessage:Mark_7001
15. */
16. @Controller
17. public class PlayerController {
18.
19. @Autowired
20. private PlayerService playerService;
21.
22. @RequestMapping("addPlayer")
23. public String addPlayer(Player player){
24. // 调用服务层方法,将数据保存进入数据库
25. playerService.addPlayer(player);
26. // 页面跳转至player信息展示页
27. return "redirect:/showPlayer.jsp";
28. }
29.
30. @RequestMapping("getAllPlayer")
31. @ResponseBody
32. public List<Player> getAllPlayer(){
33. return playerService.getAllPlayer();
34. }
35.
36. }
37.
service和Mapper 略
1、下载的基本流程
文件的上传是将用户本地的资源发送到服务器,让服务器存储到其硬盘中的过程。而下载和上传正好是相反的过程。下载是用户发起请求,请求要下载的资源。服务器根据请求,将其硬盘中的文件资源发送给浏览器的过程。
2、下载的请求数据
用户通过浏览器发起下载请求,服务器在接收到请求后,根据当前请求的用户信息,去数据库中获取当前用户要下载的资源的文件路径,然后服务器再去其硬盘中读取对应的文件,将文件响应给浏览器,基于此过程,下载请求的请求数据为:简单的下载:文件的路径直接作为一个字段存储在用户信息表中用户的ID。
[1] 下载的后台实现
1. 创建单元方法处理下载请求
2. 根据请求获取要下载的资源的流对象
3. 读取文件并将资源响应给浏览器
[2] 下载的示例代码
页面代码
1.
2. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
3. <html>
4. <head>
5. <title>Title</title>
6. <style>
7. #playerTable{
8. width: 50%;
9. border: 3px solid cadetblue;
10. margin: 0px auto;
11. text-align: center;
12. }
13. #playerTable th,td{
14. border: 1px solid gray;
15. }
16. #playerTable img{
17. width: 100px;
18. height: 100px;
19. }
20. </style>
21.
22. <script type="text/javascript" src="js/jquery.min.js"></script>
23. <script>
24. $(function(){
25. $.ajax({
26. type:"get",
27. url:"getAllPlayer",
28. success:function(players){
29. $.each(players,function(i,e){
30. $("#playerTable").append('<tr>\n' +
31. ' <td>'+e.id+'</td>\n' +
32. ' <td>'+e.name+'</td>\n' +
33. ' <td>'+e.password+'</td>\n' +
34. ' <td>'+e.nickname+'</td>\n' +
35. ' <td>\n' +
36. ' <img src="http://192.168.8.109:8090/upload/'+e.photo+'" alt="" src>\n' +
37. ' </td>\n' +
38. ' <td>\n' +
39. ' <a href="fileDownload.do?photo='+e.photo+'&filetype='+e.filetype+'">下载</a>\n' +
40. ' </td>\n' +
41. ' </tr>')
42. })
43. }
44. })
45.
46. })
47. </script>
48. </head>
49. <body>
50. <table id="playerTable" cellspacing="0xp" cellpadding="0px">
51. <tr>
52. <th>编号</th>
53. <th>用户名</th>
54. <th>密码</th>
55. <th>昵称</th>
56. <th>头像</th>
57. <th>操作</th>
58. </tr>
59. </table>
60.
61.
62. </body>
63. </html>
64.
controller层代码
1. @RequestMapping("fileDownload.do")
2. public void fileDownLoad(String photo, String filetype, HttpServletResponse response) throws IOException {
3. // 设置响应头
4. // 告诉浏览器要将数据保存到磁盘上,不在浏览器上直接解析
5. response.setHeader("Content-Disposition", "attachment;filename="+photo);
6. // 告诉浏览下载的文件类型
7. response.setContentType(filetype);
8. // 获取一个文件的输入流
9. InputStream inputStream = new URL(FILESERVER + photo).openStream();
10. // 获取一个指向浏览器的输出流
11. ServletOutputStream outputStream = response.getOutputStream();
12. // 向浏览器响应文件即可
13. IOUtils.copy(inputStream, outputStream);
14.
15. }
12_SpringMVC_拦截器
在之前学习JAVAWEB 的时候,我们学习了过滤器的知识。过滤器的作用是保护请求的服务器资源,在请求资源被执行之前,如果请求地址符合拦截范围,则会先执行过滤器。过滤器的执行时机,是在Servlet之前执行的。但是在使用了SpringMVC后,Servlet只有一个了,也就是DisptcherServlet。那么,如果我们仍然使用过滤器来完成请求的拦截,因为过滤器是在Servlet之前执行的,就会造成,过滤器会拦截DispatcherServlet所有的请求。那么,如果我们有部分请求不想被拦截,怎么办?
拦截器使用
Spring MVC中的拦截器(Interceptor)类似于Servlet中的过滤器(Filter),它主要用于拦截用户请求并作相应的处理。例如通过拦截器可以进行权限验证、记录请求信息的日志、判断用户是否登录等。
要使用Spring MVC中的拦截器,就需要对拦截器类进行定义和配置。通常拦截器类可以通过两种方式来定义。
1.通过实现HandlerInterceptor接口,或继承HandlerInterceptor接口的实现类(如HandlerInterceptorAdapter)来定义。
2.通过实现WebRequestInterceptor接口,或继承WebRequestInterceptor接口的实现类来定义。
拦截器和过滤器的区别
1拦截器SpringMVC的,而过滤器是servlet的。
2拦截器不依赖与servlet容器,由spring容器初始化,过滤器依赖与servlet容器,由servlet容器初始化。
3拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用。
4拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问。
5在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次。
6拦截器可以获取IOC容器中的各个bean,而过滤器就不太方便,这点很重要,在拦截器里注入一个service,可以调用业务逻辑。
定义一个拦截器
1. package com.msb.interceptor;
2.
3. import org.springframework.web.servlet.HandlerInterceptor;
4. import org.springframework.web.servlet.ModelAndView;
5.
6. import javax.servlet.http.HttpServletRequest;
7. import javax.servlet.http.HttpServletResponse;
8.
9. /**
10. * @Author: Ma HaiYang
11. * @Description: MircoMessage:Mark_7001
12. */
13. public class MyInterceptor implements HandlerInterceptor {
14. @Override
15. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
16. /*在请求到达我们定义的handler之前工作的*/
17. System.out.println("MyInterceptor preHandle");
18.
19. /*返回的是true,代表放行,可以继续到达handler*/
20. return true;
21. }
22.
23. @Override
24. public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
25. System.out.println("MyInterceptor postHandle");
26. /*handler 处理单元返回ModelAndView 时候进行 拦截*/
27. }
28.
29. @Override
30. public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
31. /*
32. 页面渲染完毕,但是还没有给浏览器响应数据的时候
33. */
34. System.out.println("MyInterceptor afterCompletion");
35. }
36. }
springmvc.xml中注册拦截器
<!--注册拦截器-->
2. <mvc:interceptors>
3. <mvc:interceptor>
4. <mvc:mapping path="/login.action"/>
5. <bean id="myInterceptor" class="com.msb.interceptor.MyInterceptor"></bean>
6. </mvc:interceptor>
7. </mvc:interceptors>
拦截器内容详解
1、preHandle方法
执行时机
再进入控制单元方法之前执行
如何调用
按拦截器定义顺序调用
具体作用
如果程序员决定该拦截器对请求进行拦截处理后还要调用其他的拦截器,或者是业务处理器去 进行处理,则返回 true。 如果程序员决定不需要再调用其他的组件去处理请求,则返回 false。
参数详解
HttpServletRequest arg0,拦截的请求的request对象
HttpServletResponse arg1, 拦截的请求的response对象
Object arg2 封存了单元方法对象的HandleMethod对象
/**
2. *
3. * @param request 请求对象
4. * @param response 响应对象
5. * @param handler 目标要调用的Handler
6. * @return 返回true放行,返回false拦截
7. * @throws Exception
8. */
9. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
10. /*在请求到达我们定义的handler之前工作的*/
11. System.out.println("MyInterceptor preHandle");
12. /*设置请求和响应的乱码 */
13. /* request.setCharacterEncoding("UTF-8");
14. response.setCharacterEncoding("UTF-8");*/
15. // 判断是否登录
16. /*User user =(User) request.getSession().getAttribute("user");
17. if(null == user)
18. response.sendRedirect("index.jsp");
19. return false;*/
20.
21. // 用户权限控制
22. return true;
23. }
2、postHandle方法
执行时机
在进行数据处理和做出响应之间进行这个方法的调用
如何调用
在拦截器链内所有拦截器返成功调用
具体作用
在业务处理器处理完请求后,但是 DispatcherServlet 向客户端返回响应前被调用,
在该方法中对用户请求 request域数据进行处理。
参数详解
HttpServletRequest arg0, 拦截的请求的request对象
HttpServletResponse arg1, 拦截的请求的response对象
Object arg2, 封存了单元方法对象的HandleMethod对象
ModelAndView arg3 封存了单元方法的返回值资源路径和请求转到的Map数据
1. /**
2. *
3. * @param request
4. * @param response
5. * @param handler
6. * @param modelAndView controller响应的结果,视图和数据
7. * @throws Exception
8. */
9. public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
10. System.out.println("MyInterceptor postHandle");
11. /*控制数据*/
12. /*Map<String, Object> map = modelAndView.getModel();
13. String msg = (String)map.get("msg");
14. String newMsg = msg.replaceAll("脏话", "**");
15. map.put("msg", newMsg);*/
16. /*控制视图*/
17. /*modelAndView.setViewName("/testDemo1.jsp");*/
18. }
19.
3、afterCompletion方法
执行时机
在进行页面渲染的时候执行
如何调用
按拦截器定义逆序调用
具体作用
在DispatcherServlet 完全处理完请求后被调用,可以在该方法中进行一些资源清理的操作。
参数详解
HttpServletRequest arg0, 拦截的请求的request对象
HttpServletResponsearg1, 拦截的请求的response对象
Object arg2, 封存了单元方法对象的HandleMethod对象
Exception arg3 存储了责任链的异常信息
1. /**
2. * 无论controller是否出现异常,都会执行的方法
3. * 一般来说都做一些资源释放工作
4. * @param request
5. * @param response
6. * @param handler
7. * @param ex
8. * @throws Exception
9. */
10. public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
11. /*页面渲染完毕,但是还没有给浏览器响应数据的时候*/
12. System.out.println("MyInterceptor afterCompletion");
13. System.out.println(ex);
14.
15. }
多个拦截器执行顺序
多个拦截器同时存在时,执行的顺序由配置顺序决定. 先配置谁, 谁就先执行.多个拦截器可以理解为拦截器栈, 先进后出(后进先出), 如图所示:
1. <!--注册拦截器-->
2. <mvc:interceptors>
3. <mvc:interceptor>
4. <mvc:mapping path="/login.action"/>
5. <bean id="myInterceptor1" class="com.msb.interceptor.MyInterceptor"></bean>
6. </mvc:interceptor>
7.
8. <mvc:interceptor>
9. <mvc:mapping path="/login.action"/>
10. <bean id="myInterceptor2" class="com.msb.interceptor.MyInterceptor2"></bean>
11. </mvc:interceptor>
12. </mvc:interceptors>
1. MyInterceptor preHandle
2. MyInterceptor2 preHandle
3. login.action
4. MyInterceptor2 postHandle
5. MyInterceptor postHandle
6. success.jsp
7. MyInterceptor2 afterCompletion
8. MyInterceptor afterCompletion
13_SpringMVC_异常处理
SpringMVC异常简介
系统中异常包括两类:预期异常(检查型异常)和运行时异常 RuntimeException,前者通过捕获异常从而获取异常信息, 后者主要通过规范代码开发、测试通过手段减少运行时异常的发生。
系统的 dao、service、controller 出现都通过 throws Exception 向上抛出,最后由 springmvc 前端控制器交由异常处理器进行异常处理,如下图
异常处理具体实现
1使用@ExceptionHandler注解处理异常
缺点:只能处理当前Controller中的异常。
1. @Controller
2. public class ControllerDemo1 {
3. @RequestMapping("test1.action")
4. public String test1(){
5. int i = 1/0;
6. return "success.jsp";
7. }
8. @RequestMapping("test2.action")
9. public String test2(){
10. String s =null;
11. System.out.println(s.length());
12. return "success.jsp";
13. }
14. @ExceptionHandler(value ={ArithmeticException.class,NullPointerException.class} )
15. public ModelAndView handelException(){
16. ModelAndView mv =new ModelAndView();
17. mv.setViewName("error1.jsp");
18. return mv;
19. }
20. }
2使用:@ControllerAdvice+@ExceptionHandler
此处优先级低于局部异常处理器
1. package com.msb.exceptionhandler;
2.
3. import org.springframework.web.bind.annotation.ControllerAdvice;
4. import org.springframework.web.bind.annotation.ExceptionHandler;
5. import org.springframework.web.servlet.ModelAndView;
6.
7. /**
8. * @Author: Ma HaiYang
9. * @Description: MircoMessage:Mark_7001
10. */
11. @ControllerAdvice
12. public class GloableExceptionHandler1 {
13. @ExceptionHandler(value ={ArithmeticException.class,NullPointerException.class} )
14. public ModelAndView handelException(){
15. ModelAndView mv =new ModelAndView();
16. mv.setViewName("error1.jsp");
17. return mv;
18. }
19. }
20.
配置包扫描
1. <context:component-scan base-package="com.msb.service,com.msb.exceptionhandler"/>
3使用:SimpleMappingExceptionResolver
xml配置
1. <!--自定义异常解析器-->
2. <bean id="exceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
3. <property name="exceptionMappings">
4. <props>
5. <prop key="java.lang.ArithmeticException">redirect:/error.jsp</prop>
6. <prop key="java.lang.NullPointerException">redirect:/error2.jsp</prop>
7. </props>
8. </property>
9. </bean>
配置类配置
1. /**
2.
3. * 全局异常
4.
5. */
6.
7. @Configuration
8.
9. public class GloableException2 {
10.
11. @Bean
12.
13. public SimpleMappingExceptionResolver getSimpleMappingExceptionResolver(){
14.
15. SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver();
16.
17. Properties prop = new Properties();
18.
19. prop.put("java.lang.NullPointerException","error1.jsp");
20.
21. prop.put("java.lang.ArithmeticException","error2.jsp");
22.
23. resolver.setExceptionMappings(prop);
24.
25. return resolver;
26.
27. }
28.
29. }
4自定义的HandlerExceptionResolver
1. /**
2.
3. * 全局异常
4.
5. * HandlerExceptionResolve
6.
7. */
8.
9. @Configuration
10.
11. public class GloableException3 implements HandlerExceptionResolver {
12.
13. @Override
14.
15. public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
16.
17. ModelAndView mv = new ModelAndView();
18.
19. if(e instanceof NullPointerException){
20.
21. mv.setViewName("error1");
22.
23. }
24.
25. if(e instanceof ArithmeticException){
26.
27. mv.setViewName("error2");
28.
29. }
30.
31. mv.addObject("msg",e);
32.
33. return mv;
34.
35. }}
14_SpringMVC_其他注解
SpringMVC中注解完善
1、@PostMapping
作用:
指定当前发送请求的方式只可以是post请求
属性:
和@RequestMapping中属性一致
代码实现
@PostMapping("/userControllerA")
public String userControllerA(){
return "forward:/success.jsp";
}
2、@GetMapping
作用:
指定当前发送请求的方式只可以是get请求
属性:
和@RequestMapping中属性一致
代码实现:
@GetMapping("/userControllerA")
public String userControllerA(){
return "forward:/success.jsp";
}
3、@RestController
作用:
书写到类上,代表该类中所有控制单元方法均是ajax响应 相当于@ResponseBody+@Controller
属性:
其中的属性和@Controller中一样
代码实现:
@RestController
public class UserController {
}
4、@JsonFormat
作用:
处理响应json 数据的处理
属性:
pattern :指定响应时间日期的格式
Timezone:指定响应的时区,否则会有8个小时的时间差
代码实现:
@DateTimeFormat(pattern = "yyyy-MM-dd")
2.
3. @JsonFormat(pattern = "yyyy-MM-dd" ,timezone="GMT+8")
4.
5. private Date birth;
5、@RequestBody
作用:
用于获取请求体json格式的字符串内容。直接使用得到是 key=value&key=value...结构的数据,get 请求方式不适用。
属性:
required:是否必须有请求体。默认值是:true。当取值为 true 时,get 请求方式会报错。如果取值 为 false,get 请求得到是null。
实现:
1.
2. $(function () {
3. var jsonObj ={name:"zs",pwd:"123"};
4. var str =JSON.stringify(jsonObj);
5.
6. $.ajax({
7.
8. type:"post",
9. url:"testController",
10. /*data:'{"name":"zs","password":"123"}',*/
11. data:str,
12. contentType:"application/json",
13.
14. })
15.
16. })
1.
2.
3. @RequestMapping("/useRequestBody")
4.
5.
6. public String useRequestBody(@RequestBody(required=false) User user){
7.
8.
9. System.out.println(body);
10.
11.
12. return "msb";
13.
14.
15. }
6、@CrossOrigin
什么是跨域
出于浏览器的同源策略限制。同源策略(SameOriginPolicy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互。所谓同源(即指在同一个域)就是两个页面具有相同的协议(protocol),主机(host)和端口号(port)
http://127.0.0.1:8080/msb/index.jsp基础
https://127.0.0.1:8080/msb/index.jsp 协议不一样
http://192.168.24.11:8080/msb/index.jsp IP不一致
http://127.0.0.1:8888/msb/index.jsp 端口不一致
http://localhost:8080/msb/index.jsp IP不一致
作用:
解决ajax请求之间的跨域问题
属性:
origins : 允许可访问的域列表IP
maxAge:准备响应前的缓存持续的最大时间(以秒为单位)。
代码实现:
1. @CrossOrigin(origins = "http://domain2.com", maxAge = 3600)
2. @RestController
3. @RequestMapping("/account")
4. public class AccountController {
5. @GetMapping("/{id}")
6. public Account receive(@PathVariable Long id) { }
7. }
第32章_SpringBoot
01_SpringBoot_介绍(熟悉)
原有Spring优缺点分析
Spring的优点分析:
Spring是Java企业版(Java Enterprise Edition,JEE,也称J2EE)的轻量级代替品。无需开发重量级的Enterprise JavaBean(EJB),Spring为企业级Java开发提供了一种相对简单的方法,通过依赖注入和面向切面编程,用简单 的Java对象(Plain Old Java Object,POJO)实现了EJB的功能。
Spring的缺点分析
虽然Spring的组件代码是轻量级的,但它的配置却是重量级的。一开始,Spring用XML配置,而且是很多XML配 置。Spring 2.5引入了基于注解的组件扫描,这消除了大量针对应用程序自身组件的显式XML配置。Spring 3.0引入 了基于Java的配置,这是一种类型安全的可重构配置方式,可以代替XML。 所有这些配置都代表了开发时的损耗。因为在思考Spring特性配置和解决业务问题之间需要进行思维切换,所以编 写配置挤占了编写应用程序逻辑的时间。和所有框架一样,Spring实用,但与此同时它要求的回报也不少。 除此之外,项目的依赖管理也是一件耗时耗力的事情。在环境搭建时,需要分析要导入哪些库的坐标,而且还需要 分析导入与之有依赖关系的其他库的坐标,一旦选错了依赖的版本,随之而来的不兼容问题就会严重阻碍项目的开发进度。
SpringBoot概念简介
Spring Boot是Spring公司的一个顶级项目,和Spring Framework是一个级别的。
Spring Boot实际上是利用Spring Framework 4 自动配置特性完成。编写项目时不需要编写xml文件。发展到现在,Spring Boot已经具有很很大的生态圈,各种主流技术已经都提供了Spring Boot的启动器。
什么是启动器
Spring框架在项目中作用是Spring整合各种其他技术,让其他技术使用更加方便。Spring Boot的启动器实际上就是一个依赖。这个依赖中包含了整个这个技术的相关jar包,还包含了这个技术的自动配置,以前绝大多数XML配置都不需要配置了。当然了,启动器中自动配置无法实现所有内容的自动配置,在使用Spring Boot时还需要进行少量的配置(这个配置不是在xml中了,而是在properties或yml中即可)。如果是Spring自己封装的启动器的artifact id名字满足:spring-boot-starter-xxxx,如果是第三方公司提供的启动满足:xxxx-spring-boot-starter。以后每次使用Spring Boot整合其他技术时首先需要考虑导入启动器。
Spring Boot优点
① 使用Spring Boot可以创建独立的Spring应用程序
② 在Spring Boot中直接嵌入了Tomcat、Jetty、Undertow等Web 容器,在使用SpringBoot做Web开发时不需要部署WAR文件
③ 通过提供自己的启动器(Starter)依赖,简化项目构建配置
④ 尽量的自动配置Spring和第三方库
⑤ 绝对没有代码生成,也不需要XML配置文件
<build>
2. <plugins>
3. <!-- tomcat插件,在springboot中已经不需要了 -->
4. <plugin>
5. <groupId>org.apache.tomcat.maven</groupId>
6. <artifactId>tomcat7-maven-plugin</artifactId>
7. <version>2.2</version>
8. <configuration>
9. <port>8080</port>
10. <path>/ssm02</path>
11. <uriEncoding>UTF-8</uriEncoding>
12. </configuration>
13. </plugin>
14. <plugin>
15. <groupId>org.apache.maven.plugins</groupId>
16. <artifactId>maven-compiler-plugin</artifactId>
17. <version>3.1</version>
18. <configuration>
19. <source>1.8</source>
20. <target>1.8</target>
21. <encoding>utf-8</encoding>
22. </configuration>
23. </plugin>
24. </plugins>
25. </build>
Spring Boot版本介绍
SNAPSHOT:快照版,即开发版。
CURRENT:最新版,但是不一定是稳定版。
GA:General Availability,正式发布的版本。
Spring Boot的核心
起步依赖- 起步依赖本质上是一个Maven项目对象模型(Project Object Model,POM),定义了对其他库的传递依赖,这些东西加在一起即支持某项功能。 简单的说,起步依赖就是将具备某种功能的坐标打包到一起,并提供一些默认的功能。
自动配置 -Spring Boot的自动配置是一个运行时(更准确地说,是应用程序启动时)的过程,考虑了众多因素,才决定 Spring配置应该用哪个,不该用哪个。该过程是Spring自动完成的。
02_SpringBoot_项目搭建方式1(掌握)
导入依赖方式1
1. <?xml version="1.0" encoding="UTF-8"?>
2. <project xmlns="http://maven.apache.org/POM/4.0.0"
3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5. <modelVersion>4.0.0</modelVersion>
6.
7.
8. <!--继承父项目方式-->
9. <parent>
10. <groupId>org.springframework.boot</groupId>
11. <artifactId>spring-boot-starter-parent</artifactId>
12. <version>2.4.5</version>
13. </parent>
14.
15. <groupId>com.msb</groupId>
16. <artifactId>springboot01</artifactId>
17. <version>1.0-SNAPSHOT</version>
18.
19. <dependencies>
20. <dependency>
21. <groupId>org.springframework.boot</groupId>
22. <artifactId>spring-boot-starter-web</artifactId>
23. <version>2.4.5</version>
24. </dependency>
25. </dependencies>
26. </project>
依赖传递
开发一个Controller
1. package com.msb.controller;
2.
3. import org.springframework.stereotype.Controller;
4. import org.springframework.web.bind.annotation.RequestMapping;
5. import org.springframework.web.bind.annotation.ResponseBody;
6.
7. /**
8. * @Author: Ma HaiYang
9. * @Description: MircoMessage:Mark_7001
10. */
11. @Controller
12. public class FirstController {
13. @RequestMapping("/firstController")
14. @ResponseBody
15. public String firstController(){
16. return "hello springboot";
17. }
18. }
19.
新建启动类
Spring Boot的启动类的作用是启动Spring Boot项目,是基于Main方法来运行的。
注意:启动类在启动时会做注解扫描(@Controller、@Service、@Repository......),扫描位置为同包或者子包下的注解,所以启动类的位置应放于包的根下。
启动类与启动器区别:
启动类表示项目的启动入口
启动器表示jar包的坐标
必须在包中新建这个类,不能直接放入到java文件夹。
在com.msb下新建自定义名称的类(规范:XXXXApplication),可以是项目上下文路径Application
1. package com.msb;
2.
3. import org.springframework.boot.SpringApplication;
4. import org.springframework.boot.autoconfigure.SpringBootApplication;
5.
6. //启动类
7. //可以自动扫描当前类所在包及子包的注解
8. //注意:此类要放入到包中 在 和controller包同一个层次即可
9.
10. @SpringBootApplication
11. public class Springboot01Application {
12.
13. public static void main(String[] args) {
14. SpringApplication.run(Springboot02Application.class, args);
15. }
16.
17. }
启动项目 日志如下
端口号默认8080
项目上下文路径默认 '' 其实就是没有
访问测试
03_SpringBoot_项目搭建方式2(掌握)
导入依赖方式2
在公司中可能会出现必须继承某个项目,如果Spring Boot用了继承就不能继承别的项目了。所以Spring Boot还提供了依赖的方式。
1. <?xml version="1.0" encoding="UTF-8"?>
2. <project xmlns="http://maven.apache.org/POM/4.0.0"
3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5. <modelVersion>4.0.0</modelVersion>
6.
7. <groupId>com.msb</groupId>
8. <artifactId>springboot01</artifactId>
9. <version>1.0-SNAPSHOT</version>
10.
11. <dependencyManagement>
12. <dependencies>
13. <dependency>
14. <groupId>org.springframework.boot</groupId>
15. <artifactId>spring-boot-dependencies</artifactId>
16. <version>2.4.5</version>
17. <type>pom</type>
18. <scope>import</scope>
19. </dependency>
20. </dependencies>
21. </dependencyManagement>
22.
23.
24. <dependencies>
25. <dependency>
26. <groupId>org.springframework.boot</groupId>
27. <artifactId>spring-boot-starter-web</artifactId>
28. <version>2.4.5</version>
29. </dependency>
30. </dependencies>
31.
32. </project>
使用idea自带springBoot项目初始化插件
开发controller
运行测试,同上
04_SpringBoot_启动原理分析(理解)
依赖导入原理
父项目版本控制
ctrl+ 点击spring-boot-starter-parent 进入
继续点击,进入spring-boot-dependencies
这里管理着springboot中所有依赖的版本,版本控制中心,导入依赖如果不写版本就用这里的版本
spring-boot-starter-web,也就是web启动器,导入和很多web的依赖
springboot 包扫描原理
点击进入@SpringBootApplication注解
1. @Target(ElementType.TYPE)
2. @Retention(RetentionPolicy.RUNTIME)
3. @Documented
4. @Inherited
5. @SpringBootConfiguration
6. @EnableAutoConfiguration
7. @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
8. @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
9. public @interface SpringBootApplication {
@SpringBootConfiguration 上面有一个Configuration表示这是一个配置类
1. @Target(ElementType.TYPE)
2. @Retention(RetentionPolicy.RUNTIME)
3. @Documented
4. @Configuration
5. public @interface SpringBootConfiguration {
配置类上面有@Component说明也是容器中的一个组件
1. @Target({ElementType.TYPE})
2. @Retention(RetentionPolicy.RUNTIME)
3. @Documented
4. @Component
5. public @interface Configuration {
6. @AliasFor(
7. annotation = Component.class
8. )
9. String value() default "";
10.
11. boolean proxyBeanMethods() default true;
12. }
13.
@EnableAutoConfiguration 启用自动配置功能
springboot中没有做任何配置,springboot自动帮助我们配置,但是要通过该注解才能生效
1. @Target(ElementType.TYPE)
2. @Retention(RetentionPolicy.RUNTIME)
3. @Documented
4. @Inherited
5. @AutoConfigurationPackage // 自动扫描配置类包的注解
6. @Import(AutoConfigurationImportSelector.class)// 给容器导入一些组件的选择器,导入一些默认配置
7. public @interface EnableAutoConfiguration {
@AutoConfigurationPackage ,自动配置包
1. @Target(ElementType.TYPE)
2. @Retention(RetentionPolicy.RUNTIME)
3. @Documented
4. @Inherited
5. @Import(AutoConfigurationPackages.Registrar.class)
6. public @interface AutoConfigurationPackage {
7.
@Import(AutoConfigurationPackages.Registrar.class) 导入AutoConfigurationPackages.Registrar类
@Import是spring的 底层注解,给容器导入一个组件
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
2.
3. @Override
4. public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
5. register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
6. }
7.
8. @Override
9. public Set<Object> determineImports(AnnotationMetadata metadata) {
10. return Collections.singleton(new PackageImports(metadata));
11. }
12.
13. }
通过debug 然后再这里右击计算
将配置类@SpringBootApplication标注的类所在包及下面所有子包里的所有组件扫描到spring容器
springboot自动配置原理
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
给容器中导入一些组件的选择器
这些自动配置类给我们当前项目的场景提供了一些组件和配置,有了自动配置就免除类手动编写配置文件,注入等等功能
该jar包提供一系列的配置类,替换掉了我们的XML配置信息
默认加载如下配置文件
05_SpringBoot_项目配置(掌握)
1properties配置文件
SpringBoot默认读取项目下名字为application开头的 yml yaml properties配置文件
在项目下的application.properties里修改端口号和项目上下文路径
注意,这里的每一个. 都代表一个层级
SpringBoot常见配置
查看官网文档
常见配置如下
2yml配置文件
注意,这里的每一个. 都代表一个层级 转换成yml之后,使用缩进代表层级关系
基本格式要求
① 大小写敏感
② 使用缩进代表层级关系
③ 相同的部分只出现一次
④ 注意空格
[1]普通数据类型
1. server:
2. port: 8888
3.
[2]配置对象类型数据
1. person:
2. name: zs
3. age: 12
4. sex: 男
5. #或者写成json格式
6. person2: {name: zs,age: 19 }
[3]配置数组类型
1. city:
2. - beijing
3. - tianjin
4. - shanghai
5. - chongqing
6. #或者
7. city2: [beijing,tianjin,shanghai,chongqing]
如果同一个目录下,有application.yml也有application.properties,默认先读取application.properties。
如果同一个配置属性,在多个配置文件都配置了,默认使用第1个读取到的,后面读取的不覆盖前面读取到的。
配置文件存放位置
① 当前项目根目录中
② 当前项目根目录下的一个/config子目录中
③ 项目的resources即classpath根路径中
④ 项目的resources即classpath根路径下的/config目录中
配置文件存放读取优先级
a当前项目根目录下的一个/config子目录中(最高)
config/application.properties
config/application.yml
b当前项目根目录中(其次)
application.properties
application.yml
c项目的resources即classpath根路径下的/config目录中(一般)
resources/config/application.properties
resources/config/application.yml
d项目的resources即classpath根路径中(最后)
resources/application.properties
resources/application.yml
3bootstrap配置文件
Spring Boot 中有两种上下文对象,一种是 bootstrap, 另外一种是 application(ServletContext), bootstrap 是应用程序的父上下文,也就是说 bootstrap 加载优先于 applicaton。bootstrap 主要用于从额外的资源来加载配置信息,还可以在本地外部配置文件中解密属性。这两个上下文共用一个环境,它是任何Spring应用程序的外部属性的来源。bootstrap 里面的属性会优先加载,它们默认也不能被本地相同配置覆盖。
bootstrap配置文件特征
①boostrap 由父 ApplicationContext 加载,比 applicaton 优先加载。
②boostrap 里面的属性不能被覆盖。
bootstrap与 application 的应用场景
application 配置文件主要用于 Spring Boot 项目的自动化配置。
bootstrap 配置文件有以下几个应用场景。
①使用 SpringCloudConfig 配置中心时,这时需要在 bootstrap 配置文件中添加连接到配置中心的配置属性来加载外部配置中心的配置信息。
②一些固定的不能被覆盖的属性。
③一些加密/解密的场景。
4 SpringBoot项目结构
-- 项目名
--src
--main
--java
java代码
--resources
--public 公共资源。所有共享的内容。对外公开的内容。
--static静态资源。图片、js、css。不会被服务器解析。
--js
-- jquery.js 访问:http://ip:port/js/jquery.js
注意:该目录是SpringBoot可以直接识别的目录,会将其中的
静态资源编译到web项目中,并放到tomcat中使用。静态资源的
访问路径中无需声明static 例如:localhost:8080/a.png
--templates
FreeMarker thymeleaf 页面所在目录。
--webapp 只有当页面使用jsp时才有。
--WEB-INF
设置WEB-INF
06_SpringBoot_整合Mybatis(掌握)
导入依赖
<dependency>
2. <groupId>org.mybatis.spring.boot</groupId>
3. <artifactId>mybatis-spring-boot-starter</artifactId>
4. <version>2.1.3</version>
5. </dependency>
6. <dependency>
7. <groupId>mysql</groupId>
8. <artifactId>mysql-connector-java</artifactId>
9. <version>8.0.21</version>
10. </dependency>
11.
12. <dependency>
13. <groupId>org.projectlombok</groupId>
14. <artifactId>lombok</artifactId>
15. <version>1.18.12</version>
16. <scope>provided</scope>
17. </dependency>
编写配置文件
appliction.yml中添加如下配置
1. spring:
2. datasource:
3. url: jdbc:mysql://127.0.0.1:3306/mydb?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
4. driver-class-name: com.mysql.cj.jdbc.Driver
5. username: root
6. password: root
7. mybatis:
8. mapper-locations: classpath:mybatis/*.xml
9. type-aliases-package: com.msb.pojo
mapper-locations: classpath:mybatis/*.xml mapper映射文件包扫描
type-aliases-package 实体类别名包扫描
编写功能代码
1在启动类上添加注解,表示mapper接口所在位置
1. @SpringBootApplication
2. //@MapperScan("com.msb.mapper")
3. public class MyApplication {
4. public static void main(String[] args) {
5. SpringApplication.run(MyApplication.class,args);
6. }
7. }
8.
9.
2 定义mapper接口
如果不在MyApplication启动类上添加@MapperScan必须在UserMapper接口上添加@Mapper注解。
1. //@Mapper
2. public interface UserMapper {
3. List<User> selectAll();
4. }
5.
3 定义mapper.xml映射文件
在resource下新建mybatis文件夹,mapper.xml文件名没有要求了,不需要和接口名完全对应了,是根据namespace去找接口。但是最好还是和接口名字保持一致
4 controller层代码
1. package com.msb.controller;
2.
3. import com.msb.pojo.User;
4. import com.msb.service.UserService;
5. import org.springframework.beans.factory.annotation.Autowired;
6. import org.springframework.stereotype.Controller;
7. import org.springframework.web.bind.annotation.RequestMapping;
8. import org.springframework.web.bind.annotation.ResponseBody;
9.
10. import java.util.List;
11.
12. /**
13. * @Author: Ma HaiYang
14. * @Description: MircoMessage:Mark_7001
15. */
16. @Controller
17. @RequestMapping("/user")
18. public class UserController {
19.
20. @Autowired
21. private UserService userService;
22.
23. @RequestMapping("/findAll")
24. @ResponseBody
25. public List<User> findAll(){
26. return userService.findAll();
27. }
28.
29. }
30.
5 service层代码
1. package com.msb.service.impl;
2.
3. import com.msb.pojo.User;
4. import com.msb.mapper.UserMapper;
5. import com.msb.service.UserService;
6. import org.springframework.beans.factory.annotation.Autowired;
7. import org.springframework.stereotype.Service;
8.
9. import java.util.List;
10.
11. /**
12. * @Author: Ma HaiYang
13. * @Description: MircoMessage:Mark_7001
14. */
15. @Service
16. public class UserServiceImpl implements UserService {
17.
18. @Autowired
19. private UserMapper userMapper;
20. @Override
21. public List<User> findAll() {
22. return userMapper.findAll();
23. }
24. }
6 idea中往往会误报代码错误,如果我们确定代码无问题,可以通过降低idea检查代码的严格程度来消除报错
快捷键: ctrl+alt+shift+h
07_SpringBoot_整合logback(掌握)
Spring Boot默认使用Logback组件作为日志管理。Logback是由log4j创始人设计的一个开源日志组件。
在Spring Boot项目中我们不需要额外的添加Logback的依赖,因为在spring-boot-starter或者spring-boot-starter-web中已经包含了Logback的依赖。
Logback读取配置文件的步骤
(1)在classpath下查找文件logback-test.xml
(2)如果文件不存在,则查找logback.xml
1. <?xml version="1.0" encoding="UTF-8" ?>
2. <configuration>
3. <!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->
4. <property name="LOG_HOME" value="${catalina.base}/logs/" />
5. <!-- 控制台输出 -->
6. <appender name="Stdout" class="ch.qos.logback.core.ConsoleAppender">
7. <!-- 日志输出格式 -->
8. <layout class="ch.qos.logback.classic.PatternLayout">
9. <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
10. <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
11. </pattern>
12. </layout>
13. </appender>
14. <!-- 按照每天生成日志文件 -->
15. <appender name="RollingFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
16. <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
17. <!--日志文件输出的文件名-->
18. <FileNamePattern>${LOG_HOME}/server.%d{yyyy-MM-dd}.log</FileNamePattern>
19. <MaxHistory>30</MaxHistory>
20. </rollingPolicy>
21. <layout class="ch.qos.logback.classic.PatternLayout">
22. <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
23. <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
24. </pattern>
25. </layout>
26. <!--日志文件最大的大小-->
27. <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
28. <MaxFileSize>10MB</MaxFileSize>
29. </triggeringPolicy>
30. </appender>
31.
32. <!-- 日志输出级别 -->
33. <root level="info">
34. <appender-ref ref="Stdout" />
35. <appender-ref ref="RollingFile" />
36. </root>
37.
38. <logger name="com.msb.mapper" level="DEBUG"></logger>
39.
40.
41.
42. <!--日志异步到数据库 -->
43. <!--<appender name="DB" class="ch.qos.logback.classic.db.DBAppender">
44. 日志异步到数据库
45. <connectionSource class="ch.qos.logback.core.db.DriverManagerConnectionSource">
46. 连接池
47. <dataSource class="com.mchange.v2.c3p0.ComboPooledDataSource">
48. <driverClass>com.mysql.jdbc.Driver</driverClass>
49. <url>jdbc:mysql://127.0.0.1:3306/databaseName</url>
50. <user>root</user>
51. <password>root</password>
52. </dataSource>
53. </connectionSource>
54. </appender> -->
55.
56. </configuration>