AOP
看这篇里面的AOP
- 对AOP的理解
- OOP(面向对象编程)是纵向的,而AOP(面向切面编程)是横向的
- 什么是切面。炒菜,锅与炉子共同来完成炒菜,锅与炉子就是切面。web层级设计中,每一层之间也是一个切面。编程中,对象与对象之间,方法与方法之间,模块与模块之间都是一个个切面
- 执行这个方法前,你需要干什么,执行方法后,你需要干什么,都可以用AOP的思想
- 以一个简单的例子来比喻一下 AOP 中
Aspect
, Joint point
, Pointcut
与 Advice
之间的关系
- 让我们来假设一下, 从前有一个叫爪哇的小县城, 在一个月黑风高的晚上, 这个县城中发生了命案. 作案的凶手十分狡猾, 现场没有留下什么有价值的线索. 不过万幸的是, 刚从隔壁回来的老王恰好在这时候无意中发现了凶手行凶的过程, 但是由于天色已晚, 加上凶手蒙着面, 老王并没有看清凶手的面目, 只知道凶手是个男性, 身高约七尺五寸. 爪哇县的县令根据老王的描述, 对守门的士兵下命令说: 凡是发现有身高七尺五寸的男性, 都要抓过来审问. 士兵当然不敢违背县令的命令, 只好把进出城的所有符合条件的人都抓了起来
- 来让我们看一下上面的一个小故事和
AOP
到底有什么对应关系。首先我们知道, 在 Spring AOP
中 Joint point
指代的是所有方法的执行点, 而 point cut
是一个描述信息, 它修饰的是 Joint point
, 通过 point cut
, 我们就可以确定哪些 Joint point
可以被织入 Advice
. 对应到我们在上面举的例子, 我们可以做一个简单的类比, Joint point
就相当于 爪哇的小县城里的百姓,pointcut
就相当于 老王所做的指控, 即凶手是个男性, 身高约七尺五寸, 而 Advice
则是施加在符合老王所描述的嫌疑人的动作: 抓过来审问
Joint point
: 爪哇的小县城里的百姓: 因为根据定义, Joint point
是所有可能被织入 Advice
的候选的点, 在 Spring AOP
中, 则可以认为所有方法执行点都是 Joint point
. 而在我们上面的例子中, 命案发生在小县城中, 按理说在此县城中的所有人都有可能是嫌疑人.
Pointcut
:男性, 身高约七尺五寸: 我们知道, 所有的方法(joint point
) 都可以织入 Advice
, 但是我们并不希望在所有方法上都织入 Advice
, 而 Pointcut
的作用就是提供一组规则来匹配joint point
, 给满足规则的 joint point
添加 Advice
. 同理, 对于县令来说, 他再昏庸, 也知道不能把县城中的所有百姓都抓起来审问, 而是根据凶手是个男性, 身高约七尺五寸, 把符合条件的人抓起来. 在这里 凶手是个男性, 身高约七尺五寸 就是一个修饰谓语, 它限定了凶手的范围, 满足此修饰规则的百姓都是嫌疑人, 都需要抓起来审问
Advice
:抓过来审问, Advice
是一个动作, 即一段 Java
代码, 这段 Java
代码是作用于 point cut
所限定的那些 Joint point
上的. 同理, 对比到我们的例子中, 抓过来审问 这个动作就是对作用于那些满足 男性, 身高约七尺五寸 的爪哇的小县城里的百姓
Aspect
: Aspect
是 point cut
与 Advice
的组合, 因此在这里我们就可以类比: “根据老王的线索, 凡是发现有身高七尺五寸的男性, 都要抓过来审问” 这一整个动作可以被认为是一个 Aspect
Joinpoint
可以有多种类型:构造方法调用,字段的设置和获取,方法的调用,方法的执行,异常的处理执行,类的初始化。但是在Spring
中却没有实现上面所有的Joinpoint
,确切的说,Spring
只支持方法执行类型的Joinpoint
。在Spring
中,通过动态代理
和动态字节码
技术实现AOP
- AOP的实现
- 动态代理。
Java
中的一个方法,这个方法可以实现动态创建一组指定的接口的实现对象 1
| public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
|
ClassLoader loader
:方法需要动态生成一个类,这个类实现了A和B两个接口,然后创建这个类的对象。需要生成一个类,而且这个类也需要加载到方法区中,所以我们需要一个ClassLoader来加载该类
Class<?>[] interfaces
:我们需要代理对象实现的数组
InvocationHandler h
:调用处理器,这里就是增强的地方 1 2 3 4 5 6 7
|
public interface hello { Object test(); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| public void test0() {
ClassLoader classLoader = this.getClass().getClassLoader(); InvocationHandler invocationHandler = new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("handler invoke"); return "invoke return"; } }; Object obj = Proxy.newProxyInstance(classLoader, new Class[]{hello.class}, invocationHandler); hello h = (hello) obj; Object test = h.test(); System.out.println("test" + test); }
|
- 以上是残疾版的AOP。代理对象方法的返回值其实就是invoke方法的返回值,代理对象其实就是使用反射机制实现的一个运行时对象。下面是比较完整的实现
1 2 3
| public interface BeforeAdvice { void before(); }
|
1 2 3
| public interface AfterAdvice { void after(); }
|
1 2 3 4 5 6 7
| public class HelloImpl implements hello { @Override public Object test() { System.out.println("helloImpl test"); return "helloImpl"; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
|
public class ProxyFactory { private Object target; private BeforeAdvice beforeAdvice; private AfterAdvice afterAdvice;
public Object createProxy(){ ClassLoader classLoader = this.getClass().getClassLoader(); Class<?>[] interfaces = target.getClass().getInterfaces(); InvocationHandler invocationHandler = new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { beforeAdvice.before(); Object result = method.invoke(target, args); afterAdvice.after(); return result; } }; Object obj = Proxy.newProxyInstance(classLoader, interfaces, invocationHandler); return obj; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public void test1(){ ProxyFactory proxyFactory = new ProxyFactory(); proxyFactory.setTarget(new HelloImpl());
proxyFactory.setBeforeAdvice(new BeforeAdvice() { @Override public void before() { System.out.println("before"); } }); proxyFactory.setAfterAdvice(new AfterAdvice() { @Override public void after() { System.out.println("after"); } }); hello h = (hello) proxyFactory.createProxy(); Object result = h.test(); System.out.println(result);
}
|
1 2
| public class ProxyFactory extends ProxyCreatorSupport
|
- 动态字节码技术 CGLIB
1 2 3 4 5 6 7 8 9 10
| <dependency> <groupId>cglib</groupId> <artifactId>cglib-nodep</artifactId> <version>3.2.0</version> </dependency> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.2.0</version> </dependency>
|
- 首先实现一个MethodInterceptor,方法调用会被转发到该类的intercept()方法。然后在需要使用目标对象的时候,通过CGLIB动态代理获取代理对象
1 2 3 4 5 6 7 8 9 10
| public class TargetTest { public String doFirst(){ System.out.println("first"); return "doFirst"; } public void doSecond(){ System.out.println("second"); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
|
public class CglibFactory implements MethodInterceptor { public TargetTest myCglibCreator(){ Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(TargetTest.class); enhancer.setCallback(this); return (TargetTest) enhancer.create(); }
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("intercept"); return "HHH"; } }
|
1 2 3 4 5 6 7 8
| @Test public void test0(){ TargetTest creator = new CglibFactory().myCglibCreator(); String result = creator.doFirst(); System.out.println(result); creator.doSecond(); }
|
- CGLIB也有其缺陷,那就是必须目标类必须是可以继承的,如果目标类不可继承,那么我们就无法使用CGLIB来增强该类
- Spring实现AOP的原理是JDK动态代理和cglib代理。JDK动态代理有缺陷,就是被代理对象必须实现接口才能产生代理对象,如果没有接口,就不能使用动态代理技术。我们用spring容器来实现动态代理,假如要管理的对象没有实现接口,那么就不能产生代理对象了。为了让所有的对象都能产生动态代理对象,Spring又融入了第三方代理技术CGLIB代理。CGLIB可以对任何类生成代理对象,它的原理是对目标对象进行继承代理,如果目标对象被final修饰,那么该类无法被CGLIB代理。
- 那么Spring到底使用的是JDK代理,还是cglib代理呢?
答案是混合使用。如果被代理对象实现了接口,就优先使用JDK代理,如果没有实现接口,就用用cglib代理。
- Spring切面可以应用5种类型的通知
- 前置通知(Before)
- 后置通知(After,在方法完成之后调用通知,无论方法执行是否成功)
- 后置通知(After-returning,在方法成功执行之后调用通知)
- 异常通知(After-throwing,在方法抛出异常后调用通知)
- 环绕通知(Around,在目标方法之前之后都调用)。
- AOP的应用
- spring的AOP事务
- 事务的概述
ACID。即原子性(amoticity)、一致性(consitency)、隔离性(isolation)、持久性(durability)
原子性
:指事务包含的所有操作要么全部成功,要么全部失败回滚
一致性
:指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。拿转账来说,假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是5000,这就是事务的一致性
隔离性
:当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。即要达到这么一种效果:对于任意两个并发的事务T1和T2,在事务T1看来,T2要么在T1开始之前就已经结束,要么在T1结束之后才开始,这样每个事务都感觉不到有其他事务在并发地执行
持久性
:指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作
- 事务的隔离级别
Read uncommitted
:读未提交。即一个事务可以读取另一个未提交事务的数据。这就是脏读
。解决脏读方法就是Read committed 读提交
Read committed
:读提交。一个事务要等另一个事务提交后才能读取数据。程序员拿着信用卡去享受生活(卡里当然是只有3.6万),当他埋单时(程序员事务开启),收费系统事先检测到他的卡里有3.6万,就在这个时候!!程序员的妻子要把钱全部转出充当家用,并提交。当收费系统准备扣款时,再检测卡里的金额,发现已经没钱了(第二次检测金额当然要等待妻子转出金额事务提交完)。程序员就会很郁闷,明明卡里是有钱的。这就是读提交
,若有事务对数据进行更新(UPDATE)操作时,读操作事务要等待这个更新操作事务提交后才能读取数据,可以解决脏读
问题。但在这个事例中,出现了一个事务范围内两个相同的查询却返回了不同数据,这就是不可重复读
。解决不可重复读的方法就是Repeatable read
重复读
Repeatable read
:重复读。就是在开始读取数据(事务开启)时,不再允许修改操作。程序员拿着信用卡去享受生活(卡里当然是只有3.6万),当他埋单时(事务开启,不允许其他事务的UPDATE修改操作),收费系统事先检测到他的卡里有3.6万。这个时候他的妻子不能转出金额了。接下来收费系统就可以扣款了。不可重复读
对应的是修改,即UPDATE操作
。但是可能还会有幻读
问题。因为幻读
问题对应的是插入INSERT
操作,而不是UPDATE操作
。那什么是幻读
?程序员某一天去消费,花了2千元,然后他的妻子去查看他今天的消费记录(全表扫描FTS,妻子事务开启),看到确实是花了2千元,就在这个时候,程序员花了1万买了一部电脑,即新增INSERT
了一条消费记录,并提交
。当妻子打印程序员的消费记录清单时(妻子事务提交),发现花了1.2万元,似乎出现了幻觉,这就是幻读
,怎么解决幻读问题?Serializable
序列化
Serializable
:序列化。Serializable
是最高的事务隔离级别,在该级别下,事务串行化顺序
执行,可以避免脏读
、不可重复读
与幻读
。但是这种事务隔离级别效率低下,比较耗数据库性能,一般不使用。
- 大多数数据库默认的事务隔离级别是
Read committed
,比如Sql Server
, Oracle
。Mysql
的默认隔离级别是Repeatable read
spring
中事务的分类
可以分为编程式事务控制
和声明式事务控制
。
自己手动控制事务,就叫做编程式事务控制
。开发起来比较繁琐,每次都要开启、提交、回滚。可以对指定的方法、指定的方法的某几行添加事务控制
Spring
提供了对事务的管理, 这个就叫声明式事务管理
。实现了对事务控制的最大程度的解耦。核心实现就是基于AOP。只能给整个方法应用事务,不可以对方法的某几行应用事务。Spring声明式事务管理器类:Jdbc技术:DataSourceTransactionManager、Hibernate技术:HibernateTransactionManager
使用配置文件的方法略。
spring
管理事务的属性介绍
(1)事务的隔离级别
(2)是否只读
(3)事务的传播行为。有7种。分别是REQUIRED
、REQUIRES_NEW
、SUPPORTS
、NOT_SUPPORTED
、MANDATORY
、NESTED
、NEVER
。我们常用的是propagation="REQUIRED"
,默认的就是REQUIRED
,指得是支持当前事务,如果不存在,就新建一个(默认),所以这个属性不用配置。其余6个属性几乎不用
在需要添加事务管理的方法上添加: 1
| @Transactional(isolation=Isolation.REPEATABLE_READ,readOnly=false,propagation=Propagation.REQUIRED)
|
参考文章1
参考文章2
参考文章3
参考文章4
参考文章5
参考文章6