Spring入门

Spring入门

安排:

day1: spring框架的概述以及spring中基于XML的IOC(反转控制)配置

day2:Spring中基于注解的IOC和IOC案例

day3:spring中的AOP和基于XML以及注解的AOP(面向切面编程)配置

day4:spring中的JDBC Temlate以及Spring事物控制

Day1 spring框架的概述以及spring中基于XML的IOC(反转控制)配置

1、Spring的概述

​ 1.1spring的优势:方便解耦,简化开发

AOP编程的支持

声明式事务的支持

方便程序的测试

方便集成各种优秀框架

​ Spring可以降低各种框架的使用难度,提供了对各种优秀框架(Struts、Hibernate、 Hessian、Quartz等)的直接支持。

降低JavaEE API的使用难度

Java源码是经典学习范例

2、 程序的耦合和解耦

划分模块的一个准则就是高内聚低耦合

耦合的原则:耦合是影响软件复杂程度和设计质量的一个重要因素,在设计上我们应采用以下原则:如果模块间必须存在耦合,就尽量使用数据耦合,少用控制耦合,限制公共耦合的范围,尽量避免使用内容耦合。

​ 1.2 spring 是什么?

​ 1.3 spring 的两大核心

在这里插入图片描述

在这里插入图片描述

core container是spring的核心之一,spring的任何其他部分运行都离不开它。

在这里插入图片描述

工厂模式创建对象的过程: 1. 用反射的方式创建对象 bean = Class.forName(beanPath).newInstance();

  1. 有能反射的全限定类名accountService = com.itheima.service.impl.AccountImpl

    accountDao = com.itheima.service.dao.impl.AccountDaoImpl

  2. 通过读取配置文件的方式来反射从而得到

  3. Sverlet是一个单例对象

    在此次的例子中,bean = Class.forName(beanPath).newInstance(); //每次都会调用默认构造函数 创建对象 每次都会创建新的对象,而旧的被垃圾回收机制默认回收了。这就带来一个问题,多例对象运行效率没有单例对象高,因此我们使用容器解决这个问题,使它变成个单例的。

``

package com.itheima.service.factory;

import java.io.InputStream;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
/*
* 一个创建Bean对象工厂
*
* Bean在计算机语言中,有可重用组件的含义
* JavaBean:用java语言编写的可重用组件
*       javabean > 实体类
*
* 一个创建Bean对象工厂,它是创建我们的service和dao对象。
*   第一个:需要配置文件来配置我们的service和dao
*           配置的内容:唯一标志的名字=全限定类名(key=value)
*   第二个/
*:通过读取配置文件中的内容,反射创建对象
*
* */
public class BeanFactory {
    private static Properties prop;
    //定义一个map,用来存放我们要创建的对象
    private static Map<String,Object> beans;
    //使用静态代码块为prop赋值,编译器就开始执行
    static {
        try {
            //实例化prop
            prop = new Properties();
            //获取properties的流对象
            InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
            prop.load(in);
            //实例化beans
            beans = new HashMap<String, Object>();
            Enumeration keys = prop.keys();   
             //遍历枚举
            while(keys.hasMoreElements()){
                //取出每个key值
                String key = keys.nextElement().toString();
                //根据key值得到路径
                String beanPath = prop.getProperty(key);
                //利用的到的路径,创建反射对象
                Object value = Class.forName(beanPath).newInstance();
                //把得到的key,value存到容器中
                beans.put(key,value);
            }
        }catch(Exception e){
            throw new ExceptionInInitializerError("初始化对象错误");
        }
    }

    /**
     * 根据bean的名称获取对象
     * @param beanName
     * @return
     */
    public static Object getBean(String beanName)
    {
            return beans.get(beanName);
    }

    /**
     *根据Bean的名称来获取bean对象
     * @param beanNamme
     * @return
     *
    public static Object getBean(String beanNamme){
        Object bean = null;
        try{
        String beanPath = prop.getProperty(beanNamme);
         bean = Class.forName(beanPath).newInstance();  //每次都会调用默认构造函数 创建对象
        }catch(Exception e){
            e.printStackTrace();
        }
        return bean;
    }*/
}

``

3、IOC概念和Spring中的IOC

​ spring中基于XML的环境搭建

控制反转:(Inversion of Control)把创建对象的权利交给框架,是框架的重要特征。它包括依赖注入和依赖查找。

ioc的作用,降低程序间的耦合

具体解耦过程入下面步骤所示

  1. 添加依赖
<?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>day01_eesy_spring</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>
    </dependencies>

</project>
  1. 1)配置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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

        <!--把对象的创建交给spring来管理-->
        <bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl"></bean>
        <bean id="accountService" class="com.itheima.AccountService.impl.AccountImpl"></bean>
</beans>

​ 2)解析spring对bean的管理细节

​ 1.创建bean的三种方式

​ 第一种:使用默认构造函数创建在spring的配置文件中使用bean标签,配以属性id和class后没有其 他属性时采用这种方式创建,如果此类中没有默认构造函数,则对象无法被创建。

​ 第二种:使用普通工厂中的方法创建,使用某个类中的方法创建对象,并存入spring容器(当类存 在于jar包中的,我们无法通过修改源码的方式来提供默认构造函数)

		<bean id="accountService" factory-bean="instanceFactory" factory-				method="getAccountService" >
		</bean>

​ 第三种:使用某个类中的静态方法创建

		<bean id="accountService" class="com.itheima.factory.StaticFactory"  factory-method="getAccountService" >
		</bean>

​ 3)Spring中的依赖注入

​ 注入数据: 可以注入三种数据:第一种: 基本类型和String类型

​ 第二种:其他bean类型(在配置文件中或者注解配置过的bean)

​ 第三种:复杂类型/集合类型

​ 注入的方式: ①默认构造函数注入 :

​ 使用的标签:constructor-arg

​ 属性标签中的属性:

​ type:用于指定构造函数中的某个或者某些参数的数据类型

​ index:用于指定构造函数的位置,从0开始

​ name:用于指定构造函数中的参数的名字,更为常用

​ value:为基本数据类型和String类型注入具体的内容

​ ref:用于指定其他的bean类型,它指定的是spring容器中出现过的bean对象

​ 优势:必须将构造函数中所有的参数进行内容注入

		<bean id="accountService" class="com.itheima.AccountService.impl.AccountImpl">
                <constructor-arg name="name" value="Bazahai"></constructor-arg>
                <constructor-arg name="age" value="18"></constructor-arg>
                <constructor-arg name="birthday" ref="tommoron"></constructor-arg>
        </bean>	
		<bean id="tommoron" class="java.util.Date"></bean>

​ ②用set方法

​ 使用的标签:property
​ 不可以提供构造函数,因为构造函数注入和set方法注入是不兼容的
​ 标签中的属性:
​ name:用于注入时所调用的方法名
​ value:为基本数据类型和String类型注入具体的内容
​ ref:用于指定其他的bean类型,它指定的是spring容器中出现过的bean对象
​ 优势:可以为我们想要注入的数据注入内容,更加灵活

		<bean id="accountService2" class="com.itheima.AccountService.impl.AccountImpl2">
                <property name="userName" value="Bazahai"></property>
                <property name="age" value="17"></property>
                <property name="birthday" ref="tommoron"></property>
        </bean>
		<bean id="tommoron" class="java.util.Date"></bean>

​ 3)使用注解提供/复杂类型的注入

​ 用于给List集合注入的标签:
​ list、 array、 set
​ 用于给map集合注入的标签:
​ map、 props
​ 结构相同,标签可以互换

		<bean id="accountService3" class="com.itheima.AccountService.impl.AccountImpl3">
                <property name="myList">
                        <list>
                                <value>AAA</value>
                                <value>BBB</value>
                                <value>CCC</value>
                        </list>
                </property>

                <property name="myStr">
                        <array>
                                <value>AAA</value>
                                <value>BBB</value>
                                <value>CCC</value>
                        </array>
                </property>

                <property name="mySet">
                        <set>
                                <value>AAA</value>
                                <value>BBB</value>
                                <value>CCC</value>
                        </set>
                </property>

                <property name="myMap">
                        <map>
                                <entry key="TestA" value="AAA"></entry>
                                <entry key="TestB" value="BBB"></entry>
                                <entry key="TestC" value="CCC"></entry>
                        </map>
                </property>
        </bean>
        <bean id="tommoron" class="java.util.Date"></bean>

​ 2.bean对象的生命周期

​ 单例对象,和容器同生共死

​ 多例对象,出生:当我们使用对象时

​ 活着:对象在使用过程中一直活着

​ 死亡:当对象长时间不用时,也没有别的对象引用时,被java垃圾回收机制回收

​ 3.bean对象的作用范围

​ bean标签的scope属性,用于指定bean的作用范围。

​ singleton 单例的,默认值

​ prototype 多例的

​ request 作用于web应用的请求范围

​ session 作用于web应用的的会话范围

​ global-session 作用于集群环境的会话范围,区别见下图,当不是集群时,作用等同于 session

在这里插入图片描述

  1. 编写程序ui,运行代码
package com.itheima.ui;

import com.itheima.AccountService.IAccountService;
import com.itheima.dao.AccountDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/*
* 模拟一个表现层,调用业务层
* */
public class Client {
     /**
     *获取spring的Ioc核心容器,并根据id获取对象
     * ApplicationContextde三个常用实现类
     *      ClassPathXmlApplicationContext 他可以加载类路径下的配置文件,配置文件必须在类路径下,不在的话加载不了(更常用)
     *      FlieSystemXmlApplicationContext 他可以加载磁盘任意路径下的配置文件(必须有访问权限)
     *      AnnotationConfigApplicationContext 他是用于读取注解创建容器的
     *
     *核心容器的两个接口引发的问题:
     *  ApplicationContext:
     *      它在构建核心容器时,创建的方式是立即加载的方式,当配置文件被读取后就会立即创建对象
     *  BeanFactory:
     *      它构建核心容器时采用延迟加载的方式,也就是说,什么时候根据id获取对象了,什么时候才创建对象
     *
     * @param args
     */
    public static void main(String[] args) {
//        IAccountService as = new AccountImpl();
        //获取核心容器对象
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //根据id获取bean对象
        IAccountService as = (IAccountService)ac.getBean("accountService");
        AccountDao ado = (AccountDao) ac.getBean("accountDao");
//        AccountDao ado = ac.getBean("accountDao","accountDao.class");
        System.out.println(as);
        System.out.println(ado);
    }
}

5、Aditions:

  1. java的三层架构:

    ​ SSH:

    ​ Struts(表示层)+Spring(业务层)+Hibernate(持久层)

​ Struts是一个表示层框架,主要作用是界面展示,接收请求,分发请求

​ Hibernate:Hibernate是一个持久层框架,它只负责与关系数据库的操作。

​ Spring:Spring是一个业务层框架,是一个整合的框架,能够很好地黏合表示层与持久层

  1. 编译期错误

    ​ 程序开发过程的各个阶段都可能发生错误,可以将程序设计中的错误分成五类:
    ​ 1)编译期错误
    ​ 2)连接错误
    ​ 3)运行期错误
    ​ 4)逻辑性错误
    ​ 5)警告性错误
    ​ 排错是非常困难的,有可能花费很长的时间。程序设计的目标应该是避免出现太多的问题。对减少 排错能有所帮助的技术包括:好的设计、好的风格、边界条件测试、合理性检查、限制全局数据等等

    1. Ioc到底干了一件什么事?

      层层封装,形成模块化,良好代码环境,便于维护。维护不用修改源码,只操作bean就可。

      当我们不想new一个对象,在获得容器的控制权后,利用镜像的方式创建操作的类对象,调用类方法。

第二天:spring基于注解的IOC以及IoC的案例

1、spring中ioc的常用注解
2、案例使用xml方式和注解方式实现单表的CRUD操作
持久层技术选择:dbutils
3、改造基于注解的ioc案例,使用纯注解的方式实现
spring的一些新注解使用
4、spring和Junit整合

1)关键字解析

当用于创建对象时,@Component @Service @Repository @Contoller的作用就和在XML配置文件中编写一个标签实现的功能是一样的。当使用注解注入时需要在xml文件中告知要扫描的包 “<context:component-scan base-package=“com.itheima”></context:component-scan>”

​ ①@Component

  • 作用:用于把当前类对象存入spring容器中
  • 属性:value:用于指定bean的id。当我们不写时,它的默认值是当前类名,且首字母改小写。

​ ②@Service:一般用在业务层

​ ③@Repository:一般用在持久层

​ ④@Contoller:一般用在表现层

以上三个注解他们的作用和属性与Component是一模一样。他们三个是spring框架为我们提供明确的三层使用的注解,使我们的三层对象更加清晰。

用于注入数据时,@Autowierd @Qualifiy @Resource的作用就和在xml配置文件中的bean标签中写一个标签的作用是一样的。

​ ⑤@Autowierd

  • 作用:自动按照类型注入。只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就可以注入成功
  • 如果ioc容器中没有任何bean的类型和要注入的变量类型匹配,则报错。
  • 如果Ioc容器中有多个类型匹配时:出现位置:可以是变量上,也可以是方法上
  • 细节:在使用注解注入时,set方法就不是必须的了。

在这里插入图片描述

​ ⑥@Qualifiy

  • 作用:在按照类中注入的基础之上再按照名称注入。它在给类成员注入时不能单独使用。但是在给方法参数注入时可以(稍后我们讲)

  • 属性:value:用于指定注入bean的id。

    ⑦@Resource

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

    以上三个注入都只能注入其他bean类型的数据,而基本类型和String类型无法使用上述注解实现。另外,集合类型的注入只能通过XML来实现。

/**
 * 账户的业务层实现类
 *
 * 曾经XML的配置:
 *  <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"
 *        scope=""  init-method="" destroy-method="">
 *      <property name=""  value="" || ref=""></property>
 *  </bean> 
 *
 *      Value
 *          作用:用于注入基本类型和String类型的数据
 *          属性:
 *              value:用于指定数据的值。它可以使用spring中SpEL(也就是spring的el表达式)
 *                      SpEL的写法:${表达式}
 *
 * 用于改变作用范围的
 *      他们的作用就和在bean标签中使用scope属性实现的功能是一样的
 *      Scope
 *          作用:用于指定bean的作用范围
 *          属性:
 *              value:指定范围的取值。常用取值:singleton prototype
 *
 * 和生命周期相关的(了解)
 *      他们的作用就和在bean标签中使用init-method和destroy-methode的作用是一样的
 *      PreDestroy
 *          作用:用于指定销毁方法
 *      PostConstruct
 *          作用:用于指定初始化方法
 */
//@Service("accountService")
//@Scope("prototype")
public class AccountServiceImpl implements IAccountService {

//    @Autowired
//    @Qualifier("accountDao1")
//    @Resource(name = "accountDao2")
    @Autowired
    private IAccountDao accountDao=null;

    @PostConstruct
    public void  init(){
        System.out.println("初始化方法执行了");
    }

    @PreDestroy
    public void  destroy(){
        System.out.println("销毁方法执行了");
    }

    public void  saveAccount(){
        accountDao.saveAccount();
    }
}
2)java的类实现

​ ①账户的持久层实现类(接口略)

public class AccountDaoImpl implements AccountDao {

    @Autowireds
    private QueryRunner runner;
	
/*    public void setRunner(QueryRunner runner)  当我们用注解注入的时候,set方法就不是必须的了
    {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);
        }
    }

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

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

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

    @Override
    public void deleteAccount(Integer accountId) {
        try{
            runner.update("delete from account where id=?",accountId);
        }catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

​ ②账户的业务层实现类(接口略)

public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao accountDao;

    @Override
    public List<Account> findAllAccount() {
        return accountDao.findAllAccount();
    }

    @Override
    public Account findAccountById(Integer accountId) {
        return accountDao.findAccountById(accountId);
    }

    @Override
    public void saveAccount(Account account) {
        accountDao.saveAccount(account);
    }

    @Override
    public void updateAccount(Account account) {
        accountDao.updateAccount(account);
    }

    @Override
    public void deleteAccount(Integer acccountId) {
        accountDao.deleteAccount(acccountId);
    }
}

​ ③账户的实体类

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 +
                '}';
    }
}
3) 用xml方式实现

​ ①数据配置: 数据最终注入AccountService中,因此从终结点出发,一步一步将剩下的配置齐

	<!--配置AccountService-->
	<bean id="accountService" class="AccountService的类路径">
		<!--数据的注入,accountService对象的使用必须建立在accountDao对象的建立之上-->
    	<property name="accountDao" ref="accountDao"></property>
	</bean>

	<!--配置AccountDao-->
	<bean id="accountDao" class="AccountDao的类路径">
		 <!--注入数据,AccountDao的使用必须建立在QueryRunner对象建立的基础之上-->
   		 <property name="runner" ref="runner1"></property> 
	</bean>

	<!--配置QueryRunner-->
    <bean id="runner1" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
        <!--注入数据源,需要数据源对象-->
        <constructor-arg name="ds" ref="dataSource"></constructor-arg>
    </bean>

	<!--配置dataSource-->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<properties name="driverClass" value="com.mysql.jdbc.Driver"></properties>
        <properties name="jdbcUrl" value="jdbc:mysql://localhost:3306/eesy"></properties>
        <properties name="user" value="root"></properties>
        <properties name="password" value="1234"></properties>
	</bean>

<!-- 当使用注解注入时需要告知spring在创建容器时要扫描的包 -->
<!--<context:component-scan base-package="com.itheima"></context:component-scan>
     并在需要注入的类中使用@Component  @Service 	@Repository	@Contoller 等
-->

​ ②测试类的编写

public class AccountServiceTest{
	@Test
    public void testFindAll() {
        //1.获取容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //2.得到业务层对象
        AccountServiceImpl as = ac.getBean("accountService", AccountServiceImpl.class);
        //3.执行方法
        List<Account> accounts = as.findAllAccount();
        for(Account account : accounts){
            System.out.println(account);
        }
    }
}
4)Configration,Import方式实现
/**
* 添加注解Configuration后这个类就变成了配置类,它的作用和bean.xml是一样的
* spring中的新注解
* 	@Configuration
*		作用:指定当前类是一个注解类
* 	@ComponentScan
*		作用:用于通过注解指定spring在创建容器时要扫描的包
*		属性:basePackages,value的作用是一样的,用于创建容器时指定扫描的包,我们使用此注解相当于在    *			   bean.xml中配置了
*			<context:component-scan base-package="itheima"></context:component-scan>
*		细节:当配置类作为annotationConfigApplicationContext对象创建参数时,该注解可以不写
*            例如:在下面的测试类中 ac是一个annotationConfigApplicationContext对象
*			@Test
*			public void testFindAll(){
*				ApplicationContext ac = new                                           *                annotationConfigApplicationContext(SpringConfigration.class);
*				......
*			}
*		???	当annotationConfigApplicationContext();()中没有指定配置类路径的情况下不能省略。

* 	@Bean
*		作用:用于把当前方法的返回值作为bean对象传入到spring的ioc容器中
*		属性:
*		  name:用于指定bean的id。当不写时,默认是当前的方法名。
*		细节:
*
*	 @Import
*		作用:用于导入其它配置类
*		属性:value:用于指定其他配置类的字节码
*					当我们使用@import后,有Import注解类就是主或者父配置类。(推荐使用)
*
* 
*		  
*/
@Configuration
@ConpnentScan(basePackages = {"com.itheima"})  //basePackages,和value属性都是数组,左侧为它的标准写法。
//@ConpnentScan(com.itheima)    当注解的属性有且只有一个值,那么可以写成左边的形式
@Import(JdbcConfig.class)
public class SpringConfiguration{
    
    @Bean(name="runner")
    public QueryRunner createRunner(DataSource dataSource)
    {
        return new QueryRunner(dataSource);
    }
    //上面方法的作用效果,在加@Bean后和下面注释的内容效果相同
    /*
		<bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
        <!--注入数据源,需要数据源对象-->
        <constructor-arg name="ds" ref="dataSource"></constructor-arg>
    </bean>
    
    /*
    *	创建数据源对象
    *	@return
    */
    @Bean(name="dataSource")  
     public QueryRunner createRunner(){
         CombolPooledDataSource ds = CombolPooledDataSource();
         ds.setDriverClass("com.mysql.jdbc.Driver");
         ds.setJdbcUrl("jdbc:mysql://localhost:3306/eesy");
         ds.setUser("root");
         ds.setPassWord("1234");
         return ds;
     }
    //效果和下面注释代码相同
    /* 
    
    <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="1234"></property>
    </bean>
    
    */   
    
}
5)@PropertySource方式实现

​ ①配置.properties文件 (jdbcConfig.properties)

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/eesy
jdbc.username=root
jdbc.password=1234

​ ②编写配置类

/** 
*	@PropertySource
*		作用:用于指定 .properties 文件的位置
*		属性:
*			value:指定文件的名称和路径。关键字: classpath
*/

@PropertySource(classpath:jdbcConfig.properties)
public class JdbcConfig{
    @value("${jdbc.driver}")
    private String driver;
    @value("${jdbc.url}")
    private String url;
    @value("${jdbc.usename}")
    private String userName;
    @value("${jdbc.password}")
    private String password;
    
    
    @Bean(name="runner")
    @Scope("prototyype")
    public QueryRunner createRunner(DataSource dataSource)
    {
        return new QueryRunner(dataSource);
    }  
    
     @Bean(name="dataSource")  
     public QueryRunner createRunner(){
         CombolPooledDataSource ds = CombolPooledDataSource();
         ds.setDriverClass("driver");
         ds.setJdbcUrl("url");
         ds.setUser("userName");
         ds.setPassWord("password");
         return ds;
     } 

}
6)junit整合

1、应用程序的入口

​ main方法

2、junit单元测试中,没有main方法也能执行

​ junit集成了一个main方法

​ 该方法就会判断该类中有哪些方法使用了@Test注解

​ junit就会用有@Test注解的方法执行

3、junit不管我们是否使用了spring框架

​ 在执行测试方法时junit不知道我们是否使用了spring‘框架,他也不会为我们读取配置文件/配置类,创建 spring核心容器

4、由以上三点可知,测试方法执行时,没有ioc容器的存在,所以就算使用了@Autowierd注解,也无法注入

/**
 * 使用Junit单元测试:测试我们的配置
 * Spring整合junit的配置
 *      1、导入spring整合junit的jar(坐标)
 *      2、使用Junit提供的一个注解把原有的main方法替换了,替换成spring提供的
 *             @Runwith
 *      3、告知spring的运行器,spring和ioc创建是基于xml还是注解的,并且说明位置
 *          @ContextConfiguration
 *                  locations:指定xml文件的位置,加上classpath关键字,表示在类路径下
 *                  classes:指定注解类所在地位置
 *
 *   当我们使用spring 5.x版本的时候,要求junit的jar必须是4.12及以上
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfiguration.class)
public class AccountServiceTest {

    @Autowired
    private IAccountService as = null;


    @Test
    public void testFindAll() {
        //3.执行方法
        List<Account> accounts = as.findAllAccount();
        for(Account account : accounts){
            System.out.println(account);
        }
    }

    @Test
    public void testFindOne() {
        //3.执行方法
        Account account = as.findAccountById(1);
        System.out.println(account);
    }

    @Test
    public void testSave() {
        Account account = new Account();
        account.setName("test anno");
        account.setMoney(12345f);
        //3.执行方法
        as.saveAccount(account);

    }

    @Test
    public void testUpdate() {
        //3.执行方法
        Account account = as.findAccountById(4);
        account.setMoney(23456f);
        as.updateAccount(account);
    }

    @Test
    public void testDelete() {
        //3.执行方法
        as.deleteAccount(4);
    }
}
7)总结

​ 1)spring中的注解,有@Configration和@Import两种,前者支持的是并列关系的两个或两个以上 的注解类,使用后者则代表这是一个父配置类。

​ 2)几种配置方式优缺点对比

​ xml:配置有一定的复杂

​ 注解:并不一定省事

​ 很多情况下用xml和注解混用的方式

从整个案例的过程可以看出一些端倪,业务逻辑层和持久层之间的联系。业务层调用持久层,对数据库进行操作。用maven工程创建整个案例的过程中,我们发现当创建了工程后,导入一堆坐标,即引如一堆可以使用的jar包,我们只可以使用但不可以修改这些包的内瓤。spring这个容器给了我们很多方便。容许我们导入,并且创建包内各类的对象,方便我们的使用。

第三天

1、回顾之前讲过的一个技术:动态代理(proxy)

​ 动态代理:

  • 特点:字节码随用随创建,随用随加载。它与静态代理的区别也在于此。因为静态代理是字节码一上来就 创建好,并完成加载。 装饰者模式就是静态代理的一种体现。
  • 作用:在不修改源码的基础上对方法增强
  • 分类:①基于jdk的动态代理 (基于接口的动态代理)

​ ②基于子类的动态类

  • 基于接口的动态代理

    如何创建代理对象:使用Proxy类中的newProxyInstance方法

    1)涉及的类:jdk官方提供的 Proxy;

​ 2)要求:被代理类必须至少实现一个接口,如果没有则不能使用

​ 3)newProxyInstance方法的参数:

​ classLoader类加载器:它是用于加载代理对象字节码的,和被加载对象使用相同的类加载器

​ Class[] 字节码数组

​ InvocationHandler 用于提供增强的代码,通常情况下时匿名内部类,但不必须

  • 基于子类的动态代理:

    如何创建代理对象:使用Enhancer类中的create方法

    1)涉及的类:Enhancer;提供者:cglib库

    2)要求:被代理类不能是最终类

    3)create()方法的参数:

​ class:字节码

​ 类加载器:它是用于加载代理对象字节码的,和被加载对象使用相同的类加载器

​ Callback: 用于提供增强的代码,通常情况下时匿名内部类,但不必须。我们一般写的是该接口的子 接口实现类

  • 导入依赖
<dependencies>
	<dependency>
		<groupId>cglib<groupId>
		<artifactId>cglib<artifactId>
		<version>版本号<version>
	<dependency>
<dependencies>
/*
*	基于接口的动态代理
*/
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);

/*
*	基于子类的动态代理
*/
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(12000f);
2、AOP的概念

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

​ 优势: 减少重复代码 提高开发效率 维护方便

​ AOP的实现方式:使用动态代理技术

<!--配置AOP-->
    <aop:config>
        <!-- 配置切入点表达式 id属性用于指定表达式的唯一标识。expression属性用于指定表达式内容
              此标签写在aop:aspect标签内部只能当前切面使用。
              它还可以写在aop:aspect外面,此时就变成了所有切面可用
          -->
        <aop:pointcut id="pt1" expression="execution(* com.itheima.service.impl.*.*(..))"></aop:pointcut>
        <!--配置切面 -->
        <aop:aspect id="logAdvice" ref="logger">
            <!-- 配置前置通知:在切入点方法执行之前执行-->
            <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>

            <!-- 配置环绕通知 详细的注释请看Logger类中-->
            <aop:around method="aroundPringLog" pointcut-ref="pt1"></aop:around>
        </aop:aspect>
    </aop:config>
3、spring中的AOP相关术语
* JoinPoint 连接点:所有的切入点都是连接点,反过来不成立
* pointcut 切入点:被增强的方法称为切入点
* Advice 通知:

在这里插入图片描述

  • Target 目标对象
  • Weaving 织入:是指把增强应用到目标对象来创建新的代理对象的过程。 spring 采用动态代理织入,而 AspectJ 采用编译期织入和类装载期织入
  • Proxy 代理:一个类被 AOP 织入增强后,就产生一个结果代理类
  • Aspect 切面:是切入点和通知(提供了公共代码的类)(引介)的结合。
4、spring中基于XML和注解的AOP配置

思路分析:要将数据库的四个连接进行管理,要么全部发生,要么全不发生

​ 1. 创建好转账方法后

​ 2 创建一个连接的工具类,用于从数据源上获取连接,实现和和线程的

​ 3 创建和事务管理相关的类,包含了开启、提交、回滚事务和释放连接

​ 4 为了防止因为修改某些类中的方法而造成后期维护问题,因此使用动态代理。

​ *在业务中使用动态代理,一般是为了给需要实现的方法添加预处理或者添加后续操作,但是不干 预实现类的正常业务,把一些基本业务和主要的业务逻辑分离。我们一般所熟知的Spring的AOP 原理就是基于动态代理实现的。

在这里插入图片描述

5.、总结:
  • 第一步是对我们业务进行事务管理(原例中为try,catch,finally方式实现),让所有的sql语句执行时同时发生,如果出现异常则都不执行。但第一种方法产生了新的问题:代码变得十分臃肿。
  • 第二步使用动态代理,解决代码臃肿问题(基于结构的动态代理和基于子类的动态代理)此种方法的增强应用于本地对象的所有方法
  • 使用AOP配置的方式实现前置,后置,异常,最终和环绕通知。
6、Additions:

​ 连接池:把消耗时间获取连接的一部分放到应用加载一开始。在web应用中当我们启动tomCat加载应用时,我们创建一些连接从而在后续项目运行时不再和数据库进行连接,保证了我们使用connection时的效率。此时服务器也有一个池(线程池:它的特点是当TomCat启动时会初始化一大堆的线程,放到一个容器中,此后每当我们需要时他就会从线程池中取出一个线程供我们使用)

第四天

1、Jdbc简介

jdbc都是单表使用,不加多表是因为,多表操作是sql语句的职责。

jdbcTemplate:用来操作关系型数据库的。支持增删改。

位于:spring-jdbc-5.0.2.RELEASE.jar

还需要导入:spring-tx-5.0.2.RELEASE.jar,用于事务管理的

使用jdbc模板时需要配置模板

<!--配置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="1234"></property>
    </bean>

jdbcTemplate使用操作

//JdbcTemplate的CRUD操作
public class JdbcTemplateDemo2{
    public static void main(String[] args) {
        //1.获取容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //2.获取对象
        JdbcTemplate jt = ac.getBean("jdbcTemplate",JdbcTemplate.class);
        //3.执行操作
        jt.execute("insert into account(name,money)values('ddd',2222)");
        
        //保存操作
        jt.update("insert into account(name,money)values(?,?)","fff",5000);
        
        //更新操作
        jt.update("update account set money = money-? where id = ?",300,6);
        
        //删除操作
        jt.update("delete from account where id = ?",6);
        
        //查询所有
 //        List<Account> accounts = jt.query("select * from account where money > ?",new AccountRowMapper(),1000f);
        //AccountRowMapper,BeanPropertyRowMapper从集合的角度讲他们俩的作用是一样的,都是将Account对象封装到集合中。
        List<Account> accounts = jt.query("select * from account where money > ?",new BeanPropertyRowMapper<Account>(Account.class),1000f);
	        for(Account account : accounts){
            System.out.println(account);
        }
}
    
   /**
 * 定义Account的封装策略
 */
class AccountRowMapper implements RowMapper<Account>{
    /**
     * 把结果集中的数据封装到Account中,然后由spring把每个Account加到集合中
     * @param rs
     * @param rowNum
     * @return
     * @throws SQLException
     */
    @Override
    public Account mapRow(ResultSet rs, int rowNum) throws SQLException {
        Account account = new Account();
        account.setId(rs.getInt("id"));
        account.setName(rs.getString("name"));
        account.setMoney(rs.getFloat("money"));
        return account;
    }
}
        
      //查询一个
      //使用RowMapper的方式:常用的方式
      //  List<Account> as = jt.query("select * from account where id = ? ", new AccountRowMapper(), 55);
      // 使用ResultSetExtractor的方式:不常用的方式
      //  Account account = jt.query("select * from account where id = ?", new AccountResultSetExtractor(),3);
        List<Account> accounts = jt.query("select * from account where id = ?",new BeanPropertyRowMapper<Account>(Account.class),1);
        System.out.println(accounts.isEmpty()?"没有内容":accounts.get(0));
        
        //查询返回一行一列(使用聚合函数,但不加group by子句)
        Long count = jt.queryForObject("select count(*) from account where money > ?",Long.class,1000f);
        System.out.println(count);

在这里插入图片描述

当dao有很多个时,每个dao都有重复代码,能不能把他抽取出来呢?spring框架正好给我们提供了一个dbcDaoSupport,使用时我们可以让dao继承它。使用它的set方法,方便在xml中注入。

让Dao继承JdbcDaoSupport的方式,只能用于基于XML的方式,注解用不了。

public final void setDataSource(DataSource dataSource) {
        if (this.jdbcTemplate == null || dataSource != this.jdbcTemplate.getDataSource()) {
            this.jdbcTemplate = this.createJdbcTemplate(dataSource);
            this.initTemplateConfig();
        }

    }

2、事务控制

1)事务控制简介

第一:JavaEE体系进行分层开发,事务处理位于业务层,Spring提供了分层设计业务层的事务处理解决方案。

第二:spring框架为我们提供了一组事务控制的接口。这组接口是在spring-tx-5.0.2.RELEASE.jar中。

1)PlatformTransactionManager 此接口是spring的事务管理器,它里面提供了我们常用的操作事务的方法

实现类:org.springframework.jdbc.datasource.DataSourceTransactionManager 使用Spring JDBC或iBatis 进行持久化数据时使用

–获取事务状态信息:TransactionStatue getTransaction(TransactionDefinition define)

–提交事务:void commit(TransactionStatus status)

–回滚事务:void rollback(TransactionStatus status)

2)TransactionDefinition 它是事务的定义信息对象

在这里插入图片描述

获取事务对象名称:String getName()

获取事务隔离级:int getIsolationLevel()

​ 未提交读取(Read Uncommitted) Spring标识:ISOLATION_READ_UNCOMMITTED

​ 已提交读取(Read Committed)Spring标识:ISOLATION_READ_COMMITTED。

​ 可重复读取(Repeatable Read)Spring标识:ISOLATION_REPEATABLE_READ。

​ 序列化(Serializable)Spring标识:ISOLATION_SERIALIZABLE。

读数据一致性脏读不可读幻读
未提交读取最低阶别,只能保证不读取物理上损坏的数据
已提交读取语句级 Oracle默认级×
可重复读取事务级 Mysql默认级别××
序列化最高级别,事务级×××

获取事务传播行为:int getPropagationBehavior()

​ ①REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。一般的选 择(默认值)

​ ②SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务)

​ ③MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常

​ ④REQUERS_NEW:新建事务,如果当前在事务中,把当前事务挂起。

​ ⑤NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起

​ ⑥NEVER:以非事务方式运行,如果当前存在事务,抛出异常

​ ⑦NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行REQUIRED类似的操作。

​ 获取事务超时时间:int getTimeOut() 默认值是-1,没有超时限制。如果有,以秒为单位进行设置。

​ //读写型事务,怎删改,也会开启事务

获取事务是否只读:boolean isReadOnly() //只读型事务,执行查询时也会开启事务

3)TransactionStatus 此接口提供的是事务具体的运行状态

​ 刷新事务:void flush()

​ 获取是否存在存储点:boolean hasSavepoint()

​ 获取事务是否完成:boolean isComplecated()

​ 获取事务是否为新事物:boolean isNewTransaction()

​ 获取事务是否回滚:boolean isRollBackOnly()

​ 设置事务回滚:void setRollBackOnly()

第三:spring的事务控制都是基于AOP的,它既可以使用编程的方式实现,也可以使用配置的方式实现。我们学习的重点是使用配置的方式实现。

@Component("txManager")
@Aspect
public class TransactionManager {

    @Autowired
    private ConnectionUtils connectionUtils;

    @Pointcut("execution(* com.itheima.service.impl.*.*(..))")
    private void pt1(){}


    /**
     * 开启事务
     */
    public  void beginTransaction(){
        try {
            connectionUtils.getThreadConnection().setAutoCommit(false);
        }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();
        }
    }

    @Around("pt1()")
    public Object aroundAdvice(ProceedingJoinPoint pjp){
        Object rtValue = null;
        try {
            //1.获取参数
            Object[] args = pjp.getArgs();
            //2.开启事务
            this.beginTransaction();
            //3.执行方法
            rtValue = pjp.proceed(args);
            //4.提交事务
            this.commit();

            //返回结果
            return  rtValue;

        }catch (Throwable e){
            //5.回滚事务
            this.rollback();
            throw new RuntimeException(e);
        }finally {
            //6.释放资源
            this.release();
        }
    }
}


@Component("connectionUtils")
public class ConnectionUtils {

    private ThreadLocal<Connection> tl = new ThreadLocal<Connection>();

    @Autowired
    private DataSource dataSource;


    /**
     * 获取当前线程上的连接
     * @return
     */
    public Connection getThreadConnection() {
        try{
            //1.先从ThreadLocal上获取
            Connection conn = tl.get();
            //2.判断当前线程上是否有连接
            if (conn == null) {
                //3.从数据源中获取一个连接,并且存入ThreadLocal中
                conn = dataSource.getConnection();
                conn.setAutoCommit(false);
                tl.set(conn);
            }
            //4.返回当前线程上的连接
            return conn;
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }

    /**
     * 把连接和线程解绑
     */
    public void removeConnection(){
        tl.remove();
    }
}
2)基于XML的声明式事务控制
导入xsi、aop和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">
</beans>

在配置文件中配置业务层和持久层对
<!-- 配置service --> 
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
    <property name="accountDao" ref="accountDao"></property>
</bean>
<!-- 配置dao --> 
<bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl">
<!-- 注入dataSource -->
    <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:///spring_day04">
        </property> <property name="username" value="root">
        </property> <property name="password" value="1234"></property> 
</bean>

<!-- 配置一个事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入DataSource --> 
    <property name="dataSource" ref="dataSource"></property> 
</bean>

配置事务的通知引用事务管理器
<!-- 事务的配置 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager"> 
</tx:advice>
配置事务的属性:
<!--在tx:advice标签内部 配置事务的属性 -->
<tx:attributes>
<!-- 指定方法名称:是业务核心方法 
	read-only:是否是只读事务。默认false,不只读。
 	isolation:指定事务的隔离级别。默认值是使用数据库的默认隔离级别。 
	propagation:指定事务的传播行为。 
	timeout:指定超时时间。默认值为:-1。永不超时。 
	rollback-for:用于指定一个异常,当执行产生该异常时,事务回滚。产生其他异常,事务不回滚。没有默认值,		任何异常都回滚。 
	no-rollback-for:用于指定一个异常,当产生该异常时,事务不回滚,产生其他异常时,事务回滚。没有默认值,任何异常都回滚。 -->
<tx:method name="*" read-only="false" propagation="REQUIRED"/> 
<tx:method name="find*" read-only="true" propagation="SUPPORTS"/> 
</tx:attributes>

配置切入点表达式
<!-- 配置aop -->
<aop:config>
    <!-- 配置切入点表达式 --> 
    <aop:pointcut expression="execution(* com.itheima.service.impl.*.*(..))" id="pt1"/> </aop:config>
<!-- 在aop:config标签内部:建立事务的通知和切入点表达式的关系 --> 
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"/>
3)基于注解的配置方式

创建aop、xsi、context和tx命名空间

<!-- 配置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> 

<!-- 配置spring提供的内置数据源 --> 
<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/spring_day02"></property> 
    <property name="username" value="root"></property> 
    <property name="password" value="1234"></property> 
</bean> 

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

<!-- 开启spring对注解事务的支持 --> 
<tx:annotation-driven transaction-manager="transactionManager"/>

在业务层使用@Transactional注解

@Service("accountService") 
@Transactional(readOnly=true,propagation=Propagation.SUPPORTS)
public class AccountServiceImpl implements IAccountService {
    
 @Autowired 
    private IAccountDao accountDao; 
 @Override 
    public Account findAccountById(Integer id) 
    { return accountDao.findAccountById(id); } 
    
 @Override 
 @Transactional(readOnly=false,propagation=Propagation.REQUIRED)
    public void transfer(String sourceName, String targeName, Float money){
        
    }
}
该注解的属性和xml中的属性含义一致。该注解可以出现在接口上,类上和方法上。
出现接口上,表示该接口的所有实现类都有事务支持。 
出现在类上,表示类中所有方法有事务支持 
出现在方法上,表示方法有事务支持。 
以上三个位置的优先级:方法>>接口

3、spring5新特性

核心容器的更新:Spring Framework 5.0 现在支持候选组件索引作为类路径扫描的替代方案。索引读取 &扫描类路径JetBrains Kotlin语言支持:响应式编程风格:Junit5支持:依赖类库的更新:终止支持一些类库

Spring中都用到了那些设计模式?

  • 工厂设计模式: Spring使用工厂模式通过 BeanFactory、ApplicationContext 创建 bean 对象。
  • 代理设计模式 : Spring AOP 功能的实现。
  • 单例设计模式 : Spring 中的 Bean 默认都是单例的。
  • 模板方法模式 : Spring 中 jdbcTemplate、hibernateTemplate 等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式。
  • 包装器设计模式 : 我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源。
  • 观察者模式: Spring 事件驱动模型就是观察者模式很经典的一个应用。
  • 适配器模式 :Spring AOP 的增强或通知(Advice)使用到了适配器模式、spring MVC 中也是用到了适配器模式适配Controller。
  • 10
    点赞
  • 59
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值