Spring框架学习笔记(2)——面向切面编程AOP
作者:@stars-one
本文为作者原创,转载请注明出处:https://www.cnblogs.com/kexing/p/11588066.html
介绍
概念
面向切面编程AOP与面向对象编程OOP有所不同,AOP不是对OOP的替换,而是对OOP的一种补充,AOP增强了OOP。
假设我们有几个业务代码,都调用了某个方法,按照OOP的思想,我们就会将此方法封装在一个类中,之后通过对象.方法名调用
我们可以看作我们的业务代码被其他代码入侵或者是业务代码被其他与业务不相关的代码入侵了
这个时候,如果我们使用AOP进行编写代码,我们的业务代码就可以不需要写其他与业务相关的代码,这样就可以保证业务代码的纯洁性
AOP运行流程
通过配置文件,给各个业务方法标识切入点(PointCut),即切入点方法。
之后当程序运行到切入点方法的时候,就会发出一个通知(Advice),去通知执行某个切面方法(Aspect)
专业术语
项 描述
Aspect 一个模块具有一组提供横切需求的 APIs。例如,一个日志模块为了记录日志将被 AOP 方面调用。应用程序可以拥有任意数量的方面,这取决于需求。
Join point 在你的应用程序中它代表一个点,你可以在插件 AOP 方面。你也能说,它是在实际的应用程序中,其中一个操作将使用 Spring AOP 框架。
Advice 这是实际行动之前或之后执行的方法。这是在程序执行期间通过 Spring AOP 框架实际被调用的代码。
Pointcut 这是一组一个或多个连接点,通知应该被执行。你可以使用表达式或模式指定切入点正如我们将在 AOP 的例子中看到的。
Introduction 引用允许你添加新方法或属性到现有的类中。
Target object 被一个或者多个方面所通知的对象,这个对象永远是一个被代理对象。也称为被通知对象。
Weaving Weaving 把方面连接到其它的应用程序类型或者对象上,并创建一个被通知的对象。这些可以在编译时,类加载时和运行时完成。
Advice通知
通知 类型
前置通知(Before Advice) 在切入点方法执行之前,执行通知
环绕通知(Around Advice) 在切入点方法执行的整个过程都可以执行通知
后置通知(After Returning Advice) 在切入点方法执行之后,只有在方法成功执行时,才能执行通知。
最终通知(After Finally Advices) 在一个方法执行之后,不管是方法是否成功执行 ,执行通知
异常通知(After Throwing Advice) 在一个方法执行之后,只有在方法退出抛出异常时,才能执行通知。
PS:其实,这些通知就是相当于你可以在业务方法的执行前(前置通知)、执行中(环绕通知)、执行成功之后(后置通知)、发生异常(异常通知)、不管方法是发生异常还是执行成功(最终通知),执行某些与业务功能无关的功能代码。
这样就可以降低业务功能代码的入侵和污染
下面使用两种不同的方式来实现一个方法日志打印的简单例子
后置通知例子
下面的通知是基于xml配置的
1.添加依赖
除了之前的spring的jar包,还需要两个jar包,aopalliance.jar和aspectjweaver.jar
复制
junit
junit
4.11
test
commons-logging
commons-logging
RELEASE
org.springframework
spring-context
RELEASE
org.springframework
spring-test
RELEASE
org.springframework
spring-core
RELEASE
org.springframework
spring-beans
RELEASE
org.springframework
spring-aop
RELEASE
org.springframework
spring-expression
RELEASE
org.aspectj
aspectjweaver
RELEASE
aopalliance
aopalliance
RELEASE
2.业务代码
我编写了一个TeacherDao类,里面只有add和delete方法
复制
package com.wan;
/**
* @author StarsOne
* @date Create in 2019/9/25 0025 16:34
* @description
*/
public class TeacherDao {
public void add(Teacher teacher) {
System.out.println("往数据库中插入一条数据");
}
public void delete(Teacher teacher) {
System.out.println("从数据库中删除一条数据");
}
}
3.编写MyLogging.java
前面说过了通知具有五种类型,我们根据需要,选择合适的通知类型,让某个类实现通知对应的接口,这里其实就是相当于编写切面方法
通知类型 接口 接口方法 接口方法参数说明
前置通知 org.springframework.aop.MethodBeforeAdvice before(Method method, Object[] args, Object target) method是方法,args是方法的参数,target是目标对象
环绕通知 org.aopalliance.intercept.MethodInterceptor invoke(MethodInvocation invocation) invocation对象中包含有method,方法参数和目标对象
后置通知 org.springframework.aop.AfterReturningAdvice afterReturning(Object returnValue, Method method, Object[] args, Object target) returnValue是方法的返回值,其他的参数和前置通知一样
最终通知 org.springframework.aop.AfterAdvice 无 无
异常通知 org.springframework.aop.ThrowsAdvice 无 无
我们日志输出,选择后置通知,也就是方法执行完成之后调用
MyLogging.java
复制
package com.wan;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
/**
* @author StarsOne
* @date Create in 2019/9/25 0025 16:53
* @description
*/
public class MyLogging implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
String methodName = method.getName();//方法名
int size = args.length;//参数个数
System.out.println("调用了"+target+"的"+methodName+"方法,该方法的参数个数有"+size+"个");
}
}
4.xml配置
复制
这里和之前一样,也需要引用aop命名空间,IDEA可以智能帮我们导入,输入
之后的测试代码和之前的一样
前置通知
复制
package com.wan;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
/**
* @author StarsOne
* @date Create in 2019/9/25 0025 16:53
* @description
*/
@Aspect
public class MyLogging {
@Before("execution(public void add(com.wan.Teacher))")
public void sayHello() {
System.out.println("这是前置通知");
}
}
注解使用挺简单的,大概看一下示例代码就能知道怎么使用了
获得三个参数target、args、method
AOP中有个JoinPoint的接口,此接口可以获得target、args、method这三个参数
方法名 说明
getTarget() 获得目标对象
getSignature() 获得目标方法的Signature对象,由此对象的getName可以获得方法名
getArgs() 获得参数列表
复制
package com.wan;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
/**
* @author StarsOne
* @date Create in 2019/9/25 0025 16:53
* @description
*/
@Aspect
public class MyLogging {
@AfterReturning(pointcut="execution(public boolean add(com.wan.Teacher))",returning="returnValue")
public void test(JoinPoint jp,Object returnValue) {
//上面的注解的returning属性把方法的返回值赋值给了参数returnValue
}
}
环绕通知
环绕通知有个特殊的接口ProceedingJoinPoint,此接口是JoinPoint的子接口,比JoinPoint接口多了一个proceed方法,用于执行目的对象的方法获得返回值
复制
package com.wan;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
/**
* @author StarsOne
* @date Create in 2019/9/25 0025 16:53
* @description
*/
@Aspect
public class MyLogging {
@Around("execution(public boolean add(com.wan.Teacher))")
public void test(ProceedingJoinPoint jp) {
Object returnValue = jp.proceed();
}
}
异常通知
复制
package com.wan;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
/**
* @author StarsOne
* @date Create in 2019/9/25 0025 16:53
* @description
*/
@Aspect
public class MyLogging {
@AfterThorwing(pointcut="execution(public boolean add(com.wan.Teacher))",throwing="e")
public void test(JoinPoint jp,NullPointException e) {
//上面的注解的throwing属性把异常赋值给了参数e
//参数中指定了异常为空指针异常,所有,发生异常为空指针异常时候,异常通知才会调用此方法
}
}
PS:除以上两种方式可以实现AOP,还有一种使用Schema进行配置,我看了一下步骤,觉得比上面两种还要繁琐,在这里就补充了
联系QQ1053894518,点击右侧图标添加我为好友 联系我 或者加入QQ群一起学习 Stars-One安卓学习交流群 https://www.cnblogs.com/kexing/p/11588066.html