本节介绍spring 的AOP——面向切片编程。
动态代理
动态代理是指,程序在整个运行过程中根本就不存在目标类的代理类,目标对象的代理对象只是由代理生成工具(不是真实定义的类)在程序运行时由VM根据反射等机制动态生成的。代理对象与目标对象的代理关系在程序运行时才确立。
简单来说,就是给原来的类增加一些功能,即功能增强。
JDK的动态代理
动态代理的实现方式常用的有两种:使用JDK的 Proxy,与通过CGLIB生成代理。
Jdk的动态要求目标对象必须实现接口,这是java设计上的要求。
从jdk1.3以来,java语言通过java.lang.reflect包提供三个类支持代理模式 Proxy,Method 和 InovcationHandler 。
动态代理的作用:
1) 在目标类源代码不改变的情况下,增加功能。
2) 减少代码的重复
3) 专注业务逻辑代码
4) 解耦合,让你的业务功能和日志,事务和非业务的功能分离。
案例
现有 SomeService 接口和它的实现类 SomeServiceImpl,要求使用动态代理,不改变SomeServiceImpl的源码,给类中的doSome 和 doOther() 类添加 时间 和 事务完成代码。具体代码如下
SomeService 接口
1 | package com.thorine.service; |
SomeServiceImpl
1 | package com.thorine.service.impl; |
MyInvocationHandler(代理类)
1 | package com.thorine.handler; |
测试类
1 | package com.thorine.service; |
AOP简介
AOP (Aspect Orient Programming),面向切面编程。面向切面编程是从动态角度考虑程序运行过程。
AOP底层,就是采用动态代理模式实现的。采用了两种代理 : JDK的动态代理,与CGLIB的动态代理。
AOP为Aspect Oriented Programming的缩写,意为 : 面向切面编程,可通过运行期动态代理实现程序功能的统一维护的一种技术。AOP是Spring框架中的一个重要内容。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
因为动态代理很灵活,实现方法复杂多样,为了统一规则,而出现的AOP。
总之,AOP就是动态代理的规范化。
Aspect:切面
给目标类增加的功能,就是切面。像上面用的增加日志、事务功能 等都是切面。
特点:一般都是非业务方法,可以独立使用。
业务方法:功能性的方法代码等。例如:数据库增删改查,逻辑运算(10+20)等。
怎么理解面向切面编程?
1、在分析项目功能时,找出切面。(最重要)
2、合理的安排切面的执行时间(在目标方法前,还是目标方法后)
3、合理的安全切面执行的位置,在哪个类,哪个方法增加增强方法。
AOP编程术语
1)Aspect(切面):表示增强的非业务性功能,例如:日志,统计信息,参数检查,权限验证。
2)JoinPoint(连接点):连接业务方法和切面的位置,就是某类中的业务方法。(只有一个)
3)PointCut(切入点):指多个连接点的集合。(多个方法)
4)目标对象:给哪个类的方法增加功能,该类便是目标对象。
5)Active(通知):表示切面功能执行的时间(在目标方法前,还是目标方法后)
AOP的实现
aop是一个规范,是动态的一个规范化,一个标准。
aop的技术实现框架:
spring: spring在内部实现了aop规范,能做aop的工作。spring主要在事务处理时使用aop。
我们项目开发中很少使用spring的aop实现。因为spring的aop比较笨重。
aspectJ(重点掌握) :一个开源的专门做aop的框架。
AspectJ对AOP的实现(掌握)
简介
AspectJ是一个优秀面向切面的框架,它扩展了Java语言,提供了强大的切面实现。
AspectJ实现了AOP的功能,且其实现方式更为简捷,使用更为方便,而且还支持注解式开发。所以,Spring将AspectJ的对于AOP的实现引入到了自己的框架中。
官网地址:https://www.eclipse.org/aspectj/
AspectJ框架实现aop有两种方式:
- 使用xml配置文件:配置全局事务
- 使用注解(常用),有5个注解
学习AspectJ框架的使用
1、切面的执行时间,这个执行时间在规范中叫做Advice(通知)
在AspectJ 框架中使用注解表示的。也可以使用xml配置文件中的标签
1)@Before
2)@AfterReturning
3)@Around
4)@AfterThrowing
5)@After
AspectJ的切入点表达式(掌握)
AspectJ定义了专门的表达式用于指定切入点。表达式的原型是:
1 | execution(modifiers-pattern? ret-type-pattern |
解释:
modifiers-pattern:访问权限类型(公有、私有)
ret-type-pattern:返回值类型
declaring-type-pattern:包名类名
name-pattern(param-pattern):方法名(参数类型和参数个数)
throws-pattern:抛出异常类型
?表示可选的部分
以上表达式共4个部分。
1 | execution(访问权限 方法返回值 方法声明(参数) 异常类型) // 方法返回值 和 方法声明是必须的 |
切入点表达式要匹配的对象就是目标方法的方法名。所以,execution表达式中明显就是方法的签名。
注意,表达式中第一项和第四项是可省略的部分,各部分间用空格分开。在其中可以使用以下符号:
符合 | 意义 |
---|---|
* | 0至多个任意字符 |
.. | 用在方法参数中,表示任意多个参数 用在包名后,表示当前包及其子包路径 |
+ | 用在类名后,表示当前类及其子类 用在接口后,表示当前接口及其实现类 |
举例(理解掌握):
- execution(public * (..)) :指定切入点为所有的公有方法,括号里共有三个部分,第二个 * 表示方法返回值,第三个表示表示方法声明,因为后紧跟括号。
- execution(* set*(..)) :指定切入点为任意一个以“set”开始的方法。只有两个部分,所以一点是两个必须部分,返回值和方法声明。
- execution(* com.xyz.service.*.*(..)):指定切入点为:定义在service包里的任意类的任意方法。
- execution(* com.xyz.service..*.*(..)):指定切入点为:定义在 service包或者子包里的任意类的任意方法。“..”出现在类名中时,后面必须跟 “* ”,表示包、子包下的所有类。
- execution(* *..service.*.*(..)):指定所有包下的serivce子包下所有类(接口)中所有方法为切入点