老冯课堂笔记Spring

发布时间 2023-04-09 00:18:47作者: lkjlwq

1.简单自己实现IOC

pom.xml

<?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.darksnow</groupId>
    <artifactId>SpringDemo01</artifactId>
    <version>1.0-SNAPSHOT</version>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>7</source>
                    <target>7</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <dependency>
            <groupId>dom4j</groupId>
            <artifactId>dom4j</artifactId>
            <version>1.6.1</version>
        </dependency>
        <dependency>
            <groupId>jaxen</groupId>
            <artifactId>jaxen</artifactId>
            <version>1.1.6</version>
        </dependency>
    </dependencies>
</project>

beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans>
    <bean id="userService" class="com.darksnow.service.impl.UserServiceImpl"/>
</beans>

service层代码

package com.darksnow.service.impl;

import com.darksnow.service.UserService;

public class UserServiceImpl implements UserService {
}

----------------------------------
    
package com.darksnow.service;

public interface UserService {
}    

Beanfactory

package com.darksnow.factory;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class BeanFactory {

    private static final Map<String,Object> map = new HashMap<>();
    //用Map来存储对象,因为有查找需求,相当于一个容器对象,包含了所有对象

    static {
        //获取配置文件的输入流对象
        InputStream inputStream = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");
        //解析xml,获取xml中的信息
        SAXReader reader = new SAXReader();

        try {
            //创建文档对象
            Document doc = reader.read(inputStream);
            //获取根节点
            Element root = doc.getRootElement();
            //获取根节点中的所有子节点
            List<Element> beanList = root.elements("bean");
            //遍历获取每一个bean的id和class
            for(Element element : beanList) {
                String id = element.attributeValue("id");
                String className = element.attributeValue("class");

                //通过className全限类名创建对象
                //获取字节码文件对象
                Class<?> clazz = Class.forName(className);
                //通过反射创建对象
                Object obj = clazz.newInstance();
                //存储到Map集合中
                map.put(id,obj);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //需要实现的功能:传入一个id值,获取对应的Bean对象
    public static Object getBean(String id){
        return map.get(id);
    }
}

测试类

package com.darksnow.test;

import com.darksnow.factory.BeanFactory;

public class TestIoc {
    public static void main(String[] args) {
        Object userService = BeanFactory.getBean("userService");
        System.out.println(userService);

        Object main = BeanFactory.getBean("main");
        System.out.println(main);
    }
}

2.Spring入门案例

pom.xml

<?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.darksnow</groupId>
    <artifactId>SpringDemo01</artifactId>
    <version>1.0-SNAPSHOT</version>


    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>

配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--
        scope属性取值:
            prototype:对象的创建是多例模式
            singleton:对象的创建是单例模式(默认的)
      -->
    <bean id="userDao" class="com.darksnow.dao.impl.UserDaoImpl" scope="singleton" init-method="init" destroy-method="destroy"/>
</beans>

接口与实现类

package com.darksnow.dao;

public interface UserDao {
}
-----------------------------------------------------------
package com.darksnow.dao.impl;

import com.darksnow.dao.UserDao;

public class UserDaoImpl implements UserDao {

    public void init() {
        System.out.println("userDao的初始化....");
    }

    public void destroy() {
        System.out.println("userDao的销毁....");
    }
}

测试类

package com.darksnow.test;

import com.darksnow.dao.UserDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestSpringIoc {
    public static void main(String[] args) {
        //1.创建Spring的IOC容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");

        //创建容器对象时,创建对象,只创建单例模式的对象,多例模式的对象在获取时创建
        //单例模式的对象在创建容器时创建,销毁容器时销毁
        //多例模式的对象在获取时创建,即使容器被销毁,对象也不见得立马被销毁,当对象长时间不再被引用,则被垃圾回收机制回收
        Object userDao = ac.getBean("userDao");
        System.out.println(userDao);

        UserDao userDao1 = ac.getBean(UserDao.class);
        System.out.println(userDao1);

        UserDao userDao2 = ac.getBean("userDao", UserDao.class);
        System.out.println(userDao2);

        //ac.close();  销毁容器,ClassPathXmlApplicationContext里面的方法
    }
}

3.实例化Bean的三种方式

3.1 实例工厂

package com.darksnow.factory;

import com.darksnow.dao.UserDao;
import com.darksnow.dao.impl.UserDaoImpl;

public class InstanceFactory {
    public UserDao getUserDao() {
        return new UserDaoImpl();
    }
}

3.2 静态工厂

package com.darksnow.factory;

import com.darksnow.dao.UserDao;
import com.darksnow.dao.impl.UserDaoImpl;

public class StaticFactory {
    public static UserDao getUserDao() {
        return new UserDaoImpl();
    }
}

3.3 applicationContext.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">

    <!-- 方式一:通过xml方式配置 -->
    <bean id="userDao1" class="com.darksnow.dao.impl.UserDaoImpl" />

    <!-- 方式二:实例工厂   -->
    <!-- 创建实例工厂   -->
    <bean id="instanceFactory" class="com.darksnow.factory.InstanceFactory"/>
    <!--  通过实例工厂创建UserDaoImpl对象  -->
    <bean id="userDao2" factory-bean="instanceFactory" factory-method="getUserDao"/>

    <!-- 方式三:静态工厂 -->
    <bean id="userDao3" class="com.darksnow.factory.StaticFactory" factory-method="getUserDao"/>
</beans>

3.4 测试类

package com.darksnow.test;

import com.darksnow.dao.UserDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestSpringIoc {
    public static void main(String[] args) {
        //创建Spring的IOC容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");

        //方式一:通过xml方式来实例化Bean
        UserDao userDao1 = ac.getBean("userDao1", UserDao.class);
        System.out.println(userDao1);

        //方式二:通过实例工厂来实例化Bean
        UserDao userDao2 = ac.getBean("userDao2", UserDao.class);
        System.out.println(userDao2);

        //方式三:通过静态工厂来实例化Bean
        UserDao userDao3 = ac.getBean("userDao3", UserDao.class);
        System.out.println(userDao3);
    }
}

4.Spring的依赖注入

4.1 构造方法注入

User实体

package com.darksnow.pojo;

import java.util.Date;

public class User {
    private Integer id;
    private String username;
    private Character sex;
    private Date birthday;

    public User() {
    }

    public User(Integer id, String username, Character sex, Date birthday) {
        this.id = id;
        this.username = username;
        this.sex = sex;
        this.birthday = birthday;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", sex=" + sex +
                ", birthday=" + birthday +
                '}';
    }
}

配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--
        依赖注入
        默认创建对象的方式:使用默认的无参构造方法创建对象
        但是我们也可以基于带参构造方法创建对象
    -->
    <bean id="user" class="com.darksnow.pojo.User">
        <!--   通过构造方法上参数的索引进行赋值   -->
        <constructor-arg index="0" value="1"/>
        <constructor-arg index="1" value="darksnow"/>
        <!--  通过构造方法上参数的类型进行赋值      -->
        <constructor-arg type="java.lang.Character" value="男"/>
        <!--
            value:该属性只能赋值简单的类型和String类型
            ref:pojo等复杂类型,可以关联创建好的对象
          -->
        <constructor-arg name="birthday" ref="birthday"/>
    </bean>

    <bean id="birthday" class="java.util.Date"/>
</beans>

测试类

package com.darksnow.test;

import com.darksnow.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.Date;

public class TestSpringDi {
    public static void main(String[] args) {
        //创建Spring的IOC容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");

        User user = ac.getBean("user", User.class);
        System.out.println(user);
    }
}

4.2 set方法注入和p名称空间注入

User实体

package com.darksnow.pojo;

import java.util.Date;

public class User {
    private Integer id;
    private String username;
    private Character sex;
    private Date birthday;

    public Integer getId() {
        return id;
    }

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

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Character getSex() {
        return sex;
    }

    public void setSex(Character sex) {
        this.sex = sex;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", sex=" + sex +
                ", birthday=" + birthday +
                '}';
    }
}

配置文件

<?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:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="birthday" class="java.util.Date"/>

    <!-- 通过set方法注入   -->
    <bean id="user" class="com.darksnow.pojo.User">
        <property name="id" value="110"/>
        <property name="username" value="安欣"/>
        <property name="sex" value="男" />
        <property name="birthday" ref="birthday" />
    </bean>

    <!--  使用p名称空间注入(基于set方法)   -->
    <bean id="user2" class="com.darksnow.pojo.User" p:id="520" p:username="孟钰" p:sex="女" p:birthday-ref="birthday"/>
</beans>

测试类

package com.darksnow.test;

import com.darksnow.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestSpringDi {
    public static void main(String[] args) {
        //创建Spring的IOC容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");

        User user = ac.getBean("user", User.class);
        System.out.println(user);

        User user2 = ac.getBean("user2", User.class);
        System.out.println(user2);
    }
}

4.3 集合注入

User实体

package com.darksnow.pojo;

import java.util.*;

public class User {
    private List<String> list;
    private Set<String> set;
    private String[] strs;
    private Map<String,String> map;
    private Properties prop;

    public List<String> getList() {
        return list;
    }

    public void setList(List<String> list) {
        this.list = list;
    }

    public Set<String> getSet() {
        return set;
    }

    public void setSet(Set<String> set) {
        this.set = set;
    }

    public String[] getStrs() {
        return strs;
    }

    public void setStrs(String[] strs) {
        this.strs = strs;
    }

    public Map<String, String> getMap() {
        return map;
    }

    public void setMap(Map<String, String> map) {
        this.map = map;
    }

    public Properties getProp() {
        return prop;
    }

    public void setProp(Properties prop) {
        this.prop = prop;
    }
}

配置文件

<?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:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="birthday" class="java.util.Date"/>

    <bean id="user" class="com.darksnow.pojo.User">
        <!--   array,list,set结构相同,标签可以混用     -->
        <!--   list集合注入     -->
        <property name="list">
            <list>
                <value>强哥</value>
                <value>刀哥</value>
                <value>小江</value>
            </list>
        </property>
        <!--   set集合注入     -->
        <property name="set">
            <set>
                <value>书婷</value>
                <value>黄瑶</value>
                <value>老默</value>
            </set>
        </property>
        <!--   数组注入     -->
        <property name="strs">
            <array>
                <value>高启强</value>
                <value>高启盛</value>
                <value>高启兰</value>
            </array>
        </property>

        <!--  map集合注入,map和properties结构相同,可以混用  -->
        <property name="map">
            <map>
                <entry key="1" value="李响"/>
                <entry key="2" value="谭思言"/>
            </map>
        </property>
        <!--  properties注入   -->
        <property name="prop">
            <props>
                <prop key="666">李有田</prop>
                <prop key="777">李宏伟</prop>
            </props>
        </property>
    </bean>
    
</beans>

测试类

package com.darksnow.test;

import com.darksnow.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.Arrays;

public class TestSpringDi {
    public static void main(String[] args) {
        //创建Spring的IOC容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");

        User user = ac.getBean("user", User.class);
        System.out.println(user.getList());
        System.out.println(user.getSet());
        System.out.println(Arrays.toString(user.getStrs()));
        System.out.println(user.getMap());
        System.out.println(user.getProp());
    }
}

5.CRUD案例

配置文件

<?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:p="http://www.springframework.org/schema/p"
       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 https://www.springframework.org/schema/context/spring-context.xsd">
	<!--  需要扫描包才能够使用@Controller,@Service,@Repository,@Component   -->
    <context:component-scan base-package="com.darksnow" />

    <bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
        <constructor-arg ref="comboPooledDataSource"/>
    </bean>

    <bean id="comboPooledDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver"/>
        <property name="jdbcUrl" value="jdbc:mysql:///darksnow?CharacterEncoding=utf-8"/>
        <property name="user" value="root"/>
        <property name="password" value="root"/>
    </bean>
</beans>

实体类

package com.darksnow.pojo;

public class Account {
    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 +
                '}';
    }
}

dao层

package com.darksnow.dao;

import com.darksnow.pojo.Account;

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

public interface AccountDao {

    List<Account> findAll() throws SQLException;

    Account findById(Integer id) throws SQLException;

    void save(Account account) throws SQLException;

    void update(Account account) throws SQLException;

    void delete(Integer id) throws SQLException;
}

-----------------------------------------

package com.darksnow.dao.impl;

import com.darksnow.dao.AccountDao;
import com.darksnow.pojo.Account;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;

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

@Repository
public class AccountDaoImpl implements AccountDao {

    @Autowired
    private QueryRunner runner;

    public List<Account> findAll() throws SQLException {
        String sql = "select * from account";
        return runner.query(sql,new BeanListHandler<Account>(Account.class));
    }

    public Account findById(Integer id) throws SQLException {
        String sql = "select * from account where id = ?";
        return runner.query(sql,new BeanHandler<Account>(Account.class),id);
    }

    public void save(Account account) throws SQLException {
        String sql = "insert into account values(?,?,?)";
        runner.update(sql,null,account.getName(),account.getMoney());
    }

    public void update(Account account) throws SQLException {
        String sql = "update account set name = ? where id = ?";
        runner.update(sql,account.getName(),account.getId());
    }

    public void delete(Integer id) throws SQLException {
        String sql = "delete from account where id = ?";
        runner.update(sql,id);
    }
}    

service层

package com.darksnow.service;

import com.darksnow.pojo.Account;

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

public interface AccountService {
    List<Account> findAll() throws SQLException;

    Account findById(Integer id) throws SQLException;

    void save(Account account) throws SQLException;

    void update(Account account) throws SQLException;

    void delete(Integer id) throws SQLException;
}

-----------------------------------------
    
package com.darksnow.service.impl;

import com.darksnow.dao.AccountDao;
import com.darksnow.pojo.Account;
import com.darksnow.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.sql.SQLException;
import java.util.List;

/*
 * @Component只要标记了该注解,并且标记该注解的类在扫描范围内,就会创建对象
 * 需要在xml配置文件中配置 <context:component-scan base-package="你要扫描的包路径,该路径下的所有类都会被扫描到"/>
 *
 * 除了上述注解还有三个
 * @Controller  一般用于web层
 * @Service     一般用于service层
 * @Repository  一般用于dao层
 */
//@Service   //如果没有指定名称,默认是简单类名(但是首字母小写),比如:类名为UserServiceImpl,getBean的时候就用userServiceImpl
@Service("accountService")  //主句话相当于,你在xml中配置<bean id="accountService" class="com.darksnow.service.impl.AccountServiceImpl">
public class AccountServiceImpl implements AccountService {


    /*
        必须与@Autowired结合使用
        作用:如果自动注入按照类型注入失败,则按照指定的名称注入
        不是很常用,了解
     */
    //@Qualifier("accountDao")

    /*
        @Autowired 可以自动按照类型注入
        当属性或者set方法标记了@Autowired,会自动在容器中查找该属性类型的对象,如果有多个则会选择名字相同的
     */
    //@Autowired

    /*
        一样可以自动按照类型注入,但是不同于@Autowired的是,它是不是有Spring提供的而是JDK
        而且它还能够按照指定的名称注入,但是@Autowired不行!
     */
    //@Resource(name = "accountDaoImpl")

    @Resource
    private AccountDao accountDao;

    public List<Account> findAll() throws SQLException {
        System.out.println(accountDao);
        return accountDao.findAll();
    }

    public Account findById(Integer id) throws SQLException {
        return accountDao.findById(id);
    }

    public void save(Account account) throws SQLException {
        accountDao.save(account);
    }

    public void update(Account account) throws SQLException {
        accountDao.update(account);
    }

    public void delete(Integer id) throws SQLException {
        accountDao.delete(id);
    }
}

测试类

package com.darksnow.test;

import com.darksnow.dao.AccountDao;
import com.darksnow.pojo.Account;
import com.darksnow.service.AccountService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

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

public class TestCrud {
    private ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
    private AccountService accountService = ac.getBean("accountService", AccountService.class);

    @Test
    public void testFindAll() throws SQLException {
        List<Account> accountList = accountService.findAll();
        for (Account account : accountList) {
            System.out.println(account);
        }
    }

    @Test
    public void testFindById() throws SQLException {
        Account account = accountService.findById(1);
        System.out.println(account);
    }

    @Test
    public void testSave() throws SQLException {
        Account account = new Account();
        account.setName("康健");
        account.setMoney(1500f);
        accountService.save(account);
    }

    @Test
    public void testUpdate() throws SQLException {
        Account account = new Account();
        account.setId(3);
        account.setName("康康建");
        accountService.update(account);
    }

    @Test
    public void testDelete() throws SQLException {
        accountService.delete(3);
    }
}

6.纯注解CRUD案例(无xml)

基于原来的CRUD案例,dao层和service层,pojo代码没有做任何修改

配置类

package com.darksnow.config;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

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

@Configuration
//加载properties配置文件
@PropertySource({"jdbc.properties"})
public class JdbcConfiguration {

    //给属性赋值,只能给简单类型赋值
    @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"),用在方法上,用来指定方法创建的对象存储到容器中,name就是在容器中的名称
    @Bean("queryRunner")
    public QueryRunner createQueryRunner(DataSource dataSource) {
        return new QueryRunner(dataSource);
    }

    @Bean("dataSource")
    public DataSource createDataSource()  {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        try {
            dataSource.setDriverClass(driver);
            dataSource.setJdbcUrl(url);
            dataSource.setUser(username);
            dataSource.setPassword(password);
        }catch (PropertyVetoException e){
            e.printStackTrace();
        }
        return dataSource;
    }
}

-----------------------------------
    
package com.darksnow.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

//该注解用于标记指定类为配置文件类
@Configuration
//相当于<context:component-scan base-package="com.darksnow" />
@ComponentScan({"com.darksnow"})
//引入其他配置类
@Import({JdbcConfiguration.class})
public class SpringConfiguration {
}    

测试类

package com.darksnow.test;


import com.darksnow.config.SpringConfiguration;
import com.darksnow.pojo.Account;
import com.darksnow.service.AccountService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

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

public class TestCrud {
    @Test
    public void testFindAll() throws SQLException {
        //存注解创建容器对象
        ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);

        AccountService accountService = ac.getBean("accountService", AccountService.class);

        List<Account> accountList = accountService.findAll();

        for (Account account : accountList) {
            System.out.println(account);
        }
    }
}

其他注解

//配置对象的模式,默认是单例,配置在类上
@Scope("singleton") 
//写在方法上,相当于bean标签内的属性:init-method,指定初始化方法
@PostConstruct
//写在方法上,相当于bean标签内的属性:destroy-method,指定对象的销毁方法
@PreDestroy

7.xml加载properties

<?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:p="http://www.springframework.org/schema/p"
       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 https://www.springframework.org/schema/context/spring-context.xsd">

    <!--  在xml中引入其他的配置文件  -->
    <!-- <import resource="applicationContext-dao.xml"/> -->

    <!--  加载properties配置文件  -->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    
    <!--  需要扫描包才能够使用@Controller,@Service,@Repository,@Component   -->
    <context:component-scan base-package="com.darksnow" />

    <bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner" >
        <constructor-arg ref="comboPooledDataSource"/>
    </bean>

    <bean id="comboPooledDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <!--   ${jdbc.driver} 获取的是properties中的配置  -->
        <property name="driverClass" value="${jdbc.driver}"/>
        <property name="jdbcUrl" value="${jdbc.url}"/>
        <property name="user" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
</beans>

8.Spring与Junit整合

<!-- 引入依赖坐标 -->
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-test</artifactId>
	<version>5.2.3.RELEASE</version>
</dependency>

8.1 xml的形式

package com.darksnow.test;

import com.darksnow.pojo.Account;
import com.darksnow.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

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

//替换Junit的运行器为spring与junit整合后的运行器
@RunWith(SpringJUnit4ClassRunner.class)
//指定配置文件路径,会自动创建容器对象
@ContextConfiguration({"classpath:applicationContext.xml"})
public class TestCrud {

    @Autowired
    private AccountService accountService;

    @Test
    public void testFindAll() throws SQLException {
        List<Account> accountList = accountService.findAll();

        for (Account account : accountList) {
            System.out.println(account);
        }
    }
}

8.2 纯注解的形式

package com.darksnow.test;

import com.darksnow.config.SpringConfiguration;
import com.darksnow.pojo.Account;
import com.darksnow.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

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

//替换Junit的运行器为spring与junit整合后的运行器
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {SpringConfiguration.class})
public class TestCrud {

    @Autowired
    private AccountService accountService;

    @Test
    public void testFindAll() throws SQLException {
        List<Account> accountList = accountService.findAll();

        for (Account account : accountList) {
            System.out.println(account);
        }
    }
}

9.动态代理

9.1 JDK动态代理

package com.darksnow.proxy;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.junit.Test;

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

class ProductFactory {
    //制造产品
    public void make() {
        System.out.println("生产了一个产品");
    }
}

//建立一个销售的网点:直营
class OldSale {
    public void sale(Float money) {
        System.out.println("正在以" + money + "价格卖出");
    }
}

//总经销商
interface NewSale {
    //卖产品
    void sale(Float money);
}

//经销商创建的销售网点
class NewSaleImpl implements NewSale {
    public void sale(Float money) {
        System.out.println("正在以" + money + "价格卖出");
    }
}

public class TestProxy {
    //JDK动态代理:基于接口的动态代理
    @Test
    public void testJDKProxy() {
        //代理之前对象
        NewSale newSale = new NewSaleImpl();
        //newSale.sale(2000F);

        /*
            对卖出方法进行增强,可以判断我以某个价格卖出是亏了还是赚了
            TestProxy.class.getClassLoader():类加载器
            newSale.getClass().getInterfaces():实现类的接口数组
            new InvocationHandler():以匿名内部类的方式实现了InvocationHandler接口,具体的增强逻辑写在覆盖该接口中的invoke方法中
         */
        NewSale sale = (NewSale) Proxy.newProxyInstance(TestProxy.class.getClassLoader(), newSale.getClass().getInterfaces(), new InvocationHandler() {
            /*
                Object proxy:代理对象
                Method method:代理的方法,原来未增强的方法
                Object[] args:代理的方法的参数
                返回值:代理的方法的返回值
             */
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //生产产品
                ProductFactory productFactory = new ProductFactory();
                productFactory.make();
                /*
                    开始销售
                    invoke方法的参数
                        参数1:要增强的方法所在的对象
                        参数2:要增强的方法的参数
                 */
                method.invoke(newSale, args);  //调用的是原来没有增强的方法

                if ((Float) args[0] > 1500) {
                    System.out.println("卖的价格高于成本,挣了,可卖");
                } else {
                    System.out.println("卖的价格低于成本,赔了,不可卖");
                }
                return null;
            }
        });

        sale.sale(2000F);
    }
}

9.2 cglib动态代理

	//cglib动态代理:基于类的动态代理
    @Test
    public void testCglibProxy() {

        //cglib动态代理,代理对象是要增强的方法所在的对象的一个子类
        //要增强的方法所在的对象,要增强的方法为OldSale对象中的sale(Float money)方法
        OldSale oldSale = new OldSale();

        //创建cglib代理对象
        //1.创建增强类对象
        Enhancer enhancer = new Enhancer();
        //2.指定代理对象的父类
        enhancer.setSuperclass(OldSale.class);
        //3.指定增强的内容
        //MethodInterceptor接口是方法拦截器,拦截方法进行增强
        enhancer.setCallback(new MethodInterceptor() {
            /*
                参数说明
                    Object o:代理对象,增强后的对象
                    Method method:代理的方法
                    Object[] objects:代理的方法的参数
                    MethodProxy methodProxy:代理方法,增强后的方法
             */
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                //生产产品
                ProductFactory productFactory = new ProductFactory();
                productFactory.make();

                //开始销售,执行原来要增强的方法
                method.invoke(oldSale,objects);

                if ((Float) objects[0] > 1500) {
                    System.out.println("卖的价格高于成本,挣了,可卖");
                } else {
                    System.out.println("卖的价格低于成本,赔了,不可卖");
                }
                return null;
            }
        });

        //4.创建代理对象
        OldSale oldSaleProxy = (OldSale) enhancer.create();
        oldSaleProxy.sale(500F);
    }

10.动态代理增强转账业务

配置文件

<?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:p="http://www.springframework.org/schema/p"
       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 https://www.springframework.org/schema/context/spring-context.xsd">


    <!--  加载properties配置文件  -->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    
    <!--  需要扫描包才能够使用@Controller,@Service,@Repository,@Component   -->
    <context:component-scan base-package="com.darksnow" />

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

    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <!--   ${jdbc.driver} 获取的是properties中的配置  -->
        <property name="driverClass" value="${jdbc.driver}"/>
        <property name="jdbcUrl" value="${jdbc.url}"/>
        <property name="user" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
</beans>

工具类

package com.darksnow.utils;

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

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

/*
    一个管理连接的工具类,用于实现连接和线程的绑定
 */
@Component
public class ConnectionUtil {
    private final ThreadLocal<Connection> tl = new ThreadLocal<>();

    @Autowired
    private DataSource dataSource;

    /*
        获取当前线程上绑定的连接
     */
    public Connection getThreadConnection() {
        try {
            Connection conn = tl.get();
            //判断线程上是否绑了
            if (conn == null) {
                //从数据源中获取一个连接
                conn = dataSource.getConnection();
                //和线程绑定
                tl.set(conn);
            }
            //返回线程上的连接
            return tl.get();
        }catch (SQLException e){
            throw new RuntimeException(e);
        }
    }

    /*
        把连接和当前线程解绑
     */
    public void remove() {
        tl.remove();
    }
}
package com.darksnow.utils;

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

import java.sql.SQLException;

/*
    事务管理器
 */
@Component
public class TransactionManager {

    @Autowired
    private ConnectionUtil connectionUtil;

    //开始事务
    public void beginTransaction() {
        //从当前线程上获取连接,实现开启事务
        try {
            connectionUtil.getThreadConnection().setAutoCommit(false);
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }

    //提交事务
    public void commit() {
        try {
            connectionUtil.getThreadConnection().commit();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }

    //回滚事务
    public void rollback() {
        try {
            connectionUtil.getThreadConnection().rollback();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }

    //释放连接
    public void release() {
        try {
            connectionUtil.getThreadConnection().setAutoCommit(true);
            //关闭连接
            connectionUtil.getThreadConnection().close();
            //解绑线程
            connectionUtil.remove();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }
}

实体类

package com.darksnow.pojo;

public class Account {
    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 +
                '}';
    }
}

dao层

package com.darksnow.dao;

import com.darksnow.pojo.Account;

import java.sql.SQLException;

public interface AccountDao {
    Account findByName(String fromName) throws SQLException;

    void update(Account account) throws SQLException;
}

-----------------------------------------
    
package com.darksnow.dao.impl;

import com.darksnow.dao.AccountDao;
import com.darksnow.pojo.Account;
import com.darksnow.utils.ConnectionUtil;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import java.sql.Connection;
import java.sql.SQLException;

@Repository
public class AccountDaoImpl implements AccountDao {

    @Autowired
    private ConnectionUtil connectionUtil;

    @Autowired
    private QueryRunner queryRunner;

    //根据账户名查询账户
    @Override
    public Account findByName(String fromName) throws SQLException {
        String sql = "select * from account where name = ?";
        //从线程中获取一个连接对象
        Connection conn = connectionUtil.getThreadConnection();
        return queryRunner.query(conn,sql,new BeanHandler<Account>(Account.class),fromName);
    }

    //更新账户
    @Override
    public void update(Account account) throws SQLException {
        String sql = "update account set money = ? where name = ?";
        Connection conn = connectionUtil.getThreadConnection();
        queryRunner.update(conn, sql, account.getMoney(), account.getName());
    }
}

service层

package com.darksnow.service;

public interface AccountService {
    void transfer(String fromName, String toName, Float money);
}

------------------------------------
    
package com.darksnow.service.impl;

import com.darksnow.dao.AccountDao;
import com.darksnow.pojo.Account;
import com.darksnow.service.AccountService;
import org.apache.commons.dbutils.QueryRunner;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.sql.SQLException;

/*
    一个事务必须在一个Connection中完成
    ThreadLocal:线程绑定,绑定Connection对象,业务层和持久层需要Connection就从ThreadLocal中获取
 */
@Service
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao accountDao;

    @Override
    public void transfer(String fromName, String toName, Float money) {
        try {
            //查询要转出的账号
            Account fromAccount = accountDao.findByName(fromName);

            //查询要转入的账号
            Account toAccount = accountDao.findByName(toName);

            //修改要转出的账户余额
            fromAccount.setMoney(fromAccount.getMoney() - money);

            //修改要转入的账户余额
            toAccount.setMoney(toAccount.getMoney() + money);

            //持久化到数据库
            accountDao.update(fromAccount);

//            int i = 1/0;

            accountDao.update(toAccount);
        }catch (SQLException e) {
            e.printStackTrace();
        }
    }
}    

测试类

package com.darksnow.test;

import com.darksnow.service.AccountService;
import com.darksnow.utils.TransactionManager;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

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

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestTransfer {

    @Autowired
    private AccountService accountService;

    @Autowired
    private TransactionManager txManager;

    @Test
    public void transfer() {
        accountService.transfer("张三","李四",500f);
    }

    @Test
    public void testJDKProxyService() {
        AccountService accountServiceProxy = (AccountService) Proxy.newProxyInstance(accountService.getClass().getClassLoader(), accountService.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                try {
                    //开启事务
                    txManager.beginTransaction();
                    //执行未增强之前的方法
                    method.invoke(accountService, args);
                    //提交事务
                    txManager.commit();
                } catch (Exception e) {
                    //事务回滚
                    txManager.rollback();
                    e.printStackTrace();
                } finally {
                    //还原状态
                    txManager.release();
                }
                return null;
            }
        });

        accountServiceProxy.transfer("张三", "李四", 500f);
    }

    @Test
    public void testCglibProxyService() {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(accountService.getClass());
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                try {
                    txManager.beginTransaction();
                    method.invoke(accountService,objects);
                    txManager.commit();
                }catch (Exception e) {
                    txManager.rollback();
                    e.printStackTrace();
                }finally {
                    txManager.release();
                }
                return null;
            }
        });
        AccountService as = (AccountService) enhancer.create();
        as.transfer("张三","李四",500f);
    }
}

11.AOP

11.1 概念

AOP:全称为Aspect Oriented Programming 即:面向切面编程

简单说它就是把我们程序重复的代码抽取出来,在需要执行的时候,使用动态代理的技术,在不修改源码的基础上,对我们已有的方法进行增强,所以底层基于jdk动态代理 + cglib动态代理的

作用:在程序运行期间,不修改源码对已有方法进行增强
优势:减少重复代码,松耦合,提高开发效率,维护起来比较便捷
注意:aop由多个oop组成(oop为面向对象编程)

11.2 AOP相关术语

Joinpoint(连接点):所谓的连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点

Pointcut(切入点):所谓的切入点是指我们要对那些Joinpoint进行拦截的定义

Advice(通知/增强):所谓的通知是指拦截到Joinpoint之后所要做的事情就是通知

Target(目标对象):代理的目标对象

Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程,spring采用动态代理织入,而aspect采用编译期织入和类装载期织入。

Proxy(代理):一个类被Aop织入增强后,就产生一个结果代理类

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

Aspect(切面):它是切入点和通知(引介)的结合

11.3 AOP的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:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       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/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--  需要扫描包才能够使用@Controller,@Service,@Repository,@Component   -->
    <context:component-scan base-package="com.darksnow" />


    <bean id="logger" class="com.darksnow.log.Logger" />

    <!--   配置Aop  -->
    <aop:config>
        <!--   配置切面 = 切入点  + 通知     -->
        <aop:aspect ref="logger"> <!-- 指定通知的对象是谁  -->
            <!--
                配置切入点
                    属性说明:
                        id:唯一标识
                        expression:表达式
                    表达式说明:
                        * com.darksnow.service.impl.*.*(..)
                            第一个*:代表方法的返回值类型任意
                            第二个*:代表类名任意,impl包中的所有的类
                            第三个*:代表方法名任意
                            (..):参数任意,个数任意,类型任意,顺序任意
                   表达式其他的配置方式:
                       * com.darksnow.service..*.*(..)
                       public void com.darksnow.service.impl.AccountService.findAll()
                       void com.darksnow.service.impl.AccountService.findAll()
                       * com.darksnow.service.impl.AccountService.findAll()
                       * com.darksnow.service..AccountService.findAll() 代表service包及其子包里的所有包都算
                       * *。。*。*(..):全通配方式
            -->
            <aop:pointcut id="pointcut" expression="execution(* com.darksnow.service.impl.*.*(..))"/>

            <!--
                配置通知
                织入:告诉通知对象执行,具体执行哪一个方法

                通知的类型:
                    前置通知:方法之前执行
                    后置通知:方法执行完毕之后,返回之前执行,如果遇到异常则不执行
                    最终通知:方法执行完之后总会执行
                    异常通知:方法出现异常则执行
                    环绕通知:前置通知 + 后置通知 + 最终通知 + 异常通知
            -->
            <!-- 前置通知 -->
<!--            <aop:before method="before" pointcut-ref="pointcut"/>-->

            <!-- 后置通知 -->
<!--            <aop:after-returning method="afterReturning" pointcut-ref="pointcut"/>-->

            <!-- 最终通知 -->
<!--            <aop:after method="after" pointcut-ref="pointcut"/>-->

            <!-- 异常通知 -->
<!--            <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="e"/>-->

            <!-- 环绕通知 -->
            <aop:around method="around" pointcut-ref="pointcut" />
        </aop:aspect>
    </aop:config>
</beans>

service

package com.darksnow.service;

public interface AccoutService {
    void print();

    Integer add(int a,int b);
}

---------------------------------------------
    
package com.darksnow.service.impl;

import com.darksnow.service.AccoutService;
import org.springframework.stereotype.Service;

@Service
public class AccountServiceImpl implements AccoutService {
    @Override
    public void print() {
//        int i = 1/0;
        System.out.println("打印了......");
    }

    @Override
    public Integer add(int a,int b) {
        return a+b;
    }
}

Logger类

package com.darksnow.log;


import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;

//日志类,记录什么时间访问什么类,什么方法,操作什么功能
public class Logger {

    public void before(JoinPoint joinPoint){
        //获取被代理的对象
        Object target = joinPoint.getTarget();

        //被拦截的类的名称
        String className = target.getClass().getName();
        System.out.println("被拦截到的类名:" + className);

        //获取方法对象
        Signature signature = joinPoint.getSignature();
        //获取被拦截的方法名
        String methodName = signature.getName();
        System.out.println("被拦截到的方法名:" + methodName);
        System.out.println("前置通知......");
    }

    public void afterReturning() {
        System.out.println("后置通知......");
    }

    public void after() {
        System.out.println("最终通知......");
    }

    public void afterThrowing(Exception e) {
        System.out.println("执行的方法的异常为:" + e);
        System.out.println("异常通知......");
    }

    //ProceedingJoinPoint:可以执行拦到截的方法
    public void around(ProceedingJoinPoint joinPoint) {
        try {
            System.out.println("前置通知......");
            //执行原来要增强的方法,可以得到原来方法的返回值
            Object result = joinPoint.proceed();
            System.out.println(result);
            System.out.println("后置增强......");
        }catch (Throwable e) {
            System.out.println("异常通知.....");
            e.printStackTrace();
        }finally {
            System.out.println("最终通知......");
        }
    }
}

测试类

package com.darksnow.test;

import com.darksnow.service.AccoutService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestAopXml {

    @Autowired
    private AccoutService accoutService;

    @Test
    public void testAop() {
        accoutService.print();

        System.out.println("-------------------");

        accoutService.add(1314,520);
    }
}

11.4 AOP的注解形式

其他的和11.3的内容一样,以下为更改部分

配置文件

<?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:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       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/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--  需要扫描包才能够使用@Controller,@Service,@Repository,@Component   -->
    <context:component-scan base-package="com.darksnow" />

    <!--  开启AOP的自动代理,注解方式代理   -->
    <aop:aspectj-autoproxy />
</beans>

Logger类

package com.darksnow.log;


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

//日志类,记录什么时间访问什么类,什么方法,操作什么功能
@Component //创建对象交给容器管理
@Aspect  //配置该类为切面,切面 = 切入点 + 通知
public class Logger {

    //配置切入点
    @Pointcut("execution(* com.darksnow.service.impl.*.*(..))")
    public void pointcut() {

    }

    //JoinPoint:连接点  --- 拦截到的方法
    @Before("pointcut()")  //织入
    public void before(JoinPoint joinPoint){
        //获取被代理的对象
        Object target = joinPoint.getTarget();

        //被拦截的类的名称
        String className = target.getClass().getName();
        System.out.println("被拦截到的类名:" + className);

        //获取方法对象
        Signature signature = joinPoint.getSignature();
        //获取被拦截的方法名
        String methodName = signature.getName();
        System.out.println("被拦截到的方法名:" + methodName);
        System.out.println("前置通知......");
    }

    @AfterReturning("pointcut()")
    public void afterReturning() {
        System.out.println("后置通知......");
    }

    @After("pointcut()")
    public void after() {
        System.out.println("最终通知......");
    }

    @AfterThrowing(value = "pointcut()",throwing = "e")
    public void afterThrowing(Exception e) {
        System.out.println("执行的方法的异常为:" + e);
        System.out.println("异常通知......");
    }

    //ProceedingJoinPoint:可以执行拦到截的方法
//    @Around("pointcut()")
    public void around(ProceedingJoinPoint joinPoint) {
        try {
            System.out.println("前置通知......");
            //执行原来要增强的方法,可以得到原来方法的返回值
            Object result = joinPoint.proceed();
            System.out.println(result);
            System.out.println("后置增强......");
        }catch (Throwable e) {
            System.out.println("异常通知.....");
            e.printStackTrace();
        }finally {
            System.out.println("最终通知......");
        }
    }
}

11.5 AOP配置式解决事务问题

照抄10.动态代理增强转账业务的代码,只是去掉动态代理增强的那两个测试方法

配置文件

<?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:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       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/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">


    <!--  加载properties配置文件  -->
    <context:property-placeholder location="classpath:jdbc.properties"/>

    <!--  需要扫描包才能够使用@Controller,@Service,@Repository,@Component   -->
    <context:component-scan base-package="com.darksnow" />

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

    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <!--   ${jdbc.driver} 获取的是properties中的配置  -->
        <property name="driverClass" value="${jdbc.driver}"/>
        <property name="jdbcUrl" value="${jdbc.url}"/>
        <property name="user" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <!--
        使用aop解决事务问题
      -->
    <aop:config>
        <!--   配置切面  -->
        <aop:aspect ref="transactionManager"> <!-- 指定通知对象是谁 , 这里明显通知对象是 事务管理类 -->
            <!-- 切入点 -->
            <aop:pointcut id="pointcut" expression="execution(* com.darksnow.service.impl.*.*(..))"/>
            <!-- 织入 -->
            <!-- 前置通知(增强):开启事务 -->
            <aop:before method="beginTransaction" pointcut-ref="pointcut" />
            <!-- 后置增强:提交事务 -->
            <aop:after-returning method="commit" pointcut-ref="pointcut" />
            <!-- 异常增强:回滚事务 -->
            <aop:after-throwing method="rollback" pointcut-ref="pointcut" />
            <!-- 最终增强:释放资源 -->
            <aop:after method="release" pointcut-ref="pointcut" />
        </aop:aspect>
    </aop:config>
</beans>

测试类

package com.darksnow.test;

import com.darksnow.service.AccountService;
import com.darksnow.utils.TransactionManager;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

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

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestTransfer {

    @Autowired
    private AccountService accountService;

    @Autowired
    private TransactionManager txManager;

    @Test
    public void transfer() {
        accountService.transfer("张三","李四",500f);
    }
}

11.6 AOP注解式解决事务问题

照抄10.动态代理增强转账业务的代码,然后做如下修改

配置文件

<?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:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       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/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">


    <!--  加载properties配置文件  -->
    <context:property-placeholder location="classpath:jdbc.properties"/>

    <!--  需要扫描包才能够使用@Controller,@Service,@Repository,@Component   -->
    <context:component-scan base-package="com.darksnow" />

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

    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <!--   ${jdbc.driver} 获取的是properties中的配置  -->
        <property name="driverClass" value="${jdbc.driver}"/>
        <property name="jdbcUrl" value="${jdbc.url}"/>
        <property name="user" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <!-- 开启aop自动代理注解 -->
    <aop:aspectj-autoproxy />
</beans>

事务管理类

package com.darksnow.utils;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.sql.SQLException;

/*
    事务管理器
 */
@Component
@Aspect //配置切面
public class TransactionManager {

    @Autowired
    private ConnectionUtil connectionUtil;

    @Pointcut("execution(* com.darksnow.service.impl.*.*(..))")
    public void pointcut(){

    }

    //环绕通知(增强)
    @Around("pointcut()")
    public void around(ProceedingJoinPoint joinPoint) {
        try {
            //前置增强
            beginTransaction(); //开启事务
            //执行原来未增强的方法
            joinPoint.proceed();
            //后置增强
            commit(); //提交事务
        }catch (Throwable throwable) {
            //异常增强
            rollback(); //回滚事务
            throwable.printStackTrace();
        }finally {
            //最终增强
            release(); //释放资源
        }
    }

    //开始事务
    public void beginTransaction() {
        //从当前线程上获取连接,实现开启事务
        try {
            connectionUtil.getThreadConnection().setAutoCommit(false);
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }

    //提交事务
    public void commit() {
        try {
            connectionUtil.getThreadConnection().commit();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }

    //回滚事务
    public void rollback() {
        try {
            connectionUtil.getThreadConnection().rollback();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }

    //释放连接
    public void release() {
        try {
            connectionUtil.getThreadConnection().setAutoCommit(true);
            //关闭连接
            connectionUtil.getThreadConnection().close();
            //解绑线程
            connectionUtil.remove();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }
}

测试类

package com.darksnow.test;

import com.darksnow.service.AccountService;
import com.darksnow.utils.TransactionManager;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

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

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestTransfer {

    @Autowired
    private AccountService accountService;

    @Autowired
    private TransactionManager txManager;

    @Test
    public void transfer() {
        accountService.transfer("张三","李四",500f);
    }
}

12.JDBCTemplate的使用

pom.xml

<?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.darksnow</groupId>
    <artifactId>SpringDemo01</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.2.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.2.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>2.2.2</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>commons-dbutils</groupId>
            <artifactId>commons-dbutils</artifactId>
            <version>1.4</version>
        </dependency>
        <dependency>
            <groupId>c3p0</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.1.2</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.18</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.9</version>
        </dependency>
    </dependencies>

</project>

配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       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/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">


    <!--  加载properties配置文件  -->
    <context:property-placeholder location="classpath:jdbc.properties"/>

    <!--  需要扫描包才能够使用@Controller,@Service,@Repository,@Component   -->
    <context:component-scan base-package="com.darksnow" />

    <bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner" >
        <constructor-arg ref="c3p0DataSource"/>
    </bean>

    <!--  配置c3p0的数据源  -->
    <bean id="c3p0DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <!--   ${jdbc.driver} 获取的是properties中的配置  -->
        <property name="driverClass" value="${jdbc.driver}"/>
        <property name="jdbcUrl" value="${jdbc.url}"/>
        <property name="user" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <!-- Spring自带的数据源 -->
    <bean id="springDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <!-- 创建JdbcTemplate模板对象 -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="springDataSource" />
    </bean>
</beans>

实体类

package com.darksnow.pojo;

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

    //省略getter,setter,toString
}

结果集对象

package com.darksnow.mapper;

import com.darksnow.pojo.Account;
import org.springframework.jdbc.core.RowMapper;

import java.sql.ResultSet;
import java.sql.SQLException;

//结果集对象,只包含一行数据
public class AccountRomMapper implements RowMapper<Account> {

    @Override
    public Account mapRow(ResultSet rs, int rowNum) throws SQLException {
        Account account = new Account();
        int id = rs.getInt("id");
        account.setId(id);
        account.setName(rs.getString("name"));
        account.setMoney(rs.getFloat("money"));
        return account;
    }
}

测试类

package com.darksnow.test;

import com.darksnow.mapper.AccountRomMapper;
import com.darksnow.pojo.Account;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.List;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestJdbcTemplate {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Test
    public void testFindAll() {
        String sql = "select * from account";

        //方式一:
//        List<Map<String, Object>> accoutList = jdbcTemplate.queryForList(sql);
//        for (Map<String, Object> map : accoutList) {
//            System.out.println(map);
//        }

        //方式二:
        //query方法参数一:sql语句,参数二:自定义结果集对象
        List<Account> accountList = jdbcTemplate.query(sql, new AccountRomMapper());
        for (Account account : accountList) {
            System.out.println(account);
        }
    }

    @Test
    public void testFindById() {
        String sql = "select * from account where id = ?";

        //方式一:
//        List<Account> accountList = jdbcTemplate.query(sql, new AccountRomMapper(), 20);
//        System.out.println(accountList.size() == 1 ? accountList.get(0) : "结果为:null");

        //方式二:
//        Account account = jdbcTemplate.queryForObject(sql, new AccountRomMapper(), 2);
//        System.out.println(account);

        //方式三:
        //BeanPropertyRowMapper对象会帮你做映射,但是使用的时候要保证属性和表中字段名一致
        Account account = jdbcTemplate.queryForObject(sql, new Object[]{2}, new BeanPropertyRowMapper<>(Account.class));
        System.out.println(account);
    }

    @Test
    public void testSave() {
        String sql = "insert into account values(null,?,?)";
        jdbcTemplate.update(sql,"坤坤","0.01");
    }

    @Test
    public void testUpdate() {
        String sql = "update account set name = ? where id = ?";
        jdbcTemplate.update(sql,"爱坤",3);
    }

    @Test
    public void testDelete() {
        String sql = "delete from account where id = ?";
        jdbcTemplate.update(sql, 3);
    }

    @Test
    public void testGetTotalCount() {
        String sql = "select count(*) from account";
        //queryForObject(sql语句,返回值类型)
        Integer count = jdbcTemplate.queryForObject(sql, Integer.class);
        System.out.println(count);
    }
}

13.JDBCTemplate在dao层的使用

13.1 方式一

dao层

package com.darksnow.dao.impl;

import com.darksnow.dao.AccountDao;
import com.darksnow.mapper.AccountRomMapper;
import com.darksnow.pojo.Account;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public class AccountDaoImpl implements AccountDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public List<Account> findAll() {
        String sql = "select * from account";
        return jdbcTemplate.query(sql,new AccountRomMapper());
    }

    @Override
    public Account findById(Integer id) {
        String sql = "select * from account where id = ?";
        return jdbcTemplate.queryForObject(sql,new AccountRomMapper(),id);
    }
}

测试类

package com.darksnow.test;

import com.darksnow.dao.AccountDao;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;


@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestJdbcTemplate {

    @Autowired
    private AccountDao accountDao;

    @Test
    public void testFindAll() {
        System.out.println(accountDao.findAll());
    }

    @Test
    public void testFindById() {
        System.out.println(accountDao.findById(1));
    }
}

13.2 方式二

dao层代码

package com.darksnow.dao.impl;

import com.darksnow.dao.AccountDao;
import com.darksnow.mapper.AccountRomMapper;
import com.darksnow.pojo.Account;
import org.springframework.jdbc.core.support.JdbcDaoSupport;

import java.util.List;

public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {

    @Override
    public List<Account> findAll() {
        String sql = "select * from account";
        return getJdbcTemplate().query(sql,new AccountRomMapper());
    }

    @Override
    public Account findById(Integer id) {
        String sql = "select * from account where id = ?";
        return getJdbcTemplate().queryForObject(sql,new AccountRomMapper(),id);
    }
}

测试类

package com.darksnow.test;

import com.darksnow.dao.AccountDao;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;


@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestJdbcTemplate {

    @Autowired
    private AccountDao accountDao;

    @Test
    public void testFindAll() {
        System.out.println(accountDao.findAll());
    }

    @Test
    public void testFindById() {
        System.out.println(accountDao.findById(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"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       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/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">


    <!--  加载properties配置文件  -->
    <context:property-placeholder location="classpath:jdbc.properties"/>

    <!--  需要扫描包才能够使用@Controller,@Service,@Repository,@Component   -->
    <context:component-scan base-package="com.darksnow" />

    <bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner" >
        <constructor-arg ref="c3p0DataSource"/>
    </bean>

    <!--  配置c3p0的数据源  -->
    <bean id="c3p0DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <!--   ${jdbc.driver} 获取的是properties中的配置  -->
        <property name="driverClass" value="${jdbc.driver}"/>
        <property name="jdbcUrl" value="${jdbc.url}"/>
        <property name="user" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <!-- Spring自带的数据源 -->
    <bean id="springDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <!-- 创建JdbcTemplate模板对象 -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="springDataSource" />
    </bean>

    <!-- 创建dao层对象,交给容器管理 -->
    <bean id="accountDao" class="com.darksnow.dao.impl.AccountDaoImpl">
        <!-- 通过set方法注入数据源 -->
        <property name="dataSource" ref="springDataSource"/>
    </bean>
</beans>

14.Spring事务管理

脏读:
	脏读又称无效数据读出(读出了脏数据)。一个事务读取另外一个事务还没有提交的数据叫脏读。
	例如:事务T1修改了某个表中的一行数据,但是还没有提交,这时候事务T2读取了被事务T1修改后的数据,
	之后事务T1因为某种原因回滚(Rollback)了,那么事务T2读取的数据就是脏的(无效的)。
幻读:
	幻读的重点在于新增或者删除 (数据条数变化), 同样的条件, 第1次和第2次读出来的记录数不一样。
	例如:系统事务A将数据库中所有数据都删除的时候,但是事务B就在这个时候新插入了一条记录,
	当事务A删除结束后发现还有一条数据,就好像发生了幻觉一样。这就叫幻读。
不可重复读:
	不可重复读与脏读逻辑类似。主要在于事务2在事务1第二次读取时,提交了数据。导致事务1前后两次读取的数据不一致。
	不可重复读的重点是修改并提交: 同样的条件, 你读取过的数据, 再次读取出来发现值不一样了
	不可重复读是指在同一个事务内,两次相同的查询返回了不同的结果。
	例如:事务T1会读取两次数据,在第一次读取某一条数据后,事务T2修改了该数据并提交了事务,
	T1此时再次读取该数据,两次读取便得到了不同的结果。
-------------------------------------------------------------------
事务的四个特性(ACID):
	原子性(atomicity):不可再分隔
	一致性(consistency):要么全部完成,要么全部不完成
	隔离性(isolation):事务之间的隔离级别
	持久性(durability):一旦提交,持久化到数据中
	
事务隔离级别:它反映了事务提交并发访问时的处理态度
(1)可以读取未提交数据(读未提交):read_uncommitted
	产生的问题:脏读,幻读,不可重复读
(2)只能读取已提交数据,解决脏读问题(Oracle默认级别,读已提交):red_committed
	产生的问题:幻读,不可重复读
	解决的问题:脏读
(3)是否读取其他事务提交修改后的数据(Mysql默认级别,重复读):repeatable_red
	产生的问题:幻读
	解决的问题:脏读,不可重复读
(4)是否读取其他事务提交添加后的数据(串行化):serializable
	产生的问题:无
	解决的问题:脏读,幻读,不可重复读
	隔离级别最高,效率最低
	
事务的传播行为:
(1)REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务,那么加入到这个事务中(默认值)
(2)MANDATORY:使用当前的事务,如果没有事务,就抛出异常
(3)NESTED:如果当前存在事务,则在嵌套事务内执行,如果当前没有事务,则执行REQUIRED类似的操作。
(4)NEVER:以非事务方式运行,如果当前存在事务,就抛出异常
(5)NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
(6)REQUIRES_NEW:新建事务,如果在事务中,把当前事务挂起
(7)SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行

14.1 Spring配置式事务管理

下面仅放修改了的代码,其他代码参考笔记"10.动态代理增强转账业务的代码"

pom.xml

<?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.darksnow</groupId>
    <artifactId>SpringDemo01</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.2.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.2.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.2.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>2.2.2</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>commons-dbutils</groupId>
            <artifactId>commons-dbutils</artifactId>
            <version>1.4</version>
        </dependency>
        <dependency>
            <groupId>c3p0</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.1.2</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.18</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.9</version>
        </dependency>
    </dependencies>
</project>

配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       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/context
       https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop
       https://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx.xsd">


    <!--  加载properties配置文件  -->
    <context:property-placeholder location="classpath:jdbc.properties"/>

    <!--  需要扫描包才能够使用@Controller,@Service,@Repository,@Component   -->
    <context:component-scan base-package="com.darksnow" />

    <bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner" >
        <constructor-arg ref="c3p0DataSource"/>
    </bean>

    <!--  配置c3p0的数据源  -->
    <bean id="c3p0DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <!--   ${jdbc.driver} 获取的是properties中的配置  -->
        <property name="driverClass" value="${jdbc.driver}"/>
        <property name="jdbcUrl" value="${jdbc.url}"/>
        <property name="user" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <!-- Spring自带的数据源 -->
    <bean id="springDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <!-- 创建JdbcTemplate模板对象 -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="springDataSource" />
    </bean>

    <!-- 创建事务管理对象 -->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- set注入数据源属性:事务在连接中,连接在连接池(数据源)中 -->
        <property name="dataSource" ref="springDataSource" />
    </bean>

    <!--
        配置事务的增强:过滤方法是否需要拦截
        id:唯一标识
        transaction-manager:指定事务管理对象
    -->
    <tx:advice id="txAdvice" transaction-manager="txManager">
        <!-- 增强:方法过滤 -->
        <tx:attributes>
            <!--
                指定需要拦截的方法

                name属性:匹配方法名,*代表通配符匹配,比如find*,就代表find开头的都会匹配
                isolation属性:设置事务的隔离级别,一般选默认
                propagation属性:事务传播行为
                read-only属性:是否为只读的事务,增删该设为非只读事务(false),查询设为只读事务(true)
                timeout属性:设置超时时间,默认为-1,意味着没有超时限制,单位为秒
            -->
            <tx:method name="insert*" isolation="DEFAULT"  propagation="REQUIRED" read-only="false" timeout="-1"/>
            <tx:method name="add*" />
            <tx:method name="update*"/>
            <tx:method name="del*" />
            <tx:method name="delete*"/>
            <tx:method name="save*"/>
            <tx:method name="transfer*"/>
            <!-- 查询 -->
            <tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
            <tx:method name="select*" propagation="SUPPORTS" read-only="true"/>
            <tx:method name="get*" propagation="SUPPORTS" read-only="true"/>
        </tx:attributes>
    </tx:advice>
    <aop:config>
        <!--
            配置切面
            advice-ref:关联通知对象
            pointcut:切入点
        -->
        <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.darksnow.service.impl.*.*(..))" />
    </aop:config>
</beans>

dao层代码

package com.darksnow.dao;

import com.darksnow.pojo.Account;

import java.sql.SQLException;

public interface AccountDao {
    Account findByName(String fromName) throws SQLException;

    void update(Account account) throws SQLException;
}

----------------------------------------------------------

package com.darksnow.dao.impl;

import com.darksnow.dao.AccountDao;
import com.darksnow.mapper.AccountRomMapper;
import com.darksnow.pojo.Account;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import java.sql.SQLException;

@Repository
public class AccountDaoImpl implements AccountDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    //根据账户名查询账户
    @Override
    public Account findByName(String fromName) throws SQLException {
        String sql = "select * from account where name = ?";
        //从线程中获取一个连接对象
        return jdbcTemplate.queryForObject(sql,new AccountRomMapper(),fromName);
    }

    //更新账户
    @Override
    public void update(Account account) throws SQLException {
        String sql = "update account set money = ? where name = ?";
        jdbcTemplate.update(sql,account.getMoney(),account.getName());
    }
}    

映射结果集对象

package com.darksnow.mapper;

import com.darksnow.pojo.Account;
import org.springframework.jdbc.core.RowMapper;

import java.sql.ResultSet;
import java.sql.SQLException;

//结果集对象,只包含一行数据
public class AccountRomMapper implements RowMapper<Account> {

    @Override
    public Account mapRow(ResultSet rs, int rowNum) throws SQLException {
        Account account = new Account();
        int id = rs.getInt("id");
        account.setId(id);
        account.setName(rs.getString("name"));
        account.setMoney(rs.getFloat("money"));
        return account;
    }
}

测试类

package com.darksnow.test;

import com.darksnow.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestXmlTransaction {

    @Autowired
    private AccountService accountService;

    @Test
    public void testTransfer() {
        accountService.transfer("张三","李四",1000f);
    }
}

14.2 Spring注解式事务管理

下面仅放修改了的代码,其他代码参考笔记"14.1 Spring配置式事务管理"

配置文件

<?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:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       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/context
       https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop
       https://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx.xsd">


    <!--  加载properties配置文件  -->
    <context:property-placeholder location="classpath:jdbc.properties"/>

    <!--  需要扫描包才能够使用@Controller,@Service,@Repository,@Component   -->
    <context:component-scan base-package="com.darksnow" />

    <bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner" >
        <constructor-arg ref="c3p0DataSource"/>
    </bean>

    <!--  配置c3p0的数据源  -->
    <bean id="c3p0DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <!--   ${jdbc.driver} 获取的是properties中的配置  -->
        <property name="driverClass" value="${jdbc.driver}"/>
        <property name="jdbcUrl" value="${jdbc.url}"/>
        <property name="user" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <!-- Spring自带的数据源 -->
    <bean id="springDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <!-- 创建JdbcTemplate模板对象 -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="springDataSource" />
    </bean>

    <!-- 创建事务管理对象 -->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- set注入数据源属性:事务在连接中,连接在连接池(数据源)中 -->
        <property name="dataSource" ref="springDataSource" />
    </bean>

    <!-- transaction-manager:关联事务管理器对象  -->
    <tx:annotation-driven transaction-manager="txManager" />
</beans>

service层实现类代码

package com.darksnow.service.impl;

import com.darksnow.dao.AccountDao;
import com.darksnow.pojo.Account;
import com.darksnow.service.AccountService;
import org.apache.commons.dbutils.QueryRunner;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.sql.SQLException;

@Service
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao accountDao;


    //放在类上就是整个类都有事务管理,放在方法上就只作用于该方法上
    @Transactional(isolation = Isolation.DEFAULT,propagation = Propagation.REQUIRED, readOnly = false, timeout = -1)
    @Override
    public void transfer(String fromName, String toName, Float money) {
        try {
            //查询要转出的账号
            Account fromAccount = accountDao.findByName(fromName);

            //查询要转入的账号
            Account toAccount = accountDao.findByName(toName);

            //修改要转出的账户余额
            fromAccount.setMoney(fromAccount.getMoney() - money);

            //修改要转入的账户余额
            toAccount.setMoney(toAccount.getMoney() + money);

            //持久化到数据库
            accountDao.update(fromAccount);

            int i = 1/0;

            accountDao.update(toAccount);
        }catch (SQLException e) {
            e.printStackTrace();
        }
    }
}