文章目录
- 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接口的实现类。通常情况下都是匿名内部类,但不是必须的,此接口的实现类都是谁用谁写
- ClassLoader
-
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(为方法拦截的意思)
- class:字节码
-
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表示需要增强的方法
- 配置通知时,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()
读写型事务:增加、删除和修改会开启事务。
只读型事务:执行查询时,也会开启事务。
建议查询时设置为只读。
- 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注解中,可以添加用于定义事务的相关的信息,隔离级别、超时信息、传播行为、是否只读。
- 在@Transactional注解中,可以添加用于定义事务的相关的信息,隔离级别、超时信息、传播行为、是否只读。
4.5 编程式事务与声明式事务对比
- 编程式事务: 通过硬编码的方式实现对事务的管理,存在耦合,此方式不可取
- 主要通过事务管理器和事务模板,来进行事务管理,执行事务模板的execute方法。存在严重耦合,不采用
- 声明式事务: 通过AOP的方式实现对事务的管理,不存在耦合性,便于管理(可配置)