Spring从入门到精通(二)

news/2024/11/29 1:52:29/

文章目录

  • 1.动态代理
    • 1.1 概念
    • 1.2 jdk动态代理(重点)
    • 1.3 基于子类的动态代理(了解)
  • 2.AOP
    • 2.1 概念
    • 2.2 springAop — 基于AspectJ技术
      • 2.2.1 AspectJ使用(XML)
      • 2.2.2 AspectJ使用(注解开发)
  • 3. JdbcTemplate
    • 3.1 JdbcTemplate的基本概述
    • 3.2 JdbcTemplate在spring的ioc中配置
      • (1)XML配置
      • (2)注解配置
      • (4)JdbcTemplate的CRUD操作
  • 4. Spring的事务管理
    • 4.1 Spring的事务管理的API
    • 4.2 配置编程式事务(了解)
    • 4.3 配置声明式事务(XML方式)(重点)
    • 4.4 配置声明式事务(注解方式)(重点)
    • 4.5 编程式事务与声明式事务对比

1.动态代理

1.1 概念

  • 作用: 将核心功能与辅助功能(事务、日志、性能监控代码)分离,达到核心业务功能更纯粹、辅助业务功能可复用。
  • 功能分离:
    在这里插入图片描述
  • 分类:
    • 基于接口的动态代理jdk
    • 基于子类的动态代理cglib
  • 两种代理的返回对象:
    • 基于接口的动态代理返回对象为代理接口类型(接口引用指向实现)
    • 基于子类的动态代理返回对象为代理类对象

1.2 jdk动态代理(重点)

(1)特点、作用、分类

  • 特点: 代理对象在程序的运行过程中创建
  • 作用: 不修改源码的基础上对目标类方法进行增强
  • 分类:
    • 基于接口的动态代理
      • 涉及的类:Proxy
      • 提供者:JDK官方

(2)使用

  • 如何创建代理对象

    • 使用Proxy类中的newProxyInstance方法
  • 创建代理对象的要求

    • 被代理类最少实现一个接口,如果没有则不能使用
  • newProxyInstance方法的参数

    • ClassLoader
      • 类加载器
      • 它是用于加载代理对象字节码的,和被代理对象使用相同的类加载器。
      • 固定写法(写被代理对象的类加载器
    • Class[ ]
      • 字节码数组
      • 它是用于让代理对象和被代理对象有相同的方法(只要两者都实现了同一个接口,那么两者的方法必然相同,所以我们传接口的字节码文件即可)。
      • 固定写法(写接口字节码文件
    • InvocationHandler
      • 处理器
      • 用于提供增强的代码,它是让我们写如何代理,我们一般都是写一个InvocationHandler接口的实现类。通常情况下都是匿名内部类,但不是必须的,此接口的实现类都是谁用谁写
  • InvocationHandler中invoke方法参数

    • @param proxy :当前代理对象的引用,一般很少用
    • @param method: 当前执行的方法(被代理的方法
    • @param args: 当前执行方法需要的参数
    • @return 和被代理对象一样的返回值,很少使用

(3)示例

  • 演示转账工程中,事务代理
  • 实现过程
    • 使用工厂普通方法产生代理对象
    • 配置bean.xml文件
    • 测试

1.使用工厂普通方法产生代理对象

public class BeanFactory {//    需要使用到的依赖类private TransactionManager transactionManager;private AccountService accountService;  //被代理的对象//    用于bean.xml注入(spring容器使用setter方法注入)public void setTransactionManager(TransactionManager transactionManager) {this.transactionManager = transactionManager;}public void setAccountService(AccountService accountService) {this.accountService = accountService;}//    通过工厂创建方法public AccountService getBean(){AccountService proxy = (AccountService) Proxy.newProxyInstance(accountService.getClass().getClassLoader(), accountService.getClass().getInterfaces(), new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//                判断需要代理的方法if ("transfer".equals(method.getName())) {try {
//                    开启事务transactionManager.begin();
//                        利用反射实现被代理对象的方法调用method.invoke(accountService,args);
//                    提交事务transactionManager.commit();} catch (Exception e) {
//                        回滚事务transactionManager.rollback();e.printStackTrace();} finally {
//                        释放资源transactionManager.realease();}}return null;}});return proxy;}}

2.配置bean.xml文件

<!--    工厂注册--><bean id="beanFactory" class="com.qfedu.factory.BeanFactory"><property name="transactionManager" ref="transactionManager"></property><property name="accountService" ref="accountService"></property></bean>
<!--    使用工厂产生代理对象--><bean id="proxy" class="com.qfedu.service.impl.AccountServiceImpl" factory-bean="beanFactory" factory-method="getBean"></bean>

3.测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:bean.xml" )
public class TestDemo01 {@Autowired@Qualifier("proxy")private AccountService accountService;@Testpublic void testProxy(){accountService.transfer("james","curry",100.0);}
}

1.3 基于子类的动态代理(了解)

  • 那我们要如何代理一个普通的Java类呢?我们可以使用基于子类的动态代理

(1)特点、作用、分类

  • 特点: 字节码随用随创建,随用随加载。
  • 作用: 不修改源码的基础上对目标类方法进行增强
  • 分类:
    • 第三方cglib库
    • 使用Enhancer类

(2)使用

  • 依赖的库(cglib)
	<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>2.1_3</version></dependency>
  • 如何创建代理对象

    • 使用Enhancer类中的create方法
  • 创建代理对象的要求

    • 被代理类不能是最终类(也就是这个类不能用final修饰,因为最终类不能创建子类)
  • create方法的参数

    • class:字节码
      • 它是用于指定被代理对象的字节码
    • callback:用于提供增强的代码
      • 我们一般写的都是callback接口的子接口实现类:MethodInterceptor(为方法拦截的意思)
  • MethodInterceptor实现类的intercept风法参数

    • @param o 当前代理对象的引用
    • @param method 代理对象需要执行的方法
    • @param objects 代理对象执行该方法需要用到的参数
    • @param methodProxy 当前执行方法的代理对象(不常用)
    • @return 返回值也是跟被代理对象执行方法的返回值一样
  • 示例:代理买电脑

1. 生产商(类—不实现接口)

public class Producer{public void buyProduct(String name) {System.out.println(name + "售卖了电脑");}
}

2. 代理商

/*** 消费者* 分别直接买* 让代理买*/
public class Consumer {public static void main(String[] args) {final Producer producer = new Producer();Producer proxy = (Producer) Enhancer.create(Producer.class, new MethodInterceptor() {/*** @param o 当前代理对象的引用* @param method 代理对象需要执行的方法* @param objects 代理对象执行该方法需要用到的参数* @param methodProxy 当前执行方法的代理对象(不常用)* @return 返回值也是跟被代理对象执行方法的返回值一样* @throws Throwable*/public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {if(method.getName().equals("buyProduct")){System.out.println("代理商对方法进行了增强");method.invoke(producer,objects);}return null;}});//代理商对象调用proxy.buyProduct("经销商");}
}

2.AOP

2.1 概念

  • AOP面向切面编程把通用共有的功能抽取出来,降低耦合,通过动态代理的方式来去执行。
  • AOP利用的是一种横切技术,解剖开封装的对象内部,并将那些影响多个类的公共行为封装到一个可重用模块,这就是所谓的Aspect方面/切面。
  • 切面,简单点所说,就是将哪些与业务无关,却为业务模块所共同调用的行为(方法)提取封装,减少系统的重复代码,以达到逻辑处理过程中各部分之间低耦合的隔离效果。
  • AOP采取横向抽取机制,取代了传统的纵向继承体系重复性代码
    在这里插入图片描述

2.2 springAop — 基于AspectJ技术

(1)AspectJ简介

  • 使用AspectJ的原因
    • SpringAop的底层采用的是动态代理技术,但是动态代理(jdk动态代理 cglib字节码代理)过于繁琐。于是spring引入了第三方的AspectJ框架
  • AspectJ
    • AspectJ是一个AOP框架,Spring引入AspectJ作为自身的AOP的开发
  • Spring两套AOP开发方式
    • Spring传统方式动态代理(弃用)
    • Spring基于AspectJ的AOP的开发(使用)

(2)AOP的相关术语

  • 连接点: 一个普通的方法(任何方法都可以被增强,这些方法就称为连接点)
  • 切点: 需要进行增强的方法
  • 通知: 增强的逻辑(方法增强的功能)
  • 切面: 切点+切面
  • 织入: 将通知应用到目标的过程

2.2.1 AspectJ使用(XML)

(1)引入依赖

 <dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.4</version></dependency>

(2)expression:切点表达式

  • public void com.qfdu.dao.impl.UserDaoImpl.findAll()
  • 修饰符 返回值类型 方法名(方法参数)
    • 1.修饰符可以省略
    • 2.返回值类型不能省略 可以使用*来代替
    • 3、包可以使用* 代替
    • 4.方法()不能省略 * .():代表任意方法
    • 5.(…) 方法参数使用…来替代

(3)AOP配置

  • 前置通知:在目标方法执行之前进行增强
  • 后置通知: 在目标方法执行之后进行的操作。
  • 环绕通知: 在目标方法执行前和执行后都要执行(特别
  • 异常通知: 在目标方法执行出现异常的时候,进行增强(捕获到异常后才会进行增强
  • 最终通知: 无论代码是否有异常,总会执行
    • 配置通知时,method表示切面类中的方法,pointcut-ref表示需要增强的方法
      在这里插入图片描述
  • 基于XML的AOP实现事务控制
<!--    AOP(面向切面编程)事务开发--><!--    配置目标增强对象--><bean id="accountService" class="com.qfedu.service.impl.AccountServiceImpl"><property name="accountDao" ref="accountDao"></property></bean>
<!--    将切面类交给spring管理(事务切面)--><bean id="transactionManager" class="com.qfedu.utils.TransactionManager"><property name="connectionUtil" ref="connectionUtil"></property></bean><!--    开启aop配置--><aop:config>
<!--        1.定义切点--><aop:pointcut id="transaction" expression="execution(* com.qfedu.service.impl.AccountServiceImpl.transfer(..))"/>
<!--        2.配置切面--><aop:aspect ref="transactionManager">
<!--            前置通知+切点--><aop:before method="begin" pointcut-ref="transaction"></aop:before>
<!--            后置通知+切点--><aop:after-returning method="commit" pointcut-ref="transaction"></aop:after-returning>
<!--            环绕通知+切点-->
<!--            <aop:around method="transactionController" pointcut-ref="transaction"></aop:around>--><!--            异常通知+切点--><aop:after-throwing method="rollback" pointcut-ref="transaction"></aop:after-throwing>
<!--            最终通知+切点--><aop:after method="realease" pointcut-ref="transaction"></aop:after></aop:aspect></aop:config>

(4)环绕通知

  • 切面环绕通知引用方法配置
    在这里插入图片描述
  • 在AOP配置中,配置环绕通知
    在这里插入图片描述

2.2.2 AspectJ使用(注解开发)

(1)在配置文件引入spring对aop注解的支持

<!--开启包扫描-->
<context:component-scan base-package="com.qf"></context:component-scan><!--开启spring对aop注解的支持-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

(2)声明切面类 —— @Aspect
在这里插入图片描述

(3)在切面类中配置切点和通知

  • 方式一
    在这里插入图片描述
  • 方式二:
    在这里插入图片描述

(4)存在问题

  • 在使用@Aspect时,由于Spring的版本不同,其通知注解的执行顺寻可能会存在为题
    • @After@AfterReturning@AfterThrowing执行

3. JdbcTemplate

3.1 JdbcTemplate的基本概述

(1)概念:

  • JdbcTemplate是spring框架中提供的一个模板类,是对原生jdbc的简单封装.
  • 模板类: 各种原生操作的封装工具

(2)常见操作的模板类

  • 操作关系型数据库
    • JdbcTemplate
    • HibernateTemplate
  • 操作nosql数据库
    • RedisTemplate
  • 操作消息队列
    • JmsTemplate

(3)作用

  • 它是用和数据库交互的,实现数据表的crud操作

(4)依赖

	<dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.2.0.RELEASE</version></dependency><!--  tx:事务相关-->    <dependency><groupId>org.springframework</groupId><artifactId>spring-tx</artifactId><version>5.2.0.RELEASE</version></dependency>

3.2 JdbcTemplate在spring的ioc中配置

  • JdbcTemplate 配置必须指定数据源
  • DriverManagerDataSource 时Spring提供的数据源类,其祖先继承于java.sql.DataSource

(1)XML配置

在这里插入图片描述

(2)注解配置

@PropertySource(value = "classpath:db.properties")
public class JdbcConfig {
//    数据库连接属性@Value("${driverClass}")private String driver;@Value("${jdbcUrl}")private String url;@Value("${user}")private String username;@Value("${password}")private String password;//    jdbcTemplate@Bean("jdbcTemplate")public JdbcTemplate getJdbcTemplate(DataSource dataSource){return new JdbcTemplate(dataSource);}//    数据源@Bean("dataSource")public DataSource getDataSource(){DriverManagerDataSource dataSource=new DriverManagerDataSource();dataSource.setDriverClassName(driver);dataSource.setUsername(username);dataSource.setPassword(password);dataSource.setUrl(url);return dataSource;}
}

(4)JdbcTemplate的CRUD操作

  • execute:可以执行无返回值sql语句,常用于建表、删表的操作
  • query: 用于查询结果集,并自动进行结果集映射封装(可以使用可变参数)
    在这里插入图片描述
    在这里插入图片描述
  • update: 对数据库表进行更新
    在这里插入图片描述

4. Spring的事务管理

4.1 Spring的事务管理的API

(1) PlatformTransactionManager

  • 平台事务管理器:接口,是Spring用于管理事务的真正的对象。
  • 实现类:
    • DataSourceTransactionManager :底层使用JDBC管理事务。
    • HibernateTransactionManager :底层使用Hibernate管理事务。

(2)TransactionDefinition

  • 事务定义:用于定义事务的相关的信息,隔离级别、超时信息、传播行为、是否只读。
  • 获取事务传播行为
    • REQUIRED

​ 如果当前没有事务,就新建一个事务,如果已经存在一个事务,就加入到该事务中(默认值)。

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

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

  • 获取事务是否只读:

    • boolean isReadOnly()
      读写型事务:增加、删除和修改会开启事务。
      只读型事务:执行查询时,也会开启事务。

    建议查询时设置为只读。

4.2 配置编程式事务(了解)

(1)配置平台事务管理器和事务管理模板

  • 平台事务管理器必须指定数据源
    在这里插入图片描述
  • 事务管理模板必须指定事务管理器
    在这里插入图片描述
<!--配置平台事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><!--注入数据源--><property name="dataSource" ref="dataSource"></property>
</bean><!--配置事务管理模板-->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"><!--注入平台事务管理器--><property name="transactionManager" ref="transactionManager"></property>
</bean>

(2)在业务类中注入事务管理模板

  • 因为事务管理模板才是真正干活对象,真正进行事务的控制
    private AccountDao accountDao;private TransactionTemplate transactionTemplate;//使用set方法注入public void setAccountDao(AccountDao accountDao) {this.accountDao = accountDao;}public void setTransactionTemplate(TransactionTemplate transactionTemplate) {this.transactionTemplate = transactionTemplate;}

在这里插入图片描述
(3)在业务类里面进行事务控制

  • 使用TransactionTemplate提供的execute方法
    • 其参数为抽象类的实现对象TransactionCallbackWithoutResult
    • 实现抽象方法doInTransactionWithoutResult
    • 将业务纯粹的业务逻辑写入doInTransactionWithoutResult方法中,实现事务管理
//    配置编程式事务public void transfer(String sourceName, String targetName, Double money) {transactionTemplate.execute(new TransactionCallbackWithoutResult() {@Overrideprotected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {//根据名称查询出转账用户Account sourceAccount = accountDao.findAccountByName(sourceName);//根据名称查询转入账用户Account targetAccount = accountDao.findAccountByName(targetName);//转出账户扣钱sourceAccount.setMoney(sourceAccount.getMoney() - money);//转入账户加钱targetAccount.setMoney(targetAccount.getMoney() + money);//更新转出账户accountDao.updateAccount(sourceAccount);int i = 1/0;//更新转入账户accountDao.updateAccount(targetAccount);}});}

4.3 配置声明式事务(XML方式)(重点)

在这里插入图片描述

(1)配置事务增强

  • tx:advice需要指定平台事务管理器
<!--    平台事务管理器--><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"></property></bean>
  • tx:advice在这里插入图片描述
  • 配置
<!--配置事务增强-->
<tx:advice id="txAdvice" transaction-manager="transactionManager"><!--配置事务的属性--><tx:attributes><!--name:给哪个业务方法配置事务 propagation:事务的传播行为 REQUIRED默认值--><tx:method name="transfer" propagation="REQUIRED"/></tx:attributes>
</tx:advice>

(2)配置aop,将事务增强和切点连接在一起

<!--    配置aop--><aop:config>
<!--        配置切点--><aop:pointcut id="pointCut" expression="execution(* com.qfedu.service.impl.*.*(..))"/>
<!--        配置织入--><aop:advisor advice-ref="txAdvice" pointcut-ref="pointCut"></aop:advisor></aop:config>

4.4 配置声明式事务(注解方式)(重点)

(1)配置开启注解对事务的支持

  • 方式一: bean.xml中配置(指定平台事务管理器)
<!--    扫描事务注解--><tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
  • 方式二: 在Spring配置文件中添加 @EnableTransactionManagement 注解(扫描事务注解)
    • 示例
      在这里插入图片描述

    • 疑问@EnableTransactionManagement怎么指定平台事务管理器呢?

      • @EnableTransactionManagement会从容器中按照类型获取一个PlatformTransactionManager平台事务管理器
      • 注解源码解释
        在这里插入图片描述

(2)在业务类或业务类中方法上添加注解 @Transactional
在这里插入图片描述

  • @Transactional解析
    • 在@Transactional注解中,可以添加用于定义事务的相关的信息,隔离级别、超时信息、传播行为、是否只读。
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述

4.5 编程式事务与声明式事务对比

  • 编程式事务: 通过硬编码的方式实现对事务的管理,存在耦合,此方式不可取
    • 主要通过事务管理器和事务模板,来进行事务管理,执行事务模板的execute方法。存在严重耦合,不采用
  • 声明式事务: 通过AOP的方式实现对事务的管理,不存在耦合性,便于管理(可配置)

http://www.ppmy.cn/news/2524.html

相关文章

c语言---指针进阶(2)--玩转指针

今天内容不多&#xff0c;但都是精华。 1.数组参数和指针参数 2.函数指针 2.1笔试题 3.函数指针数组 1.数组参数和指针参数 例1&#xff1a;一维数组传参 void test(int arr[]) {} void test(int arr[10]) {} void test(int *arr) {}void test2(int *arr2[20]) {} void …

Verilog语法之generate for、generate if、generate case

0、前言 Verilog-2005中有3个generate 语句可以用来很方便地实现重复赋值和例化&#xff08;generate for&#xff09;或根据条件选择性地进行编译&#xff08;generate if和generate case&#xff09;等功能。接下来就一起看下这3个语句的应用场景和应用方法吧。 1、generate …

Laravel 某个固定数据排序在列表顶部sql查询(自定义排序)

业务需要列表中某个固定用户数据处于列表顶部&#xff0c;该用户author状态有多个值&#xff08;例如&#xff1a;1-999&#xff09;&#xff0c;需要置于顶部的为中间的某个值&#xff08;例如&#xff1a;author68&#xff09; 实现方式&#xff1a; 1、判断 author的值是不…

Web服务器通信原理

电脑&#xff1a; Windows 系统 微软开发 闭源 Linux 开源的系统&#xff08;无数的子系统&#xff09; Mac os 苹果系统 WEB 安全 > WEB的安全&#xff08;网站安全&#xff09; 服务器&#xff1a; 就是一台不关机的电…

Properties类的使用

Properties类是一个配置文件类&#xff0c;主要作用就是用来封装配置文件&#xff0c;将配置文件加载成为一个Properties对象。 注意&#xff1a;Properties类一般用来加载 .properties配置文件 首先看一下.properties配置文件的样子 driverClassNamecom.mysql.cj.jdbc.Drive…

LeetCode刷题复盘笔记—一文搞懂完全背包之139. 单词拆分问题(动态规划系列第十六篇)

今日主要总结一下动态规划完全背包的一道题目&#xff0c;139. 单词拆分 题目&#xff1a;139. 单词拆分 Leetcode题目地址 题目描述&#xff1a; 给你一个字符串 s 和一个字符串列表 wordDict 作为字典。请你判断是否可以利用字典中出现的单词拼接出 s 。 注意&#xff1a;…

字符串处理【AC自动机】 - 原理 AC自动机详解

字符串处理【AC自动机】 - 原理 AC自动机详解 AC自动机&#xff08;Aho-Corasick automaton&#xff09;在1975年产生于贝尔实验室&#xff0c;是著名的多模匹配算法。 学习AC自动机&#xff0c;要有KMP和Trie&#xff08;字典树&#xff09;的基础知识。 KMP是单模匹配算法&a…

还记得上学时每周要换座位吗?第四组换到第一组、第一组换到第二组……现在用一个字母表示一个组,请你计算经历n周之后座位的情况。

还记得上学时每周要换座位吗&#xff1f;第四组换到第一组、第一组换到第二组…… 现在用一个字母表示一个组&#xff0c;请你计算经历n周之后座位的情况。 解析&#xff1a; //约瑟夫问题 #include<iostream> (720)#include<string> #include<algorithm&…