Java框架--SSM&Oracle&Maven高级

〇、内容介绍

一、MyBatis01:框架概述、环境搭建及入门案例、自定义框架

1、介绍

  • 框架:封装细节,是开发中的解决方案
  • 三层架构与SSM的关系
    • 表示层web:SpringMVC框架
    • 业务层service:Spring的IOC和AOP
    • 持久层dao:Mybatis
  • 持久层技术
    • JDBC是规范:资源浪费、难以维护
    • JdbcTemplate和DBUtils是工具类
  • Mybatis概述--持久层框架,内部封装JDBC
    • 使用XML或注解配置statement
    • 采用ORM思想实现实体类和数据库的映射---对象关系映射(Object Relational Mapping,简称 ORM)     

2、入门案例(不需要写实现类)

  • 环境搭建--基于xml的配置方式
    • 坐标、实体类、Dao接口(UserDao或UserMapper)
    • 持久层接口映射文件resources/com/itheima/dao/IUserDao.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper 
 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 
 "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 
<mapper namespace="com.itheima.dao.IUserDao"> <!-- 配置查询所有操作 -->
<select id="findAll" resultType="com.itheima.domain.User">   select * from user </select> </mapper>
    • 主配置文件resources/SqlMapConfig.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration 
 PUBLIC "-//mybatis.org//DTD Config 3.0//EN" 
 "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration>
<!-- 配置 mybatis 的环境 --> 
<environments default="mysql"> <!-- 配置 mysql 的环境 -->
<environment id="mysql"> <!-- 配置事务的类型 -->
<transactionManager type="JDBC"></transactionManager> <!-- 配置连接数据库的信息:用的是数据源(连接池) --> <dataSource type="POOLED">
       <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost: 3306/ee50"/> <property name="username" value="root"/> <property name="password" value="1234"/> </dataSource> </environment> </environments> <!-- 告知 mybatis 映射配置的位置 --> <mappers> <mapper resource="com/itheima/dao/IUserDao.xml"/> </mappers> </configuration>       
  • 环境搭建--基于注解的配置方式
    • 移除IUserDao.xml,在Dao方法上使用@Select注解,并指定SQL语句
    • SqlMapConfig.xml中的mapper配置时,使用class属性指定被注解的dao全限定类名
<mappers> 
    <mapper class="com.itheima.dao.IUserDao"/>
</mappers>
  • 测试
package com.itcast.test;
import com.itcast.dao.IUserDao;
import com.itcast.domain.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
/**
 * 入门案例
 */
public class MybatisTest {
    /**
     * 入门案例
     * @param args
     */
    public static void main(String[] args) throws IOException {
        //1.读取配置文件
        InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.创建SqlSessionFactory工厂
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(in);
        //3.使用工厂生产SqlSession对象
        SqlSession session = factory.openSession();
        //4.使用SqlSession创建Dao接口的代理对象
        IUserDao userDao = session.getMapper(IUserDao.class);
        //5.使用代理对象执行方法
        List<User> users = userDao.findAll();
        for (User user : users) {
            System.out.println(user);
        }
        //6.释放资源
        session.close();
        in.close();
    }
}
  • 设计模式分析
    • 构建者Builder模式
      • 通过给施工队钱盖工厂:SqlSessionFactory factory = builder.build(in);
    • 工厂Factory模式
      • 不再通过频繁修改实现类,使用工厂创建即可。
      • SqlSession session = factory.openSession();
    • 代理模式:在不修改源码的基础上对已有方法进行增强
      • 相当于通过注解/配置文件创建了IUserDao的实现类

二、MyBatis02:流程分析、注解、代理dao实现CRUD、参数深入、传统DAO、配置

1、自定义Mybatis的流程分析

2、CRUD操作的实现

public class MyBatisTest {
    private InputStream in;
    private SqlSession sqlSession;
    private IUserDao userDao;

    /**
     * 在测试方法执行之前执行
     * @throws IOException
     */
    @Before
    public void init() throws IOException {
        //1.读取配置文件,生成字节输入流
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.获取SqlSessionFactory
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        //3.获取SQLSession对象
        sqlSession = factory.openSession();
        //4.获取dao的代理对象
        userDao = sqlSession.getMapper(IUserDao.class);
    }
    /**
     * 释放资源
     * 在测试方法之后执行
     */
    @After
    public void destroy() throws IOException {
        //提交事务
        sqlSession.commit();
        sqlSession.close();
        in.close();
    }
}
  • 保存--使用Apache提供的ognl表达式
    • Object Graphic Navigation Language 对象图导航语言
    • 按照一定的语法格式来获取数据,语法格式就是使用 #{对象.对象}的方式,如#{user.username}

    • 省略Bean中的get后的剩余部分
<!-- 保存用户--> 
<insert id="saveUser" parameterType="com.itheima.domain.User"> insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address}) </insert>
    • 需求:保存后返回保存用户的id值
<insert id="saveUser" parameterType="USER">
<!-- 配置保存时获取插入的 id --> 
<selectKey keyColumn="id" keyProperty="id" resultType="int">   select last_insert_id(); </selectKey>   insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address}) </insert>  
  • 查询一个
  • 修改
  • 删除
  • 模糊查询
  <select id="findByName" parameterType="String" resultType="com.itcast.domain.User">
  两种方式,函数内只需要传递字符串即可        
     select * from user where username like '#{username}'; select * from user where username like '%${value}%'; </select>
  • 聚合函数

3、Mybatis的深入

  • 实体类属性和数据库列名不一致
    • 起别名
    • 配置对应关系
    <resultMap id="userMap" type="com.itcast.domain.User">
        <!--主键字段的对应-->
        <id property="id" column="id"></id>
        <!--非主键字段的对应-->
        <result property="username" column="username"></result>
        <!--等-->
    </resultMap>
    <select id="findAll" resultMap="userMap">
        select * from user;
    </select>   
  • 编写DAO实现类
    • 查询
public class UserDaoImpl implements IUserDao {
    private SqlSessionFactory factory;

    public UserDaoImpl(SqlSessionFactory factory) {
        this.factory = factory;
    }

    @Override
    public List<User> findAll() {
        //1.根据factory获取SqlSession对象
        SqlSession session = factory.openSession();
        //2.调用SQLSession中的方法实现查询列表
        List<User> users = session.selectList("com.itcast.dao.IUserDao.findAll");//参数就是能获取配置信息的key
        //3.释放资源
        session.close();
        return users;
    }
    • 保存
    @Override
    public void saveUser(User user) {
        //1.根据factory获取SqlSession对象
        SqlSession session = factory.openSession();
        //2.调用SQLSession中的方法实现查询列表
        session.insert("com.itcast.dao.IUserDao.saveUser",user);
        //3.提交事务
        session.commit();
        //4.关闭
        session.close();
    }

4、标签的使用

  • properties标签
<properties resource="jdbcConfig.properties">
        <!--<property name="driver" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/eesy"></property>
        <property name="username" value="root"></property>
        <property name="password" value="root"></property>-->
    </properties>
    <!--配置环境-->
    <environments default="mysql">
        <!--配置mysql的环境-->
        <environment id="mysql">
            <!--配置事务-->
            <transactionManager type="JDBC"></transactionManager>
            <!--配置连接池-->
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"></property>
                <property name="url" value="${jdbc.url}"></property>
                <property name="username" value="${jdbc.username}"></property>
                <property name="password" value="${jdbc.password}"></property>
            </dataSource>
        </environment>
  • typeAliases标签和package标签
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <properties resource="jdbcConfig.properties">
        <!--<property name="driver" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/eesy"></property>
        <property name="username" value="root"></property>
        <property name="password" value="root"></property>-->
    </properties>
    <!--使用typeAliases配置别名,只能配置domain中类的别名-->
    <typeAliases>
        <!--typeAliases配置别名,type指定全限定类名,alias指定别名,指定别名后不再区分大小写,全局配置,在IUserDao.xml中使用-->
     <typeAlias type="com.itcast.domain.User" alias="user"></typeAlias> <!--package用于指定要配置别名的包,当指定后,该报下的实体类都会注册别名,并且类名就是别名,不再区分大小写--> <package name="com.itcast.domain.User"></package> </typeAliases> <mappers> <mapper resource="com/itcast/dao/IUserDao.xml"></mapper> <!--用于指定dao接口所在的包,当指定完成之后就不需要再写mapper、resource或class了--> <package name="com.itcast.dao"/> </mappers> </configuration>  

三、MyBatis03:连接池及事务控制、xml动态SQL语句、多表操作

1、Mybatis连接池和事务控制

  • 连接池:存储连接的容器,该容器是线程安全(两个线程不能拿到同一个连接)的,实现了队列的先进先出
    • 模拟
while (conn == null) { //连接为空时创建
      synchronized (state) {
        if (!state.idleConnections.isEmpty()) {
          // Pool has available connection
          conn = state.idleConnections.remove(0);
    }
}
  • 连接池的分类:SqlMapConfig.xml的dataSource标签中的type属性表示采用何种连接池方式
    • POOLED:采用DataSource规范的连接池--从连接池中获取一个连接
    • UNPOOLED:实现了DataSource规范,但没有使用连接池。每次都会获取一个新的连接。--注册驱动,获取连接
    • JNDI:由服务器提供的DataSource实现对象
      • 只有web工程或基于maven的war工程可以使用
      • tomcat服务器采用的JNDI连接池为DHCP连接池
  • 事务控制:通过sqlSession对象的commit方法和rollback方法实现
    • 概念、ACID特性、会产生的问题、四种隔离级别
    • 设置事务的自动提交
    @Override
    public void saveUser(User user) {
        //1.根据factory获取SqlSession对象
        SqlSession session = factory.openSession(true);
        //2.调用SQLSession中的方法实现查询列表
        session.insert("com.itcast.dao.IUserDao.saveUser",user);
        //3.提交事务
        //session.commit();
        //4.关闭
        session.close();
    }  

2、动态SQL语句/映射文件的SQL深入

  • if标签
    <!--根据条件查询-->
    <select id="findUserByCondition" resultType="com.itcast.domain.User" parameterType="user">
        select * from user where 1=1
        <if test="username != null">
            and username = #{username}
        </if>
    </select> 
  • where标签
   <!--根据条件查询-->
    <select id="findUserByCondition" resultType="com.itcast.domain.User" parameterType="user">
        select * from user
    <where>
        <if test="username != null">
            and username = #{username}
        </if>
        <if test="sex != null">
            and sex = #{sex}
        </if>
    </where>
    </select>   
  • foreach
    <!--根据queryvo中的id集合实现查询用户列表-->
    <select id="findByUserInIds" resultType="com.itcast.domain.User">
        select * from user where id
    <where>
        <if test="ids != null and ids.size()>0">
            <foreach collection="ids" open="id in(" close=")" item="id" separator=",">
                #{id}
            </foreach>
        </if>
    </where>
    </select>
  • SQL标签抽取代码片段
<sql id="defaultUser">
        select * from user
    </sql>
    <select id="findAll" resultType="com.itcast.domain.User" >
        <include refid="defaultUser"></include>
    </select>

3、Mybatis的多表操作

  • 一对一操作(建立实体类关系)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itcast.dao.IAccountDao">
    <!--定义封装account和user的resultMap-->
    <resultMap id="accountUserMap" type="account">
        <id property="id" column="aid"></id>
        <result property="uid" column="uid"></result>
        <result property="money" column="money"></result>
        <!--应当建立一对一的关系映射:配置封装user的内容-->
        <association property="user" column="uid" javaType="user">
            <id property="id" column="id"></id>
            <result property="username" column="username"></result>
            <result property="address" column="address"></result>
            <result property="sex" column="sex"></result>
            <result property="birthday" column="birthday"></result>
        </association>
    </resultMap>
    <select id="findAll" resultMap="accountUserMap" >
        SELECT u.*,a.id AS aid,a.money,a.uid FROM account a, USER u WHERE a.uid = u.id
    </select>
    <!--查询所有账户同时包含用户名和地址信息-->
    <select id="findAllAccount" resultType="accountuser" >
        SELECT a.*,u.username,u.address FROM account a, USER u WHERE a.uid = u.id
    </select>
</mapper>
  • 一对多操作
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.itcast.dao.IUserDao">
    <!--定义user的resultMap-->
    <resultMap id="userAccountMap" type="user">
        <id property="id" column="id"></id>
        <result property="username" column="username"></result>
        <result property="address" column="address"></result>
        <result property="sex" column="sex"></result>
        <result property="birthday" column="birthday"></result>
        <!--配置user对象中account集合的映射-->
        <collection property="accounts" ofType="account">
            <id column="aid" property="id"></id>
            <result column="uid" property="uid"></result>
            <result column="money" property="money"></result>
        </collection>
    </resultMap>
    <select id="findAll" resultMap="userAccountMap" >
        SELECT * FROM USER u LEFT OUTER JOIN account a ON u.id = a.UID
    </select>
</mapper>
  • 多对多操作
    • 查询角色下的用户
<mapper namespace="com.itcast.dao.IRoleDao">
    <!--定义role表的resultMap-->
    <resultMap id="roleMap" type="role">
        <id property="roleId" column="id"></id>
        <!--windows不区分大小写,linux区分大小写-->
        <result property="roleName" column="role_name"></result>
        <result property="roleDesc" column="role_desc"></result>
        <collection property="users" ofType="user">
            <id column="id" property="id"></id>
            <result column="username" property="username"></result>
            <result column="address" property="address"></result>
            <result column="sex" property="sex"></result>
            <result column="birthday" property="birthday"></result>
        </collection>
    </resultMap>
    <select id="findAll" resultMap="roleMap">
        SELECT u.*,r.`ID` rid,r.`ROLE_NAME`,r.`ROLE_DESC` FROM role r
              LEFT OUTER JOIN user_role ur ON r.id = ur.`RID`
              LEFT OUTER JOIN USER u ON u.id = ur.`UID`
    </select>
</mapper>
    • 查询用户下的角色
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.itcast.dao.IUserDao">
    <!--定义user的resultMap-->
    <resultMap id="userAccountMap" type="user">
        <id property="id" column="id"></id>
        <result property="username" column="username"></result>
        <result property="address" column="address"></result>
        <result property="sex" column="sex"></result>
        <result property="birthday" column="birthday"></result>
        <!--配置角色集合的映射-->
        <collection property="roles" ofType="role">
            <id property="roleId" column="rid"></id>
            <result property="roleName" column="role_name"></result>
            <result property="roleDesc" column="role_desc"></result>
        </collection>
    </resultMap>
    <select id="findAll" resultMap="userMap" >
        SELECT u.*,r.ID rid,r.ROLE_NAME,r.ROLE_DESC FROM USER u
               LEFT OUTER JOIN user_role ur ON u.id = ur.UID
               LEFT OUTER JOIN role r ON r.id = ur.RID
    </select>
</mapper>    

4、JNDI介绍(连接池类型,目的是模仿注册表)

  • 概念:JNDI(Java Naming and Directory Interface,Java命名和目录接口),标准的Java命名系统接口。
  • 创建context.xml配置文件

<?xml version="1.0" encoding="UTF-8"?>
<Context>
<!--
<Resource
name="jdbc/eesy_mybatis"                  数据源的名称
type="javax.sql.DataSource"                   数据源类型
auth="Container"                        数据源提供者
maxActive="20"                         最大活动数
maxWait="10000"                            最大等待时间
maxIdle="5"                               最大空闲数
username="root"                            用户名
password="1234"                            密码
driverClassName="com.mysql.jdbc.Driver"          驱动类
url="jdbc:mysql://localhost:3306/eesy_mybatis" 连接url字符串
/>
 -->
</Context>
  • 修改主配置文件

四、Mybatis04:延迟加载、一二级缓存、注解开发

1、延迟加载

  • 背景
    • 查询用户的账户:什么时候用到什么时候查(一对多,多对多:延迟加载/懒加载)
    • 查询账户关联的用户:随账户一起查询出来(多对一,一对一:立即加载)
  • 一对一--association
  • 一对多--collection
<configuration>
    <properties resource="jdbcConfig.properties">
    </properties>
    <settings>
        <!--开启Mybatis支持延迟加载-->
        <setting name="lazyLoadingEnabled" value="true"/>
        <!--Mybatis每个属性都会按需加载-->
        <setting name="aggressiveLazyLoading" value="false"/>
    </settings>
</configuration>

2、Mybatis的一二级缓存

  • 概念:
    • 存放在内存中的临时数据,减少与DB交互,提高效率
    • 适用于:不常改变且经常查询,数据正确与否对结果影响不大
  • 一级缓存:存放在SqlSession对象中的缓存
  • 触发清空一级缓存的情况:一级缓存是 SqlSession 范围的缓存,当调用 SqlSession 的修改,添加,删除,commit(),close()等方法时,就会清空一级缓存。
/**
     * session关闭,缓存消失
     */
    @Test
    public void testFirstLevelCache(){
     sqlSession = factory.openSession();
        userDao = sqlSession.getMapper(IUserDao.class);
    User user1 = userDao.findById(41);   
     System.out.println(user1);
    //关闭session sqlSession.close(); 
    //SQLSession清空缓存的方法,也可以清空缓存,执行两次查询
    sqlSession.clearCache();
    //userDao.updateUser(user1);
    User user2 = userDao.findById(41);
    System.out.println(user2);
    System.out.println(user1==user2);
}
      • 二级缓存:SqlSessionFactory对象的缓存(存放数据,而非对象)
        • 步骤:
          • 框架支持:SqlMapConfig.xml
<configuration>
    <properties resource="jdbcConfig.properties">
    </properties>
    <settings>
        <setting name="cacheEnabled" value="true"/>
        <!--默认为true-->
    </settings>
    • 映射文件支持:IUserDao.xml中配置
    • 当前操作支持:select标签中配置
<mapper namespace="com.itcast.dao.IUserDao">
    <!--开启user支持二级缓存-->
    <select id="findAll" resultType="user" useCache="true">
        SELECT * FROM USER
    </select>

存放的是数据,而不是对象,每次都会创建新的用户对象

    /**
     * session关闭,缓存消失
     */
    @Test
    public void testFirstLevelCache(){
        SqlSession sqlSession1 = factory.openSession();
        IUserDao dao1 = sqlSession1.getMapper(IUserDao.class);
        User user1 = dao1.findById(41);
        System.out.println(user1);
        sqlSession1.close();//一级缓存消失
        SqlSession sqlSession2 = factory.openSession();
        IUserDao dao2 = sqlSession2.getMapper(IUserDao.class);
        User user2 = dao2.findById(41);
        System.out.println(user2);
        sqlSession2.close();
        System.out.println(user1==user2);
    }
//结果不可以

3、注解开发

  • 注解开发--主配置
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!-- 引入外部配置文件 -->
    <properties resource="jdbcConfig.properties"></properties>
    <!-- 配置别名 -->
    <typeAliases>
        <package name="com.itheima.domain"></package>
    </typeAliases>
    <!-- 配置环境 -->
    <environments default="mysql">
        <environment id="mysql">
            <transactionManager type="jdbc"></transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>
    <!-- 指定带有注解的dao接口所在位置 -->
    <mappers>
        <package name="com.itheima.dao"></package>
    </mappers>
</configuration>
  • 注解开发--注解类
package com.itheima.dao;
import com.itheima.domain.User;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
 * 在Mybatis中,针对CRUD共有4个注解
 * @Select @Insert @Update @Delete
 */
public interface IUserDao {
    /**
     * 查询所有用户
     * @return
     */
    @Select("select * from user")
    List<User> findAll();
    /**
     * 删除用户
     * @param userId
     */
    @Delete("delete from user where id = #{id}")
    void deleteUser(Integer userId);

    /**
     * 根据id查询用户
     * @param userId
     * @return
     */
    @Select("select * from user where id = #{id}")
    User findById(Integer userId);
    /**
     * 根据用户名称模糊查询
     * @param username
     * @return
     */
    //@Select("select * from user where username like #{username}")---字符串拼接
    //模糊查询的另外一种写法,固定属性名称value---参数占位符
    @Select("select * from user where username like '%${value}%'")
    List<User> findByName(String username);
    /**
     * 查询总用户数量
     * @return
     */
    @Select("select count(*) from user")
    int findTotal();
}
  • 注解开发--测试
package com.itheima.test;
import com.itheima.dao.IUserDao;
import com.itheima.domain.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class AnnotationCRUDTest {
    private InputStream in;
    private SqlSession sqlSession;
    private IUserDao userDao;
    private SqlSessionFactory factory;
    /**
     * 在测试方法执行之前执行
     * @throws IOException
     */
    @Before
    public void init() throws IOException {
        //1.读取配置文件,生成字节输入流
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.获取SqlSessionFactory
        factory = new SqlSessionFactoryBuilder().build(in);
        //3.使用工厂对象,创建dao对象
        //userDao = new UserDaoImpl(factory);
        sqlSession = factory.openSession();
        //4.获取dao的代理对象
        userDao = sqlSession.getMapper(IUserDao.class);
    }
    /**
     * 释放资源
     * 在测试方法之后执行
     */
    @After
    public void destroy() throws IOException {
        //提交事务
        sqlSession.commit();
        sqlSession.close();
        in.close();
    }
    @Test
    public void testDelete(){
        /*User user = new User();
        user.setId(52);*/
        userDao.deleteUser(52);
    }
    @Test
    public void testFindOne(){
        User user = userDao.findById(53);
        System.out.println(user);
    }
    @Test
    public void testFindByName(){
        //List<User> users = userDao.findByName("%王%");
        List<User> users = userDao.findByName("王");
        for (User user : users) {
            System.out.println(user);
        }
    }
    @Test
    public void testFindTotal(){
        int total = userDao.findTotal();
        System.out.println(total);
    }
}
  • 注解开发--建立实体类属性和数据库表中列的对应关系
package com.itheima.dao;
import com.itheima.domain.User;
import org.apache.ibatis.annotations.*;
import java.util.List;
/**
 * 在Mybatis中,针对CRUD共有4个注解
 * @Select @Insert @Update @Delete
 */
public interface IUserDao {
    /**
     * 查询所有用户
     * @return
     */
    @Select("select * from user")
    @Results(id="userMap",value={
            @Result(id=true,column = "id",property = "userId"),
            @Result(column = "username",property = "userName"),
            @Result(column = "address",property = "userAddress"),
            @Result(column = "sex",property = "userSex"),
            @Result(column = "birthday",property = "userBirthday"),
    })
    List<User> findAll();
}
  • 注解开发--一对一的查询配置(多表查询)
package com.itheima.dao;

import com.itheima.domain.Account;
import org.apache.ibatis.annotations.One;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.mapping.FetchType;

import java.util.List;

public interface IAccountDao {
    /**
     * 查询所有账户,并且获取每个账户的用户信息
     * @return
     */
    @Select("select * from account")
    @Results(id = "accountMap",value = {
            @Result(id = true,column = "id",property = "id"),
            @Result(column = "uid",property = "uid"),
            @Result(column = "money",property = "money"),
            @Result(property = "user",column = "uid",one=@One(select="com.itheima.dao.IUserDao.findById",fetchType= FetchType.EAGER))
    })
    List<Account> findAll();
}
  • 注解开发--一对多的查询配置
public interface IUserDao {
    /**
     * 查询所有用户
     * @return
     */
    @Select("select * from user")
    @Results(id="userMap",value={
            @Result(id=true,column = "id",property = "userId"),
            @Result(column = "username",property = "userName"),
            @Result(column = "address",property = "userAddress"),
            @Result(column = "sex",property = "userSex"),
            @Result(column = "birthday",property = "userBirthday"),
            @Result(property = "accounts",column = "id",
                    many=@Many(select = "com.itheima.dao.IAccountDao.findAccountByUid",
                            fetchType = FetchType.LAZY))
    })
    List<User> findAll();
  • 注解开发--使用二级缓存
<configuration>
    <!-- 引入外部配置文件 -->
    <properties resource="jdbcConfig.properties"></properties>
    <!--全局配置:开启二级缓存(默认开启)-->
    <settings>
        <setting name="cacheEnabled" value="true"/>
    </settingspackage com.itheima.test;
import com.itheima.dao.IUserDao;
import com.itheima.domain.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class AnnotationCRUDTest {
    private InputStream in;
    private SqlSession sqlSession;
    private IUserDao userDao;
    private SqlSessionFactory factory;

    /**
     * 在测试方法执行之前执行
     * @throws IOException
     */
    @Before
    public void init() throws IOException {
        //1.读取配置文件,生成字节输入流
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.获取SqlSessionFactory
        factory = new SqlSessionFactoryBuilder().build(in);
        //3.使用工厂对象,创建dao对象
        //userDao = new UserDaoImpl(factory);
        sqlSession = factory.openSession();
        //4.获取dao的代理对象
        userDao = sqlSession.getMapper(IUserDao.class);
    }

    /**
     * 释放资源
     * 在测试方法之后执行
     */
    @After
    public void destroy() throws IOException {
        //提交事务
        sqlSession.commit();
        sqlSession.close();
        in.close();
    }
    @Test
    public void testFindAll(){
        List<User> users = userDao.findAll();
        for (User user : users) {
            System.out.println("---每个用户的信息:---");
            System.out.println(user);
            //实现了延迟加载,没有立即查询account
            //System.out.println(user.getAccounts());

        }
    }
}
@CacheNamespace(blocking = true)
public interface IUserDao {

五、Spring01:概述、工厂模式解耦、Spring中的IOC

1、概述(表示层SpringMVC、业务层√、持久层Mybatis)

  • 内核:IoC(Inverse Of Control:反转控制)和 AOP(Aspect Oriented Programming
  • 需要核心容器IOC的支持

  

2、程序的耦合与解耦

  • 解耦:编译器不依赖,运行时才依赖
  • 步骤:反射创建对象,读取配置文件获取全限定类名
  • Bean工厂
package com.itheima.factory;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
public class BeanFactory {
    //定义一个Properties对象
    private static Properties props;
    //使用静态代码块为Properties对象赋值
    static{
        try {
            //实例化对象
            props = new Properties();
            //获取properties的流对象
            InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("Bean.properties");
            props.load(in);
        } catch (IOException e) {
            throw new ExceptionInInitializerError("初始化properties失败");
        }
    }
    /**
     * 根据Bean的名称获取bean对象
     * @param beanName
     * @return
     */
    public static Object getBean(String beanName){
        Object bean = null;
        try {
            String beanPath = props.getProperty(beanName);
            System.out.println(beanPath);
            //反射
            bean = Class.forName(beanPath).newInstance();
        }catch (Exception e) {
            e.printStackTrace();
        }
        return bean;
    }
}
  • 业务层
package com.itheima.service.impl;
import com.itheima.dao.IAccountDao;
import com.itheima.dao.impl.AccountDaoImpl;
import com.itheima.factory.BeanFactory;
import com.itheima.service.IAccountService;
/**
 * 账户的业务层实现类
 */
public class AccountServiceImpl implements IAccountService {
    //业务层调用持久层
    //避免写new
    private IAccountDao accountDao = (IAccountDao)BeanFactory.getBean("accountDao");
    @Override
    public void saveAccount() {
        accountDao.saveAccount();
    }
}
  • 工厂模式解耦,单例升级版
package com.itheima.factory;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
public class BeanFactory {
    //定义一个Properties对象
    private static Properties props;
    //定义一个map,用于存放创建的对象,我们将其称之为容器
    private static Map<String,Object> beans;
    //使用静态代码块为Properties对象赋值
    static{
        try {
            //实例化对象
            props = new Properties();
            //获取properties的流对象
            InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("Bean.properties");
            props.load(in);
            //实例化容器
            beans = new HashMap<>();
            //取出配置文件中所有的key
            Enumeration keys = props.keys();
            //遍历枚举
            while(keys.hasMoreElements()){
                //取出每个key
                String key = keys.nextElement().toString();
                //根据key获取value
                String beanPath = props.getProperty(key);
                //反射创建对象
                Object value = Class.forName(beanPath).newInstance();
                //把key和value存入容器之中
                beans.put(key,value);
            }
        } catch (Exception e) {
            throw new ExceptionInInitializerError("初始化properties失败");
        }
    }
    /**
     * 根据Bean的名称获取bean对象
     * @param beanName
     * @return
     */
    public static Object getBean(String beanName){
       return beans.get(beanName);
    }
}
  • 优化版表现层调用业务层
package com.itheima.ui;
import com.itheima.dao.IAccountDao;
import com.itheima.factory.BeanFactory;
import com.itheima.service.IAccountService;
import com.itheima.service.impl.AccountServiceImpl;

/**
 * 模拟一个表现层,用于调用业务层
 */
public class Client {
    private IAccountDao accountDao = (IAccountDao)BeanFactory.getBean("accountDao");
    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            IAccountService as = (IAccountService)BeanFactory.getBean("accountService");
            System.out.println(as);
            as.saveAccount();
        }
    }
}
  • 优化版业务层实现类
package com.itheima.service.impl;

import com.itheima.dao.IAccountDao;
import com.itheima.dao.impl.AccountDaoImpl;
import com.itheima.factory.BeanFactory;
import com.itheima.service.IAccountService;

/**
 * 账户的业务层实现类
 */
public class AccountServiceImpl implements IAccountService {
    //业务层调用持久层
    //避免写new
    //private IAccountDao accountDao = new AccountDaoImpl();
    private IAccountDao accountDao;
    //private int i = 1;
    public void saveAccount() {
        accountDao = (IAccountDao)BeanFactory.getBean("accountDao");
        int i = 1;
        //如果想每次调用得到的是新值,则需要定义到方法内部
        accountDao.saveAccount();
        System.out.println(i);
        i++;
    }
}

3、IOC的概念和Spring的IOC

  • 创建对象:主动new、IOC被动接受
  • 基于XML的IOC--配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--把对象的创建交给Spring管理-->
    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"></bean>
    <bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl"></bean>
</beans>
  • 基于XML的IOC--获取核心容器并创建对象
package com.itheima.ui;
import com.itheima.dao.IAccountDao;
import com.itheima.service.IAccountService;
import com.itheima.service.impl.AccountServiceImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
 *
 */
public class Client {
    /**
     * 获取Spring的IOC核心容器,并根据id获取对象
     * @param args
     */
    public static void main(String[] args) {
        //1.获取核心容器对象
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //2.根据id获取bean对象---两种方式
        IAccountService as = (IAccountService) ac.getBean("accountService");
        IAccountDao adao = ac.getBean("accountDao",IAccountDao.class);
        System.out.println(as);
        System.out.println(adao);
    }
}
  • 核心容器对象ApplicationContext的三个实现类
    • ClassPathXmlApplicationContext:类路径下
    • FileSystemApplicationContext:磁盘文件
    • AnnotationConfigApplicationContext:注解创建
  • BeanFactory和ApplicationContext的区别
    • BeanFactory:延迟加载,适用于多例对象,bean.properties
    • ApplicationContext:立即加载适用于单例对象,bean.xml 
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--把对象的创建交给Spring管理-->
    <!--
    Spring对Bean的管理细节
        1、创建Bean的三种方式
        2、Bean对象的作用范围
        3、Bean对象的生命周期
        -->
    <!--创建Bean的三种方式-->
    <!--第一种方式:使用默认构造函数创建
        在Spring的配置文件中使用bean标签 ,配以id和class属性后,且没有其他属性和标签时
        采用的就是默认构造函数创建Bean对象,此时如果没有构造函数,则对象无法创建
        -->
    <!--<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"></bean>-->
    <!--jar中只有class文件,获取有些对象的返回值,则需要采用第二种或第三种创建对象-->
    <!--第二种方式:使用普通工厂中的方法创建对象(使用类中的方法创建对象,并存入Spring容器)-->
    <!--<bean id="instanceFactory" class="com.itheima.factory.InstancsFactory"></bean>
    <bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService"></bean>-->

    <!--第三种方式:使用静态工厂中的静态方法创建对象,并存入Spring容器-->
<!--
    <bean id="accountService" class="com.itheima.factory.StaticFactory" factory-method="getAccountService"></bean>
-->
    <!--bean的作用范围调整
        默认是单例
        通过bean标签的scope属性,调整bean的作用范围
        取值:(单例和多例最常用)
            singleton:单例的(默认值)
            prototype:多例的
            request:作用于web应用的请求范围
            session:作用于web应用的会话范围
            global-session:作用于集群环境的全局会话范围,当不是集群环境时,就是session
    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl" scope="prototype"></bean>
    -->
    <!--bean对象的生命周期
        区分单例对象/多例对象
        单例对象
            出生:当容器创建时,对象出生
            存活:只要容器还在,对象就一直活着
            死亡:容器销毁,对象消亡
            总结:单例对象的生命周期和容器相同
        多例对象
            出生:当使用对象时,Spring框架为我们创建
            存活:对象在使用过程中一直存活
            死亡:当对象长时间不用且没有其他对象引用时,由Java的垃圾回收期回收
    -->
    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl" scope="prototype"
    init-method="init" destroy-method="destroy"></bean>

</beans>

4、依赖注入:Dependency Injection

  • 概念:Spring通过配置文件对依赖关系的维护,就称为依赖注入,适用于不常变化的情况,目的是降低类之间的依赖关系
  • 注入方式:构造函数、set方法、注解
  • 构造函数注入
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--构造函数注入
        使用的标签:constructure-arg
        标签出现的位置:bean标签的内部
        标签中的属性:
            type:指定要注入数据的数据类型,该数据类型也是构造函数中某个或某些参数的类型
            index:用于指定要注入的数据给构造函数中指定索引位置的参数赋值。参数索引的位置从0开始
            name:用于指定给构造函数中指定名称的参数赋值※常用的是名称
            ===================以上三个用于指定给构造函数中的哪个参数赋值=====================
            value:用于提供基本类型和String类型的数据
            ref:引用关联的bean对象,指定其他的bean类型数据,指的是在Spring的IOC容器中出现过的bean对象

        优势:在获取bean对象时,注入数据是必须操作,否则对象无法创建成功【不需要getset方法】
        弊端:改变了bean对象的实例化方式,使我们在创建对象时,如果用不到这些数据,也必须提供
    -->
    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
        <constructor-arg name="name" value="字符串"></constructor-arg>
        <constructor-arg name="age" value="18"></constructor-arg>
        <constructor-arg name="birthday" ref="now"></constructor-arg>
    </bean>
    <!--配置一个日期对象-->
    <bean id="now" class="java.util.Date"></bean>
</beans>
  • set方法注入
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--配置一个日期对象-->
    <bean id="now" class="java.util.Date"></bean>
    <!--set方法注入※更常用
        涉及的标签:property
        出现的位置:bean标签的内部
        标签的属性:
            name:指定注入时所调用的set方法名称,关心set方法去掉set和大写
            ===================以上三个用于指定给构造函数中的哪个参数赋值=====================
            value:用于提供基本类型和String类型的数据
            ref:引用关联的bean对象,指定其他的bean类型数据,指的是在Spring的IOC容器中出现过的bean对象
        优势:
            创建对象时没有明确的限制,可以直接使用默认构造函数
        弊端:
            如果有某个成员必须有值,则获取对象时,有可能set方法没有执行
            即调用了AccountServiceImpl2构造,对象用完,set无法执行
    -->
    <bean id="accountService2" class="com.itheima.service.impl.AccountServiceImpl2">
        <property name="username" value="test"></property>
        <property name="age" value="21"></property>
        <property name="birthday" ref="now"></property>
     </bean>
</beans>
  • 集合类型的注入
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!-- Spring中的依赖注入
        依赖注入:Dependency Injection
        IOC的作用:
            降低/削减程序间的耦合程度(依赖关系)
        依赖关系的管理
            以后都交给了Spring维护
        在当前类中需要用到其他类的对象,由Spring为我们提供,我们只需要在配置文件中说明
        依赖关系的维护就称之为“依赖注入”
        依赖注入:
            能注入的数据由三类:
                基本类型和String
                其他bean类型(在配置文件中或者注解配置的bean)
                复杂类型/集合类型
            注入的方式有三种:
                第一种:使用构造函数提供
                第二种:使用set方法提供
                第三种:使用注解提供(明天的内容)
     -->
    <!--构造函数注入
        使用的标签:constructure-arg
        标签出现的位置:bean标签的内部
        标签中的属性:
            type:指定要注入数据的数据类型,该数据类型也是构造函数中某个或某些参数的类型
            index:用于指定要注入的数据给构造函数中指定索引位置的参数赋值。参数索引的位置从0开始
            name:用于指定给构造函数中指定名称的参数赋值※常用的是名称
            ===================以上三个用于指定给构造函数中的哪个参数赋值=====================
            value:用于提供基本类型和String类型的数据
            ref:引用关联的bean对象,指定其他的bean类型数据,指的是在Spring的IOC容器中出现过的bean对象

        优势:在获取bean对象时,注入数据是必须操作,否则对象无法创建成功【不需要getset方法】
        弊端:改变了bean对象的实例化方式,使我们在创建对象时,如果用不到这些数据,也必须提供
    -->
    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
        <constructor-arg name="name" value="字符串"></constructor-arg>
        <constructor-arg name="age" value="18"></constructor-arg>
        <constructor-arg name="birthday" ref="now"></constructor-arg>
    </bean>

    <!--复杂类型(集合类型)的注入(两大类)
        用于给list结构集合注入的标签:list array set
        用于给map结构集合注入的标签:map prop
        结构相同,标签可以互换
    -->
    <bean id="accountService3" class="com.itheima.service.impl.AccountServiceImpl3">
        <property name="myStrs">
            <array>
                <value>aaa</value>
                <value>bbb</value>
                <value>ccc</value>
            </array>
        </property>
        <property name="myList">
            <list>
                <value>aaa</value>
                <value>bbb</value>
                <value>ccc</value>
            </list>
        </property>
        <property name="mySet">
            <list>
                <value>aaa</value>
                <value>bbb</value>
                <value>ccc</value>
            </list>
        </property>
        <property name="myMap">
            <map>
                <entry key="testA" value="aaa"></entry>
                <entry key="testB">
                    <value>BBB</value>
                </entry>
            </map>
        </property>
        <property name="myProp">
            <props>
                <prop key="testc">cccc</prop>
                <prop key="testd">ddd</prop>
            </props>
        </property>
    </bean>
</beans>
  • 测试
package com.itheima.service.impl;
import com.itheima.service.IAccountService;

import java.util.*;

/**
 * 账户的业务层实现类
 */
public class AccountServiceImpl3 implements IAccountService {
    //如果是经常变化的数据,并不适用于注入的方式
    private String[] myStrs;
    private List<String> myList;
    private Set<String> mySet;
    private Map<String,String> myMap;
    private Properties myProp;

    public void setMyStrs(String[] myStrs) {
        this.myStrs = myStrs;
    }

    public void setMyList(List<String> myList) {
        this.myList = myList;
    }

    public void setMySet(Set<String> mySet) {
        this.mySet = mySet;
    }

    public void setMyMap(Map<String, String> myMap) {
        this.myMap = myMap;
    }

    public void setMyProp(Properties myProp) {
        this.myProp = myProp;
    }

    public void saveAccount() {
        System.out.println(Arrays.toString(myStrs));
        System.out.println(myList);
        System.out.println(mySet);
        System.out.println(myMap);
        System.out.println(myProp);
    }
}
package com.itheima.ui;
import com.itheima.service.IAccountService;
import com.itheima.service.impl.AccountServiceImpl;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;

/**
 *
 */
public class Client {
    /**
     * 获取Spring的IOC核心容器,并根据id获取对象
     * @param args
     */
    public static void main(String[] args) {
        //1.获取核心容器对象
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //2.根据id获取bean对象---两种方式
        /*IAccountService as = (IAccountService) ac.getBean("accountService");
        as.saveAccount();*/
        //没有调用销毁时,容器已经消失了
        //可以手动关闭容器
        IAccountService as = (IAccountService) ac.getBean("accountService3");
        as.saveAccount();
    }
}

六、Spring02:注解IOC、DBUtils单表CRUD、与Junit整合

1、Spring中IOC的常用注解

  • 分类:创建对象、注入数据、改变作用范围、与生命周期相关
  • Component注解--将当前类对象装入容器
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
    <!--通过配置告知Spring在创建容器时要扫描的包,配置所需要的标签不是在beans的约束中,而是一个名称为Context的名称空间和约束中-->
    <context:component-scan base-package="com.itheima"></context:component-scan>
</beans>
package com.itheima.service.impl;

import com.itheima.dao.IAccountDao;
import com.itheima.dao.impl.AccountDaoImpl;
import com.itheima.service.IAccountService;
import org.springframework.stereotype.Component;

/**
 * 账户的业务层实现类
 *
 * 曾经的xml配置
 * <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"
 *  scope="" init-method="" destroy-method="">
 *      <property name = "" value="" ref=""></property>
 *  </bean>
 *
 * 注解分为四类:
 *      用于创建对象的
 *          作用与xml配置文件中编写一个bean标签<bean></bean>实现的功能相同
 *          @Component
 *          作用:用于把当前类对象存入Spring容器中
 *          属性:
 *              value:用于指定bean的id,当我们不写时,它的默认值是当前类名,且首字母改小写
 */
@Component(value="accountService")
public class AccountServiceImpl implements IAccountService {
    private IAccountDao accountDao = null;
    public AccountServiceImpl(){
        System.out.println("service对象创建了");
    }
    public void saveAccount() {
        accountDao.saveAccount();
    }
}
  • Component的衍生注解
    • @Controller:表现层
    • @Service:业务层
    • @Repository:持久层(相当于dao)
public class Client {
    /**
     * 获取Spring的IOC核心容器,并根据id获取对象
     * @param args
     */
    public static void main(String[] args) {
        //1.获取核心容器对象
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //2.根据id获取bean对象---两种方式
        IAccountService as = (IAccountService) ac.getBean("accountService");
        System.out.println(as);
        IAccountDao adao = ac.getBean("accountDao",IAccountDao.class);
        System.out.println(adao);
    }
}
  • 用于注入数据的注解
    • @Autowired实现自动按照类型注入: 作用和xml配置文件中的bean标签写一个property标签的作用相同
    • @Qualifier:按照名称注入
    • @Resource:按照bean的id进行注入
    • @Value:对基本类型和String类型数据进行注入,使用SPEL表达式:${表达式}
@Service(value="accountService")
public class AccountServiceImpl implements IAccountService {
    @Autowired
    @Qualifier("accountDao1")
    //@Resource(name="accountDao2")
    private IAccountDao accountDao = null;
    public void saveAccount() {
        accountDao.saveAccount();
    }
}
  • 改变作用范围及生命周期注解
    • 改变作用范围:@Scope,singleton prototype,默认单例singleton
    • 生命周期:@PreDestroy,@PostConstruct
@Service(value="accountService")
//@Scope("single")
public class AccountServiceImpl implements IAccountService {
    //@Autowired
    //@Qualifier("accountDao1")
    @Resource(name="accountDao2")
    private IAccountDao accountDao = null;
    @PostConstruct
    public void init() {
        System.out.println("初始化方法执行了");
    }
    @PreDestroy
    public void destroy() {
        System.out.println("销毁方法执行了");
    }
    public void saveAccount() {
        accountDao.saveAccount();
    }
}
  • 测试  
   public static void main(String[] args) {
        //1.获取核心容器对象
        ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //2.根据id获取bean对象---两种方式
        IAccountService as = (IAccountService) ac.getBean("accountService");
        IAccountService as2 = (IAccountService) ac.getBean("accountService");
        /*System.out.println(as);
        IAccountDao adao = ac.getBean("accountDao",IAccountDao.class);
        System.out.println(adao);*/
        //System.out.println(as==as2);
        as.saveAccount();
        ac.close();
    }
}  

2、使用xml方式和注解方式实现单表的CRUD操作(dbutils.QueryRunner+Spring)

  • 案例必备代码
package com.itheima.dao.impl;
import com.itheima.dao.IAccountDao;
import com.itheima.domain.Account;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import java.sql.SQLException;
import java.util.List;
/**
 * 账户的持久层实现类
 */
public class AccountDaoImpl implements IAccountDao {
    private QueryRunner runner;

    public void setRunner(QueryRunner runner) {
        this.runner = runner;
    }
    @Override
    public List<Account> findAllAccount() {
        try {
            return runner.query("select * from account", new BeanListHandler<Account>(Account.class));
        } catch (Exception e) {
            throw new RuntimeException(e);//相当于return
        }
    }
    @Override
    public Account findAccountById(Integer accountId) {
        try {
            return runner.query("select * from account where id = ?", new BeanHandler<Account>(Account.class),accountId);
        } catch (Exception e) {
            throw new RuntimeException(e);//相当于return
        }
    }
    @Override
    public void saveAccount(Account account) {
        try {
            runner.update("insert into account(name,money) values(?,?)",account.getName(),account.getMoney());
        } catch (Exception e) {
            throw new RuntimeException(e);//相当于return
        }
    }
    @Override
    public void updateAccount(Account account) {
        try {
            runner.update("update account set name=?,money=? where id = ?",account.getName(),account.getMoney(),account.getId());
        } catch (Exception e) {
            throw new RuntimeException(e);//相当于return
        }
    }
    @Override
    public void deleteAccount(Integer accountId) {
        try {
            runner.update("delete from account where id = ?",accountId);
        } catch (Exception e) {
            throw new RuntimeException(e);//相当于return
        }
    }
}
  • Spring的IOC配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!-- 配置Service -->
    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
        <!-- 注入dao对象 -->
        <property name="accountDao" ref="accountDao"></property>
    </bean>
    <!--配置dao对象-->
    <bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl">
        <!--注入QueryRunner-->
        <property name="runner" ref="runner"></property>
    </bean>
    <!--配置QueryRunner对象-->
    <bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
        <!--注入数据源-->
        <constructor-arg name="ds" ref="dataSource"></constructor-arg>
    </bean>
    <!--配置数据源-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <!--连接数据库的必备信息-->
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/eesy"></property>
        <property name="user" value="root"></property>
        <property name="password" value="root"></property>
    </bean>
</beans>
  • 案例测试
package com.itheima.test;
import com.itheima.domain.Account;
import com.itheima.service.IAccountService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.List;
/**
 * 使用Junit单元测试:测试我们的配置
 */
public class AccountServiceTest {
    @Test
    public void testFindAll() {
        //1.获取容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //2.得到业务层对象
        IAccountService as = ac.getBean("accountService",IAccountService.class);
        //2.执行方法
        List<Account> accounts = as.findAllAccount();
        for (Account account : accounts) {
            System.out.println(account);
        }
    }
    @Test
    public void testFindOne() {
        //1.获取容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //2.得到业务层对象
        IAccountService as = ac.getBean("accountService",IAccountService.class);
        //2.执行方法
        Account account = as.findAccountById(1);
        System.out.println(account);
    }
    @Test
    public void testSave() {
        Account account = new Account();
        account.setName("johann");
        account.setMoney(500.0f);
        //1.获取容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //2.得到业务层对象
        IAccountService as = ac.getBean("accountService",IAccountService.class);
        //2.执行方法
        as.saveAccount(account);
    }
    @Test
    public void testUpdate() {
        //1.获取容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //2.得到业务层对象
        IAccountService as = ac.getBean("accountService",IAccountService.class);
        //2.执行方法
        Account account = as.findAccountById(1);
        account.setMoney(256f);
        as.updateAccount(account);
    }
    @Test
    public void testDelete() {
        //1.获取容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //2.得到业务层对象
        IAccountService as = ac.getBean("accountService",IAccountService.class);
        //2.执行方法
        as.deleteAccount(4);
    }
}
  • 自定义类使用注解配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <!--告知Spring在创建容器时要扫描的包-->
    <context:component-scan base-package="com.itheima"></context:component-scan>
    <!--配置QueryRunner对象-->
    <bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
        <!--注入数据源-->
        <constructor-arg name="ds" ref="dataSource"></constructor-arg>
    </bean>
    <!--配置数据源-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <!--连接数据库的必备信息-->
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/eesy"></property>
        <property name="user" value="root"></property>
        <property name="password" value="root"></property>
    </bean>
</beans>
  • 业务层实现类
/**
 * 账户的业务层实现类
 */
@Service("accountService")
public class AccountServiceImpl implements IAccountService {
    //唯一的对象在容器中,使用autowired实现自动注入
    @Autowired
    private IAccountDao accountDao;

    @Override
    public List<Account> findAllAccount() {
        return accountDao.findAllAccount();
    } 
  • 持久层实现类
/**
 * 账户的持久层实现类
 */
@Repository("accountDao")
public class AccountDaoImpl implements IAccountDao {
    @Autowired
    private QueryRunner runner;

3、Spring的新注解

  • Configuration(指定配置类)和ComponentScan(指定扫描包)
  • Bean:指定存入IOC容器中的bean对象
package config;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
import java.beans.PropertyVetoException;
@Configuration
@ComponentScan(basePackages = "com.itheima")//类路径,内容是一个数组,可以写{xxx,xxx}或xxx
public class SpringConfiguration {
    /**
     * 用于创建一个QueryRunner对象
     * @param dataSource
     * @return
     */
    @Bean(name="runner")//相当于bean的id
    public QueryRunner createQueryRunner(DataSource dataSource){
        return new QueryRunner(dataSource);
    }
    /**
     * 创建数据源 对象
     */
    @Bean(name="dataSource")
    public DataSource createDataSource(){
        try {
            ComboPooledDataSource ds = new ComboPooledDataSource();
            ds.setDriverClass("");
            ds.setJdbcUrl("");
            ds.setUser("");
            ds.setPassword("");
            return ds;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
  • AnnotationConfigApplicationContext
package com.itheima.test;

import com.itheima.domain.Account;
import com.itheima.service.IAccountService;
import config.SpringConfiguration;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.List;

/**
 * 使用Junit单元测试:测试我们的配置
 */
public class AccountServiceTest {
    @Test
    public void testFindAll() {
        //1.获取容器
        //ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
        //2.得到业务层对象
        IAccountService as = ac.getBean("accountService",IAccountService.class);
        //2.执行方法
        List<Account> accounts = as.findAllAccount();
        for (Account account : accounts) {
            System.out.println(account);
        }

    }
    @Test
    public void testFindOne() {
        //1.获取容器
        ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
        //2.得到业务层对象
        IAccountService as = ac.getBean("accountService",IAccountService.class);
        //2.执行方法
        Account account = as.findAccountById(1);
        System.out.println(account);
    }
    @Test
    public void testSave() {
        Account account = new Account();
        account.setName("johann");
        account.setMoney(500.0f);
        //1.获取容器
        ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
        //2.得到业务层对象
        IAccountService as = ac.getBean("accountService",IAccountService.class);
        //2.执行方法
        as.saveAccount(account);

    }
    @Test
    public void testUpdate() {
        //1.获取容器
        ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
        //2.得到业务层对象
        IAccountService as = ac.getBean("accountService",IAccountService.class);
        //2.执行方法
        Account account = as.findAccountById(1);
        account.setMoney(256f);
        as.updateAccount(account);
    }
    @Test
    public void testDelete() {
        //1.获取容器
        ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
        //2.得到业务层对象
        IAccountService as = ac.getBean("accountService",IAccountService.class);
        //2.执行方法
        as.deleteAccount(4);
    }
}
  • Import:指定是一个配置类
package config;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.springframework.context.annotation.*;
import javax.sql.DataSource;
import java.beans.PropertyVetoException;
/**
 * Import
 *      作用:用于导入其他的配置类
 *      属性:
 *          value:用于指定其他配置类的字节码
 *                 当使用import注解之后,有import注解的类就是父配置类,而导入的都是子配置类
 */
//@Configuration
@ComponentScan({"com.itheima"})//类路径,内容是一个数组,可以写{xxx,xxx}或xxx
@Import(JdbcConfig.class)
public class SpringConfiguration {
    //期望是公共配置,而不是只配置连接数据库的
}
  • PropertySource
package config;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.springframework.context.annotation.*;
import javax.sql.DataSource;
import java.beans.PropertyVetoException;
/**
 * PropertySource
 *      作用:用于导入其他的配置类
 *      属性:
 *          value:指定文件的名称和路径
 *              关键字:classpath表示类路径下
 *              有包:config/itheima/xxx
 *
 */
//@Configuration
@ComponentScan({"com.itheima"})//类路径,内容是一个数组,可以写{xxx,xxx}或xxx
@Import(JdbcConfig.class)
@PropertySource("classpath:jdbcConfig.properties")
public class SpringConfiguration {
    //期望是公共配置,而不是只配置连接数据库的
}
package config;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import javax.sql.DataSource;
/**
 * 和Spring连接数据库相关的配置类
 */
//@Configuration
public class JdbcConfig {
    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;
    /**
     * 用于创建一个QueryRunner对象
     * @param dataSource
     * @return
     */
    @Bean(name="runner")//相当于bean的id
    @Scope("prototype")
    public QueryRunner createQueryRunner(DataSource dataSource){
        return new QueryRunner(dataSource);
    }

    /**
     * 创建数据源对象
     */
    @Bean(name="dataSource")
    @Scope("prototype")
    public DataSource createDataSource(){
        try {
            //希望读取配置文件
            ComboPooledDataSource ds = new ComboPooledDataSource();
            ds.setDriverClass(driver);
            ds.setJdbcUrl(url);
            ds.setUser(username);
            ds.setPassword(password);
            return ds;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

4、Spring整合Junit

/**
 * 使用Junit单元测试:测试我们的配置
 * Spring整合Junit的配置
 *    1、导入Spring整合Junit的坐标
 *    2、使用Junit提供的一个注解把原有的main方法替换成Spring提供的
 *      @Runwith
 *    3、告知Spring的运行期,Spring和IOC创建是基于xml还是注解的,并且说明位置
 *      @ContextConfiguration
 *          locations:指定xml文件的位置,加上classpath关键字,表示在类路径下
 *          classes:指定注解类所在的位置
 *          当使用Spring5.x版本时,要求Junit的jar包必须是4.1.2及以上
 */
@RunWith(SpringJUnit4ClassRunner.class) //相当于main方法,自动调用test类
@ContextConfiguration(classes=SpringConfiguration.class) //配置文件
public class AccountServiceTest {
    //private ApplicationContext ac;
    @Autowired //自动注入
    private IAccountService as = null;
    @Test
    public void testFindAll() {
        //2.执行方法
        List<Account> accounts = as.findAllAccount();
        for (Account account : accounts) {
            System.out.println(account);
        }
    }

配置类

package config;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.springframework.context.annotation.*;

import javax.sql.DataSource;
import java.beans.PropertyVetoException;

/**
 * 该类是一个配置类,其作用和bean.xml作用相同
 * Spring中的新注解
 * Configuration
 *      作用:指定当前类是是一个配置类
 *      细节:当配置类作为AnnotationCofigApplicationContext对象创建的参数时,该注解可以不写
 * ComponentScan
 *      作用:用于通过注解指定spring在创建容器时要扫描的包
 *      value属性和basePackages的作用相同,都是用于指定创建容器时要扫描的包
 *           我们使用此注解就等同于在xml中配置了
 * <context:component-scan base-package="com.itheima"></context:component-scan>
 * Bean
 *      作用:用于把当前方法的返回值作为bean对象存入spring的IOC容器中
 *      属性:
 *          name:用于指定bean的id,当不写时默认值是当前方法的名称
 *      细节:
 *          当使用注解配置方法时,如果方法有参数,spring框架会去容器中查找有没有可用的bean对象
 *          查找的方式和AutoWired是一样的,查找类型匹配,一个 ,没有,多个
 *
 *     <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
 *         <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
 *         <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/eesy"></property>
 *         <property name="user" value="root"></property>
 *         <property name="password" value="root"></property>
 *     </bean>
 * Import
 *      作用:用于导入其他的配置类
 *      属性:
 *          value:用于指定其他配置类的字节码
 *                 当使用import注解之后,有import注解的类就是父配置类,而导入的都是子配置类
 * PropertySource
 *      作用:用于导入其他的配置类
 *      属性:
 *          value:指定文件的名称和路径
 *              关键字:classpath表示类路径下
 *              有包:config/itheima/xxx
 *
 */
//@Configuration
@ComponentScan({"com.itheima"})//类路径,内容是一个数组,可以写{xxx,xxx}或xxx
@Import(JdbcConfig.class)
@PropertySource("classpath:jdbcConfig.properties")
public class SpringConfiguration {
    //期望是公共配置,而不是只配置连接数据库的
}

七、Spring03:案例转账功能(事务问题)、动态代理解决、AOP

1、完善Account转账案例

  • 演示事务问题
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!-- 配置Service -->
    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
        <!-- 注入dao对象 -->
        <property name="accountDao" ref="accountDao"></property>
    </bean>
    <!--配置dao对象-->
    <bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl">
        <!--注入QueryRunner-->
        <property name="runner" ref="runner"></property>
    </bean>
    <!--配置QueryRunner对象-->
    <bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
        <!--注入数据源-->
        <constructor-arg name="ds" ref="dataSource"></constructor-arg>
    </bean>
    <!--配置数据源-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <!--连接数据库的必备信息-->
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/eesy02"></property>
        <property name="user" value="root"></property>
        <property name="password" value="root"></property>
    </bean>
</beans>
package com.itheima.test;
import com.itheima.domain.Account;
import com.itheima.service.IAccountService;
import com.itheima.service.impl.AccountServiceImpl;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.List;
/**
 * 使用Junit单元测试:测试我们的配置
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"/bean.xml"})
public class AccountServiceTest {
    @Autowired
    private IAccountService as;
    @Test
    public void testTransfer(){
        as.transfer("aaa","bbb",100f);
    }
}
    @Override
    public Account findAccountByName(String accountName) {
        try {
            List<Account> accounts = runner.query("select * from account where name = ?", new BeanListHandler<Account>(Account.class),accountName);
            if (accounts == null || accounts.size() == 0) return null;
            if (accounts.size() > 1) throw new RuntimeException("结果集不唯一,数据存在问题");
            return accounts.get(0);
        } catch (Exception e) {
            throw new RuntimeException(e);//相当于return
        }
    }

    @Override
    public void transfer(String sourceName, String targetName, Float money) {
        //1.根据名称查询转出账户
        Account source = accountDao.findAccountByName(sourceName);
        //2.根据名称查询转入账户
        Account target = accountDao.findAccountByName(targetName);
        //3.转出账户金额减少
        source.setMoney(source.getMoney() - money);
        //4.转入账户金额增加
        target.setMoney(target.getMoney() + money);
        //int i = 1 / 0;
        //5.更新转出账户
        accountDao.updateAccount(source);
        //6.更新转入账户
        accountDao.updateAccount(target);
    }
  • 分析事务的问题并编写ConnectionUtils
    • 线程管理工具类:获取当前线程连接池上的连接
package com.itheima.utils;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

/**
 *  连接的工具类,用于从数据源中获取一个连接,并且实现和线程的绑定
 */
public class ConnectionUtils {
    private ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
    private DataSource dataSource;

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    /**
     * 获取当前线程上的连接
     */
    public Connection getThreadConnection(){
        try {
            //1.先从ThreadLocal上获取
            Connection conn = tl.get();
            //2.判断当前线程上是否有连接
            if (conn == null){
                //3.从数据源中获取一个连接,并且和线程绑定,存入ThreadLocal中
                conn = dataSource.getConnection();
                tl.set(conn);
            }
            //4.返回当前线程上的连接
            return conn;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
    • 事务管理工具类:开启、提交、回滚、释放
package com.itheima.utils;
import java.sql.SQLException;
/**
 * 和事务管理相关的工具类,包含开启事务、提交事务、回滚事务和释放连接
 */
public class TransactionManager {
    //获取当前线程上的Connection
    private ConnectionUtils connectionUtils;
    public void setConnectionUtils(ConnectionUtils connectionUtils) {
        this.connectionUtils = connectionUtils;
    }
    /**
     * 开启事务
     */
    public void beginTransaction(){
        try {
            connectionUtils.getThreadConnection().setAutoCommit(true);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 提交事务
     */
    public void commit(){
        try {
            connectionUtils.getThreadConnection().commit();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 回滚事务
     */
    public void rollback(){
        try {
            connectionUtils.getThreadConnection().rollback();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 释放连接
     */
    public void release(){
        try {
            connectionUtils.getThreadConnection().close();//并不是真正关闭连接,而是还回连接池中
            connectionUtils.removeConnection();//进行线程的解绑
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
    • 业务层实现类
package com.itheima.service.impl;

import com.itheima.dao.IAccountDao;
import com.itheima.domain.Account;
import com.itheima.service.IAccountService;
import com.itheima.utils.TransactionManager;

import java.util.List;

/**
 * 账户的业务层实现类
 * 事务控制应当在业务层
 */
public class AccountServiceImpl implements IAccountService {

    private IAccountDao accountDao;
    private TransactionManager txManager;

    public void setTxManager(TransactionManager txManager) {
        this.txManager = txManager;
    }

    public void setAccountDao(IAccountDao accountDao) {
        this.accountDao = accountDao;
    }

    @Override
    public List<Account> findAllAccount() {
        try {
            //1.开启事务
            txManager.beginTransaction();
            //2.执行操作
            List<Account> accounts = accountDao.findAllAccount();
            //3.提交事务
            txManager.commit();
            //4.返回结果
            return accounts;
        } catch (Exception e) {
            //回滚操作
            txManager.rollback();
            throw new RuntimeException(e);
        } finally {
            //释放连接
            txManager.release();
        }
    }
    @Override
    public Account findAccountById(Integer accountId) {
        try {
            //1.开启事务
            txManager.beginTransaction();
            //2.执行操作
            Account account = accountDao.findAccountById(accountId);
            //3.提交事务
            txManager.commit();
            //4.返回结果
            return account;


        } catch (Exception e) {
            //回滚操作
            txManager.rollback();
        }finally {
            //释放连接
            txManager.release();
        }
        return accountDao.findAccountById(accountId);
    }

    @Override
    public void saveAccount(Account account) {
        try {
            //1.开启事务
            txManager.beginTransaction();
            //2.执行操作
            accountDao.saveAccount(account);
            //3.提交事务
            txManager.commit();
            //4.返回结果


        } catch (Exception e) {
            //回滚操作
            txManager.rollback();
        }finally {
            //释放连接
            txManager.release();
        }

    }

    @Override
    public void updateAccount(Account account) {
        try {
            //1.开启事务
            txManager.beginTransaction();
            //2.执行操作
            accountDao.updateAccount(account);
            //3.提交事务
            txManager.commit();
            //4.返回结果

        } catch (Exception e) {
            //回滚操作
            txManager.rollback();
        }finally {
            //释放连接
            txManager.release();
        }
    }

    @Override
    public void deleteAccount(Integer accountId) {
        try {
            //1.开启事务
            txManager.beginTransaction();
            //2.执行操作
            accountDao.deleteAccount(accountId);
            //3.提交事务
            txManager.commit();
            //4.返回结果

        } catch (Exception e) {
            //回滚操作
            txManager.rollback();
        }finally {
            //释放连接
            txManager.release();
        }
    }

    @Override
    public void transfer(String sourceName, String targetName, Float money) {
        try {
            //1.开启事务
            txManager.beginTransaction();
            //2.执行操作
            //2.1根据名称查询转出账户
            Account source = accountDao.findAccountByName(sourceName);
            //2.2根据名称查询转入账户
            Account target = accountDao.findAccountByName(targetName);
            //2.3转出账户金额减少
            source.setMoney(source.getMoney() - money);
            //2.4.转入账户金额增加
            target.setMoney(target.getMoney() + money);
            //int i = 1 / 0;
            //2.5.更新转出账户
            accountDao.updateAccount(source);
            //2.6.更新转入账户
            accountDao.updateAccount(target);
            //3.提交事务
            txManager.commit();
            //4.返回结果
        } catch (Exception e) {
            //回滚操作
            txManager.rollback();
        } finally {
            //释放连接
            txManager.release();
        }
    }
}
    • ConnectionUtils配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!-- 配置Service -->
    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
        <!-- 注入dao对象 -->
        <property name="accountDao" ref="accountDao"></property>
        <!--注入事务管理器-->
        <property name="txManager" ref="txManager"></property>
    </bean>
    <!--配置dao对象-->
    <bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl">
        <!--注入QueryRunner-->
        <property name="runner" ref="runner"></property>
        <!--注入ConnectionUtils-->
        <property name="connectionUtils" ref="connectionUtils"></property>
    </bean>
    <!--配置QueryRunner对象-->
    <!--不再提供Connection对象,没有数据源,不会从数据源中获取连接-->
    <bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype"></bean>
    <!--配置数据源-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <!--连接数据库的必备信息-->
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/eesy02"></property>
        <property name="user" value="root"></property>
        <property name="password" value="root"></property>
    </bean>
    <!--配置Connection的工具类-ConnectionUtils-->
    <bean id="connectionUtils" class="com.itheima.utils.ConnectionUtils">
        <!--注入数据源的配置-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--配置事务管理器-->
    <bean id="txManager" class="com.itheima.utils.TransactionManager">
        <!--注入ConnectionUtils-->
        <property name="connectionUtils" ref="connectionUtils"></property>
    </bean>
</beans>

2、动态代理

  • 基于接口的动态代理回顾
package com.itheima.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 模拟一个消费者
 */
public class Client {
    public static void main(String[] args) {
        Producer producer = new Producer();
        /**
         * 动态代理
         *     特点:字节码随用随创建,随用随加载
         *     作用:在不修改源码的基础上对方法增强
         *     分类:
         *          基于接口的动态代理
         *          基于子类的动态代理
         * 基于接口的动态代理:
         *      设计的类:Proxy
         *      提供者:JDK官方
         *  如何创建代理对象:
         *      使用Proxy类中的newProxyInstance方法
         *  创建代理对象的要求:
         *      被代理类至少实现一个接口,如果没有则不能使用
         *  newProxyInstance方法的参数:
         *      Classloader:类加载器
         *          用于加载代理对象字节码,和被代理对象使用相同的类加载器
         *      Class[]:字节码数组
         *          用于让代理对象和被代理对象有相同的方法
         *      InvocationHandler:用于提供增强的代码
         *          让我们写如何代理,一般情况下写该接口的实现类,通常情况下是匿名内部类
         *          此接口的实现类都是谁用谁写
         */
        //不实现任何接口时,无法正常使用
        IProducer proxyProducer = (IProducer)Proxy.newProxyInstance(producer.getClass().getClassLoader(),
            producer.getClass().getInterfaces(),
            new InvocationHandler() {
                /**
                 * 作用:执行被代理对象的任何接口方法,都会经过该方法
                 * 方法参数
                 * @param proxy:代理对象的引用
                 * @param method:当前执行的方法
                 * @param args:当前执行方法所需的参数
                 * @return:和被代理对象有相同的返回值
                 * @throws Throwable
                 */
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    //想要增强,可以在此处提供增强的代码
                    Object returnValue = null;
                    //1.获取方法执行的参数
                    float money = (float) args[0];
                    //2.判断当前方法是不是销售
                    if ("saleProduct".equals(method.getName())){
                        returnValue = method.invoke(producer,money * 0.8f);
                    }
                    return returnValue;
                }
            });
        proxyProducer.saleProduct(10000f);
    }
}
package com.itheima.proxy;
/**
 * 一个生产者
 * 生产厂家需要有标准-销售和售后(接口)
 */
public class Producer implements IProducer{
    /**
     * 销售
     * @param money
     */
    //@Override
    public void saleProduct(float money){
        System.out.println("销售产品,并拿到"+money+"元钱");
    }

    /**
     * 售后
     * @param money
     */
    //@Override
    public void afterService(float money){
        System.out.println("提供售后服务,并拿到"+money+"元钱");
    }
}
  • 基于子类的动态代理--导入cglib依赖坐标
package com.itheima.cglib;

import com.itheima.proxy.IProducer;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 模拟一个消费者
 */
public class Client {
    public static void main(String[] args) {
        Producer producer = new Producer();
        /**
         * 动态代理
         *     特点:字节码随用随创建,随用随加载
         *     作用:在不修改源码的基础上对方法增强
         *     分类:
         *          基于接口的动态代理
         *          基于子类的动态代理
         * 基于接口的动态代理:
         *      设计的类:Proxy
         *      提供者:第三方cglib库
         *  如何创建代理对象:
         *      使用Ehancer类中的create方法
         *  创建代理对象的要求:
         *      被代理类不能是最终类
         *  create方法的参数:
         *      Class:字节码
         *          用于指定被代理对象的字节码,producer.getClass()
         *      Callback:用于提供增强的代码
         *          让我们写如何代理,一般情况下写该接口的实现类,通常情况下是匿名内部类
         *          此接口的实现类都是谁用谁写
         *          一般写的都是该接口的子接口实现类:MethodIntercepter
         */
        Producer cglibProducer = (Producer) Enhancer.create(producer.getClass(), new MethodInterceptor() {
            /**
             * 执行被代理对象的任何方法都会经过该方法
             * @param proxy
             * @param method
             * @param args
             *      以上三个参数和基于接口的动态代理中invoke方法的参数相同
             * @param methodProxy 当前执行方法的代理对象
             * @return
             * @throws Throwable
             */
            @Override
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                //想要增强,可以在此处提供增强的代码
                Object returnValue = null;
                //1.获取方法执行的参数
                float money = (float) args[0];
                //2.判断当前方法是不是销售
                if ("saleProduct".equals(method.getName())) {
                    returnValue = method.invoke(producer, money * 0.8f);
                }
                return returnValue;
            }
        });
        cglibProducer.saleProduct(10000f);
    }
}
  • 使用动态代理实现事务控制
package com.itheima.factory;
import com.itheima.dao.IAccountDao;
import com.itheima.domain.Account;
import com.itheima.service.IAccountService;
import com.itheima.utils.TransactionManager;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 用于创建service代理对象的工厂
 */
public class BeanFactory {
    private IAccountService accountService;
    private TransactionManager txManager;
    public void setTxManager(TransactionManager txManager) {
        this.txManager = txManager;
    }
    /**
     * 获取service的代理对象
     *
     * @return
     */
    public IAccountService getAccountService() {
        Proxy.newProxyInstance(accountService.getClass().getClassLoader(),
                accountService.getClass().getInterfaces(),
                new InvocationHandler() {
                    /**
                     * 添加事务的支持
                     *
                     * @param proxy
                     * @param method
                     * @param args
                     * @return
                     * @throws Throwable
                     */
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        Object rtValue = null;
                        try {
                            //1.开启事务
                            txManager.beginTransaction();
                            //2.执行操作
                            rtValue = method.invoke(accountService, args);
                            // 3.提交事务
                            txManager.commit();
                            //4.返回结果
                            return rtValue;
                        } catch (Exception e) {
                            //回滚操作
                            txManager.rollback();
                            throw new RuntimeException(e);
                        } finally {
                            //释放连接
                            txManager.release();
                        }
                    }
                });
        return accountService;
    }
    public final void setAccountService(IAccountService accountService) {
        this.accountService = accountService;
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--配置代理的service对象-->
    <bean id="proxyAccountService" factory-bean="beanFactory" factory-method="getAccountService"></bean>
    <!--配置beanfactory-->
    <bean id="beanFactory" class="com.itheima.factory.BeanFactory">
        <!--注入Service-->
        <property name="accountService" ref="accountService"></property>
        <!--注入事务管理器-->
        <property name="txManager" ref="txManager"></property>
    </bean>
    <!-- 配置Service -->
    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
        <!-- 注入dao对象 -->
        <property name="accountDao" ref="accountDao"></property>

    </bean>
</beans>
package com.itheima.test;
import com.itheima.domain.Account;
import com.itheima.service.IAccountService;
import com.itheima.service.impl.AccountServiceImpl;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.List;
/**
 * 使用Junit单元测试:测试我们的配置
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"/bean.xml"})
public class AccountServiceTest {
    @Autowired
    @Qualifier("proxyAccountService")
    private IAccountService as;
    @Test
    public void testTransfer(){
        as.transfer("aaa","bbb",100f);
    }
}

3、AOP

  • Aspect Oriented Programming 即:面向切面编程。
  • 概念:抽取重复代码,在不修改源码的基础上,使用动态代理,对已有方法进行增强
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.itheima</groupId>
    <artifactId>day03_eesy_003springaop</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <properties>
        <maven.compiler.source>9</maven.compiler.source>
        <maven.compiler.target>9</maven.compiler.target>
    </properties>
    <dependencies>
        <!--spring的aop会用到-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.3.RELEASE</version>
        </dependency>
        <!--用于解析切入点表达式-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.13</version>
        </dependency>
    </dependencies>

</project>
  • 实现类
package com.itheima.service.impl;

import com.itheima.service.IAccountService;

/**
 * 账户的业务层实现类
 */
public class AccountServiceImpl implements IAccountService {
    @Override
    public void saveAccount() {
        System.out.println("执行了保存");
    }

    @Override
    public void updateAccount(int i) {
        System.out.println("执行了更新"+i);
    }

    @Override
    public int deleteAccount() {
        System.out.println("执行了删除");
        return 0;
    }
}
package com.itheima.utils;

/**
 * 用具记录日志的工具类,内部提供公共代码
 */
public class Logger {
    /**
     * 向控制台打印日志:计划在其切入点方法执行之前执行(切入点方法就是业务层方法)
     */
    public void printLog(){
        System.out.println("Logger类中的printLog方法开始记录日志了");
    }
}
  • 基于XML的AOP-配置步骤
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--先配置Spring的IOC,把Service对象配置进来【控制反转】-->
    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"></bean>
    <!--Spring中基于xml的aop配置步骤【面向切面编程,动态代理】
        1、把通知bean(logger类)也交给Spring管理
        2、使用aop:config标签表明aop的配置
        3、使用aop:aspect标签表明开始配置切面
            id属性用于给切面一个唯一标识
            ref属性指定通知类bean的id
        4、在aop:aspect标签的内部,使用对应的标签来配置通知的类型
             当前示例是让printLog方法在切入点之前执行,所以是前置通知
             aop:before表示配置前置通知
                    method属性:表示哪个方法是前置通知
                    pointcut属性:用于指定切入点表达式,该表达式的含义是对业务层中的哪些方法增强
            切入点表达式的写法:
                关键字:execution(表达式)
                表达式:
                    访问修饰符 返回值 包名.类名.方法名 参数列表
                标准写法:
                    public void com.itheima.service.impl.AccountService.saveAccount()
        5、
        -->
    <!--配置logger类-->
    <bean id="Logger" class="com.itheima.utils.Logger"></bean>
    <!--配置aop-->
    <aop:config>
        <!--配置切面-->
        <aop:aspect id="logAdvice" ref="Logger">
            <!--配置通知的类型,并建立通知方法和切入点方法的关联-->
            <aop:before method="printLog" pointcut="execution(public void com.itheima.service.impl.AccountService.saveAccount())">
       </aop:before> </aop:aspect> </aop:config> </beans>
  • 测试类
package com.itheima.test;

import com.itheima.service.IAccountService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * 测试AOP的配置
 */
public class AOPTest {
    public static void main(String[] args) {
        //1.获取容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //2.获取对象
        IAccountService as = (IAccountService)ac.getBean("accountService");
        //3.执行方法
        //切入点表达式只配置了对保存的增强
        as.saveAccount();//查看是否实现了记录日志/日志的打印
        as.updateAccount(1);
        as.deleteAccount();
    }
}
  • 切入点表达式配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--先配置Spring的IOC,把Service对象配置进来【控制反转】-->
    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"></bean>
    <!--Spring中基于xml的aop配置步骤【面向切面编程,动态代理】
            切入点表达式的写法:
                关键字:execution(表达式)
                表达式:
                    访问修饰符 返回值 包名.类名.方法名 参数列表
                标准写法:
                    public void com.itheima.service.impl.AccountService.saveAccount()
                    1.访问修饰符可以省略 void com.itheima.service.impl.AccountService.saveAccount()
                    2.返回值可以使用通配符表示任意返回值 * com.itheima.service.impl.AccountService.saveAccount()
                    3.包名可以使用通配符表示任意包,但是有几级包,就需要写几个*.
                        3.1包名可以使用*..表示当前包及子包 * *..
                    4.类名和方法名都可以使用*实现通配 * *..*(*)
                        4.1参数列表:
                            可以直接写数据类型
                                基本类型直接写名称 int * *..*(int)
                                引用类型写报名.类名的方式 java.lang.String
                            类型可以使用通配符*表示任意类型,但必须有参数int * *..*(*)
                            可以使用..表示有无参数均可,有参数时表示任意类型 * *..*(..)
                全通配写法:
                    * *..*.#(..)
                实际开发中切入点表达式的通常写法:
                    切到业务层实现类下的所有方法
                        * com.itheima.service.impl.*.*(..)
                aspectj坐标表示切入点表达式的语言联盟
        -->
    <!--配置logger类-->
    <bean id="Logger" class="com.itheima.utils.Logger"></bean>
    <!--配置aop-->
    <aop:config>
        <!--配置切面-->
        <aop:aspect id="logAdvice" ref="Logger">
            <!--配置通知的类型,并建立通知方法和切入点方法的关联-->
            <aop:before method="printLog" pointcut="execution(* *..*.*(..))"></aop:before>
        </aop:aspect>
    </aop:config>
</beans>
  • 常见的通知类型
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--先配置Spring的IOC,把Service对象配置进来【控制反转】-->
    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"></bean>
    <!--配置logger类-->
    <bean id="Logger" class="com.itheima.utils.Logger"></bean>
    <!--配置aop-->
    <aop:config>
        <!--配置切面-->
        <aop:aspect id="logAdvice" ref="Logger">
            <!--配置通知的类型,并建立通知方法和切入点方法的关联-->
            <!--事务要么try提交,要么catch回滚-->
            <!--配置前置通知:在切入点方法执行之前执行-->
            <aop:before method="beforePrintLog" pointcut="execution(* *..*.*(..))"></aop:before>
            <!--配置后置通知:在切入点方法正常执行之后执行,它和异常通知永远只能执行一个-->
            <aop:after-returning method="afterReturningPrintLog" pointcut="execution(* *..*.*(..))"></aop:after-returning>
            <!--配置异常通知:在切入点方法执行产生异常之后执行,它和后置通知永远只能执行一个-->
            <aop:after-throwing method="afterThrowingPrintLog" pointcut="execution(* *..*.*(..))"></aop:after-throwing>
            <!--配置最终通知:无论切入点方法是否正常执行,它都会在其后面执行-->
            <aop:after method="afterPrintLog" pointcut="execution(* *..*.*(..))"></aop:after>
        </aop:aspect>
    </aop:config>
</beans>
  • 通知方法
package com.itheima.utils;

/**
 * 用具记录日志的工具类,内部提供公共代码
 */
public class Logger {
    /**
     * 前置通知
     */
    public void beforePrintLog(){
        System.out.println("Logger类中的beforePrintLog方法开始记录日志了");
    }
    /**
     * 后置通知
     */
    public void afterReturningPrintLog(){
        System.out.println("Logger类中的afterReturningPrintLog方法开始记录日志了");
    }
    /**
     * 异常通知
     */
    public void afterThrowingPrintLog(){
        System.out.println("Logger类中的afterThrowingPrintLog方法开始记录日志了");
    }
    /**
     * 最终通知
     */
    public void afterPrintLog(){
        System.out.println("Logger类中的afterPrintLog方法开始记录日志了");
    }
}
  • 通用切入点表达式
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--先配置Spring的IOC,把Service对象配置进来【控制反转】-->
    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"></bean>
    <!--配置logger类-->
    <bean id="Logger" class="com.itheima.utils.Logger"></bean>
    <!--配置aop-->
    <aop:config>
<!--
        <aop:pointcut id="pt1" expression="execution(* com.itheima.service.impl.*.*(..))"/>
-->
        <!--配置切面-->
        <aop:aspect id="logAdvice" ref="Logger">
            <!--配置通知的类型,并建立通知方法和切入点方法的关联-->
            <!--事务要么try提交,要么catch回滚-->
            <!--配置前置通知:在切入点方法执行之前执行-->
            <aop:before method="beforePrintLog" pointcut-ref="pt1"></aop:before>
            <!--配置后置通知:在切入点方法正常执行之后执行,它和异常通知永远只能执行一个-->
            <aop:after-returning method="afterReturningPrintLog" pointcut-ref="pt1"></aop:after-returning>
            <!--配置异常通知:在切入点方法执行产生异常之后执行,它和后置通知永远只能执行一个-->
            <aop:after-throwing method="afterThrowingPrintLog" pointcut-ref="pt1"></aop:after-throwing>
            <!--配置最终通知:无论切入点方法是否正常执行,它都会在其后面执行-->
            <aop:after method="afterPrintLog" pointcut-ref="pt1"></aop:after>
            <!--配置切入点表达式,id属性用于表示表达式的唯一标识,expression指定表达式内容
                此标签写在aop:aspect标签内部,只能在当前切面使用
                也可以写在aop:aspect标签外部(必须写在aspect标签之前),可以在所有切面使用
            -->
            <aop:pointcut id="pt1" expression="execution(* com.itheima.service.impl.*.*(..))"/>
        </aop:aspect>
    </aop:config>
</beans>
  • 环绕通知
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--先配置Spring的IOC,把Service对象配置进来【控制反转】-->
    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"></bean>
    <!--配置logger类-->
    <bean id="Logger" class="com.itheima.utils.Logger"></bean>
    <!--配置aop-->
    <aop:config>
<!--
        <aop:pointcut id="pt1" expression="execution(* com.itheima.service.impl.*.*(..))"/>
-->

        <!--配置切面-->
        <aop:aspect id="logAdvice" ref="Logger">
            <aop:pointcut id="pt1" expression="execution(* com.itheima.service.impl.*.*(..))"/>
            <!--配置环绕通知 详细的注释请看logger类中-->
            <aop:around method="aroundAroundPrintLog" pointcut-ref="pt1"></aop:around>
        </aop:aspect>
    </aop:config>
</beans>
  • 通知代码
package com.itheima.utils;
import org.aspectj.lang.ProceedingJoinPoint;
/**
 * 用具记录日志的工具类,内部提供公共代码
 */
public class Logger {
    /**
     * 环绕通知
     *  问题:
     *      当配置了环绕通知之后,切入点方法没有执行,而通知方法执行了
     *  分析:
     *      通过对比动态代理中的环绕代理通知代码,发现动态代理的环绕通知有明确的切入点方法调用
     *
     *      动态代理的环绕通知-有明确的切入点调用
     *      我们没有切入点方法
     *  解决:
     *      Spring框架提供了一个接口:ProceedingJoinPoint,该接口有一个方法proceed,此方法就相当于明确调用切入点方法
     *      该接口可以作为环绕通知的方法参数,在程序执行时,spring框架会为我们提供该接口的实现类供我们使用
     *  Spring中的环绕通知:
     *      是Spring框架为我们提供的一种可以在代码中手动控制增强方法的实现方式
     */
    public Object aroundAroundPrintLog(ProceedingJoinPoint pjp){
        Object rtValue = null;
        try {
            Object[] args = pjp.getArgs();//得到方法执行所需的参数
            System.out.println("前置通知开始记录日志了");
            rtValue = pjp.proceed(args);//明确调用业务层/切入点方法
            System.out.println("后置通知开始记录日志了");
            return rtValue;
        } catch (Throwable throwable) {
            System.out.println("异常通知开始记录日志了");
            throw new RuntimeException(throwable);
        }finally {
            System.out.println("最终通知开始记录日志了");
            return rtValue;
        }
    }
}
  • 基于注解的AOP配置
package com.itheima.utils;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

/**
 * 用具记录日志的工具类,内部提供公共代码
 */
@Component("logger")
@Aspect //表示当前类是一个切面类
public class Logger {
    @Pointcut("execution(* com.itheima.service.impl.*.*(..))")
    private void pt1(){}
    /**
     * 前置通知
     */
    @Before("pt1()")
    public void beforePrintLog(){
        System.out.println("Logger类中的beforePrintLog方法开始记录日志了");
    }
    /**
     * 后置通知
     */
    @AfterReturning("pt1()")
    public void afterReturningPrintLog(){
        System.out.println("Logger类中的afterReturningPrintLog方法开始记录日志了");
    }
    /**
     * 异常通知
     */
    @AfterThrowing("pt1()")
    public void afterThrowingPrintLog(){
        System.out.println("Logger类中的afterThrowingPrintLog方法开始记录日志了");
    }
    /**
     * 最终通知
     */
    @After("pt1()")
    public void afterPrintLog(){
        System.out.println("Logger类中的afterPrintLog方法开始记录日志了");
    }

    /**
     * 环绕通知
     *  问题:
     *      当配置了环绕通知之后,切入点方法没有执行,而通知方法执行了
     *  分析:
     *      通过对比动态代理中的环绕代理通知代码,发现动态代理的环绕通知有明确的切入点方法调用
     *
     *      动态代理的环绕通知-有明确的切入点调用
     *      我们没有切入点方法
     *  解决:
     *      Spring框架提供了一个接口:ProceedingJoinPoint,该接口有一个方法proceed,此方法就相当于明确调用切入点方法
     *      该接口可以作为环绕通知的方法参数,在程序执行时,spring框架会为我们提供该接口的实现类供我们使用
     *  Spring中的环绕通知:
     *      是Spring框架为我们提供的一种可以在代码中手动控制增强方法的实现方式
     */
    //@Around("pt1()")
    public Object aroundAroundPrintLog(ProceedingJoinPoint pjp){
        Object rtValue = null;
        try {
            Object[] args = pjp.getArgs();//得到方法执行所需的参数
            System.out.println("前置通知开始记录日志了");
            rtValue = pjp.proceed(args);//明确调用业务层/切入点方法
            System.out.println("后置通知开始记录日志了");
            return rtValue;
        } catch (Throwable throwable) {
            System.out.println("异常通知开始记录日志了");
            throw new RuntimeException(throwable);
        }finally {
            System.out.println("最终通知开始记录日志了");
            return rtValue;
        }
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <!--配置Spring创建容器时要扫描的包-->
    <context:component-scan base-package="com.itheima"></context:component-scan>
    <!--不再需要配置service-->
    <!--把通知类交给Spring管理-->
    <!--配置Spring开启注解AOP注解的支持-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    <!--基于注解的aop有顺序的调用过程,而环绕通知没有调用顺序的问题※-->
</beans>

八、Spring04:JdbcTemplate及事务控制(AOP、XML、注解)

1、JdbcTemplate

  • 基本用法
package com.itheima.jdbcTemplate;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;

import java.sql.DriverManager;

/**
 * JdbcTemplate的最基本用法
 */
public class JdbcTemplateDemo1 {
    public static void main(String[] args) {
        //准备数据源:c3p0、dbcp都可,今天介绍Spring的内置数据源
        DriverManagerDataSource ds = new DriverManagerDataSource();
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl("jdbc:mysql://localhost:3306/eesy");
        ds.setUsername("root");
        ds.setPassword("root");
        //1.创建JdbcTemplate对象
        JdbcTemplate jt = new JdbcTemplate();//可以加带数据源的构造方法
        //给jt设置数据源
        jt.setDataSource(ds);
        //2.执行操作
        jt.execute("insert into account(name,money) values('ccc',1000)");
    }
}
  • 在Spring的IOC中使用
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--配置JdbcTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/eesy"></property>
        <property name="username" value="root"></property>
        <property name="password" value="root"></property>
    </bean>
</beans>
package com.itheima.jdbcTemplate;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import java.sql.DriverManager;
/**
 * JdbcTemplate的最基本用法
 */
public class JdbcTemplateDemo1 {
    public static void main(String[] args) {
        //1.获取容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //2.获取对象
        JdbcTemplate jt = ac.getBean("jdbcTemplate",JdbcTemplate.class);
        //2.执行操作
        jt.execute("insert into account(name,money) values('ccc',1000)");
    }
}
  • CRUD操作
package com.itheima.jdbcTemplate;

import com.itheima.domain.Account;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

/**
 * JdbcTemplate的CRUD操作
 */
public class JdbcTemplateDemo3 {
    public static void main(String[] args) {
        //1.获取容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //2.获取对象
        JdbcTemplate jt = ac.getBean("jdbcTemplate",JdbcTemplate.class);
        //2.可带参数的执行操作(单表)
        //保存
        //jt.update("insert into account(name,money) values (?,?)","ssss",333f);
        //更新
        //jt.update("update account set name=?,money=? where id=?","ssss",333f,5);
        //删除
        //jt.update("delete from account where id=?",1);
        //查询所有
        //queryRunner提供的
        //List<Account> accounts = jt.query("select * from account where money > ?",new AccountRowMapper(),100f);
        //Spring提供的
        /*List<Account> accounts1 = jt.query("select * from account where money > ?",new BeanPropertyRowMapper<Account>(Account.class),100f);
        for (Account account : accounts1) {
            System.out.println(account);
        }*/
        //查询一个
        /*List<Account> account = jt.query("select * from account where id = ?",new BeanPropertyRowMapper<Account>(Account.class),1);
        System.out.println(account.isEmpty()?"没有内容":account.get(0));*/
        //使用聚合函数查询,返回一行一列,但不加groupby子句
        //int count = jt.queryForObject("select count(*) from account where money >= ?",Integer.class,100f);
        Long count = jt.queryForObject("select count(*) from account where money >= ?",Long.class,100f);
        //大数据,常用long接收
        System.out.println(count);
    }
}

/**
 * 定义Account的封装策略
 */
class AccountRowMapper implements RowMapper<Account> {
    /**
     * 把结果集中的数据封装到Account中,然后由Spring把每个Account加入到集合中
     * @param resultSet
     * @param i
     * @return
     * @throws SQLException
     */
    @Override
    public Account mapRow(ResultSet resultSet, int i) throws SQLException {
        Account account = new Account();
        account.setId(resultSet.getInt("id"));
        account.setName(resultSet.getString("name"));
        account.setMoney(resultSet.getFloat("money"));
        return account;
    }
}
  • 在Dao中的使用
   <!--配置账户的持久层-->
    <bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl">
        <property name="jdbcTemplate" ref="jdbcTemplate"></property>
    </bean>
    <!--配置JdbcTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
package com.itheima.dao.impl;
import com.itheima.dao.IAccountDao;
import com.itheima.domain.Account;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;

import java.util.List;

/**
 * 账户的持久层实现类
 */
public class AccountDaoImpl implements IAccountDao {
    private JdbcTemplate jdbcTemplate;

    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    @Override
    public Account findAccounById(Integer accountId) {
        List<Account> accounts = jdbcTemplate.query("select * from account where id = ?",new BeanPropertyRowMapper<Account>(Account.class),accountId);
        return accounts.isEmpty()?null:accounts.get(0);
    }

    @Override
    public Account findAccountByName(String accounName) {
        List<Account> accounts = jdbcTemplate.query("select * from account where name = ?",new BeanPropertyRowMapper<Account>(Account.class),accounName);
        if (accounts.isEmpty()){
            return null;
        }
        if (accounts.size()>1){
            throw new RuntimeException("结果集不一致");
        }
        return accounts.get(0);
    }

    @Override
    public void updateAccount(Account account) {
        jdbcTemplate.update("update account set name=?,money=? where id=?",account.getName(),account.getMoney(),account.getId());
    }
}
package com.itheima.jdbcTemplate;

import com.itheima.dao.IAccountDao;
import com.itheima.domain.Account;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;

/**
 * JdbcTemplate的最基本用法
 */
public class JdbcTemplateDemo4 {
    public static void main(String[] args) {
        //1.获取容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //2.获取对象
        IAccountDao accountDao =ac.getBean("accountDao",IAccountDao.class);
        Account account = accountDao.findAccounById(5);
        System.out.println(account);
        account.setMoney(10000f);
        accountDao.updateAccount(account);
    }
}
  • JdbcDaoSupport与Dao的两种编写方式
/**
 * 账户的持久层实现类
 */
@Repository
public class AccountDaoImpl2 implements IAccountDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    /*public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }*/

    @Override
    public Account findAccounById(Integer accountId) {
        List<Account> accounts = jdbcTemplate.query("select * from account where id = ?",new BeanPropertyRowMapper<Account>(Account.class),accountId);
        return accounts.isEmpty()?null:accounts.get(0);
    }
package com.itheima.dao.impl;

import org.springframework.jdbc.core.JdbcTemplate;

import javax.sql.DataSource;

/**
 * 此类用于抽取dao中的重复代码
 */
public class JdbcDaoSupport {
    private JdbcTemplate jdbcTemplate;
    public JdbcTemplate getJdbcTemplate() {
        return jdbcTemplate;
    }
    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    public DataSource getDateSource() {
        return dateSource;
    }

    public void setDateSource(DataSource dateSource) {
        //this.dateSource = dateSource;
        if(jdbcTemplate == null){
            jdbcTemplate = createJdbcTemplate(dateSource);
        }
    }

    private JdbcTemplate createJdbcTemplate(DataSource dateSource) {
        return new JdbcTemplate(dateSource);//支持构造函数和set方法
    }
    private DataSource dateSource;

}
/**
 * 账户的持久层实现类
 */
public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao {
    /*private JdbcTemplate jdbcTemplate;

    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }*/

    @Override
    public Account findAccounById(Integer accountId) {
        List<Account> accounts = getJdbcTemplate() .query("select * from account where id = ?",new BeanPropertyRowMapper<Account>(Account.class),accountId);
        return accounts.isEmpty()?null:accounts.get(0);
    }
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--配置账户的持久层-->
    <bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl">
        <property name="dateSource" ref="dataSource"></property>
    </bean>
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/eesy"></property>
        <property name="username" value="root"></property>
        <property name="password" value="root"></property>
    </bean>
</beans>

2、作业:AOP实现事务控制

  • 基于XML的AOP实现
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- 配置Service -->
    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
        <!-- 注入dao对象 -->
        <property name="accountDao" ref="accountDao"></property>

    </bean>
    <!--配置dao对象-->
    <bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl">
        <!--注入QueryRunner-->
        <property name="runner" ref="runner"></property>
        <!--注入ConnectionUtils-->
        <property name="connectionUtils" ref="connectionUtils"></property>
    </bean>
    <!--配置QueryRunner对象-->
    <!--不再提供Connection对象,没有数据源,不会从数据源中获取连接-->
    <bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype"></bean>
    <!--配置数据源-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <!--连接数据库的必备信息-->
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/eesy02"></property>
        <property name="user" value="root"></property>
        <property name="password" value="root"></property>
    </bean>
    <!--配置Connection的工具类-ConnectionUtils-->
    <bean id="connectionUtils" class="com.itheima.utils.ConnectionUtils">
        <!--注入数据源的配置-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--配置事务管理器-->
    <bean id="txManager" class="com.itheima.utils.TransactionManager">
        <!--注入ConnectionUtils-->
        <property name="connectionUtils" ref="connectionUtils"></property>
    </bean>
    <!--配置AOP-->
    <aop:config>
        <aop:aspect id="txAdvice" ref="txManager">
            <!--配置通用的切入点表达式-->
            <aop:pointcut id="pt1" expression="execution(* com.itheima.service.impl.*.*(..))"/>
            <!--配置前置通知:开启事务-->
            <aop:before method="beginTransaction" pointcut-ref="pt1"></aop:before>
            <!--配置后置通知:提交事务-->
            <aop:after-returning method="commit" pointcut-ref="pt1"></aop:after-returning>
            <!--配置异常通知:回滚事务-->
            <aop:after-throwing method="rollback" pointcut-ref="pt1"></aop:after-throwing>
            <!--配置最终通知:释放连接-->
            <aop:after method="release" pointcut-ref="pt1"></aop:after>
        </aop:aspect>
    </aop:config>
</beans>
  • 基于注解的AOP实现
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">
    <!--配置Spring创建容器时要扫描的包-->
    <context:component-scan base-package="com.itheima"></context:component-scan>
    <!-- 配置Service -->

    <!--配置QueryRunner对象-->
    <!--不再提供Connection对象,没有数据源,不会从数据源中获取连接-->
    <bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype"></bean>
    <!--配置数据源-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <!--连接数据库的必备信息-->
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/eesy02"></property>
        <property name="user" value="root"></property>
        <property name="password" value="root"></property>
    </bean>
    <!--开启Spring对注解aop的支持-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
  • 连接工具类
package com.itheima.utils;

import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.sql.SQLException;

/**
 * 和事务管理相关的工具类,包含开启事务、提交事务、回滚事务和释放连接
 */
@Component("txManager")
public class TransactionManager {
    //获取当前线程上的Connection
    @Autowired
    private ConnectionUtils connectionUtils;
    @Pointcut("execution(* com.itheima.*.*(..))")
    private void pt1(){}
    /**
     * 开启事务
     */
    @Before("pt1")
    public void beginTransaction(){
        try {
            connectionUtils.getThreadConnection().setAutoCommit(true);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 提交事务
     */
    @AfterReturning("pt1")
    public void commit(){
        try {
            connectionUtils.getThreadConnection().commit();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 回滚事务
     */
    @AfterThrowing("pt1")
    public void rollback(){
        try {
            connectionUtils.getThreadConnection().rollback();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 释放连接
     */
    @After("pt1")
    public void release(){
        try {
            connectionUtils.getThreadConnection().close();//并不是真正关闭连接,而是还回连接池中
            connectionUtils.removeConnection();//进行线程的解绑
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
package com.itheima.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

/**
 *  连接的工具类,用于从数据源中获取一个连接,并且实现和线程的绑定
 */
@Component("connectionUtils")
public class ConnectionUtils {
    private ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
    @Autowired
    private DataSource dataSource;
    /**
     * 获取当前线程上的连接
     */
    public Connection getThreadConnection(){
        try {
            //1.先从ThreadLocal上获取
            Connection conn = tl.get();
            //2.判断当前线程上是否有连接
            if (conn == null){
                //3.从数据源中获取一个连接,并且和线程绑定,存入ThreadLocal中
                conn = dataSource.getConnection();
                tl.set(conn);
            }
            //4.返回当前线程上的连接
            return conn;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 把连接和线程解绑
     */
    public void removeConnection(){
        tl.remove();
    }
}

3、Spring中的事务控制

  • 事务控制的一组API
  • 代码准备
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--配置业务层-->
    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"></property>
    </bean>
    <!--配置账户的持久层-->
    <bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl">
<!--        <property name="jdbcTemplate" ref="jdbcTemplate"></property>-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/eesy"></property>
        <property name="username" value="root"></property>
        <property name="password" value="root"></property>
    </bean>
</beans>
import java.util.List;
/**
 * 使用Junit单元测试:测试我们的配置
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:bean.xml"})
public class AccountServiceTest {
    @Autowired
    private IAccountService as;
    @Test
    public void testTransfer(){
        as.transfer("aaa","bbb",100f);
    }
}
  • 基于XML的声明式事务控制
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--配置业务层-->
    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"></property>
    </bean>
    <!--配置账户的持久层-->
    <bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl">
<!--        <property name="jdbcTemplate" ref="jdbcTemplate"></property>-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/eesy"></property>
        <property name="username" value="root"></property>
        <property name="password" value="root"></property>
    </bean>
    <!--Spring中基于XML的声明式事务配置步骤
        1、配置事务管理器
        2、配置事务的通知
            此时需要导入事务的约束:tx名称空间和约束,同时也需要aop的
            使用tx:advice标签配置事务通知
                属性:
                    id:给事务通知起一个唯一标识
                    transaction-manager:给事务通知提供一个事务管理器引用
        3、配置AOP中的通用切入点表达式
        4、建立事务通知和切入点表达式的对应关系
        5、配置事务的属性
            在事务的通知tx:advice标签的内部配置
    -->
    <!--配置事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--配置事务通知的标签-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!--配置事务的属性-->
        <tx:attributes>
    <!--标识transfer是业务层接口的方法,其需要一些属性
         isolation="" 指定事务的隔离级别,默认是default,表示是数据库的默认隔离级别
         no-rollback-for="" 用于指定一个异常,当产生该异常时,事务不会滚 ,产生其他异常时,事务回滚。没有默认值,表示任何异常都回滚
         propagation="" 用于指定事务的传播行为,默认是REQUIRED,表示一定会有事务,增删改的选择,查询方法可以选择SUPPORT
         read-only="" 指定事务是否只读,只有查询才能设置为true,默认为false表示读写
         rollback-for="" 用于指定一个异常,当产生异常时,事务回滚。产生其他异常时,事务不回滚,没有默认值,表示任何异常都会滚
         timeout="" 指定事务的超时时间,默认值是-1,表示永不超时,如果指定了数值,以秒为单位
    -->
            <tx:method name="*" read-only="false" propagation="REQUIRED"/>
            <!--指定查询方法-->
            <tx:method name="find*" propagation="SUPPORTS" read-only="true"></tx:method>
        </tx:attributes>
    </tx:advice>
    <!--配置AOP-->
    <aop:config>
        <!--配置切入点表达式-->
        <aop:pointcut id="pt1" expression="execution(* com.itheima.*.*(..))"/>
        <!--建立切入点表达式和事务通知的对应关系-->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"></aop:advisor>
    </aop:config>
</beans>
  • 基于注解的声明式事务控制
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
    <!--配置业务层-->
    <!--配置spring创建容器时要扫描的包-->
    <context:component-scan base-package="com.itheima"></context:component-scan>
    <!--配置JdbcTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/eesy02"></property>
        <property name="username" value="root"></property>
        <property name="password" value="root"></property>
    </bean>
    <!--Spring中基于注解的声明式事务配置步骤
        1、配置事务管理器
        2、开启Spring对注解事务的支持
        3、在需要事务支持的地方使用@Transactional注解
        4、建立事务通知和切入点表达式的对应关系
        5、配置事务的属性
            在事务的通知tx:advice标签的内部配置
    -->
    <!--配置事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--开启Spring对注解事务的支持-->
    <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
</beans>
package com.itheima.service.impl;

import com.itheima.dao.IAccountDao;
import com.itheima.domain.Account;
import com.itheima.service.IAccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service("accountService")
@Transactional(propagation = Propagation.SUPPORTS,readOnly = true)//只读型事务控制
public class AccountServiceImpl implements IAccountService {
    @Autowired
    private IAccountDao accountDao;

    @Override
    public Account findAccountById(Integer accountId) {
        return accountDao.findAccountById(accountId);
    }
    //需要的是读写型事务控制
    @Transactional(propagation = Propagation.REQUIRED,readOnly = false)//读写型
    //xml一劳永逸
    @Override
    public void transfer(String sourceName, String targetName, Float money) {
        System.out.println("transfer开始执行");
        //2.1根据名称查询转出账户
        Account source = accountDao.findAccountByName(sourceName);
        //2.2根据名称查询转入账户
        Account target = accountDao.findAccountByName(targetName);
        //2.3转出账户金额减少
        source.setMoney(source.getMoney() - money);
        //2.4.转入账户金额增加
        target.setMoney(target.getMoney() + money);
        //2.5.更新转出账户
        accountDao.updateAccount(source);
        //2.6.更新转入账户
        accountDao.updateAccount(target);
        //int i = 1 / 0;
        //3.提交事务
        //txManager.commit();
    }
}
  • 基于纯注解的声明式事务控制
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/eesy02
jdbc.username=root
jdbc.password=root

package config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;

import javax.sql.DataSource;

/**
 * 和连接数据库相关的配置类
 */
public class JdbcConfig {
    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;
    /**
     * 创建JdbcTemplate对象
     * @param dataSource
     * @return
     */
    //进入容器需要bean注解
    @Bean(name="jdbcTemplate")
    public JdbcTemplate createJdbcTemplate(DataSource dataSource){
        return new JdbcTemplate(dataSource);
    }

    /**
     * 创建一个数据源对象
     * @return
     */
    @Bean(name="dataSource")
    public DataSource createDatasouce(){
        DriverManagerDataSource ds = new DriverManagerDataSource();
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        ds.setUsername(username);
        ds.setPassword(password);
        return ds;
    }
}
package config;

import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;

/**
 * 和事务相关的配置类
 */
public class TransactionConfig {
    /**
     * 用于创建事务管理器对象
     * @param dataSource
     * @return
     */
    @Bean(name="transactionManager")
    public PlatformTransactionManager createTransactionManager(DataSource dataSource){
        return new DataSourceTransactionManager(dataSource);
    }
}
  • 编程式事务控制
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--配置业务层-->
    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"></property>
        <property name="transactionTemplate" ref="transactionTemplate"></property>
    </bean>
    <!--配置账户的持久层-->
    <bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl">
<!--        <property name="jdbcTemplate" ref="jdbcTemplate"></property>-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/eesy02"></property>
        <property name="username" value="root"></property>
        <property name="password" value="root"></property>
    </bean>
    <!--配置事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
        <!--注入连接池数据源-->
    </bean>
    <!--配置事务模板对象-->
    <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
        <property name="transactionManager" ref="transactionManager"></property>
    </bean>
</beans>

九、SpringMVC01:入门、请求参数绑定、自定义类型转换器、常见注解

1、三层架构和MVC的介绍

2、入门程序案例

3、请求参数绑定入门

4、自定义类型转换器

5、其他常用注解

十、SpringMVC02:返回值、json数据、文件上传、拦截器

1、响应返回值

2、响应JSON数据

3、文件上传

4、异常处理--友好页面

5、SpringMVC拦截器

十一、SpringMVC03:SSM整合

1、搭建整合环境

2、Spring框架代码的编写

3、Spring整合SpringMVC框架

4、Spring整合Mybatis框架

十二、Oracle01:概念、常用查询

1、简介及安装

一个数据库可以有多个实例

某某用户下有几张表,用户是数据库管理的基本单位

2、用户和表的介绍及表的操作

3、查询深入

4、复杂查询

十三、Oracle02:视图、索引、plsql、存储过程、存储函数、触发器

1、视图、索引及plsql操作

2、存储过程

3、触发器

4、使用Java调用存储过程

十四、Maven02:项目拆分聚合、私服

1、基础回顾

  • 功能:依赖管理、一键构建(tomcat插件)
  • 仓库:本地仓库、远程仓库(私服)、中央仓库
    • 关系:默认从中央仓库下,公司中从私服中下
  • 常用命令:clean、compile、test、package(打包至本地target目录)、install(放在本地仓库)、deploy(上传到私服)

2、Maven案例

  • 导入jar包时的冲突解决原则(直接排除法)
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
      <version>4.2.4.RELEASE</version>
      <!--排除jar包的依赖包-->
      <exclusions>
        <exclusion>
          <groupId>org.springframework</groupId>
          <artifactId>spring-core</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
  • Dao配置文件编写
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.dao.ItemsDao">
    <select id="findById" parameterType="int" resultType="items">
        select * from items where id = #{id}
    </select>
</mapper>
  • 配置文件编写
    <!--dao层配置文件的编写-->
    <!--dao层配置文件的开始-->
    <!--配置Druid连接池(阿里巴巴提供)-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql:///maven"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </bean>
    <!--配置生产SqlSession对象的工厂-->
    <bean id="sessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <!--扫描pojo包,给包下所有pojo对象起别名-->
        <property name="typeAliasesPackage" value="com.itheima.domain"/>
    </bean>
    <!--扫描接口包路径,生成包下所有接口的代理对象,并且放入Spring容器中-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.itheima.dao"/>
    </bean>
    <!--dao层配置文件的结束-->
</beans>
  • 测试
package com.itheima.test;
import com.itheima.dao.ItemsDao;
import com.itheima.domain.Items;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ItemsTest {
    @Test
    public void findById(){
        //先获取Spring容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        // 从容器中拿到所需dao的代理对象
        ItemsDao itemsDao = ac.getBean(ItemsDao.class);
        //调用方法
        Items items = itemsDao.findById(1);
        System.out.println(items.getName());
    }
}
  • Service层Spring配置
    <!--service层配置文件开始-->
    <!--开启组件扫描配置-->
    <context:component-scan base-package="com.itheima.service"/>
    <!--aop面向切面编程,切面就是切入点和通知的组合-->
    <!--配置事务管理器aop-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--需要用到datasource-->
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!--配置事务的通知-->
    <tx:advice id="advice">
        <tx:attributes>
            <tx:method name="save*" propagation="REQUIRED"/>
            <tx:method name="update*" propagation="REQUIRED"/>
            <tx:method name="delete*" propagation="REQUIRED"/>
            <tx:method name="find*" read-only="true"/>
            <!--全局扫描-->
            <tx:method name="*" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>
    <!--配置切面-->
    <aop:config>
        <aop:pointcut id="pointcut" expression="execution(* com.itheima.service.*.*(..))"/>
        <aop:advisor advice-ref="advice" pointcut-ref="pointcut"/>
    </aop:config>
    <!--service层配置文件结束-->
</beans>
  • web层配置
    <!--过滤器、监听器、配置文件、核心servlet-->
    <!--配置编码过滤器-->
    <filter>
        <filter-name>encoding</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
        <init-param>
            <param-name>forceEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>encoding</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <!--配置Spring核心监听器-->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <!--重新制定Spring配置文件的路径-->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
    </context-param>
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
        <!--也可以写*.do-->
    </servlet-mapping>
</web-app>
  • SpringMVC配置
    <!--配置组件扫描-->
    <context:component-scan base-package="com.itheima.controller"/>

    <!--处理器映射器,处理器适配器-->
    <mvc:annotation-driven/>

    <!--配置视图解析器-->
    <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/pages/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
    <!--释放静态资源-->
    <mvc:default-servlet-handler/>
</beans>
  • Controller代码
@Controller
@RequestMapping("/items")
public class ItemsController {
    @Autowired
    private ItemsService itemsService;
    @RequestMapping("/findDetail")
    public String findDetail(Model model){
        Items items = itemsService.findById(1);
        model.addAttribute("item",items);
        return "itemDetail";
    }
}

3、Maven工程的拆分与整合

  • 父工程
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.itheima</groupId>
    <artifactId>maven_day02_parent</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <!--子模块的显示-->
    <modules>
        <module>maven_day02_dao</module>
        <module>maven_day02_service</module>
        <module>maven_day02_web</module>
    </modules>
    <properties>
        <maven.compiler.source>9</maven.compiler.source>
        <maven.compiler.target>9</maven.compiler.target>
    </properties>
    <!--管理子工程的jar包-->
    <!--dao、service、controller-->
</project>
  • 子工程
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>maven_day02_parent</artifactId>
        <groupId>com.itheima</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>maven_day02_web</artifactId>
    <packaging>war</packaging>
</project>
  • 工程与模块:模块可以使用父工程资源,作用域不同可能会不能使用,需要重新导入
  • 父工程填充
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans>
    <import resource="classpath:spring/applicationContext-service.xml"/>
    <import resource="classpath:spring/applicationContext-dao.xml"/>
</beans>
  • 启动:父工程、web模块(需要打包web项目)、本地的tomcat

4、私服

  • nexus安装与启动
  • 仓库介绍
    • host发行版,
    • 本地仓库上传到snapshot(测试)
    • proxy代理
    • central是中央仓库
    • 3rd party是第三方插件 
    • releses是私服 
  • 应用:删除本地dao,会从私服中下载
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>maven_day02_parent</artifactId>
        <groupId>com.itheima</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <!--模块的坐标com.itheima.maven_day02_dao-->
    <artifactId>maven_day02_dao</artifactId>

    <properties>
        <maven.compiler.source>9</maven.compiler.source>
        <maven.compiler.target>9</maven.compiler.target>
    </properties>
    <distributionManagement>
        <repository>
            <id>releases</id>
            <url>http://localhost:8081/nexus/content/repositories/releases/</url>
        </repository>
        <snapshotRepository>
            <id>snapshots</id>
            <url>http://localhost:8081/nexus/content/repositories/snapshots/</url>
        </snapshotRepository>
    </distributionManagement>
</project>
  • 安装第三方jar包

@Controller@RequestMapping("/items")public class ItemsController {    @Autowired    private ItemsService itemsService;    @RequestMapping("/findDetail")    public String findDetail(Model model){        Items items = itemsService.findById(1);        model.addAttribute("item",items);        return "itemDetail";    }}

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"       xsi:schemaLocation="http://www.springframework.org/schema/beans        http://www.springframework.org/schema/beans/spring-beans.xsd">    <!--把对象的创建交给Spring管理-->    <!--    Spring对Bean的管理细节        1、创建Bean的三种方式        2、Bean对象的作用范围        3、Bean对象的生命周期        -->    <!--创建Bean的三种方式-->    <!--第一种方式:使用默认构造函数创建        在Spring的配置文件中使用bean标签 ,配以id和class属性后,且没有其他属性和标签时        采用的就是默认构造函数创建Bean对象,此时如果没有构造函数,则对象无法创建        -->    <!--<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"></bean>-->    <!--jar中只有class文件,获取有些对象的返回值,则需要采用第二种或第三种创建对象-->    <!--第二种方式:使用普通工厂中的方法创建对象(使用类中的方法创建对象,并存入Spring容器)-->    <!--<bean id="instanceFactory" class="com.itheima.factory.InstancsFactory"></bean>    <bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService"></bean>-->
    <!--第三种方式:使用静态工厂中的静态方法创建对象,并存入Spring容器--><!--    <bean id="accountService" class="com.itheima.factory.StaticFactory" factory-method="getAccountService"></bean>-->    <!--bean的作用范围调整        默认是单例        通过bean标签的scope属性,调整bean的作用范围        取值:(单例和多例最常用)            singleton:单例的(默认值)            prototype:多例的            request:作用于web应用的请求范围            session:作用于web应用的会话范围            global-session:作用于集群环境的全局会话范围,当不是集群环境时,就是session    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl" scope="prototype"></bean>    -->    <!--bean对象的生命周期        区分单例对象/多例对象        单例对象            出生:当容器创建时,对象出生            存活:只要容器还在,对象就一直活着            死亡:容器销毁,对象消亡            总结:单例对象的生命周期和容器相同        多例对象            出生:当使用对象时,Spring框架为我们创建            存活:对象在使用过程中一直存活            死亡:当对象长时间不用且没有其他对象引用时,由Java的垃圾回收期回收    -->    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl" scope="prototype"    init-method="init" destroy-method="destroy"></bean>
</beans>

package com.itheima.test;
import com.itheima.service.IAccountService;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;
/** * 测试AOP的配置 */public class AOPTest {    public static void main(String[] args) {        //1.获取容器        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");        //2.获取对象        IAccountService as = (IAccountService)ac.getBean("accountService");        //3.执行方法        //切入点表达式只配置了对保存的增强        as.saveAccount();//查看是否实现了记录日志/日志的打印        as.updateAccount(1);        as.deleteAccount();    }}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值