转码小白的Spring笔记整理

文章目录

  • Spring
    • spring框架的概述以及spring中基于XML的IOC配置
      • Spring是什么
      • Spring的优势
      • Spring简单使用
      • Spring的依赖注入
    • spring中基于注解的IOC和ioc的案例
      • 各个实现类的注解配置
        • 用于创建对象的注解
        • 用于注入数据的注解
        • 用于改变作用范围的
        • 和生命周期相关(了解)
      • 依赖xml文件的注解配置
      • 配置类的注解配置
    • spring中的aop和基于XML以及注解的AOP
      • Spring中的AOP
        • AOP相关术语
    • spring中的JdbcTemplate以及Spring事务控制
      • spring中基于注解的事务控制
        • spring中基于注解的声明式事务控制配置步骤
        • spring中全注解配置的事务控制代码展示

Spring

spring框架的概述以及spring中基于XML的IOC配置

Spring是什么

full-stack轻量级开源框架

内核

  • Ioc(Inverse of Control) 反转控制
  • Aop(Aspect Oriented Programming) 面向切面编程

Spring的优势

  • 方便解耦
  • 简化开发
  • 声明式事务的支持
  • 方便程序的测试
  • 方便集成各种优秀框架
  • 降低JavaEE API的使用难度 (如JDBC、JavaMail、远程调用)
  • 经典学习范例

Spring简单使用

简单地将就是把对象的创建交给spring来管理,直接在XML文件中通过配置来完成对象创建需要的各类信息的注入,从而极大降低了类与类之间的依赖。

  • XML文件简单配置

    一个bean标签对应一类对象,bean标签内可以配置对象各个属性,id是获取特定类的统一标识符,class是该类全类名。

      
    
  • IoC核心容器 CoreContainer

    通过这种容器对象就可以轻松获取各种想要的bean对象。
    容器对象主要由两种

ApplicationContext BeanFactory
单例对象适用 多例对象适用
采用立即加载方式创建对象 采用延迟加载方式

所谓单例对象是这类对象即便要使用多次,但我们只打算用同一个对象;而多例对象将会创建很多次同一类的对象。像dao,service这种对象,我们主要是要调用它们的方法,而不同对象的方法都是相同的,因此这类对象一般都是单例的。但是单例对象线程不安全,这里需要注意。
所谓立即加载就是在创建容器对象、读取xml文件时就将对象加载进内存;而延迟加载就是创建容器对象时先不加载里面所bean,等到实际需要对象时再将其加载进内存。一静态,一动态。

一般来说,ApplicationContext更为常用。而它的获取方法主要由三种。

ClassPathXmlApplicationContext:它可以加载类路径下的配置文件 如,放置在resources目录下的"bean.xml"
FileSystemXmlApplicationContext:它可以加载磁盘仁义路径下的配置文件(“必须有访问权限”)如"C:\Users\jackson\Desklop\bean.xml"
AnnotationConfigApplicationContext:用于读取注解创建容器

代码如下

public class Client {

    public static void main(String[] args) {

        // ApplicationContext

        // 1. 获取核心容器对象
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml"); // 立即加载
        // 2. 根据id获取Bean对象
        AccountService accountService = (AccountService) ac.getBean("accountService");
        AccountDao accountDao = ac.getBean("accountDao", AccountDao.class);

        System.out.println(accountService);
        accountService.saveAccount();
        System.out.println(accountDao);

       // BeanFactory
        Resource resource = new ClassPathResource("bean.xml");
        BeanFactory beanFactory = new XmlBeanFactory(resource);

        AccountService accountService = (AccountService)beanFactory.getBean("AccountService"); // 延迟加载
        System.out.println(accountService);
    }
}

Spring的依赖注入

  • 依赖注入的概念
    Dependency Injection

    我的理解就是由于我们所创建的各个类之间存在的复杂的调用关系,比如网络编程中,Servlet需要创建Service对象并进行调用,Service对象需要创建Dao对象进行调用,那么在上述两个例子中,Service类的信息就注入到了Servlet中,而Dao类的信息就注入到了Service中。Servlet依赖于Service,而Service类依赖于Dao类。

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

  • 依赖关系的管理
    交给spring来维护。在当前类中需要用到其他类的对象,由spring为我们提供,我们只需要在配置文件中说明依赖关系即可。

  • 依赖注入使用

    如前所述,依赖注入的配置主要通过编辑xml文件来完成,而xml文件中,我们主要关注的就是对应着实体类的bean标签,通过修改bean标签的各个属性以及标签体就可以完成这个bean标签所对应的类的注入配置。

属性名 作用
type 用于指定要注入的数据的数据类型,该数据类型也是构造函数中某个或某些要注入的数据类型
name 用于指定给对象中指定名称的参数赋值
value 用于提供基本类型和String类型的数据
ref 用于指定其他的bean类型数据,一般一个ref = 另一个bean标签的id
scope 单例对象/多例对象或者对象作用域

能注入的数据类型有三类
- 基本类型和String 直接用value属性配置即可
- 其他bean类型(在配置文件或者注解配置过的bean) 一般需要ref
- 复杂类型/复合类型 比如list、Map这样的迭代器类型,还需要在property配置其标签体

注入的方式有三种
- 使用构造函数提供 xml配置 constructor-arg 这一子标签 , 要求类中要有相应的构造方法
- 使用set方法提供 xml配置 property 这一子标签, 要求类中要有相应的setter
- 使用注解提供
例如,配置一bean对象

		
				  // 配置姓名属性
		          // 配置一个字符串数列
		            
		                AAA
		                BBB
		                CCC
		            
		        
		          // 配置一个map
		            
		                ddd
		                eee
		            
		        
		    
  • xml配置详情
<?xml version="1.0" encoding="UTF-8"?>
<!--suppress SpringFacetInspection -->
<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">

    <!--构造函数注入:
        使用的标签:constructor-arg
        标签位置:bean标签内部
        标签属性
            type: 用于指定要注入的数据的数据类型,该数据类型也是构造函数中某个或某些要注入的数据类型
            index: 用于指定要注入的数据给构造函数中指定索引位置的参数赋值
            name: 用于指定给构造函数中指定名称的参数赋值
            ===============以上三个用于指定给构造函数中哪个参数赋值==================================
            value:用于提供基本类型和String类型的数据
            ref:用于指定其他的bean类型数据。即在spring的IoC核心容器中出现过的bean对象
        优势:
            在获取bean对象时,注入数据是必须的操作,否则对象无法创建成功
        弊端:
            改变了bean对象的实例化方式,使我们在创建对象时,如果用不到这些数据,也必须注入到对象中
    -->
    <bean id="accountService" class="com.zjuee.service.impl.AccountServiceImpl" scope="prototype">
        <constructor-arg name="name" value="test"/>
        <constructor-arg name="age" value="18"/>
        <constructor-arg name="birthday" ref="now"/>
        <constructor-arg name="accountDao" ref="accountDao"/>
    </bean>

    <!--配置一个日期对象,并将其读取到了ApplicationContext容器中-->
    <bean id="now" class="java.util.Date" scope="prototype"/>

    <!--配置AccountDao的bean对象,并将其注入到accountService2与accountService1中-->
    <bean id="accountDao" class="com.zjuee.dao.impl.AccountDaoImpl"/>

    <!--set方法注入    更常用
        涉及的标签:property
        出现的位置:bean标签的内部
        标签属性
            type: 用于指定要注入的数据的数据类型,该数据类型也是构造函数中某个或某些要注入的数据类型
            name: 用于指定给对象中指定名称的参数赋值
            value:用于提供基本类型和String类型的数据
            ref:用于指定其他的bean类型数据。即在spring的IoC核心容器中出现过的bean对象
        优势:
            创建对象时没有明确的限制,可以直接使用默认构造函数
        弊端
            存在某个成员必须有值,但获取对象时的set方法可能没有执行
    -->
    <bean id="accountService2" class="com.zjuee.service.impl.AccountServiceImpl2" scope="prototype">
        <property name="name" value="test"/>
        <property name="age" value="22"/>
        <property name="birthday" ref="now"/>
        <property name="accountDao" ref="accountDao"/>
    </bean>

    <!--复杂类型的注入/集合类型的注入
        用于给List结构集合注入的标签:
            list array set
        用于给Map结构集合注入的标签:
            map props
        结构相同,标签可以互换
    -->
    <bean id="accountService3" class="com.zjuee.service.impl.AccountServiceImpl3" scope="prototype">
        <property name="myStrs">
            <array>
                <value>AAA</value>
                <value>BBB</value>
                <value>CCC</value>
            </array>
        </property>
        <property name="myMap">
            <props>
                <prop key="testD">ddd</prop>
                <prop key="testE">eee</prop>
            </props>
        </property>
        <property name="myProp">
            <map>
            <entry key="testA" value="aaa"></entry>
            <entry key="testB" value="bbb"></entry>
            <entry key="testC" value="ccc"></entry>
        </map>
        </property>
    </bean>
</beans>
  • xml配置jdbc连接池对象
    使用c3p0和dbUtils技术,直接在spring的xml文件中完成数据库连接池的获取,并将其封装进accountDao对象。
    代码如下:
<?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.zjuee.service.impl.AccountServiceImpl">
        <!--注入dao对象-->
        <property name="accountDao" ref="accountDao"/>
    </bean>

    <!--配置dao对象-->
    <bean id="accountDao" class="com.zjuee.dao.impl.AccountDaoImpl">
        <!--注入QueryRunner-->
        <property name="runner" ref="runner"/>
    </bean>

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

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

</beans>

spring中基于注解的IOC和ioc的案例

除了使用xml文件配置各类对象信息外,我们还可以使用基于注解的方法,个人感觉注解的方法优势在于简单,但是使用注解有很多细节需要注意,相比于xml犯错的概率也更高。因此使用注解进行配置时要格外小心。

各个实现类的注解配置

用于创建对象的注解

作用于xml文件中的 bean标签一样,在spring容器中加载对象并指明对象的id。

@Component

  • 作用:用于把当前类对象存入spring容器中
  • 属性:
    value:用于指定bean的id。不写的话,bean的id默认是该类名(首字母小写)

@Controller 表现层

@Service 服务层

@Repository 持久层

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

用于注入数据的注解

作用就和在xml配置文件中的bean标签中写一个 property 标签的作用是一样的

@AutoWried: (自动连线?)

  • 作用:自动按照类型注入,只要容器中有唯一一个bean对象类型和要注入的变量类型匹配,就可以注入。
  • 出现位置:可以是变量上,也可以是方法上
  • 细节:1. 可以不用set方法
    2. 优先选择类名匹配,如果有同一个类型有多个bean对象,就会选择id与变量名相同的bean对象
  • 属性:
    value: 用于指定bean的id
@Service("accountService")  // 在容器中创建Bean对象,id为accountService
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao accountDao;  // 自动连接容器中AccountDao类的实现类对象

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

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

@Qualifier

  • 作用:在按照类中注入的基础上再按照名称注入。它再给类成员注入时不能单独使用(需要与AutoWired配合)。但是在给成员方法作参数注入时可以。
  • 举例:比如说,我们在Jdbc配置类中需要将c3p0获取的数据库连接池对象传递到dbUtils中获取一个Runner对象,就可以通过@Qualifier标签说明我们需要哪一个连接池对象。
@Bean(name = "runner")
    @Scope("prototype")
    public QueryRunner createQueryRunner(@Qualifier("dataSource2") DataSource dataSource) { // 我们需要id为dataSource2的连接池bean对象
        return new QueryRunner(dataSource);
    }

@Resource

  • 作用:直接按照bean的id注入。可以独立使用
  • 属性:name: 用于指定bean的id
    以上三个注入都只能注入其他bean类型,不能注入基本类型与复杂类型

@Value

  • 作用:用于注入基本类型和String类型的数据
  • 属性:
    value:用于指定数据的值。可以使用spring中的SpEL(Spring的EL表达式)
    SpEL的写法: ${表达式}
用于改变作用范围的

他们的作用就和在bean标签使用scope属性实现的功能一样

@Scope

  • 作用:用于指定bean的作用范围
  • 属性:
    value:指定范围的取值。常用取值: singleton(默认), prototype
和生命周期相关(了解)

它们的作用就和在bean标签中使用init-method和destroy-method的作用是一样的

@PreDestroy
用于指定销毁方法

@PostConstruct
用于指定初始化方法


使用注解也可以分为两种情况,仍然依赖xml配置中约束的信息或者完全摆脱xml文件,依赖xml配置格式如下。

依赖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: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">

    <context:component-scan base-package="com.zjuee"/>

</beans>

指明了我们将采用注解的方式以及配置对象的位置。当spring读取该xml文件后,就会自动在指定位置扫描各个类文件。

配置类的注解配置

完全独立的注解配置需要有一个描述配置信息的类文件,并在主函数获取核心容器ApplicationContext时传递配置信息类文件的字节码对象。
获取格式如下:

ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class)

那么如何编写配置类的代码呢?示例如下:

/**
* 主配置类
*/
@Configuration
@ComponentScan({"com.zjuee"}) // 指向两个待扫描的包
@Import(JdbcConfig.class)
@PropertySource("classpath:jdbcConfig.properties")
public class SpringConfiguration{}

/**
* JDBC配置类
*/
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")
    @Scope("prototype")
    public QueryRunner createQueryRunner(@Qualifier("dataSource2") DataSource dataSource) {
        return new QueryRunner(dataSource);
    }

    @Bean(name = "dataSource1")
    public ComboPooledDataSource createDataSource() {
        ComboPooledDataSource ds = new ComboPooledDataSource();
        try {
            ds.setDriverClass(driver);
            ds.setJdbcUrl(url);
            ds.setUser(username);
            ds.setPassword(password);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return ds;
    }

    @Bean(name = "dataSource2")
    public ComboPooledDataSource createDataSource2() {
        ComboPooledDataSource ds = new ComboPooledDataSource();
        try {
            ds.setDriverClass(driver);
            ds.setJdbcUrl("jdbc:mysql://localhost:3306/spring2");
            ds.setUser(username);
            ds.setPassword(password);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return ds;
    }
}

上面的示例中,SpringConfiguration是主配置文件,也称为父配置文件,我们可以在这一主配置文件中完成其他子配置文件的布局。
而JdbcConfiguration就是其中之一子配置文件,里面完成数据库相关类对象的配置。

可以看到,注解配置与xml配置在本质上是相同的。
各个注解解释如下:

@Configuration

  • 作用:指定当前类是一个配置类
  • 细节:当配置类作为AnnotationConfigApplicationContext对象创建的参数时,该注解可以不写,
    因为spring一定会读取该类。

@ComponentScan

  • 作用:用于通过注解指定spring在创建容器时要扫描的包

  • 属性:
    value:和basePackages的作用是一样的,都是创建容器时指定要扫描的包
    等同于在xml中配置

    
    

@Bean

  • 作用:用于把当前方法的返回值作为bean对象存入到spring的ioc容器中

  • 属性:
    name:用于指定bean的id。不写时默认是当前方法的名称

  • 等同于在xml中配置

     
         
         
         
    
  • 细节:
    当我们使用注解配置方法时,如果方法有参数,spring框架会去容器中查找有没有可用的bean对象,
    查找方式和Autowired是一样的。

@Scope

  • 作用:与@Bean配合使用,配置指定对象的类型,单例singleton还是多例prototype。等同于bean标签的属性scope

@Value

  • 作用:简单类型变量的数据注入。 类似于xml中bean标签的属性value

@Import

  • 作用:用于导入其他配置类
  • 属性:
    value:用于指定其他配置类的字节码。
    当我们使用import注解之后,有import注解的类就是主配置/父配置类,
    而导入的都是子配置类。

@PropertySource

  • 作用:用于指定properties文件的位置
  • 属性:
    value:指定文件的名称和路径。
    关键字:classpath,表示类路径下(resources下的文件目录)
  • 细节:注意properties中的格式,不能有分号!
  • 使用方式:在配置类名前使用@PropertySource配置properties文件的读取,然后使用EL表达式${“键名”}来获取properties中指定的值。

@PreDestroy

  • 作用:用于指定销毁方法

@PostConstruct

  • 作用:用于指定初始化方法

spring中的aop和基于XML以及注解的AOP

Spring中的AOP

通过配置的(xml或anno)方式,实现动态代理,对业务层/控制层/持久层各对象进行增强,以满足不同的需求。
比如说:可以通过AOP,将业务层中的各CRUD操作封装进一个事务中,提高数据库的安全性。

AOP相关术语
  • JoinPoint 连接点 被代理类的所有成员方法

  • Pointcut 切入点 被拦截增强的方法

  • Advice 通知 拦截到切入点之后要做的事

    知类型 执行位置
    前置通知 invoke前 初始化事务时
    后置通知 invoke后 事务提交后
    异常通知 catch 出现异常后
    最终通知 finally 事务结束,释放资源时
    环绕通知 全体部分,自己通过注解、代码等方式指定
  • Introduce 引介

  • Target 代理的目标对象

  • Weaving 织入 把增强应用到目标对象中得到代理对象

  • Proxy 代理

  • Aspect 切入点和通知的集合

spring中的JdbcTemplate以及Spring事务控制

  1. 配置事务管理器

     
     
         
     
    
  2. 配置事务的通知
    此时需要导入事务的约束 tx名称空间和约束,同时也需要aop的
    使用 tx:advice 标签配置事务通知
    属性:
    id:给事务通知起一个唯一标识
    transaction-manager 给事务通知提供一个事务管理器引用

  3. 配置事务的属性
    在事务的通知 tx:advice 标签的内部,使用 tx:attributes 标签配置事务的属性。
    method: 配置的目标方法名
    isolation:用于指定事务的隔离级别。默认值是DEFAULT,表示使用数据库的默认隔离级别
    propagation:用于指定事物的传播行为。默认值是REQUIRED,表示一定会有失误,增删改的选择。查询方法可以选择SUPPORTS
    read-only:用于指定事务是否只读。只有查询方法才能设置为true。默认值是false,表示读写
    timeout: 用于指定事务的超时时间,默认值是-1,表示永不超时。如果指定了数值,以秒为单位。
    rollback-for: 用于指定一个异常,当产生该异常时,事务回滚,产生其他异常时,事物不会滚。没有默认值,表示任何异常都回滚
    no-rollbackf-for: 用于指定一个异常,当产生该异常时,事务不回滚,产生其他异常时,事物回滚。没有默认值,表示任何异常都回滚

		<!--配置事务的通知-->
	    <tx:advice id="txAdvice" transaction-manager="transactionManager">
	        <tx:attributes>
	            <tx:method name="*" propagation="REQUIRED" read-only="false" />
	            <tx:method name="find" propagation="REQUIRED" read-only="true" />
	        </tx:attributes>
	    </tx:advice>
  1. 配置AOP中的通用切入点表达式
  2. 建立事务通知和切入点表达式的对应关系
    aop:config 标签内部配置 aop:advisor 标签表明切入的通知方法(事务控制)
		<!--配置aop-->
	    <aop:config>
	        <!--配置切入点表达式-->
	        <aop:pointcut id="pt1" expression="execution(* com.zjuee.service.impl.*.*(..))"/>
	        <!--建立切入点表达式和事务通知的对应关系-->
	        <aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"/>
	    </aop:config>

spring中基于注解的事务控制

spring中基于注解的声明式事务控制配置步骤
  1. 配置事务管理器

     
     
         
     
    
  2. 开启spring对注解事务的支持

	<!--声明采用注解方式,指定spring要扫描的包-->
	<context:component-scan base-package="com.zjuee"/>
	
	<!--xml配置文件中开启spring对注解事务的支持-->
    <tx:annotation-driven transaction-manager="transactionManager"/>
  1. 在需要事务支持的地方使用@Transactional注解,相当于手动选择切面,不再需要spring中AOP的支持
@Service("accountService")
@Transactional(propagation = Propagation.SUPPORTS, readOnly=true)
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao accountDao;

    @Transactional(propagation = Propagation.REQUIRED, readOnly = false)
    public void transfer(String sourceName, String targetName, Float money) {

        // 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);

    }

    public Account findAccountById(Integer id) {
        return accountDao.findAccountById(id);
    }
}

注意:使用注解时xml配置文件的约束空间要新命名context,对应的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: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/context
        http://www.springframework.org/schema/context/spring-context.xsd">
spring中全注解配置的事务控制代码展示

全注解即不再需要xml配置文件,主要通过@Configuration配置类完成spring信息配置。

账户的持久层实现类

@Repository("accountDao")
public class AccountDaoImpl implements AccountDao {

    @Autowired
    private JdbcTemplate template;

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

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

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

    public List<Account> findAllAccount() {
        return template.query("select * from account ", new BeanPropertyRowMapper<Account>(Account.class));
    }
}

业务层实现类

@Service("accountService")
@Transactional(propagation = Propagation.SUPPORTS, readOnly=true)
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao accountDao;

    @Transactional(propagation = Propagation.REQUIRED, readOnly = false)
    public void transfer(String sourceName, String targetName, Float money) {

        // 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);

    }

    public Account findAccountById(Integer id) {
        return accountDao.findAccountById(id);
    }
}

spring的配置类,相当于bean.xml

@Configuration
@ComponentScan("com.zjuee")
@Import({JdbcConfig.class, TransactionConfig.class})
@PropertySource("jdbcConfig.properties")
@EnableTransactionManagement // 开启事务的注解配置声明
public class SpringConfiguration {
}

和事务相关的配置类

public class TransactionConfig {

    /**
     * 配置事务管理器
     * @param dataSource
     * @return
     */
    @Bean("transactionManager")
    public PlatformTransactionManager createTxManager(DataSource dataSource) {
        return new DataSourceTransactionManager(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(name = "jdbcTemplate")
    public JdbcTemplate createJdbcTemplate(DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }

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

测试类

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfiguration.class)
public class TestSpringTxManager {

    @Resource(name = "accountService")
    private AccountService as;

    @Test
    public void testFind() {
        Account account = as.findAccountById(3);
        System.out.println(account);
    }

    @Test
    public void testTransfer() {
        as.transfer("bbb","aaa",200f);
    }
}

主要参考:传智播客视频教程
Spring官方文档:spring-framework-5.0.2.RELEASE-dist/spring-framework-5.0.2.RELEASE/docs/spring-framework-reference/index.html

你可能感兴趣的:(spring,intellij,idea,java)