Spring-学习笔记

JavaBean

Bean:在计算机英语中,有可重用组件的含义。

JavaBean:用java语言编写的可重用组件

​ javabean >> 实体类

单例模式:

初始化一次:使得对象唯一

多列模式:

每次创建的对象是不一样的,每次调用,每次重现创建

单例模式中对象是唯一的,每次使用都是一个对象;

Spring核心容器Ioc

ApplicationContext的三个常用实现类:

  • ClassPathXmlApplicationContext:只能加载类路径下的配置文件
  • FileSystemXmlApplicationContext:可以加载磁盘任意路径下的配置文件(必须有访问权限的)
  • AnnotationConfigApplicationContext:用于读取注解创建容器的

获取方法:

ApplicationContext = ClassPathXmlApplicationContext(String configLocation)

注意:

  • 核心容器的两个接口引发出的问题
    • ApplicationContext: 适用于单例模式
      • 它在构架核心容器时,创建对象采取的策略是采用立即加载的方式,在读取完配置文件马上就创建文件中配置的对象。
    • BeanFactory 适用于多例模式
      • 它在构建核心容器时,创建对象的策略是采用延迟加载的方式,什么时候根据id获取对象,什么时候创建对象。
Spring创建对象的三种方式

第一种:通过构造方法创建对象

<!--方式一:根据默认(无参)构造方法创建-->
<bean id="accountService" class="cn.study.service.impl.IAccountServiceImpl"></bean>-->

<!--方式二:根据有参构造方法创建-->
<bean id="accountServiceArgs" class="cn.study.service.impl.IAccountServiceImpl">
    <constructor-arg name="name" value=""></constructor-arg>
</bean>

第二种:通过工厂的静态方法创建对象

public class StaticFactory {

    public static IAccountService getIAccountService(){
        return new IAccountServiceImpl();
    }

    public static IAccountService getIAccountService(String name){
        return new IAccountServiceImpl();
    }
}
<!--方式1,根据无参方法创建对象-->
<bean id="accountServiceStaticfactory" class="cn.study.factory.StaticFactory" factory-method="getIAccountService"></bean>

<!--方式1,根据有参方法创建对象-->
<bean id="accountServiceStaticFactoryArgs" class="cn.study.factory.StaticFactory" factory-method="getIAccountService">
    <constructor-arg name="name" value="hh"></constructor-arg>
</bean>

第三种:通过工厂的方法创建

public class Factory {
    public IAccountService getIAccountService(){
        return new IAccountServiceImpl();
    }
    public IAccountService getIAccountService(String name){
        return new IAccountServiceImpl();
    }
}
<!--第三种,根据无参方法创建对象-->
<bean id="accountServiceFactory" class="cn.study.factory.Factory"></bean>
<bean id="accountServiceFactoryMethod" factory-bean="accountServiceFactory" factory-method="getIAccountService"></bean>

<!--第三种,根据有参方法创建对象-->
<bean id="accountServiceFactoryArgs" class="cn.study.factory.Factory"></bean>
<bean id="accountServiceFactoryMethodArgs" factory-bean="accountServiceFactoryArgs" factory-method="getIAccountService">
    <constructor-arg name="name" value="yy"></constructor-arg>
</bean>

Spring bean标签的作用范围

bean标签中的scope属性:

作用:指定bean的作用范围

取值:常用的是单例模式和多例模式

取值范围
singleton单例的(默认值)(对象立即创建)
prototype多例的(对象需要时创建)
request作用于web应用的请求范围
session作用于web应用的会话范围
global-session作用于集群环境的会话范围(全局会话范围),当不是集群范围是,它就是session
bean对象的生命周期

单例对象

出生(初始化):当容器创建时,创建对象

CalssPathXmlApplicationConText ac = new CalssPathXmlApplicationConText("bean.xml")//此时对象创建

存活:容器存在,对象就存在

死亡(销毁):容器关闭时,对象随着容器关闭而死亡。可调用close方法关闭

多例对象

出生:当需要使用对象时,Spring框架创建对象

存活:对象使用过程中一直存活

死亡:当对象长时间不使用,且没有其他对象引用,由java垃圾回收机制回收。

Spring中依赖注入

**依赖注入:**Dependency Injection

IOC的作用:降低程序间的耦合(依赖关系)

依赖关系的管理:以后都交于Spring来维护

  • 在当前需要用到其他类的对象,在配置文件中说明,由spring提供。

依赖关系的维护:依赖关系的注入

依赖注入(xml):
  • 能注入的数据:有三类
    • 基本数据类型和String
    • 其他bean类型(在配置文件中或者注解配置过的bean)
    • 复杂类型/集合类型
  • 注入的方式:有三种
    • 第一种:使用构造函数提供
    • 第二种:使用set方法提供
    • 第三种:使用注解提供

构造函数注入

  • 使用的标签:constructor-arg

  • 标签出现的位置:bean标签的内部

  • 标签的属性:

    • 指定参数
      • type:用于指定注入的数据类型,该数据类型也是构造函数中某个或某些参数的类型
      • index:用于指定要注入的数据给构造函数中指定索引位置的参数赋值,索引的位置是0开始
      • name:用于指定给构造函数中指定名称的参数赋值 —常用
    • 指定参数值
      • value:用于提供基本数据和String类型的数据
      • ref:用于指定其他bean类数据,它指的就是spring的Ioc核心容器中出现过的bean对象
    • 优势:在获取bean对象时,注入数据是必须的操作,否则对象无法创建成功
    • 弊端:改变了bean对象的实例化
  • 范例:

    <!--birthday为日期对象,需要使用ref来引用已存在的bean对象-->
    <bean id="birthDate" class="java.util.Date"></bean>
    <bean id="accountService" class="cn.study.service.impl.IAccountServiceImpl">
        <constructor-arg name="name" value="张飞"></constructor-arg>
        <constructor-arg name="age" value="20"></constructor-arg>
        <constructor-arg name="birthday" ref="birthDate"></constructor-arg>
    </bean>
    
    public class IAccountServiceImpl implements IAccountService {
        private String name;
        private Integer age;
        private Date birthday;
    
        public IAccountServiceImpl(String name, Integer age, Date birthday) {
            this.name = name;
            this.age = age;
            this.birthday = birthday;
        }
    }
    

set方法注入 --常用

  • 涉及的标签:property
  • 出现的位置:bean标签的内部
  • 标签的属性
    • 指定参数
      • name:用于指定给构造函数中指定名称的参数赋值 —常用
    • 指定参数值
      • value:用于提供基本数据和String类型的数据
      • ref:用于指定其他bean类数据,它指的就是spring的Ioc核心容器中出现过的bean对象
    • 优势:创建对象时没有明确的限制,可以直接使用默认构造函数
    • 弊端:如果某个成员变量必须有值,正则获取对象时有可能set方法没有执行

注意:name取值:与set方法相关,与变量名无关:如setAge name=“age”

范例:基本数据,String类型,,其他bean数据(对象),复杂数据,和集合

  • 复杂数据类型的注入/集合的注入

    • 用于给List结构集合注入的标签:list array set
    • 用于Map结构集合注入的标签:map props

    结构一样可以互换

<!--并非有属性的每个set都要赋值-->
<bean id="accountServiceSet1" class="cn.study.service.impl.IAccountServiceImpl3">
        <property name="name" value="张飞"></property>
        <property name="age" value="20"></property>
        <property name="birthday" ref="birthDate"></property>
        <property name="strs">
            <array>
                <value>str1</value>
                <value>str2</value>
                <value>str3</value>
            </array>
        </property>
        <property name="lists">
            <list>
                <value>list1</value>
                <value>list2</value>
                <value>list3</value>
            </list>
        </property>
        <property name="sets">
            <set>
                <value>set1</value>
                <value>set2</value>
                <value>set3</value>
            </set>
        </property>
        <property name="maps">
            <map>
                <entry key="map1" value="map"></entry>
                <entry key="map2" value="map"></entry>
                <entry key="map3" value="map"></entry>
            </map>
        </property>
        <property name="pros">
            <props>
                <prop key="key1">value</prop>
                <prop key="key2">value</prop>
                <prop key="key3">value</prop>
            </props>
        </property>
    </bean>

注解依赖

xml配置文件中配置相关约束:

配置注解扫描:xmlns:context约束

<?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">
    
    <!--指定扫描的包为:cn.study-->
    <context:component-scan base-packing="cn.study"></context:component-scan>
    
</beans>
注解:
  • 用于创建对象的(如:类)

    • @Component:用于将当前对象存入spring容器中。

      • 作用:和xml配置文件中编写一个标签功能一样
      • 属性:
        • value:用于指定bean的id;注意:不写默认为当前类名首字母小写。
        • 如:类为AccountServiceImp,value赋值。
        • @Component默认为@Component(value={“accountService”})
    • 下面三个注解和@Component注解功能是一样的,便于我们明确区分三层:

      • @Controller:一般用于表现层
      • @Service:一般用于业务层
      • @Repository:一般用于持久层(数据访问层)
    • 使用方式

      @Repository("accountDaoImpl")
      public class AccountDaoImpl implements IAccountDao {
      }
      
  • 用于注入数据的(如:成员变量)

    • @Atuowired

      • 作用:和xml配置文件中的bean标签中写一个标签的作用一样
      • 原理:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iPUhtT8v-1586165431161)(C:\Users\zhanggengming\AppData\Roaming\Typora\typora-user-images\image-20200403140257959.png)]
      • 细节:在使用注解注入时,set方法不是必须的.
    • 使用方法:可以在变量上,也可以在类上。

       @Autowired
       private IAccountDao accountDao = null;
      
    • @Qualifier

      • 作用:在按照类中注入的基础之上再按照名称注入,它在给类成员注入时,不能单独使用,但是在方法参数注入时可以
      • 属性:value:用于指定注入bean的id;

      使用方式:

      //方式1:使用在类成员
      @Autowired
      @Qualifier("queryRunner")
      private QueryRunner queryRunenr;
      //方式2:使用在方法参数中
      public QueryRunner getQueryRunner(@Qualifier("ds1")DataSource dataSource)
      
    • @Resource

      • 作用:直接按照bean的id注入,它可以直接使用
    • 属性:name,用于指定bean的id。

    • 以上三个注解都只能注入其他bean类型的数据,而基本类型无法用上述注解实现,集合类型的注入只能通过xml来实现

    • @Value

    • 作用:用于注入基本数据类型和Spring类型的数据

      • 属性:value:用于指定基本数据的值,可以使用spring中的SpEL(Spring的el表达式)
        • SpEL的写法:{表达式}
  • 用于改变作用范围的

    • @Scope
      • 作用:相当于标签中的scope
      • 属性:value:用于指定类的作用范围:默认为scope(单例)
    • 使用方法:在类上
  • 生命周期:初始化和销毁的方法注解(了解)

    • @PostConstruct:用于指定初始化方法
    • @PreDestory:用于指定销毁方法

spring中的新注解

指定配置类和相关配置

  • @Configuration

    • 作用:指定当前类时一个配置类,作用和bean.xml是一样的。
    • 注意:当前类作为AnnotationConfigApplicationContext对象创建的参数时,该注解可以不写:

    如:

    //SpringConfiguration为配置类,可以省略@Configuration
    ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
    
  • @ComponentScan 代替xml中的注解扫描

    • 作用:用于通过注解指定spring在创建容器时要扫描的包;和xml中的context:component-scan标签作用一样。
    • 属性:value:指定创建容器时要扫描的包。和basePackages作用一样。

    如:

    @ComponentScan("cn.study") 等效于
    <context:component-scan base-package="cn.study"></context:component-scan>
    
  • @Bean

    • 作用:用于将当前的方法的的返回值作为bean对象存入spring的ioc容器中
    • 属性:name:用于指定bean的id。默认值为当前方法的名称
    • 注意:当我们使用注解配置方法时,如果方法有参数,spring框架会取容器中查找有没有可用的bean对象;查找的方式和Autowired注解的作用是一样的
  • @Import

    • 作用:用于导入其他配置类。(作为当前配置类的子类)

    • 属性:

      value:用于指定其他配置的字节码。

      当我们使用Import的注解之后,和Import注解的类为父配置类,导入的都是子配置类

  • @PropertySource

    • 作用:指定加载文件的路径
    • 属性:value:指定文件的名称和路径;关键字:classpath,表示类路径下

    使用方式:

    //加载配置文件时,加载类路径下得到jddbcConfig。properties到
    @PropertySource("classpath:jdbcConfig.properties")
    public class SpringConfiguration{
        
    }
    @PropertySource("classpath:jdbcConfig.properties")
    
IOC案例
使用xml配置案例

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">
    <!--配置业务层Service-->
    <bean id="accountService" class="cn.study.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"></property>
    </bean>

    <!--配置持久层Dao对象-->
    <bean id="accountDao" class="cn.study.dao.impl.AccountDaoImpl">
        <property name="queryRunner" ref="queryRunner"></property>
    </bean>

    <!--配置queryRunner-->
    <bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
        <constructor-arg name="ds" value="dataSource"></constructor-arg>
    </bean>

    <!--配置数据源 ComboPooledDataSource中有4个参数的set方法-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <!--连接数据库的4个必备信息-->
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring"></property>
        <property name="user" value="root"></property>
        <property name="password" value="root"></property>
    </bean>
</beans>

**注意:**QueryRunner数据可操作对象,需要为多例对象,每个dao的QueryRunner对象不能共用,以免等待

(单例对象:一个dao接口在调用,另一个dao就不能使用)

使用dbutils执行数据库操作:

AccountDaoImp.java

package cn.study.dao.impl;
import cn.study.dao.IAccountDao;
import cn.study.domain.Account;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;

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

public class AccountDaoImpl implements IAccountDao {

    private QueryRunner queryRunner;

    public void setQueryRunner(QueryRunner queryRunner) {
        this.queryRunner = queryRunner;
    }

    @Override
    public List<Account> findAll() {
        try {
            return queryRunner.query("select * from account", new BeanListHandler<Account>(Account.class));
        } catch (SQLException e) {
            throw new RuntimeException(e);

        }
    }

    @Override
    public Account findByAccountId(Integer accountId) {
        try {
            return queryRunner.query("select * from account where id =?", new BeanHandler<Account>(Account.class), accountId);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void saveAccount(Account account) {
        try {
            queryRunner.update("insert into account(name,money) values(?,?)", account.getName(), account.getMoney());
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void updateAccount(Account account) {
        try {
            queryRunner.update("update account set name = ?, money = ? where id = ?", account.getName(), account.getMoney(), account.getId());
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void deleteById(Integer accountId) {
        try {
            queryRunner.update("delete from account where id = ?", accountId);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
	
    //获取的数据总条数时,为object,强转为long值
    @Override
    public long findTotalCount() {
        try {
            return (long)queryRunner.query("select count(id) from account", new ScalarHandler());
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}

Spring整合junit

问题分析
1.用用程序的入口
	main方法
2.junit单元测试中,没有main方法也能执行
	junit借成了一个main方法
	该方法就会判断当前测试类中有哪些方法有@Test注解
	junit就让Test注解的方法执行
3.junit不会管我们是否采用spring框架
	在执行测试方法时,junit不知道我们使用了spring框架
	所以不会为我们读取配置文件/配置类创建的spring核心容器
4.由以上可知:
	测试方法执行时,没有Ioc容器,无法实现注入。
Spring整合junit配置步骤
1.导入Spring整合Junit的jar(坐标)
	spring-test
2.使用Junit提供的一个注解把原有的main方法替换掉,替换成spring提供的	@Runwith(SpringJUnit4ClassRunner.class)
3.告知Spring的运行器,Spring和ioc创建是基于xml还是注解
	@ContextConfiguration
		属性:locationa:指定xml文件的位置,加上classpath的关键字,表示类路径下。
		属性:classes:指定注解类所在的位置

注意:junit版本需要是4.12 or higher

注解使用:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfiguration.class)
public class TestAnno {
    @Autowired
    private IAccountService as = null;
}

Spring(AOP)

  • AOP:面向切面编程
(为什么要引入AOP)AOP原理
问题分析:

事务控制应该在业务层,当前会出现的事务问题

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j9qvbgFf-1586165431163)(C:\Users\zhanggengming\AppData\Roaming\Typora\typora-user-images\1586010697355.png)]

ThreaLocal类:https://www.jianshu.com/p/e200e96a41a0

解决方案:

AccountServiceImpl.java

public class AccountServiceImpl implements IAccountService {

    private IAccountDao accountDao;
    private TransactionManager tsManager;

    public void setTsManager(TransactionManager tsManager) {
        this.tsManager = tsManager;
    }

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

    @Override
    public void transfer(String sourceName, String targetName, Float money) {
        try {
            //1.开始事务
            tsManager.begainTransaction();

            //2.执行操作
            //2.1 根据名称查询转出账户
            Account sourceAccount = accountDao.findByName(sourceName);
            //2.2 根据名称查询转入账户
            Account targetAccount = accountDao.findByName(targetName);
            //2.3 转出账户-转出金额
            sourceAccount.setMoney(sourceAccount.getMoney()- money);
            //2.4 转入账户+转入金额
            targetAccount.setMoney(targetAccount.getMoney() + money);
            //2.5 更新转出账户
            accountDao.updateAccount(sourceAccount);
            //int i = 1/0;
            //2.6 更新转入账户
            accountDao.updateAccount(targetAccount);
            //3.事务提交
            tsManager.commit();
        } catch (Exception e) {
            //4.事务回滚
            tsManager.rollback();
            e.printStackTrace();
        } finally {
            //5.释放资源
            tsManager.close();
        }
    }
}

ConnectionUtils.java

/**
 * 连接的工具类,用于从数据源中获取一个连接,并且实现和线程绑定
 */
public class ConnectionUtils {
    private ThreadLocal<Connection> t1 = new ThreadLocal<Connection>();
    private DataSource dataSource;
    
    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }
    
    //获取当前线程上连接对象
    public Connection getThreadConnection(){
        Connection conn = t1.get();
        //判断连接对象是否存在
        if(conn == null){
            try {
                conn = dataSource.getConnection();
                t1.set(conn);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return conn;
    }
    
    /**
     * 解绑连接和线程
     */
    public void release(){
        t1.remove();
    }
}

TransactionManager.java

/**
 * 事务管理的工具类:
 * 包含:开启事务,提交事务,回滚事务和释放连接
 */
public class TransactionManager {
    /**
     * 用于获取ThreadLocal的connection
     */
    private ConnectionUtils connectionUtils;

    public void setConnectionUtils(ConnectionUtils connectionUtils) {
        this.connectionUtils = connectionUtils;
    }

    /**
     * 开始事务
     */
    public void begainTransaction(){
        try {
            connectionUtils.getThreadConnection().setAutoCommit(false);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 提交事务
     */
    public void commit(){
        try {
            connectionUtils.getThreadConnection().commit();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 回滚事务
     */
    public void rollback(){
        try {
            connectionUtils.getThreadConnection().rollback();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 释放资源
     */
    public void close(){
        try {
            connectionUtils.getThreadConnection().close();
            connectionUtils.release();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}
分析方案

虽然解决了事务问题,出现了新的问题;

新问题分析
  • 业务层的代码重复量增加;
  • 冗余过多:即每个方法都需要 开启事务,提交事务,回滚事务,释放资源;
  • 类之间存在依赖:比如修改TranscationManager中的事务方法,即业务层涉及的代码(方法调用)都需要更改。

解决方法

使用动态代理方式解决重复代码

beanFactory.java

public class BeanFactory {
    /**
     * 被final修饰的成员变量
     * 只有两种赋值方式
     * 1.初始化赋值
     * 2.构造函数赋值
     */
    private IAccountService accountService;

    private TransactionManager tsManager;

    public void setAccountService(IAccountService accountService) {
        this.accountService = accountService;
    }

    public void setTsManager(TransactionManager tsManager) {
        this.tsManager = tsManager;
    }

    /**
     * 获取IAccountService
     * @return
     */
    public IAccountService getAccountService(){
        /**
         * 方式1 使用接口的代理方式
         */
        /*return (IAccountService) Proxy.newProxyInstance(accountService.getClass().getClassLoader(), accountService.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Object rtValue = null;
                try {
                    //1.开启事务
                    tsManager.begainTransaction();
                    //2.执行操作
                    rtValue = method.invoke(accountService,args);
                    //3.提交事务
                    tsManager.commit();
                    //4.返回结果
                    return rtValue;
                } catch (IllegalAccessException e) {
                    //5.回滚事务
                    tsManager.rollback();
                    throw new RuntimeException(e);
                } finally {
                    //6.释放资源
                    tsManager.relese();
                }
            }
        });*/

        return (IAccountService) Enhancer.create(accountService.getClass(), new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                Object rtValue = null;
                try {
                    //1. 开启事务
                    tsManager.begainTransaction();
                    //2. 执行操作
                    method.invoke(accountService,objects);
                    //3. 提交事务
                    tsManager.commit();
                    //4. 返回结果
                    return rtValue;
                } catch (Exception e) {
                    //5. 回滚事务
                    tsManager.rollback();
                    throw new RuntimeException(e);
                } finally {
                    //6.释放资源
                    tsManager.relese();
                }
            }
        });
    }
}

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">

    <!--配置获取proxyAccountService-->
    <bean id="proxyAccountService" factory-bean="beanFactory" factory-method="getAccountService"></bean>

    <!--配置beanFactory-->
    <bean id="beanFactory" class="cn.study.factory.BeanFactory">
        <property name="accountService" ref="accountService"></property>
        <property name="tsManager" ref="transactionManager"></property>
    </bean>
    
    <!--配置业务层-->
    <bean id="accountService" class="cn.study.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"></property>
    </bean>

    <!--配置dao层-->
    <bean id="accountDao" class="cn.study.dao.impl.AccountDaoImpl">
        <property name="queryRunner" ref="queryRunner"></property>
        <property name="connectionUtils" ref="connectionUtils"></property>
    </bean>

    <!--配置queryRunner-->
    <bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
        <constructor-arg name="ds" ref="dataSource"></constructor-arg>
    </bean>

    <!--配置transactionManager-->
    <bean id="transactionManager" class="cn.study.utils.TransactionManager">
        <property name="connectionUtils" ref="connectionUtils"></property>
    </bean>

    <!--配置connectionUtils-->
    <bean id="connectionUtils" class="cn.study.utils.ConnectionUtils">
        <property name="dataSource" ref="dataSource"></property>
    </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/spring"></property>
        <property name="user" value="root"></property>
        <property name="password" value="root"></property>
    </bean>
</beans>

案例分析:配置文件配置信息太繁杂:于是引入了AOP

AOP引入

概念:AOP全称是Aspect Oriented Programming,即面向切面编程

  • 通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续。是函数式子的一种衍生泛型,利用AOP可以对业务逻辑各个部分及逆行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高开发效率。

作用:在程序运行期间,不修改源代码对已有方法进行增强。

优势:

  1. 减少重复代码
  2. 提高开发效率
  3. 维护方便

实现方式

  1. 使用动态代理技术

代理的选择

  1. 在Spring中,框架会根据目标类是否实现了接口来决定采用了哪种动态代理的方式。
Spring中AOP

说明:通过配置的方式,实现功能

AOP相关术语

Joinpoint(连接点)

  • 所谓连接点是指那些被拦截到的点,在spring中,这些点指的是方法。spring中只支持方法类型的连接点

Pointcut(切入点)

  • 切入点是指我们要对哪些Jointpoint进行拦截的定义。(被增强的方法)

Advice(通知)

  • 拦截到Joinpoint之后要做的事情就是通知
  • 通知类型:前置通知,后置通知,异常通知,最终通知,环绕通知

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CEbbJlli-1586165431164)(C:\Users\zhanggengming\AppData\Roaming\Typora\typora-user-images\1586067991771.png)]

Introduction(引介)

  • 引介是一种特殊的的通知在不修改类代码的前提下,Introduction可以在运行期为类动态地添加一些方法或Field

Target(目标对象)

  • 代理的目标对象

Weaving(织入)

  • 把增强应用到目标对象来创建新的代理对象的过程
  • spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入

Proxy(代理)

  • 一个类被AOP织入增强后,就产生一个结果代理类

Aspect(切面)

  • 切入点和通知(引介)的结合
Spring AOP中的使用步骤
  1. 开发阶段
    1. 编写核心业务代码;
    2. 抽取公共类,作为通知;
    3. aop配置,声明切入点和通知间的关系,即切面;
    1. Spring框架监控切入点方法的执行,一旦监控切入点方法被运行,使用代理机制,动态创建目标对象的代理对象,根据通知类别,在代理对象的对应位置,将通知对应的功能织入,完成完整代码的逻辑运行.
Spring中基于XML的AOP配置步骤

1.把通知Bean交给spring来管理

2.使用aop:config标签表明开始AOP的配置

  1. aop:aspect
  2. aop:pointcut:配置pointcut 配置在aop:config下:所有切面可用
    1. aop:aspect中通知标签可以使用pointcut-ref调用

3.使用aop:aspect标签表明配置切面

  1. id属性:给切面提供一个唯一标识
  2. ref属性:指定通知类bean的Id

4.在aop:aspect标签内部使用对应标签来配置通知的类型

  1. aop:beafore:表示配置 前置通知

  2. aop:after-returning:表示配置 后置通知

  3. aop:after-throwing:表示配置 异常通知

  4. aop:after:表示配置 最终通知

  5. aop:around:表示配置 环绕通知

  6. 标签属性

    1. method属性:用于指定[aop:aspect]中指定类的哪个方法为前置通知

    2. pointcut属性:用于指定切入点表达式(指对业务层中哪些方法进行增强)

      1. execution(表达式)

      2. 表达式:

        1. 访问修饰符 返回值 包名.包名…类名.方法名(参数)
        2. 实际开发中切入点表达式的通常写法
          1. 切到业务层实现类下的所有方法
          2. cn.study.service.impl..(…)
      3. 例子

        pointcut = “execution(public void cn.study.service.impl.AccountServiceImpl.transfer(String,String,float)”

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sYzcMXZ4-1586165431165)(C:\Users\zhanggengming\AppData\Roaming\Typora\typora-user-images\1586072842716.png)]

<!--
切入点表达式的写法:
   关键字:execution表达式
   表达式:
      访问修饰符 返回值 包名.包名.类名.方法名(参数列表)
   标准的表达式写法:
       public void cn.study.service.impl.AccountServiceImpl.saveAccount()
          返回值可以使用通配符,方法名使用*:
             * cn.study.service.impl.AccountServiceImpl.*();
             //cn.study.service.impl.AccountServiceImpl类下的所有无参方法
          包名的可以使用通配符
             * *.*.*.*.AccountServiceImpl.*(..)
             //表示任意4四级包下的AccountServiceImpl类中任意方法
          包名使用.. 表示当前包及其子包
          全通配写法:
          * *..*.*(..)
   实际开发中切入点表达式的通常写法:
       切到业务层实现类下的所有方法
       * cn.study.service.impl.*.*(..)
    -->
注意:环绕通知

编码实现

使用了环绕通知,就不能使用前置 后置 异常 最终通知

环绕通知方法

Spring框架提供了一个接口:ProceedingJoinPoint.该接口有一个方法proceed(),此方法相当于明确调用切入点方法,活该几口可以作为环绕通知的方法参数,在程序执行时,spring框架会为我们提供该接口的实现类供我们使用.

环绕通知方法:

/**
     * 环绕通知方法
     * @param pjp
     * @return
     */
    public Object around(ProceedingJoinPoint pjp){
        Object rtValue = null;
        try{
            Object[] args =pjp.getArgs();//得到方法执行所需的参数
            this.begainTransaction(); //前置通知
            rtValue = pjp.proceed(args);//明确业务层的调用方法(切入点方法)
            this.commit();  //后置通知
            return rtValue;
        }catch (Throwable t){
            this.rollback(); //异常通知
            throw new RuntimeException(t);
        }finally {
            this.relese();  //最终通知
        }
    }
Spring中基于注解的AOP配置
需要在xml中开启AOP注解的支持
<!--配置spring创建容器要扫描的包-->
<context:component-scan base-package="cn.study"></context:component-scan>
<!--配置spring开启注解AOP的支持-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

不使用XML的配置方式

@EnableAspectJAutoProxy 使用在配置类上

作用:开启注解AOP支持

等效于:

<!--配置spring开启注解AOP的支持-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

使用方式:

@Configuration  //表示SpringConfiguration为配置类
@ComponentScan(basePackages="cn.study")   //开启注解扫描,扫描的包为cn.study
@EnableAspectJAutoProxy  //开启注解AOP的支持
public class SpringConfiguration{
    
}
作用在类**上
  1. @Aspect:表示当前类为一个切面类 相当于aop:aspect标签
作用在方法上:
  1. @Pointcut:和 aop:pointcut标签一样的功能

    使用方式:

    @Pointcut("execution(* cn.study.service.impl.*.*(..))")
    private void pt1(){}
    
  2. @Before:表示前置通知 和 aop:before标签一样的功能

    使用方式

    @Before("pt1()")//注意:()不能省略
    public void beafore(){}
    
  3. @AfterReturning:表示后置通知 和 aop:after-returing标签一样的功能

    @AfterReturning("pt1()")//注意:()不能省略
    public void AfterReturning(){}
    
  4. @AfterThrowing:表示异常通知 和 aop:after-throwing标签一样的功能

    @AfterThrowing("pt1()")//注意:()不能省略
    public void AfterThrowing(){}
    
  5. @After:表示最终通知 aop:after标签一样的功能

    @After("pt1()")//注意:()不能省略
    public void After(){}
    
  6. @around:表示环绕通知 aop:around标签一样的功能

    @around("pt1()")//注意:()不能省略
    public Object around(ProceedingJoinPoint pjp){
        Object rtValue = null;
        try{
            Object[] args =pjp.getArgs();//得到方法执行所需的参数
            this.begainTransaction();
            rtValue = pjp.proceed(args);//明确业务层的调用方法(切入点方法)
            this.commit();
            return rtValue;
        }catch (Throwable t){
            this.rollback();
            throw new RuntimeException(t);
        }finally {
            this.relese();
        }
    }
    

    注意

    执行注解配置 前置 后置 异常 最终 通知自时,出现异常

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v6FxnNRS-1586165431166)(C:\Users\zhanggengming\AppData\Roaming\Typora\typora-user-images\1586080792622.png)]

    1. 使用注解配置环绕通知时,不能同时注解配置其他四个通知。反之也一样。不然会使用两次
    2. 如果注解配置的是前置 后置 异常 最终 spring框架中有BUG,执行的顺序为:
      1. 前置->最终->后置(异常) 所以上述会出现异常
      2. 所以注解配置时,最好选用环绕通知配置
AOP案例

xml配置aop:

<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-->
    <!--配置accountService-->
    <bean id="accountService" class="cn.study.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"></property>
    </bean>

    <bean id="accountDao" class="cn.study.dao.impl.AccountDaoImpl">
        <property name="queryRunner" ref="queryRunner"></property>
        <property name="connectionUtils" ref="connectionUtils"></property>
    </bean>

    <bean id="connectionUtils" class="cn.study.utils.ConnectionUtils">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
        <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/spring"></property>
        <property name="user" value="root"></property>
        <property name="password" value="root"></property>
    </bean>

    <!--spring中基于AOP配置步骤-->
    <!--配置事务类 transactionManager-->
    <bean id="tsManager" class="cn.study.utils.TransactionManager">
        <property name="connectionUtils" ref="connectionUtils"></property>
    </bean>


    <!--配置AOP-->
    <aop:config>
        <!---->
        <aop:pointcut id="pt1" expression="execution(* cn.study.service.impl.*.*(..))"/>
        <!--配置切面 指定bean为tsManager-->
        <aop:aspect id="tsManagerAdvice" ref="tsManager">
            <!--配置前置通知,在切入点方法执行之前执行-->
            <aop:before method="begainTransaction" pointcut-ref="pt1"></aop:before>
            <!--配置后置通知:在切入点方法正常执行之后执行,和异常通知中只能执行一个-->
            <aop:after-returning method="commit" pointcut-ref="pt1"></aop:after-returning>
            <!--配置异常通知:在切入点方法执行产生异常之后执行,-->
            <aop:after-returning method="rollback" pointcut-ref="pt1"></aop:after-returning>
            <!--配置最终通知:无论切入点方法是否正常执行,它都会在其后面执行-->
            <aop:after-returning method="relese" pointcut-ref="pt1"></aop:after-returning>
            <!--&lt;!&ndash;配置环绕通知&ndash;&gt;
            <aop:around method="around" pointcut-ref="pt1"></aop:around>-->
        </aop:aspect>
    </aop:config>
</beans>

Spring-JdbcTemplate

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iOMjwzik-1586165431168)(D:\heima\06-Spring\04-第四天\截图\持久层总图.jpg)]

JdbcTemplate
  • 操作关系型数据的对象
  1. 作用:它就是用于和数据库交互的,实现对表的CRUD;
JdbcTemplate对象的创建

Spring内置数据源对象:DriverManagerDataSource


设置参数方法:

  1. setDriverClassName(“com.mysql.jdbc.Driver”)
  2. setUrl(“jdbc:mysql://localhost:3306/spring”)
  3. setUsername(“root”)
  4. setPassword(“root”)

获取JdbcTemplate对象

  1. JdbcTemplate template = new JdbcTemplate()
  2. JdbcTemplate template = new JdbcTemplate(Datasource ds)

加载数据源方法

  1. template.setDataSource(DataSource ds)
JdbcTemplate的CRUD方法
  1. 查询所有账户

    List<Account> accounts = template.query("select * from account",new BeanPropertyRowMapper<Account>(Account.class));
    
  2. 通过id查询一个账户

    Account account = template.queryForObject("select * from account where id = ?",new BeanPropertyRowMapper<Account>(Account.class),5);
    
  3. 查询一列一行

    Long count = template.queryForObject("select count(id) from account",Long.class)
    
  4. 保存账户

    template.update("insert into account(name,money) values(?,?)",name,money);
    
  5. 修改账户

    template.update("update account set name = ?,money = ? where id = ?",name,money.id)
    
  6. 删除用户

    template.update("delete from account where id = ?",id)
    
Spring-JdbcTemplate依赖注入
xml注入

持久层实现类

public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao{

}
注解注入

持久层实现类

public class AccountDaoImpl implements IAccountDao{
	private JdbcTemplate jdbcTemplate;
	private void setJdbcTemplate(JdbcTemplate jdbcTemplate){
		this.jdbcTemplate = jdbcTemplate;
	}
}

Spring-事务控制

事务控制管理类

接口:PlatformTransactionManager

子类:DataSourceTransactionManager

基于xml的声明式事务控制

基于xml的声明式事务控制步骤

  1. 配置事务管理器

    <!--配置事务管理器-->
    <bean id="transactionManager" 	class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    
  2. 配置事务的通知

    1. 需要导入事务的约束,tx名称空间和约束,同时也需要aop的
    2. 使用tx:advice标签配置事务通知
      1. 属性:
        1. id:给事务通知起一个唯一标识
        2. transaction-manager:给事务通知提供一个业务管理器引用
    <!--配置事务的通知-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
    	<!--配置事务的属性-->
    </tx:advice>
    
  3. 配置AOP中的通用切入点表达式

  4. 建立事务通知和切入点表达式的对应关系

     <!---配置AOP-->
        <aop:config>
            <!--配置切入点表达式-->
            <aop:pointcut id="pt1" expression="execution(* cn.study.service.impl.*.*(..))"/>
            <!--建立切入点表达式和事务通知的对应关系-->
            <aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"></aop:advisor>
        </aop:config>
    
  5. 配置事务的属性

    1. 是在事务的通知tx:advice标签的内部

    2. 使用tx:attributes标签

    3. 在tx:attributes标签内部使用tx:method标签指定

      1. isolation:用于指定事务的隔离级别,默认值是DEFAULT,表示数据库的默认隔离级别(可重复读);

      2. propagation:用于指定事务的传播行为,默认值是REQUIRED,表示一定会有事务,增删改的选择。查询方法可以选择SUPPPORTS;

      3. read-only:用于指定事务是否只读,只有查询方法才能设置为true,默认值是false,表示读写;

      4. timeout:用于指定事务的超时时间,默认值是-1。表示永不超时,如果指定了数值,以秒为单位;

      5. rollback-for:用于指定一个异常,当产生该异常时,事务回滚,产生其他异常时,事务不回滚,没有默认值。表示任何异常都回滚。

      6. no-rollback-for:用于指定一个异常,当产生该异常时,事务不回滚,产生其他异常时事务回滚,没有默认值,表示任何异常都回滚。

      7. name:指定切入点方法**的事务

    <!--配置事务的通知-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!--配置事务的属性-->
        <tx:attributes>
            <!--注意:find* 比 *(全通配符) 范围小,所以find*的优先级会更高 -->
            <!--表示对AOP切入点中所有方法都适用-->
            <tx:method name="*" propagation="REQUIRED" read-only="false" />
            <!--表示对AOP中切入点中所有的find开头的方法都适用-->
            <tx:method name="find*" propagation="SUPPORTS" read-only="true"></tx:method>
        </tx:attributes>
    </tx:advice>
    
基于注解的声明式事务控制配置
  1. @Transaction 相当于tx:method

    使用方式:

    @Service("accountService")
    @Transactional(propagation = Propagation.REQUIRED ,readOnly = false)
    public class AccountServiceImpl implements IAccountService {
    	@Override
        @Transactional(propagation = Propagation.SUPPORTS ,readOnly = true)
        public Account findAccountById(Integer accountId) {
            return accountDao.findAccountById(accountId);
        }
    }
    
  2. @EnableTransactionManagement

    1. 作用:开启Spring注解事务的支持

    2. 相当于

      <!--开启spring对注解事务的支持-->
      <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
      
基于注解的声明式事务控制配置步骤
  1. 配置事务管理器

    <!--配置事务管理器-->
    <bean id="transactionManager" 	class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    
  2. 开启Spring对注解事务的支持

    <!--开启spring对注解事务的支持-->
        <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
    
  3. 在需要事务支持的地方使用@Transaction注解 相当于tx:method

基于编程式事务控制

事务模板对象

  • transactionTemplate

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JYBhPbEk-1586165431169)(C:\Users\zhanggengming\AppData\Roaming\Typora\typora-user-images\1586161242975.png)]

代码实现:

@Override
    public void transfer(String sourceName, String targetName, Float money) {
        transactionTemplate.execute(new TransactionCallback<Object>() {
            @Override
            public Object doInTransaction(TransactionStatus status) {
                Account source = accountDao.findAccountByName(sourceName);
                Account target = accountDao.findAccountByName(targetName);

                source.setMoney(source.getMoney() + 100);
                target.setMoney(target.getMoney() - 100);

                //更新账户
                accountDao.update(source);
                int i = 1/0;
                accountDao.update(target);

                return null;
            }
        });
    }

TransactionTempalte类中

案例
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/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd 
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!--配置ioc容器-->
    <bean id="accountService" class="cn.study.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"></property>
    </bean>

    <bean id="accountDao" class="cn.study.dao.impl.AccountDaoImpl">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--配置数据源DataSource-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql:///spring"></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>
    
    <!--配置事务的通知-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!--配置事务的属性-->
        <tx:attributes>
            <!--注意:find* 比 *(全通配符) 范围小,所以find*的优先级会更高 -->
            <!--表示对AOP切入点中所有方法都适用-->
            <tx:method name="*" propagation="REQUIRED" read-only="false" />
            <!--表示对AOP中切入点中所有的find开头的方法都适用-->
            <tx:method name="find*" propagation="SUPPORTS" read-only="true"></tx:method>
        </tx:attributes>
    </tx:advice>

    <!---配置AOP-->
    <aop:config>
        <!--配置切入点表达式-->
        <aop:pointcut id="pt1" expression="execution(* cn.study.service.impl.*.*(..))"/>
        <!--建立切入点表达式和事务通知的对应关系-->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"></aop:advisor>
    </aop:config>

</beans>
纯注解配置
cn.config包下的文件

SpringConfiguration.java Spring的注解配置类(相当于bean.xml)

/**
 * Spring的配置类,相当于bean.xml
 */
@Configuration  									   //该类为一个配置类
@ComponentScan("cn.study")  						  //指定注解扫描的包
@Import({JdbcConfig.class, TransactionConfig.class})  //导入的其他配置类,为当前配置类的子类
@PropertySource("classpath:jdbcConfig.properties")    //指定加载文件的路径
@EnableAspectJAutoProxy 							  //开启注解AOP的支持
@EnableTransactionManagement()						  //开启注解对事务的支持
public class SpringConfiguration {
}

JdbcConfig.java

/**
 * 和连接数据库相关的配置类
 */
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;

    @Bean(name="jdbcTemplate")     //用于将当前的方法的的返回值作为bean对象存入spring的ioc容器中
    public JdbcTemplate createJdbcTemplate(@Qualifier("dataSource") DataSource dataSource){
        return new JdbcTemplate(dataSource);
    }

    @Bean(name="dataSource")
    public DataSource createDataSource(){
        DriverManagerDataSource ds = new DriverManagerDataSource();
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        ds.setUsername(username);
        ds.setPassword(password);
        return ds;
    }
}

TransactionManager.java

/**
 * 和事务相关的配置类
 */
public class TransactionConfig {
    @Bean(name="transactionManager")
    public PlatformTransactionManager createTransactionManager(DataSource dataSource){
        return new DataSourceTransactionManager(dataSource);
    }
}
cn.study包下

dao包

IAccountDao.java 和impl.AccountDaoImpl.java

/**
 * 账户持久层接口
 */
public interface IAccountDao {
    /**
     * 根据id查询用户
     * @return
     */
    Account findAccountById(Integer accountId);

    /**
     * 通过名字查找账户
     * @param name
     * @return
     */
    Account findAccountByName(String name);

    /**
     * 更新账户
     * @param account
     */
    void update(Account account);
}



@Repository("accountDao")				//用于将当前对象存入spring容器中
public class AccountDaoImpl implements IAccountDao {
    
    @Autowired							//注入引用数据类型的值
    private JdbcTemplate jdbcTemplate;
    
    @Override
    public Account findAccountById(Integer accountId) {
        try {
            return jdbcTemplate.queryForObject("select * from account where id = ?", new BeanPropertyRowMapper<Account>(Account.class), accountId);
        } catch (Exception e){
            throw new RuntimeException(e);
        }
    }

    @Override
    public Account findAccountByName(String name) {
        try {
            List<Account> accounts = jdbcTemplate.query("select * from account where name = ?", new BeanPropertyRowMapper<Account>(Account.class),name);
            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);
        }
    }

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

service包下

IAccountService.java和impl.AccountServiceImpl.java

public interface IAccountService {
    /**
     * 通过id查询账户
     * @param accountId
     * @return
     */
    Account findAccountById(Integer accountId);

    /**
     * 通过名字查询账户
     * @param name
     * @return
     */
    Account findAccountByName(String name);

    /**
     * 通过账户名进行转账
     * @param sourceName
     * @param targetName
     * @param money
     */
    void transfer(String sourceName, String targetName, Float money);
}



@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);
    }

    @Override
    public Account findAccountByName(String name) {
        return accountDao.findAccountByName(name);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED ,readOnly = false)
    public void transfer(String sourceName, String targetName, Float money) {
        Account source = accountDao.findAccountByName(sourceName);
        Account target = accountDao.findAccountByName(targetName);

        source.setMoney(source.getMoney() - 100);
        target.setMoney(target.getMoney() + 100);

        //更新账户
        accountDao.update(source);
        int i = 1/0;
        accountDao.update(target);
    }
}

domain包下

account.java

public class Account implements Serializable {
    private Integer id;
    private String name;
    private Float money;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Float getMoney() {
        return money;
    }

    public void setMoney(Float money) {
        this.money = money;
    }

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", money=" + money +
                '}';
    }
}

依赖

IOC容器的依赖
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.0.2.RELEASE</version>
</dependency>
解析切入点表达式

aspectj:软件联盟,可以解析切入点表达式

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.7</version>
</dependency>
整合junit
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.0.2.RELEASE</version>
</dependency>

jdbc的依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.0.2.RELEASE</version>
</dependency>

事务相关的依赖

 <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-tx</artifactId>
     <version>5.0.2.RELEASE</version>
</dependency>

约束

aop

core -> (xmlns:aop)

<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">
</beans>
tx:事务的约束

Data Access ->(xmlns:tx)

<?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">
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值