二、AOP面向切面编程
官网下载地址
能源节点spring材料
视频观看地址
https://www.bilibili.com/video/BV1nz4y1d7uy
2.1 概述
AOP(Aspect Orient Programming)。面向切面编程是从动静角度思考程序运行过程
- AOP 底层,就是采纳动静代理模式实现的。采纳了两种代理:JDK 的动静代理,与 CGLIB的动静代理,AOP就是动静代理的规范化, 把动静代理的实现步骤,形式都定义好了, 让开发人员用一种对立的形式,应用动静代理
- Aspect: 切面,给你的指标类减少的性能,就是切面。 像下面用的日志,事务都是切面。切面的特点:个别都是非业务办法,独立应用的
- Orient:面向, 对着
- Programming:编程
2.2 相干术语
1. Aspect:切面,示意加强的性能, 就是一堆代码,实现某个一个性能。非业务性能,常见的切面性能有日志, 事务, 统计信息, 参数查看, 权限验证,切面用于组织多个Advice,Advice放在切面中定义,理论就是对主业务逻辑的一种加强
2. JoinPoint:连接点 ,连贯业务办法和切面的地位。就某类中的业务办法,程序执行过程中明确的点,如办法的调用,或者异样的抛出。在Spring AOP中,连接点总是办法的调用
3. Pointcut:切入点 ,指多个连接点办法的汇合。多个办法。能够插入加强解决的连接点。简而言之,当某个连接点满足指定要求时,该连接点将被增加加强解决,该连接点也就变成了切入点
4. Advice:AOP框架在特定的切入点执行的加强解决。解决有”around”、”before”和”after”等类型,能示意切面性能执行的工夫,切入点定义切入的地位,告诉定义切入的工夫
5. Target:指标对象,目 标 对 象 指 将 要 被 增 强 的 对 象 。 即 包 含 主 业 务 逻 辑 的 类 的 对 象
2.3 AspectJ
2.3.1 概述
AspectJ是一个基于Java语言的AOP框架,提供了弱小的AOP性能,其次要包含两个局部:
- 一个局部定义了如何表白、定义AOP编程中的语法标准;
- 另一个局部是工具局部,包含编译、调试工具等
aspectJ框架实现aop的两种形式:
- 应用xml的配置文件 : 配置全局事务
- 应用注解,咱们在我的项目中要做aop性能,个别都应用注解,aspectj有5个注解
- @Before
- @AfterReturning
- @Around
- @AfterThrowing
- @After
2.3.2 AspectJ的切入点表达式
表达式原型:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
![/upload-images.jianshu.io/upload_images/27220553-f998cc2391d3017d.gif?imageMogr2/auto-orient/strip)
相干解释:
- modifiers-pattern? 拜访权限类型
- ret-type-pattern 返回值类型
- declaring-type-pattern? 包名类名
- name-pattern(param-pattern) 办法名(参数类型和参数个数)
- throws-pattern 抛出异样类型
- ?示意可选的局部
以上表达式一共4个局部
execution(拜访权限 办法返回值 办法申明(参数) 异样类型)
符号 | 意义 |
---|---|
* | 0至多个任意字符 |
. . | 用在办法参数中,示意任意多个参数;用在包名后,示意以后包与子包门路 |
+ | 用在类名后,示意以后类及其子类;用在接口后,示意以后接口及其实现类 |
相干实例:
- execution(public (..)):任意公共办法
- execution( set(..)):任何一个以“set”开始的办法
- execution( com.xyz.service..*(..)):定义在 service 包里的任意类的任意办法
- execution( com.xyz.service...(..)):定义在 service 包或者子包里的任意类的任意办法。“..”呈现在类名中时,前面必须跟“”,示意包、子包下的所有类
- execution( ..service..(..)):指定所有包下的 serivce 子包下所有类(接口)中所有办法为切入点
- execution( com.xyz.service.IAccountService+.(..)):IAccountService 若为接口,则为接口中的任意办法及其所有实现类中的任意办法;若为类,则为该类及其子类中的任意办法
- execution(* joke(String,int))):所有的 joke(String,int)办法,且 joke()办法的第一个参数是 String,第二个参数是 int;如果办法中的参数类型是 java.lang 包下的类,能够间接应用类名,否则必须应用全限定类名,如 joke( java.util.List, int)
2.3.3 前置告诉:@Before
1.办法的定义要求:
- 公共办法 public
- 办法没有返回值
- 办法名称自定义
- 办法能够有参数,也能够没有参数
2.@Before: 前置告诉注解
属性:value ,是切入点表达式,示意切面的性能执行的地位
地位:在办法的下面
1.配置依赖
<!--spring依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!--aspectj依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
2.创立业务接口与实现类对象
Service.interface
public interface Service {
public void doSome();
}
ServiceImpl.java
@org.springframework.stereotype.Service("myService")
public class ServiceImpl implements Service {
@Override
public void doSome() {
System.out.println("这是我的业务办法!!!");
}
}
3.创立切面类:MyAspect.java
@Aspect
@Component("myAspect")
public class MyAspect {
/**
*指定告诉办法中的参数:JoinPoint
*
*/
@Before(value = "execution(void *..doSome(..))")
public void before(){
System.out.println("这是前置告诉");
}
}
4.配置applicationContext.xml文件
<!--扫描文件-->
<context:component-scan base-package="com.jjh.*"/>
<!--申明主动代理生成器-->
<aop:aspectj-autoproxy/>
5.测试类调用
@org.junit.Test
public void demo01(){
String config = "applicationContext.xml";
ApplicationContext app = new ClassPathXmlApplicationContext(config);
Service proxy = (Service) app.getBean("myService");
//输入以后类的信息:com.sun.proxy.$Proxy17
//证实其应用的是JDK动静代理
System.out.println(proxy.getClass().getName());
proxy.doSome();
}
2.3.4 JoinPoint
- 指定告诉办法中的参数 : JoinPoint
- JoinPoint:业务办法,要退出切面性能的业务办法
- 作用:能够在告诉办法中获取办法执行时的信息, 例如办法名称,办法的实参
- 如果须要切面性能中办法的信息,就退出JoinPoint
- JoinPoint参数的值是由框架赋予, 必须是第一个地位的参数
- 不止前置告诉的办法,能够蕴含一个 JoinPoint 类型参数,所有的告诉办法均可蕴含该参数
MyAspect.java
@Aspect
@Component("myAspect")
public class MyAspect {
@Before(value = "execution(void *..doSome(..))")
public void before(JoinPoint joinPoint){
//获取办法的定义
System.out.println("办法的签名(定义):" + joinPoint.getSignature());
System.out.println("办法的名称:" + joinPoint.getSignature().getName());
//获取办法的实参
Object[] args = joinPoint.getArgs();
for (Object arg : args) {
System.out.println("参数:" + arg);
}
System.out.println("这是前置告诉!!!");
}
}
测试类
@org.junit.Test
public void demo01(){
String config = "applicationContext.xml";
ApplicationContext app = new ClassPathXmlApplicationContext(config);
Service proxy = (Service) app.getBean("myService");
proxy.doSome("张三",20);
}
打印后果
2.3.5 后置告诉:@AfterReturning
- 在指标办法执行之后执行
- 因为是指标办法之后执行,所以能够获取到指标办法的返回值
- 该注解的returning属性就是用于指定接管办法返回值的变量名的
- 所以,被注解为后置告诉的办法,除了能够蕴含 JoinPoint 参数外,还能够蕴含用于接管返回值的变量
- 该变量最好为 Object 类型,因为指标办法的返回值可能是任何类型
业务办法
@Component("myService2")
public class SomeServiceImpl implements SomeService {
@Override
public void doSome() {
System.out.println("这是业务办法!!!");
}
@Override
public String doOther(String str, int i) {
return "业务办法doOther的返回值!!!";
}
}
后置告诉
1.该注解的 returning 属性就是用于指定接管办法返回值的变量名的
2.除了能够蕴含 JoinPoint 参数外,还能够蕴含用于接管返回值的变量
3.该变量最好为Object 类型,因为指标办法的返回值可能是任何类型
4.办法的定义要求:
- 公共办法 public
- 办法没有返回值
- 办法名称自定义
- 办法有参数的,举荐是Object ,参数名自定义
5.@AfterReturning:后置告诉
- value:切入点表达式
- returning:自定义的变量,示意指标办法的返回值的,自定义变量名必须和告诉办法的形参名一样
- 能够依据业务办法的返回值做出相应的操作
@AfterReturning(value = "execution(String *..doOther(..))",returning = "res")
public void myAfterReturning(JoinPoint joinPoint,Object res){
//获取办法的签属(定义)
System.out.println(joinPoint.getSignature());
//获取办法的参数
Object[] args = joinPoint.getArgs();
for (Object arg : args) {
System.out.println("指标办法参数:" + arg);
}
//指标办法的返回值
System.out.println(res);
//后置告诉
System.out.println("后置告诉!!!");
}
测试类
@Test
public void demo02(){
String config = "applicationContext.xml";
ApplicationContext app = new ClassPathXmlApplicationContext(config);
SomeService proxy = (SomeService)app.getBean("myService2");
proxy.doOther("Dick",20);
}
2.3.6 盘绕告诉:@Around
在指标办法执行之前之后执行。被注解为盘绕加强的办法要有返回值
被注解为盘绕加强的办法要有返回值,Object 类型。并且办法能够蕴含一个ProceedingJoinPoint类型的参数
接口ProceedingJoinPoint其中有一个proceed() 办法,用于执行指标办法
若指标办法有返回值,则该办法的返回值就是指标办法的返回值。最初,盘绕加强办法将其返回值返回。该加强办法理论是拦挡了指标办法的执行
- 切面类
1. 盘绕告诉办法的定义格局:
- public
- 必须有一个返回值,举荐应用Object
- 办法名称自定义
- 办法有参数,固定的参数 ProceedingJoinPoint
2. 特点
- 在指标办法的前和后都能加强性能
- 控制目标办法是否被调用执行
- 批改原来的指标办法的执行后果,影响最初的调用后果
- 它是性能最强的告诉
3.盘绕告诉等同于jdk动静代理的InvocationHandler接口
4.参数:ProceedingJoinPoint 就等同于 Method,用于执行指标办法
5.返回值: 就是指标办法的执行后果,能够被批改
6.盘绕告诉:常常做事务, 在指标办法之前开启事务,执行指标办法, 在指标办法之后提交事务
@Around(value = "execution(String *..do(..))")
public Object myAround(ProceedingJoinPoint p) throws Throwable {
Object result = null;
//前置性能加强
System.out.println("前置性能加强!!");
//等同于method.invoke(); Object result = doFirst();
result = p.proceed();
//后置性能加强
System.out.println("后置性能加强");
return result;
}
测试类
@Test
public void demo01(){
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
SomeService proxy = (SomeService)app.getBean("Service3");
String result = proxy.doFirst("Dick", 99);
//输入返回后果
System.out.println(result);
}
2.3.7 异样告诉:@AfterThrowing
在指标办法抛出异样后执行
该注解的 throwing 属性用于指定所产生的异样类对象
被注解为异样告诉的办法能够蕴含一个参数 Throwable,参数名称为 throwing 指定的名称,示意产生的异样对象
- 业务办法
@Override
public void doSecond() {
System.out.println("执行业务办法doSecond()" + (10/0));
}
- 切面类
1. 异样告诉办法的定义格局:
- 拜访权限public
- 没有返回值
- 办法名称自定义
- 办法有个一个Exception, 也能够应用JoinPoint
2. @AfterThrowing:异样告诉
- 属性:
value 切入点表达式
throwinng 自定义的变量,示意指标办法抛出的异样对象,变量名必须和办法的参数名一样
- 特点:
在指标办法抛出异样时执行的
能够做异样的监控程序, 监控指标办法执行时是不是有异样,如果有异样,能够发送邮件,短信进行告诉
@AfterThrowing(value = "execution(* *..SomeServiceImpl.doSecond(..))",
throwing = "ex")
public void myAfterThrowing(Exception ex) {
System.out.println("异样告诉:办法产生异样时,执行:"+ex.getMessage());
//发送邮件,短信,告诉开发人员
}
2.3.8 最终告诉:@After
无论指标办法是否抛出异样,该加强均会被执行
- 业务办法
@Override
public void doThird() {
System.out.println("执行业务办法doThird()");
}
切面类
1. 最终告诉的定义格局:
- 拜访权限public
- 没有返回值
- 办法名称自定义
- 办法没有参数,然而能够应用JoinPoint
2. @After :最终告诉特点
- 总是会执行
- 在指标办法之后执行
//等同以下执行形式
try{
SomeServiceImpl.doThird(..)
}catch(Exception e){
}finally{
myAfter()
}
@After(value = "execution(* *..SomeServiceImpl.doThird(..))")
public void myAfter(){
System.out.println("执行最终告诉,总是会被执行的代码");
//个别做资源革除工作的。
}
2.3.9 @Pointcut 定义切入点
当较多的告诉加强办法应用雷同的 execution 切入点表达式时,编写、保护均较为麻烦;AspectJ 提供了@Pointcut 注解,用于定义 execution 切入点表达式
将@Pointcut 注解在一个办法之上,当前所有的 execution 的 value 属性值均可应用该办法名作为切入点
代表的就是@Pointcut定义的切入点。这个应用@Pointcut注解的办法个别应用 private 的标识办法,即没有理论作用的办法
- 切面类
1.@Pointcut: 定义和治理切入点, 如果你的我的项目中有多个切入点表达式是反复的,能够复用的。
2.特点:
当应用@Pointcut定义在一个办法的下面 ,此时这个办法的名称就是切入点表达式的别名
其它的告诉中,value属性就能够应用这个办法名称,代替切入点表达式了
@After(value = "mypt()")
public void myAfter(){
System.out.println("执行最终告诉,总是会被执行的代码");
//个别做资源革除工作的。
}
@Before(value = "mypt()")
public void myBefore(){
System.out.println("前置告诉,在指标办法之前先执行的");
}
@Pointcut(value = "execution(* *..SomeServiceImpl.doThird(..))" )
private void mypt(){
//无需代码,
}
2.4 代理形式更换
如果指标类有接口,默认应用jdk动静代理,如果指标类没有接口,则应用CGlib动静代理
如果想让具备接口的指标类应用CGlib的代理形式,须要以下配置文件
<aop:aspectj-autoproxy proxy-target-class="true"/>
发表回复