SpringAOP:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率,在springAOP中业务逻辑仅仅只关注业务本身,将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。
AOP有两种实现方式一种是基于XML的文件配置方式另一种是基于注解的方式下面大概总结下两种方式的使用:
一.基于注解方式的使用
1.在切面类(为切点服务的类)前用@Aspect注释修饰,声明为一个切面类。
2.用@Pointcut注释声明一个切点,目的是为了告诉切面,谁是它的服务对象。(此注释修饰的方法的方法体为空,不需要写功能比如 public void say(){};就可以了,方法名可以被候命的具体服务功能所以引用,它可以被理解为切点对象的一个代理对象方法)也可以不用这个直接在通知类型中声名。
3.在对应的方法前用对应的通知类型注释修饰,将对应的方法声明称一个切面功能,为了切点而服务
注解方式声明aop
1.用@Aspect注解将类声明为切面(如果用@Component("")注解注释为一个bean对象,那么就要在spring配置文件中开启注解扫描,<context:component-scan base-package="com.package包名"/>
否则要在spring配置文件中声明一个bean对象)
2.在切面需要实现相应方法的前面加上相应的注释,也就是通知类型。
3.此处有环绕通知,环绕通知方法一定要有ProceedingJoinPoint类型的参数传入,然后执行对应的proceed()方法,环绕才能实现。
@Aspect:作用是把当前类标识为一个切面供容器读取
@Pointcut:Pointcut是植入Advice的触发条件。每个Pointcut的定义包括2部分,一是表达式,二是方法签名。方法签名必须是 public及void型。可以将Pointcut中的方法看作是一个被Advice引用的助记符,因为表达式不直观,因此我们可以通过方法签名的方式为 此表达式命名。因此Pointcut中的方法只需要方法签名,而不需要在方法体内编写实际代码。
@Around:环绕增强,相当于MethodInterceptor
@AfterReturning:后置增强,相当于AfterReturningAdvice,方法正常退出时执行
@Before:标识一个前置增强方法,相当于BeforeAdvice的功能,相似功能的还有
@AfterThrowing:异常抛出增强,相当于ThrowsAdvice //配置抛出异常后通知,使用在切面方法上注册的切入点
@After: final增强,不管是抛出异常或者正常退出都会执行
Component("annotationTest")
@Aspect
public class AnnotationTest {
//@Pointcut:Pointcut是植入Advice的触发条件。每个Pointcut的定义包括2部分,一是表达式,二是方法签名。方法签名必须是 public及void型。可以将Pointcut中的方法看作是一个被Advice引用的助记符,因为表达式不直观,因此我们可以通过方法签名的方式为 此表达式命名。因此Pointcut中的方法只需要方法签名,而不需要在方法体内编写实际代码。
//定义切点
@Pointcut("execution(com.cj.*.*(..)))") //切入点表达式 (..)代表后面的子包方法
public void sayings(){ //切入点签名
}
//前置通知 也可以不用切点直接用 @Before("execution(* *.saying(..))")和下面作用是一样的
@Before("sayings()")
public void sayHello(){
System.out.println("注解类型前置通知");
}
//后置通知
@After("sayings()")
public void sayGoodbey(){
System.out.println("注解类型后置通知");
}
//环绕通知。注意要有ProceedingJoinPoint参数传入。 环绕通知就是前置通知和后置通知都有,其中proceed为执行方法的过程
@Around("sayings()")
public void sayAround(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("注解类型环绕通知..环绕前");
pjp.proceed();//执行方法
System.out.println("注解类型环绕通知..环绕后");
}
下面是例2:
public static final String POINT = "execution(* com.oppo.bot.voice.center.impl.*.*(..)) || execution(* com.oppo.bot.voice.asr.impl.*.*(..))"; 定义切面为两个不同包下面的所有class类都为切面
@Around(POINT)
public Object timeAround(ProceedingJoinPoint joinPoint) {
// 定义返回对象、得到方法需要的参数
Object obj = null;
//得到方法需要的参数 ,调用切面方法传过来的参数
Object[] args = joinPoint.getArgs();
long startTime = System.currentTimeMillis();
System.out.println("切面运行之前");
try {
System.out.println("开始运行自身逻辑");
obj = joinPoint.proceed(args); //执行方法
System.out.println("切面运行之后");
} catch (Throwable e) {
logger.error("统计某方法执行耗时环绕通知出错{}", e);
throw new InternalSystemException(Constants.BOT_VOICE_CORE, "统计某方法执行耗时环绕通知出错...", e); }
// 获取执行的方法名
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
long endTime = System.currentTimeMillis();
//signature.getDeclaringTypeName()获取类名 signature.getName()获取方法名
String methodName = signature.getDeclaringTypeName() + "." + signature.getName();
// 打印耗时的信息
System.out.println(endTime-startTime+":运行时间");
return obj; }
1.@PointCut 注解的方法不会被执行,只起到了一个把切面表达式抽象出来的作用。
2.@AfterReturning是最常用的:
3.@AfterReturning注解中的returning = "object"应该和形参的object名字一致 ,用来接收目标方法的返回值。目标方法返回值抽象为Object对象
@AfterReturning(pointcut="execution(...) " returning="object")
public void afterReturning(JointPoint jp,Object object){
//注意这里的object 应该和returning="object"保持一致
System.out.println(object); //object是目标方法返回的参数
System.out.println(jp.getArgs() ); //通过这种方法可以获取目标方法的传入参数
}
point也可以用注解实现
先声明一个注解方法:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface TraceRequest {}
然后在point中使用@annotation声明
private final static String POINT = "@annotation(com.oppo.bot.classifier.common.log.TraceRequest)";
@AfterReturning(pointcut = "contentFilterPointcut()", returning = "ret") public void afterReturningContentFilter(Object ret) { Stopwatch stopwatch = Stopwatch.createStarted(); FilteredContent.FilteredContentBuilder builder = null; String customerId = null
}
@AfterReturning为方法执行完之后执行,returning = "ret"代表方法返回结果,放在ret对象中
表示point为使用了该注解的所有方法。
二:基于xml配置的AOP实现:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!-- 系统服务组件的切面Bean -->
<bean id="serviceAspect" class="cn.ysh.studio.spring.aop.aspect.ServiceAspect"/>
<!-- AOP配置 -->
<aop:config>
<!-- 声明一个切面,并注入切面Bean,相当于@Aspect -->
<aop:aspect id="simpleAspect" ref="serviceAspect">
<!-- 配置一个切入点,相当于@Pointcut -->
<aop:pointcut expression="execution(* cn.ysh.studio.spring.aop.service..*(..))" id="simplePointcut"/>
<!-- 配置通知,相当于@Before、@After、@AfterReturn、@Around、@AfterThrowing -->
<aop:before pointcut-ref="simplePointcut" method="before"/>
<aop:after pointcut-ref="simplePointcut" method="after"/>
<aop:after-returning pointcut-ref="simplePointcut" method="afterReturn"/>
<aop:after-throwing pointcut-ref="simplePointcut" method="afterThrow" throwing="ex"/>
</aop:aspect>
</aop:config>
</beans>
相关的学习链接为:
@Pointcut()的execution、@annotation等参数说明_java_green_hand0909的博客-优快云博客