Java框架之Spring02-AOP-动态代理-AspectJ-JdbcTemplate-事务
AOP
动态代理
代理设计模式的原理:使用一个代理将原本对象包装起来,然后用该代理对象”取代”原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。
代理模式的三要素:
代理主题接口
代理者
被代理者
代理模式的主要优点
代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
代理对象可以扩展目标对象的功能;
代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度;
其主要缺点
在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢;
增加了系统的复杂度;
动态代理的方式
静态代理类只能替一个主题接口进行代理工作
基于接口实现动态代理: JDK动态代理
基于继承实现动态代理: Cglib、Javassist动态代理
JDK动态代理步骤:
* 1、编写主题接口
* 2、编写被代理类
* 3、编写代理工作处理器:即代理类要替被代理类做什么事情(有参构造器)
* 要求:必须实现InvocationHandler,重写
* Object invoke(Object proxy, Method method, Object[] args)
* 第一个参数:代理类对象
* 第二个参数:被代理类和代理类 要执行的方法
* 第三个参数:要执行方法的实参列表
* 这个invoke方法不是程序员调用,当代理类对象执行对应的代理方法时,自动调用的
* 4、创建代理类及其对象
* 需要:Proxy:提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。
* static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
* 第一个参数:被代理类的类加载器,我们希望被代理和代理类使用同一个类加载器
* 第二个参数:被代理类实现的接口们
* 第三个参数:代理工作处理器对象
* 5、调用被代理的方法
注意:代理对象和实现类对象,都实现了相同的接口。属于兄弟关系。(不能强制转换为,实现类对象)
AOP概述
1) AOP(Aspect-Oriented Programming,面向切面编程):是一种新的方法论,是对传统 OOP(ObjectOrientedProgramming),面向对象编程)的补充。
面向对象 纵向继承机制
面向切面 横向抽取机制
2) AOP编程操作的主要对象是切面(aspect),而切面用于横切关注点。
3) 在应用AOP编程时,仍然需要定义公共功能,但可以明确的定义这个功能应用在哪里,以什么方式应用,并且不必修改受影响的类。这样一来横切关注点就被模块化到特殊的类里——这样的类我们通常称之为“切面”。
4) AOP的好处
① 每个事物逻辑位于一个位置,代码不分散,便于维护和升级
② 业务模块更简洁,只包含核心业务代码
AOP术语
1.横切关注点
从每个方法中抽取出来的同一类非核心业务。
2.切面(Aspect)
封装横切关注点信息的类,每个关注点体现为一个通知方法。
3.通知(Advice)
切面必须要完成的各个具体工作
4.目标(Target)
被通知的对象
5.代理(Proxy)
向目标对象应用通知之后创建的代理对象
6. 连接点(Joinpoint)
横切关注点在程序代码中的具体体现,对应程序执行的某个特定位置。例如:类某个方法调用前、调用后、方法捕获到异常后等。
7. 切入点(pointcut):
定位连接点的方式。每个类的方法中都包含多个连接点,所以连接点是类中客观存在的事物。
如果把连接点看作数据库中的记录,那么切入点就是查询条件——AOP可以通过切入点定位到特定的连接点。
切点通过org.springframework.aop.Pointcut 接口进行描述,它使用类和方法作为连接点的查询条件。
AspectJ
启用AspectJ注解支持
1、导入JAR包
2、引入aop名称空间
3、配置:<aop:aspectj-autoproxy>
当Spring IOC容器侦测到bean配置文件中的<aop:aspectj-autoproxy>元素时,会自动为与AspectJ切面匹配的bean创建代理
用AspectJ注解声明切面
在Spring中声明AspectJ切面为bean实例
初始化AspectJ切面之后,容器就会为那些与 AspectJ切面相匹配的bean创建代理
在AspectJ注解中,切面只是一个带有@Aspect注解的Java类
通知是标注有某种注解的Java方法
5种类型的通知注解:@Before(value="切入点表达式")
① @Before:前置通知,在方法执行之前执行
② @After:后置通知,在方法执行之后执行,即无论连接点是正常返回还是抛出异常,后置通知都会执行
③ @AfterRunning:返回通知,在方法返回结果之后执行 (如果异常,不执行 )
在返回通知中访问连接点的返回值,如果只想在连接点返回的时候记录日志,应使用返回通知代替后置通知
①在返回通知中,只要将returning属性添加到@AfterReturning注解中,就可以访问连接点的返回值。
②必须在通知方法的签名中添加一个同名参数。在运行时Spring AOP会通过这个参数传递返回值
③原始的切点表达式需要出现在pointcut属性中
④ @AfterThrowing:异常通知,在方法抛出异常之后执行 (如果无异常,不执行)
将throwing属性添加到@AfterThrowing注解中,在异常通知方法可以捕获到任何错误和异常。
也可以将参数声明为其他异常的参数类型。然后通知就只在抛出这个类型及其子类的异常时才被执行
⑤ @Around:环绕通知,围绕着方法执行
能够全面地控制连接点,甚至可以控制是否执行连接点。
连接点的参数类型必须是ProceedingJoinPoint。它是 JoinPoint的子接口,允许控制何时执行,是否执行连接点。
需要明确调用ProceedingJoinPoint的proceed()方法来执行被代理的方法。
注意:环绕通知的方法需要返回目标方法执行之后的结果,即调用 joinPoint.proceed();的返回值,否则会出现空指针异常
@Around(value = "rePointCut()")
public Object aroundMethod(ProceedingJoinPoint pjp) {
Object obj = null;
try {
//前置通知
System.out.println("前置通知");
obj = pjp.proceed(); //调用目标对象的方法
//返回通知
System.out.println("返回通知,结果:" + obj);
} catch (Throwable e) {
//异常通知
System.out.println("异常通知,ex:" + e);
e.printStackTrace();
} finally {
//后置通知
System.out.println("后置通知");
}
return obj;
}
切入点表达式
通过表达式的方式定位一个或多个具体的连接点。
语法格式
execution([权限修饰符] [返回值类型] [简单类名/全类名] [方法名]([参数列表]))
表达式: @Pointcut(value="execution(* com.spring.*.*(..))")
含义: ArithmeticCalculator接口中声明的所有方法。
第一个“*”代表任意修饰符及任意返回值。
第二个“*”代表,任意类的全类名称|任意类名
第三个“*”代表任意方法。
“..”匹配任意数量、任意类型的参数。
若目标类、接口与该切面类在同一个包中可以省略包名。
切入点表达式可以通过 “&&”、“||”、“!”等操作符结合起来。
重用切入点
在AspectJ切面中,可以通过@Pointcut注解将一个切入点声明成简单的方法。切入点的方法体通常是空的
切入点方法的访问控制符同时也控制着这个切入点的可见性。
在引入这个切入点时,必须将类名也包括在内。如果类没有与这个切面放在同一个包中,还必须包含包名。
其他通知可以通过方法名称引入该切入点
//提取表达式
@Pointcut(value="execution(* com.spring.aspectj.*.*(..))")
public void rePointCut() {}
@Before(value="rePointCut()"):当前类中重用切入点表达式
指定切面的优先级
在同一个连接点上应用不止一个切面时,除非明确指定,否则它们的优先级是不确定的
使用@Order注解,序号出现在注解中
@Aspect
@Order(0) //int类型,数值越小,优先级越高。
public class TestAspect{}
XML方式配置切面
基于注解的声明要优先于基于XML的声明,通过AspectJ注解,切面可以与AspectJ兼容,而基于XML的配置则是Spring专有的,所以不推荐
在bean配置文件中,所有的Spring AOP配置都必须定义在<aop:config>元素内部。对于每个切面而言,都要创建一个<aop:aspect>元素来为具体的切面实现引用后端bean实例。
切面bean必须有一个标识符,供<aop:aspect>元素引用。
1)声明切入点
切入点使用<aop:pointcut>元素声明。
① 定义在<aop:aspect>元素下:只对当前切面有效
② 定义在<aop:config>元素下:对所有切面都有效
基于XML的AOP配置不允许在切入点表达式中用名称引用其他切入点
2)声明通知
通知元素需要使用<pointcut-ref>来引用切入点
method属性指定切面类中通知方法的名称
<aop:config>
<aop:pointcut id="myPointcut" expression="execution(* com.spring.aspectj_xml.*.*(..))" />
<!-- 定义日志切面 -->
<aop:aspect id="loggingAspect" ref="loggingAspect" order="1">
<aop:before method="beforeMethod" pointcut-ref="myPointcut"/>
<aop:after method="afterMethod" pointcut-ref="myPointcut"/>
<aop:after-returning method="afterReturnMethod" returning="rs" pointcut-ref="myPointcut"/>
<aop:after-throwing method="afterThrowingMethod" throwing="ex" pointcut-ref="myPointcut"/>
</aop:aspect>
</aop:config>
JdbcTemplate
可以将Spring的JdbcTemplate看作是一个小型的轻量级持久化层框架,JdbcTemplate类是线程安全的
JdbcTemplate所需要的JAR包
spring-jdbc-4.0.0.RELEASE.jar
spring-orm-4.0.0.RELEASE.jar
spring-tx-4.0.0.RELEASE.jar
数据库驱动和数据源
druid-1.1.9.jar
mysql-connector-java-5.1.7-bin.jar
配置文件中配置相关的bean
<!-- 引入jdbc配置文件-->
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
<!-- 装配Druid数据源conn-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
p:url="${jdbc.url}"
p:username="${jdbc.username}"
p:password="${jdbc.password}"
p:driverClassName="${jdbc.driverClass}"
></bean>
<!-- 通过数据源装配JdbcTemplate-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 通过数据源装配事务管理器-->
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 启用事务管理器-->
<tx:annotation-driven transaction-manager="dataSourceTransactionManager"></tx:annotation-driven>
持久化操作
1) 增删改
update(String sql,Object... args)
2) 批处理增删改
batchUpdate(String sql,List<Object[]> batchArgs)
Object[]封装了SQL语句每一次执行时所需要的参数
List集合封装了SQL语句多次执行时的所有参数
3)获取单个数值型
queryForObject(String sql,Class<T> requiredType,Object... args)
4)获取单个对象类型
queryForObject(String sql,RowMapper rowMapper,Object... args)
5)获取多个JavaBean类型
query(String sql,RowMapper rowMapper,Object... args)
RowMapper对象可以使用BeanPropertyRowMapper实现类:注意new对象时指定类型
事务管理
事务就是一组由于逻辑上紧密关联而合并成一个整体(工作单元)的多个数据库操作,这些操作要么都执行,要么都不执行。
事务的四个属性(ACID)
①原子性(atomicity):“原子”的本意是“不可再分”,事务的原子性表现为一个事务中涉及到的多个操作在逻辑上缺一不可。事务的原子性要求事务中的所有操作要么都执行,要么都不执行。
②一致性(consistency):“一致”指的是数据的一致,具体是指:所有数据都处于满足业务规则的一致性状态。
③隔离性(isolation):隔离性原则要求多个事务在并发执行过程中不会互相干扰。
④持久性(durability):通常情况下,事务对数据的修改应该被写入到持久化存储器中。
编程式事务
使用原生的JDBC API实现事务管理是所有事务管理方式的基石,但是需要将事务管理代码嵌入到业务方法中,事务与业务代码相耦合,代码相对分散且混乱。所以:建议使用声明式事务。
①获取数据库连接Connection对象
②取消事务的自动提交
③执行操作
④正常完成操作时手动提交事务
⑤执行失败时回滚事务
⑥关闭相关资源
声明式事务
事务管理代码的固定模式作为一种横切关注点,可以通过AOP方法模块化,进而借助Spring AOP框架实现声明式事务管理。它将事务管理代码从业务方法中分离出来
Spring的核心事务管理抽象是它为事务管理封装了一组独立于技术的方法。无论使用Spring的哪种事务管理策略(编程式或声明式),事务管理器都是必须的。开发人员可以通过配置的方式进行事务管理。
事务管理器可以以普通的bean的形式声明在Spring IOC容器中。
事务管理器的主要实现
1) DataSourceTransactionManager:在应用程序中只需要处理一个数据源,而且通过JDBC存取。
2) JtaTransactionManager:在JavaEE应用服务器上用JTA(Java Transaction API)进行事务管理
3) HibernateTransactionManager:用Hibernate框架存取数据库
实现
1) 配置文件:如上图
2) 在需要进行事务控制的方法上加注解 @Transactional
@Transactional(propagation=Propagation.REQUIRES_NEW,
isolation=Isolation.READ_COMMITTED,
timeout=3,
readOnly=false,
noRollbackFor=RuntimeException.class)
public void purchase(String username, String isbn) {}
propagation属性详解
事务的传播行为
当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。事务的传播行为可以由传播属性指定。Spring定义了7种类传播行为。
事务传播属性通过在@Transactional注解的propagation属性中定义。
①REQUIRED传播行为
当一个事务方法调用另一个事务方法时,它默认会在现有的事务内运行。因此在整个事务方法的开始和终止边界内只有一个事务。即:如果当前存在事务,就使用当前事务。如果当前没事务,就创建一个新的事务,去使用。
②. REQUIRES_NEW传播行为
表示该事务方法必须启动一个新事务,并在自己的事务内运行。如果有事务在运行,就应该先挂起它。即:无论当前是否存在事务,都必须创建新事务,去使用。等新建事务运行结束后,继续执行被挂起事务
事务的隔离级别
一个事务与其他事务隔离的程度称为隔离级别。数据库规定了多种事务隔离级别, 不同隔离级别对应不同的干扰程度, 隔离级别越高, 数据一致性就越好, 但并发性越弱。主要为避免各种并发问题。
数据库提供的 4 种事务隔离级别:
读未提交(1),存在问题:脏读
读已提交(2),存在问题:不可重复读(建议使用)
可重复读(4),存在问题:幻读(建议使用)
串行化 (8),存在问题:效率低
用@Transactional注解声明式地管理事务时可以在@Transactional的isolation属性中设置隔离级别
触发事务回滚的异常
捕获到RuntimeException或Error时回滚,而捕获到编译时异常不回滚。
通过@Transactional 注解
① rollbackFor属性:指定遇到时必须进行回滚的异常类型,可以为多个
② noRollbackFor属性:指定遇到时不回滚的异常类型,可以为多个
事务的超时和只读属性
超时事务属性:事务在强制回滚之前可以保持多久。这样可以防止长期运行的事务占用资源。
只读事务属性: 表示这个事务只读取数据但不更新数据, 这样可以帮助数据库引擎优化事务。
readOnly=true,
true:事务只读,一旦设置只读属性,该事务就不能进行:增删改操作。
false:设置事务为,不只读。
timeout=3, 设置事务的“强制回滚”时间秒。
基于xml方式配置声明式事务
<!-- 配置事务切面 -->
<aop:config>
<aop:pointcut expression="execution(* com.tx.component.service.BookShopServiceImpl.purchase(..))"
id="txPointCut"/>
<!-- 将切入点表达式和事务属性配置关联到一起 -->
<aop:advisor advice-ref="myTx" pointcut-ref="txPointCut"/>
</aop:config>
<!-- 配置基于XML的声明式事务 -->
<tx:advice id="myTx" transaction-manager="transactionManager">
<tx:attributes>
<!-- 设置具体方法的事务属性 -->
<tx:method name="find*" read-only="true"/>
<tx:method name="get*" read-only="true"/>
<tx:method name="purchase"
isolation="READ_COMMITTED"
no-rollback-for="java.lang.ArithmeticException,java.lang.NullPointerException"
propagation="REQUIRES_NEW"
read-only="false"
timeout="10"/>
</tx:attributes>
</tx:advice>
Java框架之Spring02-AOP-动态代理-AspectJ-JdbcTemplate-事务的更多相关文章
- Atitit 代理CGLIB 动态代理 AspectJ静态代理区别
Atitit 代理CGLIB 动态代理 AspectJ静态代理区别 1.1. AOP 代理主要分为静态代理和动态代理两大类,静态代理以 AspectJ 为代表:而动态代理则以 spring AOP 为 ...
- Spring学习笔记之aop动态代理(3)
Spring学习笔记之aop动态代理(3) 1.0 静态代理模式的缺点: 1.在该系统中有多少的dao就的写多少的proxy,麻烦 2.如果目标接口有方法的改动,则proxy也需要改动. Person ...
- 技术的正宗与野路子 c#, AOP动态代理实现动态权限控制(一) 探索基于.NET下实现一句话木马之asmx篇 asp.net core 系列 9 环境(Development、Staging 、Production)
黄衫女子的武功似乎与周芷若乃是一路,飘忽灵动,变幻无方,但举手抬足之间却是正而不邪,如说周芷若形似鬼魅,那黄衫女子便是态拟神仙. 这段描写出自<倚天屠龙记>第三十八回. “九阴神抓”本是& ...
- JDK动态代理给Spring事务埋下的坑!
一.场景分析 最近做项目遇到了一个很奇怪的问题,大致的业务场景是这样的:我们首先设定两个事务,事务parent和事务child,在Controller里边同时调用这两个方法,示例代码如下: 1.场景A ...
- (转)面试必备技能:JDK动态代理给Spring事务埋下的坑!
一.场景分析 最近做项目遇到了一个很奇怪的问题,大致的业务场景是这样的:我们首先设定两个事务,事务parent和事务child,在Controller里边同时调用这两个方法,示例代码如下: 1.场景A ...
- .Net 框架实现AOP(动态代理实现AOP,本文为翻译)
在上一节,我们将静态实现AOP,但是对于一个大型项目,要想为每个类,每个方法都去实现AOP ,进行日志记录和权限验证似乎是不可能的. 即使可能对于成百上千个类维护,也是很难维护.所以今天的主题就是如标 ...
- java框架篇---spring AOP 实现原理
什么是AOP AOP(Aspect-OrientedProgramming,面向方面编程),可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善.OOP引入 ...
- Java的反射机制和动态代理
介绍Java注解的时候,多次提到了Java的反射API.与javax.lang.model不同的是,通过反射API可以获取程序在运行时刻的内部结构.反射API中提供的动态代理也是非常强大的功能,可以原 ...
- Spring ( 四 )Spring的AOP动态代理、切面编程
个人博客网:https://wushaopei.github.io/ (你想要这里多有) 一.AOP切面编程 1.什么是AOP AOP是面向切面编程.全称:Aspect Oriented Pro ...
- 反射实现 AOP 动态代理模式(Spring AOP 的实现 原理)
好长时间没有用过Spring了. 突然拿起书.我都发现自己对AOP都不熟悉了. 其实AOP的意思就是面向切面编程. OO注重的是我们解决问题的方法(封装成Method),而AOP注重的是许多解决解决问 ...
随机推荐
- 分布式全局唯一ID
方案一.UUID UUID的方式能生成一串唯一随机32位长度数据,它是无序的一串数据,按照开放软件基金会(OSF)制定的标准计算,UUID的生成用到了以太网卡地址.纳秒级时间.芯片ID码和许多可能的数 ...
- HDU 1358 Period(KMP next数组运用)
Period Problem Description For each prefix of a given string S with N characters (each character has ...
- springBoot中使用使用junit测试文件上传,以及文件下载接口编写
本篇文章将介绍如何使junit在springBoot中测试文件的上传,首先先阅读如何在springBoot中进行接口测试. 文件上传操作测试代码 import org.junit.Before; im ...
- vue-learning:8-template-v-on-and-modifier
绑定元素事件的指令 v-on 及事件和修饰符 目录 对比原生事件绑定.jQuery事件绑定 Vue事件绑定 Vue绑定事件中获取事件对象event 事件修饰符 事件行为修饰符: stop / prev ...
- codeforces gym100801 Problem G. Graph
传送门:https://codeforces.com/gym/100801 题意: 给你一个DAG图,你最多可以进行k次操作,每次操作可以连一条有向边,问你经过连边操作后最小拓扑序的最大值是多少 题解 ...
- 【k8s】kubeadm快速部署Kubernetes
1.Kubernetes 架构图 kubeadm是官方社区推出的一个用于快速部署kubernetes集群的工具. 这个工具能通过两条指令完成一个kubernetes集群的部署: # 创建一个 Mast ...
- Team Foundation Server 2015使用教程【8】:读取器tfs组的checkin权限修改
- 聊聊多线程哪一些事儿(task)之 一
多线程,一个多么熟悉的词汇,作为一名程序员,我相信无论是从事什么开发语言,都能够轻轻松松说出几种实现多线程的方式,并且在实际工作种也一定用到过多线程,比如:定时器.异步作业等等,如果你说你没有用过多线 ...
- 第三阶段:3.Web端产品设计:1.以用户为中心的产品设计2
从功能到体验.提供不同的附加值.
- 记录我的 python 学习历程-Day11 两个被忽视的坑、补充知识点、函数名的应用、新版格式化输出、迭代器
补充知识点 函数形参中默认参数的陷阱 针对不可变数据类型,它是没有陷阱的 def func(name, sex='男'): print(name) print(sex) func('Dylan') # ...