如果你读完这篇文章,恭喜你!你的Spring入门了!

第一篇:Spring框架的概述以及Spring中基于XML的IOC配置

  • 1.spring的概述

    • spring是什么

      Spring框架是由于软件开发的复杂性而创建的。Spring使用的是基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅仅限于服务器端的开发。从简单性、可测试性和松耦合性角度而言,绝大部分Java应用都可以从Spring中受益。
      ◆目的:解决企业应用开发的复杂性
      ◆功能:使用基本的JavaBean代替EJB,并提供了更多的企业应用功能
      ◆范围:任何Java应用

    • spring两大核心

      Spring是一个轻量级控制反转(IoC)和面向切面(AOP)的容器框架。

    • spring的优势

      1.JAVA EE应该更加容易使用。

      2.面向对象的设计比任何实现技术(比如JAVA EE)都重要。

      3.面向接口编程,而不是针对类编程。Spring将使用接口的复杂度降低到零。

      4.代码应该易于测试。Spring框架会帮助你,使代码的测试更加简单。

      5.JavaBean提供了应用程序配置的最好方法。

      6.在Java中,已检查异常(Checked exception)被过度使用。框架不应该迫使你捕获不能恢复的异常。

    • spring体系结构
    在这里插入图片描述
  • 2.程序的耦合及解耦

    • 程序的耦合
      • 耦合:程序间的依赖关系
        • 包括:
          • 类之间的依赖
          • 方法之间的依赖
      • 解耦:降低程序间的依赖关系
      • 实际开发中:
        • 应该做到:编译期不依赖,运行时才依赖
      • 解耦思路
        • 第一步:使用反射来创建对象,避免使用new关键字
        • 第二步:通过读取配置文件来获取想要创建的对象全限定类名
    • 工厂模式解耦
package com.it.factory;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 * @Author: 东方老赢
 * @Date: 2020/3/31 9:47
 * 一个创建Bean对象的工厂
 *  工厂:是一个创建Bean对象的工厂
     * Bean:在计算机英语中,有可重用组件的含义
     * 他就是创建我们的service和dao对象的
 *
 * 第一个:需要一个配置文件来配置我们的service和dao
 *          配置内容:唯一标识 = 全限定类名(key = value)
 * 第二个:通过读取配置文件中的配置内容,放射创建对象
 *
 * 配置文件可以是xml 也可以是properties
 */
public class BeanFactory {
    private static Properties properties;
    //创建一个容器
    private static Map<String,Object> beans;
    static {
        try {
            properties = new Properties();
            InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
            properties.load(in);
            //实例化容器
            beans = new HashMap<String, Object>();
            //取出配置文件中所有的key
            Enumeration keys = properties.keys();
            //遍历枚举
            while (keys.hasMoreElements()){
                //取出每个key
                String key = keys.nextElement().toString();
                //根据key获取value
                String beanPath = properties.getProperty(key);
                //反射创建对象
                Object value = Class.forName(beanPath).newInstance();
                //将key和value放入容器
                beans.put(key,value);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public Object getBean(String beanName){
        return beans.get(beanName);
    }

//    public Object getBean(String beanName){
//        Object bean = null;
//        try {
//            String beanPath = properties.getProperty(beanName);
//            bean = Class.forName(beanPath).newInstance();
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//        return  bean;
//    }
}

accountService = com.it.service.AccountServiceImpl
accountDao = com.it.dao.AccountDaoImpl
  • 3.IOC概念和spring中的IOC

    • IOC的概念
      IOC(Inversion of Control),中文名称控制反转。把创建对象的权利交给框架,是框架的重要特征,并非面向对象变成的专业术语。它包括依赖注入 DI(Dependency Injection)和依赖查找 DL (Dependency Lookup)。
      明确IOC的作用:削减计算机程序的耦合(解除我们代码之间的依赖关系)
    • spring中基于XML的IOC环境搭建
      前期需准备spring开发包https://repo.spring.io/libs-release-local/org/springframework/spring/
      • 首先在pom.xml中导入依赖
        在这里插入图片描述
      • 然后再resource中创建bean.xml,然后添加这段内容
        在这里插入图片描述
      • 然后将对象的创建交给bean来管理
        在这里插入图片描述
      • 接下来就是调用了
package com.it.ui;

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

/**
 * @Author: 东方老赢
 * @Date: 2020/3/31 9:19
 * 获取spring的ioc核心容器,并根据id获取对象
 *
 * 模拟一个表现层,用于调用业务层
 */
public class Client {
    public static void main(String[] args) {
           // IAccountService as = new AccountServiceImpl();
            //1.首先获取核心容器对象
            ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
            //2.根据id获取Bean对象(两种方式)
            IAccountService as = (IAccountService) ac.getBean("accountService");
            IAccountService as2 = ac.getBean("accountService",IAccountService.class);
            as.saveAccount();
            as2.saveAccount();
    }
}

ApplicationContext的三个实现类:
  • ClassPathXmlApplicationContext:
    它可以加载类路径下的配置文件,要求配置文件必须在类路径下。不在的话,加载不了(更常用)
  • FileSystemXmlApplicationContext:
    他可以加载磁盘任意路径下的配置文件(必须有访问权限)
  • AnnotationConfigApplicationContext:
    它是用于读取注解创建容器的
核心容器的两个接口引发出的问题:
  • ApplicationContext:单例对象使用
    它在构建核心容器时,创建对象才去的策略是采用立即加载的方式。也就是说,只要一读取完配置文件马上就创建配置文件中的配置对象
  • BeanFactory:多例对象使用
    它在构建核心容器是,创建对象才去的策略是采用延迟加载的方式,也就是说,什么时候根据id获取对象了,什么时候才真正的创建对象
spring对bean的管理细节
  • 1.创建bean的三种方式
    	第一种方式:使用默认构造函数创建
                在spring的配置文件中使用bean标签,配以id和class属性之后,
                且没有其他属性和标签时。才用的就是默认构造函数(即无参数)创建Bean对象,
                此时如果类中没有默认构造函数,则对象无法创建
         	<bean id="accountService" class="com.it.service.AccountServiceImpl"></bean>
         
         
        第二种方法:使用普通工厂的方法创建对象(使用某个类中的方法创建对象,并存入spring容器)
            <bean id="intancefactory" class="com.it.factory.InstanceFactory"></bean>
            <bean id="accountService" factory-bean="intancefactory" factory-method="getAccountService"></bean>


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

单例对象:
在这里插入图片描述
多例对象:
在这里插入图片描述

  • 4.spring中的依赖注入(Dependency Injection)

    IOC作用:降低程序间的耦合(依赖关系)
    • 依赖关系的管理: 以后都交给spring维护

      在当前类需要用到其他类的对象,由spring为我们提供,我们只需要在配置文件中说明
      依赖关系的维护:就称之为依赖注入

    • 依赖注入:
      • 能注入的数据有三类
        • 基本类型和String
        • 其他bean类型(在配置文件中或者注解配置过的bean)
        • 复杂类型/集合类型
      • 注入的方式有三种
        • 第一种:使用构造函数(除了必要,一般不用)
		使用的标签:constructor-arg
        标签出现的位置:bean标签内部
        标签中的属性:
              type:用于指定要注入的数据的数据类型,该数据类型也是构造函数中某个或某些参数额类型
              index:用于指定要注入的数据给构造函数中指定索引位置的参数赋值。索引的位置是从0开始
              name(常用):用于指定给构造函数中指定名称的参数赋值

              value:用于提供基本类型和String类型的数据
              ref:用于指定其他的bean类型数据。它指的就是在spring的IOC核心容器中出现过的bean对象

		<bean id="accountService" class="com.it.service.AccountServiceImpl" >
	        <constructor-arg name="name" value="孙志浩"></constructor-arg>
	        <constructor-arg name="age" value="22"></constructor-arg>
	        <constructor-arg name="bir" ref="now"></constructor-arg>
	    </bean>
	        <!--配置一个日期对象-->
	    <bean id="now" class="java.util.Date"></bean>

       # 优势:在获取bean对象时,注入数据是必须的操作,否则对象无法创建成功
       # 弊端:改变了bean对象实例化的方式,使我们在创建对象时,如果用不到这些数据,也必须提供

                              第二种:使用set方法提供(更常用)

扫描二维码关注公众号,回复: 10590347 查看本文章
涉及的标签:property
                    出现的位置:bean标签内部
                    标签的属性:
                        name:用于指定注入时所调用的set方法名称
                        value:用于提供基本类型和String类型的数据
                        ref:用于指定其他的bean类型数据。它指的就是在spring的IOC核心容器中出现过的bean对象

                     优势:创建对象时没有明确的限制,可以直接使用默认构造函数
                     弊端:如果有某个成员必须有值,则获取对象是有可能set方法没有执行
        <bean id="accountService" class="com.it.service.AccountServiceImpl" >
            <property name="name" value="孙志浩"></property>
            <property name="age" value="22"></property>
            <property name="bir" ref="now"></property>
        </bean>
         </bean>
	        <!--配置一个日期对象-->
	    <bean id="now" class="java.util.Date"></bean>

                              第三种:使用注解提供

用于给List结构集合注入的标签
                                list,set,array
                            用于给Map结构集合注入的标签:
                                 map,props
                            结构相同,标签可以互换
    <bean id="accountService" class="com.it.service.AccountServiceImpl" >
        <property name="arr">
            <list>
                <value>A</value>
                <value>A</value>
            </list>
        </property>
        <property name="list">
            <set>
                <value>B</value>
            </set>
        </property>
        <property name="set">
            <array>
                <value>c</value>
            </array>
        </property>
        <property name="map">

            <props>
                <prop key="ee">EE</prop>
            </props>
        </property>
        <property name="pro">
            <map>
                <entry key="dd" value="DD"></entry>
                <entry key="ee">
                    <value>DD</value>
                </entry>
            </map>
        </property>
    </bean>

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

  • 1.Spring中IOC的常用注解

    • 用于创建对象的 :@Component
      • 作用:能把当前类对象存入spring容器中 ,相当于:
        在这里插入图片描述
      • 属性
        • value:用于指定bean的id。当我们不写时,它的默认值时当前类名,且首字母改小写
      • 由Component衍生出的三个注解
        • Controller:一般用在表现层
        • Service:一般用在业务层
        • Repository: 一般用在持久层
        • 以上三个注解的作用于Component一模一样,只是为了明确三层使用的注解
    • 用于注入数据的:@Autowired、@Qualifier、@Resource、 @Value()
      • @Autowired
        • 作用: 按照类型自动注入。只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就可以成功注入
        • 出现位置:可以是变量上,也可以是方法上
        • 细节:在使用注释注入时,set方法就不是必须的了
        • 注入数据过程:
          • 若bean对象类型唯一
            在这里插入图片描述
          • 若bean对象类型重复
            在这里插入图片描述
      • @Qualifier(需与 @Autowired 一同使用,解决了上述bean标签重复的问题)
        • 作用:在按照类中注入的基础之上在按照名称注入,他在给类成员注入时不能单独使用。但是再给方法参数注入时可以
        • 属性:
          • value:用于指定注入bean的id
      • @Resource(可单独使用,解决了上述与 @Autowired 绑定使用的问题)
        • 作用:直接按照bean的id注入,它可以独立使用
        • 属性:
          • name:用于指定bean的id
      • 注意:以上三个注入只能注入其他bean类型的数据。而基本类型和String类型无法使用上述注解实现,因此便引出 @Value() 注解
      • @Value()
        • 作用:用于注入基本类型和String类型的数据
        • 属性:
          • value:用于指定数据的值,它可以使用spring中的SpEl(也就是spring中的el表达式)
    • 用于改变作用范围的 :@Scope
      • 作用:他们的作用就和在bean标签中使用scope属性实现的功能是一致的
      • 属性
        • value:指定范围内的取值。常用取值:singleton(单例)、prototype(多例)
    • 和生命周期相关的(了解即可) :@PreDestroy 、@PostConstruct
      • @PreDestroy
        • 作用:用于指定销毁方法
      • @PostConstruct
        • 作用:用于指定初始化方法
  • 2.案例使用xml方式和注解方式实现单表的CRUD操作

    • 持久技术层选择:dbutils

    • 案例展示:https://blog.csdn.net/qq_40181435/article/details/105267144

  • 3.改造基于注解的IOC案例,使用纯注解方式实现spring的一些新注解的作用

    • spring的一些新注解的作用

@Configuration 
	作用:指定当前类是一个配置类
	
@ComponentScan(basePackages = "com.it")
  	作用:通过注解指定spring在创建容器时要扫描的包
       属性:
          value,basePackages:二者作用一样,都是指定创建容器时要扫描的包
       等同于注解:<context:component-scan base-package="com.it"></context:component-scan>
       
@Bean(name = "runner")
	作用:用于把当前方法的返回值最为bean对象存入spring的ioc容器中
    属性:
      name:用于指定bean的id,当不写时,默认值是当前方法名
    细节:
      当我们使用注释配置方法时,如果方法有参数,spring框架会去容器中查找有没有可用的bean对象
      查找的方法和Autowired注解的作用是一样的
      
@Import(JDBCConfig.class)
 作用:用于导入其他的配置类
 属性:
      value:用于指定其他配置类的字节码
            当我们使用Import注解后,有Import注解的类就是父配置类,而导入的就是子配置类
 等同于Import的操作:
 		1.在子配置类中加注解@Configuration,且扫描注解修改成@ComponentScan(basePackages = {"com.spring","config"})
 		2.    ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class,JDBCConfig.class);
 
 @PropertySource("classpath:jdbcconfig.properties")(配合@Value使用)
			 @Value("${jdbc.driver}")
    private String driver;

    @Value("${jdbc.url}")
    private String url;

    @Value("${jdbc.username}")
    private String username;

    @Value("${jdbc.password}")
    private String password;
    
	ds.setDriverClass(driver);
    ds.setJdbcUrl(url);
    ds.setUser(username);
    ds.setPassword(password);
  • 4.spring和Junit整合

    • 问题分析:

      1. 应用程序的入口:main方法
      2. Junit单元测试中,没有main方法也能执行,这是因为Junit集成了一个main方法,该方法会判断当前测试类中哪些方法有 @Test 注解
      3. Junit不知道我们在执行测试方法时是否用了spring框架,所以也就不会为我们读取配置文件/配置类创建spring核心容器
        由以上三点可知:当测试方法执行时,没有IOC容器,就算写了Autowire的注解,也无法实现注入
    • 问题解决:

      1. 导入spring整合Junit的jar:spring-test
      2. 使用Junit提供的一个注解把原有的main方法替换成spring提供的
        @RunWith
      3. 告知spring运行器,spring和ioc创建是基于xml还是注解,并说明位置
        == @ContextConfiguration==
        locations:指定xml文件所在的位置,加上classpath关键字,表示在类路径下
        classes:指定注解类所在的位置
        当我们使用spring 5.X版本时,要求Junit的jar必须是4.12及以上
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfiguration.class)
public class AccountServiceTest {
 @Autowired
    private IAccountService as;

    @Test
    public void findAll(){
//        ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
//        as = ac.getBean("accountService",IAccountService.class);
        //执行方法
        List<Account> accounts = as.findAllAccount();
        for (Account account: accounts) {
            System.out.println(account);
        }
    }

第三篇 AOP

1.完善Account案例

  • 这里做一个常见的账户转账的操作
//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);
int i = 1/0;
//2.6.更新转入账户
accountDao.updateAccount(target);

很明显,当之一系列操作到 “int i = 1/0;” 后会报异常。但是转账的账户钱已经减少了,但是收钱的账户明显收不到钱,这时就引出了事务

 try {
            //1.开启事务
            txManager.beginTransaction();
            //2.执行操作
                //2.1.根据名称查询转出账户
                Account source = accountDao.findAccountByName(sourceName);
                //2.2.根据名称查询转入账户
                Account target = accountDao.findAccountByName(targetName);
                //2.3.转出账户减钱
                source.setMoney(source.getMoney()-money);
                //2.4.转入账户加钱
                target.setMoney(target.getMoney()+money);
                //2.5.更新转出账户
                accountDao.updateAccount(source);
                int i = 1/0;
                //2.6.更新转入账户
                accountDao.updateAccount(target);
            //3.提交事务
            txManager.commit();
        }catch (Exception e){
            //5.回滚操作
            txManager.rollback();
            throw new RuntimeException(e);
        }finally {
            //6.释放连接
            txManager.release();
        }

2.分析案例中的问题

  • 在JDBC中相信大家都学过事务的使用,他有效的解决了上述问题,但是同时另一个题也出现了,就是事务在spring中的使用极其麻烦,而且造成了方法之间的耦合极高,改动一个方法名就会牵连一片,极其不方便
package com.it.utils;

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

/**
 * @Author: 东方老赢
 * @Date: 2020/4/5 9:49
 *
 * 连接的工具类,它用于从数据源获取一个链接,并且实现和线程的绑定
 */
public class ConnectionUtils {
    private ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
    private DataSource dataSource;

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

    }

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

}

package com.it.utils;

import java.sql.SQLException;

/**
 * @Author: 东方老赢
 * @Date: 2020/4/5 9:59
 */
public class TransactionManager {
    private ConnectionUtils connectionUtils;

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

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

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

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

    /**
     * 释放连接
     */
    public void release(){
        try {
            connectionUtils.getThreadConnection().close(); //还回连接池中
            connectionUtils.removeConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

package com.it.dao.impl;

import com.it.dao.IAccountDao;
import com.it.domain.Account;
import com.it.utils.ConnectionUtils;
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;

/**
 * @Author: 东方老赢
 * @Date: 2020/4/2 11:19
 */
public class AccountDaoImpl implements IAccountDao{
    private QueryRunner runner;
    private ConnectionUtils connectionUtils;

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

    public AccountDaoImpl(QueryRunner runner) {
        this.runner = runner;
    }

    public AccountDaoImpl() {

    }


    public List<Account> findAllAccount() {
        try {
            return runner.query(connectionUtils.getThreadConnection(),"select * from account",new BeanListHandler<Account>(Account.class));
        } catch (Exception e) {
           throw new RuntimeException(e);
        }
    }

    public Account findAccount(Integer accountId) {
        try {
            return runner.query(connectionUtils.getThreadConnection(),"select * from account where id = ?",new BeanHandler<Account>(Account.class),accountId);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public void saveAccount(Account account) {
        try {
             runner.update(connectionUtils.getThreadConnection(),"insert into account(name,money) values(?,?)",account.getName(),account.getMoney());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public void updateAccount(Account account) {
        try {
            runner.update(connectionUtils.getThreadConnection(),"update account set name = ?,money = ? where id = ?",account.getName(),account.getMoney(),account.getId());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

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

    public Account findAccountByName(String accountName) {
        try {
            List<Account> accounts = runner.query(connectionUtils.getThreadConnection(),"select * from account where name = ?",new BeanListHandler<Account>(Account.class),accountName);
            if (accounts == null || accounts.size() == 0){
                return null;
            }
            if (accounts.size()>1){
                throw new RuntimeException("结果集不唯一,数据有问题");
            }
            return accounts.get(0);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public void setRunner(QueryRunner runner) {
        this.runner = runner;
    }

    public QueryRunner getRunner() {
        return runner;
    }
}

package com.it.service.impl;

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

import java.util.List;

/**
 * @Author: 东方老赢
 * @Date: 2020/4/2 11:18
 *
 * 账户的业务层
 */
public class AccountServiceImpl implements IAccountService {
    private IAccountDao accountDao;
    private TransactionManager txManager;

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

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

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

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

    public void saveAccount(Account account) {
        try {
            //1.开启事务
            txManager.beginTransaction();
            //2.执行操作
            accountDao.saveAccount(account);
            //3.提交事务
            txManager.commit();
        }catch (Exception e){
            //5.回滚操作
            txManager.rollback();
        }finally {
            //6.释放连接
            txManager.release();
        }

    }

    public void updateAccount(Account account) {
        try {
            //1.开启事务
            txManager.beginTransaction();
            //2.执行操作
            accountDao.updateAccount(account);
            //3.提交事务
            txManager.commit();
        }catch (Exception e){
            //5.回滚操作
            txManager.rollback();
        }finally {
            //6.释放连接
            txManager.release();
        }

    }

    public void deleteAccount(Integer accountId) {
        try {
            //1.开启事务
            txManager.beginTransaction();
            //2.执行操作
            accountDao.deleteAccount(accountId);
            //3.提交事务
            txManager.commit();
        }catch (Exception e){
            //5.回滚操作
            txManager.rollback();
        }finally {
            //6.释放连接
            txManager.release();
        }
    }

    public void transfer(String sourceName, String targetName, Float money) {
        try {
            //1.开启事务
            txManager.beginTransaction();
            //2.执行操作
                //2.1.根据名称查询转出账户
                Account source = accountDao.findAccountByName(sourceName);
                //2.2.根据名称查询转入账户
                Account target = accountDao.findAccountByName(targetName);
                //2.3.转出账户减钱
                source.setMoney(source.getMoney()-money);
                //2.4.转入账户加钱
                target.setMoney(target.getMoney()+money);
                int i = 1/0;
                //2.5.更新转出账户
                accountDao.updateAccount(source);
                //2.6.更新转入账户
                accountDao.updateAccount(target);
            //3.提交事务
            txManager.commit();
        }catch (Exception e){
            //5.回滚操作
            txManager.rollback();
            throw new RuntimeException(e);
        }finally {
            //6.释放连接
            txManager.release();
        }

    }
}

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

    <!--1.配置Service对象-->
    <bean id="accountService" class="com.it.service.impl.AccountServiceImpl">
        <!--注入dao-->
        <property name="accountDao" ref="accountDao"></property>
        <!--注入事务管理器-->
        <property name="txManager" ref="txManager"></property>
    </bean>

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

    <!--3.配置Queryrunner对象:多例,防止线程冲突-->
    <bean id="runner" class="org.apache.commons.dbutils.QueryRunner"  scope="prototype">
    </bean>

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

    <!--配置Connection的工具类 ConnectionUtils-->
    <bean id="connectionUtils" class="com.it.utils.ConnectionUtils">
        <!--注入数据源-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--注入ConnectionUtils-->
    <bean id="txManager" class="com.it.utils.TransactionManager">
        <property name="connectionUtils" ref="connectionUtils"></property>
    </bean>

</beans>
package com.it.test;

import com.it.domain.Account;
import com.it.service.IAccountService;
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.util.List;

/**
 * @Author: 东方老赢
 * @Date: 2020/4/2 11:59
 *
 * 使用Junit单元测试,测试我们的配置
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:bean.xml")
public class AccountServiceTest {

    @Autowired
    private IAccountService as;

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

    @Test
    public void findAll(){
        List<Account> accounts = as.findAllAccount();
        for (Account account:
             accounts) {
            System.out.println(account);
        }
    }
}

3.回顾之前讲过的一个技术:动态代理(基于接口)

  • 动态代理:
    • 特点:字节码随用随创建,随用随加载
    • 作用:不修改源码的基础上对方法增强
    • 分类:
      • 基于接口的动态代理
      • 基于子类的动态代理
    基于接口的动态代理:
    • 涉及的类:Proxy
    • 提供者:JDK官方
    • 如何创建代理对象
      •  使用Proxy类中的newProxyInstance方法
        
    • 创建代理对象的要求
      •  被代理类最少实现一个接口,如果没有则不能使用
        
    • newProxyInstance方法的参数:
      •     ClassLoader:类加载器
         		他是用于加载代理对象字节码的。和被代理对象使用相同的类加载器。固定写法
            Class[]:字节码数组
            	他是用于让代理对象有相同方法。固定写法
            InvocationHandler:用于提供增强的代码
                他是让我们写如何代理。我们一般都是写一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的
                此接口的实现类都是谁用谁写
        
 Iproducer proxyProducer = (Iproducer) Proxy.newProxyInstance(producer.getClass().getClassLoader(),
                producer.getClass().getInterfaces(),
                new InvocationHandler() {
                    /**
                     * 作用:执行被代理对象的任何接口方法都会经过该方法
                     * @param proxy:代理对象的引用(引用对象需要final修饰)
                     * @param method:当前执行的方法
                     * @param args:当前执行方法所需的参数
                     * @return    :和被代理对象方法有相同的返回值
                     * @throws Throwable
                     */
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //提供增强的代码
                        Object returnValue = null;
                        //1.获取方法执行的参数
                        Float money = (Float) args[0];
                        //2.判断当前方法是不是销售
                        if ("saleProduct".equals(method.getName())){
                            returnValue =  method.invoke(producer,money*0.8f);
                        }
                        return returnValue;
                    }
                });
        proxyProducer.saleProduct(10000f);
    }

4.动态代理另一种实现方式:基于子类

上述基于接口的动态代理方式有一个很明显的问题就是太过于依赖接口,一次动态代理还有另一种实现方式:基于子类

  • 这种动态代理方式是通过第三方jar包实现的
<dependencies>
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib-nodep</artifactId>
            <version>2.1_3</version>
        </dependency>
    </dependencies>
  • 基于子类的动态代理:
       *  涉及的类:Enhancer
       *      提供者:第三方cglib库
       * 如何创建代理对象
       *      使用Enhancer类中的create方法
       * 创建代理对象的要求
       *      被代理类不能是最终类
       *    create方法的参数:
       *          Class:字节码
       *              他是用于指定被代理对象的字节码
       *          Callback:用于提供增强的代码
       *              他是让我们写如何代理。我们一般都是写一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的
       *              此接口的实现类都是谁用谁写
       *              我们一般写的是改接口的子接口实现类
    
   Producer cglibProducer = (Producer) Enhancer.create(producer.getClass(), new MethodInterceptor() {
            /**
             * 执行被代理对象的任何方法都会经过该方法
             * @param o
             * @param method
             * @param objects
             *      以上三个参数和基于动态代理中invoke方法的参数是一样的
             * @param methodProxy:当前执行方法的代理对象
             * @return
             * @throws Throwable
             */
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                //提供增强的代码
                Object returnValue = null;
                //获取方法执行的参数
                Float money = (Float) objects[0];
                //判断当前方法是不是销售
                if ("saleProduct".equals(method.getName())) {
                    returnValue = method.invoke(producer, money * 0.8f);
                }
                return returnValue;
            }
        });
        cglibProducer.saleProduct(10000f);

5.解决案例中的问题

package com.it.factory;

import com.it.service.IAccountService;
import com.it.utils.TransactionManager;

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

/**
 * @Author: 东方老赢
 * @Date: 2020/4/5 16:09
 *
 * 用于创建Service的代理对象工厂
 */
public class BeanFactory {
    private IAccountService accountService;
    private TransactionManager txManager;

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

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

    /**
     * 获取Service代理对象
     */
    public IAccountService getAccountService(){
       return (IAccountService) Proxy.newProxyInstance(accountService.getClass().getClassLoader(),
                accountService.getClass().getInterfaces(),
                new InvocationHandler() {
                    /**
                     * 添加事物的支持
                     * @param proxy
                     * @param method
                     * @param args
                     * @return
                     * @throws Throwable
                     */
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        Object returnValue = null;
                        try {
                            //1.开启事务
                            txManager.beginTransaction();
                            //2.执行操作
                            returnValue = method.invoke(accountService,args);
                            //3.提交事务
                            txManager.commit();
                            //4.返回结果
                            return returnValue;
                        }catch (Exception e){
                            //5.回滚操作
                            txManager.rollback();
                            throw new RuntimeException(e);
                        }finally {
                            //6.释放连接
                            txManager.release();
                        }
                    }
                });
    }
}

<!--配置BeanFactory对象-->
    <bean id="beanFactory" class="com.it.factory.BeanFactory">
        <property name="accountService" ref="accountService"></property>
        <property name="txManager" ref="txManager"></property>
    </bean>
    <bean id="proxyAccountService" factory-bean="beanFactory" factory-method="getAccountService"></bean>
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:bean.xml")
public class AccountServiceTest {

    @Autowired
    @Qualifier("proxyAccountService")
    private IAccountService as;

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

6.AOP的概念

       在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
  • 作用:在程序运行期间,不修改源码对已有方法进行增强
  • 优势
    • 减少重复代码
    • 提高开发效率
    • 维护方便
  • AOP实现方式:使用动态代理技术

7.Spring中的AOP相关术语和细节

Spring中的AOP,就是通过配置的方式实现的
  • 相关术语:
    • Joinpoin(连接点):所谓连接点就是值那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点。所有的方法都是连接点
    • Pointcut(切入点):所谓切入点是指我们要对哪些Joinpoint进行拦截的定义。只有被增强的方法才是切入点
    • Advice(通知/增强):所谓通知是指拦截到Joinpoint之后要做的事情就是通知
      • 通知类型:前置通知、后置通知、异常通知、最终通知、环绕通知
        在这里插入图片描述
    • Introduction(引介):引介是一种特殊情况下的通知,在不修改类代码的前提下,Introduction可以在运行期为类动态地添加一些方法或Field
    • Target(目标对象):代理的目标对象
    • Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程
      spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入
    • Proxy(代理):一个类被AOP织入增强后,就产生一个结果代理类
    • Aspect(切面):是切入点和通知(引介)的结合

8.Spring中基于XML和注解的AOP配置

一、基于XML

  • 首先导入jar包
<dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.5.3</version>
        </dependency>
    </dependencies>
  • 然后添加bean.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>        

接下来开始展示

package com.spring.Impl;

import com.spring.IAccountService;

/**
 * @Author: 东方老赢
 * @Date: 2020/4/5 20:21
 */
public class AccountServiceImpl implements IAccountService {

    public void saveAccount() {
        System.out.println("执行了保存账户");
    }

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

    public int deleteAccount() {
        System.out.println("执行了删除账户");
        return 0;
    }
}

package com.spring.util;

/**
 * @Author: 东方老赢
 * @Date: 2020/4/5 20:24
 *
 * 用于打印日志,计划让其在切入点方法执行之前执行(切入点方法就是业务层方法)
 */
public class Logger {
    public void printLog(){
        System.out.println("Logger类中的printLog方法开始记录日志了");
    }
}

package com.spring.test;

import com.spring.IAccountService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @Author: 东方老赢
 * @Date: 2020/4/6 9:09
 */
public class TestAOP {
    public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        IAccountService as = (IAccountService) ac.getBean("accountService");
        as.saveAccount();
        as.updateAccount(1);
        as.deleteAccount();
    }
}
  <!--配置spring的IOC,把Service对象配置进来-->
    <bean id="accountService" class="com.spring.Impl.AccountServiceImpl"></bean>
    <!--配置Logger类-->
    <bean id="logger" class="com.spring.util.Logger"></bean>

    <!--配置AOP-->
    <aop:config>
        <!--配置切面-->
        <aop:aspect id="logAdvice" ref="logger">
            <!--配置通知的类型,并且建立通知方法和切入点方法的关联-->
<!--            <aop:before method="printLog" pointcut="execution(public void com.spring.Impl.AccountServiceImpl.saveAccount())"></aop:before>-->
            <aop:before method="printLog" pointcut="execution(*  *..*.*(..))"></aop:before>
        </aop:aspect>
    </aop:config>
spring中基于XML的AOP配置步骤:
    1.把通知Bean也交给spring来管理
    2.使用aop:config标签表名aop的配置
    3.使用aop:aspect标签表明配置切面
            id属性:是给切面提供一个唯一标识
            ref属性:是指定通知类bean的id
    4.在aop:aspect标签的内部使用对应标签来配置通知的类型
            我们现在示例是让printLog方法在切入点方法执行之前执行,所以是前置通知
            aop:before:表示配置前置通知
                method属性:用于指定Logger类中那个方法是前置通知
                pointcut属性:用于指定切入点表达式,该表达式的含义指的是对业务层中哪些方法增强

          切入点表达式的写法:
                关键字:execution(表达式)
                表达式:访问修饰符   返回值    包名.包名.包名.....类名.方法名(参数列表)
             例如:execution(public  void  com.spring.service.Impl.AccountServiceImpl.saveAccount())
           表达式不同写法:
                返回值可以用通配符,表示任意返回值
                    execution(*  com.spring.service.Impl.AccountServiceImpl.saveAccount())
                包名可以使用通配符,表示任意包。但是有几级包,就需要写几个*.
                    execution(*  *.*.*.*.AccountServiceImpl.saveAccount())
                包名还可以升级使用..表示当前包和子包
                    execution(*  *..AccountServiceImpl.saveAccount())
                类名和方法名都可以使用*来实现通配
                    execution(*  *..*.*())
                参数列表:
                    可以直接写数据类型:
                        基本数据类型:int、String
                        引用类型写包名.类名方式:java.lang.String
                    可以使用通配符表示任意类型,但是必须有参数:*
                    可以使用..表示有无参数均可,有参数可以是任意类型
                        全统配写法:*  *..*.*(..)
                        <aop:before method="printLog" pointcut="execution(*  *..*.*(..))"></aop:before>

                  实际开发中切入点表达式的通常写法:
                        切到业务层实现类下的所有方法
                            * com.spring.service.impl.*.*(..)
  • 四种常用通知类型:
  <!--配置AOP-->
    <aop:config>
        <!--配置切入点表达式
                此标签写在aop:aspect标签内部只能当前切面用
                写在aop:aspect标签外面,就变成了所有切面可用
                    注:在外面只能配置在切面之前-->
        <aop:pointcut id="pt1" expression="execution(*  com.spring.service.Impl.*.*(..))"/>
        
        <!--配置切面-->
        <aop:aspect id="logAdvice" ref="logger">
           
            <!--前置通知:在切入点方法执行之前执行-->
            <aop:before method="beforePrintLog" pointcut-ref="pt1"></aop:before>
            <!--后置通知:在切入点方法正常执行之后执行。他与异常通知只能执行一个-->
            <aop:after-returning method="afterPrintLog" pointcut-ref="pt1"></aop:after-returning>
            <!--异常通知:在切入点方法执行产生异常以后执行。他与后置通知只能执行一个-->
            <aop:after-throwing method="throwPrintLog" pointcut-ref="pt1"></aop:after-throwing>
            <!--最终通知:无论切入点是否正常执行他都会在其后面执行-->
            <aop:after method="finalPrintLog" pointcut-ref="pt1"></aop:after>


        </aop:aspect>
    </aop:config>
  • spring中的环绕日志
 				<!--配置环绕通知-->
                <aop:around method="aroundPrintLog" pointcut-ref="pt1"></aop:around>
 public void aroundPrintLog(ProceedingJoinPoint point) {
        System.out.println("Logger类中的aroundPrintLog方法开始记录日志了");
    }

在这里插入图片描述

问题:
	当我们配置了环绕通知后,切入点方法没有执行,而通知方法执行了
分析:
 	通过对比动态代理中的环绕通知代码,发现动态代理的环绕通知有明确的切入点方法调用,
 	而我们的代码中没有
解决:
	Spring框架为我们提供了一个接口:ProceedingJoinpoint。该接口会有一个方法 proceed() ,此方法就相当于明确调用切入点方法
    该接口可以作为环绕通知的方法参数,在程序执行时,spring框架会为我们提供该接口的实现类供我们使用
 	spring中的环绕通知
    他是spring框架为我们提供的一种可以在代码中手动控制增强方法何时执行的方式
 public Object aroundPrintLog(ProceedingJoinPoint point) {
        Object rtValue = null;
        try {
            Object[] args = point.getArgs();//得到方法执行所需的参数
            System.out.println("Logger类中的aroundPrintLog方法开始记录日志了...前置");
            rtValue = point.proceed(args);//明确调用业务层方法(切入点方法)
            System.out.println("Logger类中的aroundPrintLog方法开始记录日志了...后置");
            return rtValue;
        } catch (Throwable throwable) {
            System.out.println("Logger类中的aroundPrintLog方法开始记录日志了...异常");
            throw new RuntimeException(throwable);
        } finally {
            System.out.println("Logger类中的aroundPrintLog方法开始记录日志了...最终");
        }
    }

在这里插入图片描述

二、基于注解

<?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.spring</groupId>
    <artifactId>day03_eesy_03springAOP</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.7</version>
        </dependency>
    </dependencies>
</project>
package com.spring.service;

/**
 * @Author: 东方老赢
 * @Date: 2020/4/5 20:19
 */
public interface IAccountService {
    void saveAccount();
    void updateAccount(int i);
    int deleteAccount();
}
package com.spring.service.Impl;

import com.spring.service.IAccountService;
import org.springframework.stereotype.Service;

/**
 * @Author: 东方老赢
 * @Date: 2020/4/5 20:21
 */
@Service("accountService")
public class AccountServiceImpl implements IAccountService {

    public void saveAccount() {
        System.out.println("执行了保存账户");
    }

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

    public int deleteAccount() {
        System.out.println("执行了删除账户");
        return 0;
    }
}

package com.spring.util;

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

/**
 * @Author: 东方老赢
 * @Date: 2020/4/5 20:24
 *
 * 用于打印日志,计划让其在切入点方法执行之前执行(切入点方法就是业务层方法)
 */
@Component("logger")
@Aspect // 表示当前类是一个切面类
public class Logger {
    @Pointcut("execution(* com.spring.service.Impl.*.*(..))")
    public void pt1(){}
    /**
     * 前置通知
     */
    @Before("pt1()")
    public void beforePrintLog(){
        System.out.println("前置通知Logger类中的beforePrintLog方法开始记录日志了");
    }

    /**
     * 后置通知
     */
    @AfterReturning("pt1()")
    public void afterPrintLog(){
        System.out.println("后置通知Logger类中的afterPrintLog方法开始记录日志了");
    }

    /**
     * 异常通知
     */
    @AfterThrowing("pt1()")
    public void throwPrintLog(){
        System.out.println("异常通知Logger类中的throwPrintLog方法开始记录日志了");
    }

    /**
     * 最终通知
     */
    @After("pt1()")
    public void finalPrintLog(){
        System.out.println("最终通知Logger类中的finalPrintLog方法开始记录日志了");
    }

    /**
     * 环绕通知
     * @param point
     * @return
     */
    @Around("pt1()")
    public Object aroundPrintLog(ProceedingJoinPoint point) {
        Object rtValue = null;
        try {
            Object[] args = point.getArgs();//得到方法执行所需的参数
            System.out.println("Logger类中的aroundPrintLog方法开始记录日志了...前置");
            rtValue = point.proceed(args);//明确调用业务层方法(切入点方法)
            System.out.println("Logger类中的aroundPrintLog方法开始记录日志了...后置");
            return rtValue;
        } catch (Throwable throwable) {
            System.out.println("Logger类中的aroundPrintLog方法开始记录日志了...异常");
            throw new RuntimeException(throwable);
        } finally {
            System.out.println("Logger类中的aroundPrintLog方法开始记录日志了...最终");
        }
    }
}
package com.spring.test;

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

/**
 * @Author: 东方老赢
 * @Date: 2020/4/6 9:09
 */
public class TestAOP {
    public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        IAccountService as = (IAccountService) ac.getBean("accountService");
        as.saveAccount();
//        as.updateAccount(1);
//        as.deleteAccount();
    }
}

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
    <!--配置spring创建容器时要扫描的包-->
    <context:component-scan base-package="com.spring"></context:component-scan>
    <!--配置spring开启注解AOP的支持-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
  • 使用四种通知的执行结果:后置与最终顺序不正常,那是因为spring本身注解的问题,无法更改

在这里插入图片描述

  • 使用环绕通知的执行结果:顺序正常

在这里插入图片描述

因此,注解AOP推荐环绕通知

第四篇:JdbcTemplate & spring中事务的控制

在这里插入图片描述

  • 1.spring中的JdbcTemplate(会用)
    • JdbcTemplate的作用:他就是用于和数据库交互的,实现对表的CRUD操作
    • 使用方式:
      • 使用之前需要导包
 <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>
    </dependencies>
  • 直接使用
package com.spring.jdbcTemplate;

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


/**
 * @Author: 东方老赢
 * @Date: 2020/4/6 17:20
 */
public class jdbcTemplateDemo1 {
    public static void main(String[] args) {
        //准备数据源
        DriverManagerDataSource ds = new DriverManagerDataSource();
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl("jdbc:mysql://localhost:3306/spring");
        ds.setUsername("root");
        ds.setPassword("123456");

        //创建JdbcTemplate对象
        JdbcTemplate jt = new JdbcTemplate();
        //给jt设置数据源
        jt.setDataSource(ds);
        //执行操作
        jt.execute("insert into account(name,money) values('ccc',1000)");
    }
}

  • 配合XML使用
package com.spring.jdbcTemplate;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;

/**
 * @Author: 东方老赢
 * @Date: 2020/4/6 18:40
 */
public class jdbcTemplateDemo2 {
    public static void main(String[] args) {
       ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        JdbcTemplate jt = ac.getBean("jdbcTemplate",JdbcTemplate.class);
        //增加
//        jt.update("insert into account(name,money) values(?,?)","eee",1000);
        //更新
//        jt.update("update account set name = ?,money = ? where id = ?","ede",1234,6);
        //删除
//        jt.update("delete from account where id = ?",6);
        //查询所有
//        List<Account> accounts = jt.query("select * from account", new BeanPropertyRowMapper<Account>(Account.class));
//        for (Account account:
//             accounts) {
//            System.out.println(account);
//        }
        //查询单个
//        List<Account> account = jt.query("select * from account where id = ?", new BeanPropertyRowMapper<Account>(Account.class), 1);
//        System.out.println(account.isEmpty()? "查询结果为空":account);
        //查询返回一行一列(使用聚合函数,但是不加group by子句)
        Integer count = jt.queryForObject("select count(*) from account where money > ?", Integer.class, 1);
        System.out.println(count);
    }
}
<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="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/spring"></property>
        <property name="username" value="root"></property>
        <property name="password" value="123456"></property>
    </bean>
</beans>
  • 配合XML&Dao使用
package com.spring.dao.impl;

import com.spring.dao.IAccountDao;
import com.spring.domain.Account;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.support.JdbcDaoSupport;

import java.util.List;

/**
 * @Author: 东方老赢
 * @Date: 2020/4/7 10:42
 *
 */
public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao {
    private JdbcTemplate template;

    public void setTemplate(JdbcTemplate template) {
        this.template = template;
    }

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

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

    public void updateaccoutn(Account account) {
        template.update("update account set name = ?,money = ? where id = ?",account.getName(),account.getMoney(),account.getId());
       
    }
}
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">


   <bean id="accountDao" class="com.spring.dao.impl.AccountDaoImpl">
        <property name="template" ref="jdbcTemplate"></property>
   </bean>-->

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/spring"></property>
        <property name="username" value="root"></property>
        <property name="password" value="123456"></property>
    </bean>
</beans>
  • 关于JdbcSupport的两种写法:用于解决多个Dao中重复代码
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">


    <bean id="accountDao" class="com.spring.dao.impl.AccountDaoImpl">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/spring"></property>
        <property name="username" value="root"></property>
        <property name="password" value="123456"></property>
    </bean>
</beans>
package com.spring.dao.impl;

import com.spring.dao.IAccountDao;
import com.spring.domain.Account;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.support.JdbcDaoSupport;

import java.util.List;

/**
 * @Author: 东方老赢
 * @Date: 2020/4/7 10:42
 *
 * JdbcDaoSupport 基于xml与org.springframework.jdbc.core.support.JdbcDaoSupport实现
 */
public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao {
//    private JdbcTemplate template;
//
//    public void setTemplate(JdbcTemplate template) {
//        this.template = template;
//    }

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

    public Account findaccountByName(String name) {
        List<Account> accout = getJdbcTemplate().query("select * from account where name = ?", new BeanPropertyRowMapper<Account>(Account.class), name);
        if (accout.isEmpty()){
            return null;
        }
        if (accout.size()>1){
            throw  new RuntimeException("结果集重复");
        }
        return accout.get(0);
    }

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

  • 2.spring中的事务控制
    • 基于XML
 <!--4.配置数据源-->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <!--连接数据库的必备信息-->
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/spring"></property>
        <property name="username" value="root"></property>
        <property name="password" value="123456"></property>
    </bean>

    <!--spring中基于XML的声明事务控制配置步骤
            1.配置事务管理器
            2.配置事务的通知:
                此时我们需要导入事物的约束  tx名称空间和约束  同时也需要aop的
                使用tx:advice标签配置事务通知
                    属性:
                        id:给事务通知起一个唯一的标识
                        transaction-manager:给事务通知提供一个事务管理器引用
            3.配置AOP的通用切入点表达式
            4.建立事务通知和切入点表达式的对应关系
            5.配置事务的属性
                    是在事务的通知tx:advice标签的内部-->

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

<!--    配置事务的通知-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
    <!-- 配置事务的属性
            isolation:用于指定事物的隔离级别。默认值是default,表示数据库的默认隔离级别
            propagation:用于指定事务的传播行为。默认值是required,表示一定会有事务,增删改的选择。查询方法可以选择supports
            read-only:指定事务是否只读。只有查询方式才能设置为true。默认值是false,表示读写
            timeout:用于指定事务的超时时间,默认值是-1,表示永不超时。如果指定了数值,以秒为单位
            rollback-for:用于指定一个异常,当产生该异常时,事务回滚,产生其他异常时,事物不回滚。没有默认值,表示任何异常都回滚
            no-rollback-for:用于指定一个异常,当产生该异常时,事务不回滚,产生其他异常时事务回滚。没有默认值,表示任何异常都回滚-->
        <tx:attributes>
            <tx:method name="*" propagation="REQUIRED" read-only="false"/>
            <tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
        </tx:attributes>
    </tx:advice>

    <!--配置AOP-->
    <aop:config>
<!--        配置切入点表达式-->
        <aop:pointcut id="pt1" expression="execution(* com.it.service.impl.*.*(..))"/>
<!--        建立切入点表达式和事务通知的对应关系-->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"></aop:advisor>
    </aop:config>
  • 基于注解
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

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

    <bean id="Jdbctempalte" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--4.配置数据源-->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <!--连接数据库的必备信息-->
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/spring"></property>
        <property name="username" value="root"></property>
        <property name="password" value="123456"></property>
    </bean>

    <!--spring中基于XML的声明事务控制配置步骤
            1.配置事务管理器
            2.开启spring对注解事务的支持
            3.在需要事务支持的地方使用@Transactional注解(AccountService)
         -->

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

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

注:注解配置事务的属性不如xml方便

发布了23 篇原创文章 · 获赞 5 · 访问量 1127

猜你喜欢

转载自blog.csdn.net/qq_40181435/article/details/105198340
今日推荐