聊聊注解@Aspect的AOP实现操作

Spring只支持XML方式而没有实现注解的方式(也叫AspectJ方式)的AOP,所以要使用@Aspect注解,只能引入AspectJ相关的 jar 包 aopalliance-1.0.jar 和 aspectjweaver.jar,这个坑把我给坑惨了。

使用步骤如下:

1、引入相关jar包

2、Spring的配置文件 applicationContext.xml 中引入context、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:aop="http://www.springframework.org/schema/aop"
 xmlns:context="http://www.springframework.org/schema/context"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
 <!-- 配置自动扫描的包 -->
 <context:component-scan base-package="com.qcc.beans.aop"></context:component-scan>
 <!-- 自动为切面方法中匹配的方法所在的类生成代理对象。 -->
 <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

3、创建简单计算器的接口ArithmeticCalculator.java及实现类ArithmeticCalculatorImpl.java

package com.qcc.beans.aop;
public interface ArithmeticCalculator {
 int add(int i, int j);
 int sub(int i, int j);
 int mul(int i, int j);
 int div(int i, int j);
}
package com.qcc.beans.aop;
import org.springframework.stereotype.Component;
//将实现类加入Spring的IOC容器进行管理
@Component("arithmeticCalculator")
public class ArithmeticCalculatorImpl implements ArithmeticCalculator {
 @Override
 public int add(int i, int j) {
 int result = i + j;
 return result;
 }
 @Override
 public int sub(int i, int j) {
 int result = i - j;
 return result;
 }
 @Override
 public int mul(int i, int j) {
 int result = i * j;
 return result;
 }
 @Override
 public int div(int i, int j) {
 int result = i / j;
 return result;
 }
}

4、现在想在实现类中的每个方法执行前、后、以及是否发生异常等信息打印出来,需要把日志信息抽取出来,写到对应的切面的类中 LoggingAspect.java 中

要想把一个类变成切面类,需要两步,

① 在类上使用 @Component 注解 把切面类加入到IOC容器中

② 在类上使用 @Aspect 注解 使之成为切面类

下面直接上完整代码,用@Aspect注解方式来实现前置通知、返回通知、后置通知、异常通知、环绕通知。

package com.qcc.beans.aop;
import java.util.Arrays;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
/**
 * 日志切面
 *
 * @author QianChaoChen 00002336<br>
 * @date 2017年3月3日 下午3:03:29
 */
@Component
@Aspect
public class LoggingAspect {
 /**
 * 前置通知:目标方法执行之前执行以下方法体的内容
 * @param jp
 */
 @Before("execution(* com.qcc.beans.aop.*.*(..))")
 public void beforeMethod(JoinPoint jp){
 String methodName = jp.getSignature().getName();
 System.out.println("【前置通知】the method 【" + methodName + "】 begins with " + Arrays.asList(jp.getArgs()));
 }
 /**
 * 返回通知:目标方法正常执行完毕时执行以下代码
 * @param jp
 * @param result
 */
 @AfterReturning(value="execution(* com.qcc.beans.aop.*.*(..))",returning="result")
 public void afterReturningMethod(JoinPoint jp, Object result){
 String methodName = jp.getSignature().getName();
 System.out.println("【返回通知】the method 【" + methodName + "】 ends with 【" + result + "】");
 }
 /**
 * 后置通知:目标方法执行之后执行以下方法体的内容,不管是否发生异常。
 * @param jp
 */
 @After("execution(* com.qcc.beans.aop.*.*(..))")
 public void afterMethod(JoinPoint jp){
 System.out.println("【后置通知】this is a afterMethod advice...");
 }
 /**
 * 异常通知:目标方法发生异常的时候执行以下代码
 */
 @AfterThrowing(value="execution(* com.qcc.beans.aop.*.*(..))",throwing="e")
 public void afterThorwingMethod(JoinPoint jp, NullPointerException e){
 String methodName = jp.getSignature().getName();
 System.out.println("【异常通知】the method 【" + methodName + "】 occurs exception: " + e);
 }
// /**
// * 环绕通知:目标方法执行前后分别执行一些代码,发生异常的时候执行另外一些代码
// * @return
// */
// @Around(value="execution(* com.qcc.beans.aop.*.*(..))")
// public Object aroundMethod(ProceedingJoinPoint jp){
// String methodName = jp.getSignature().getName();
// Object result = null;
// try {
//  System.out.println("【环绕通知中的--->前置通知】:the method 【" + methodName + "】 begins with " + Arrays.asList(jp.getArgs()));
//  //执行目标方法
//  result = jp.proceed();
//  System.out.println("【环绕通知中的--->返回通知】:the method 【" + methodName + "】 ends with " + result);
// } catch (Throwable e) {
//  System.out.println("【环绕通知中的--->异常通知】:the method 【" + methodName + "】 occurs exception " + e);
// }
//
// System.out.println("【环绕通知中的--->后置通知】:-----------------end.----------------------");
// return result;
// }
}

5、编写Main方法进行测试

package com.qcc.beans.aop;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
 public static void main(String[] args) {
 ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
 ArithmeticCalculator arithmeticCalculator = (ArithmeticCalculator) ctx.getBean("arithmeticCalculator");
 System.out.println(arithmeticCalculator.getClass());
 int result = arithmeticCalculator.add(3, 5);
 System.out.println("result: " + result);
 result = arithmeticCalculator.div(5, 0);
 System.out.println("result: " + result);
 }
}

运行结果:

class com.sun.proxy.$Proxy10
【前置通知】the method 【add】 begins with [3, 5]
【后置通知】this is a afterMethod advice...
【返回通知】the method 【add】 ends with 【8】
result: 8
【前置通知】the method 【div】 begins with [5, 0]
【后置通知】this is a afterMethod advice...
Exception in thread "main" java.lang.ArithmeticException: / by zero
 at com.qcc.beans.aop.ArithmeticCalculatorImpl.div(ArithmeticCalculatorImpl.java:28)
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
 at java.lang.reflect.Method.invoke(Method.java:606)
 at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317)
 at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
 at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
 at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:52)
 at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
 at org.springframework.aop.aspectj.AspectJAfterAdvice.invoke(AspectJAfterAdvice.java:43)
 at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
 at org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor.invoke(AfterReturningAdviceInterceptor.java:52)
 at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
 at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:58)
 at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
 at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
 at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
 at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
 at com.sun.proxy.$Proxy10.div(Unknown Source)
 at com.qcc.beans.aop.Main.main(Main.java:15)

把其它代码都注释掉,把环绕通知的方法释放出来,测试结果如下:

【环绕通知中的--->前置通知】:the method 【add】 begins with [3, 5]
【环绕通知中的--->返回通知】:the method 【add】 ends with 8
【环绕通知中的--->后置通知】:-----------------end.----------------------
result: 8
【环绕通知中的--->前置通知】:the method 【div】 begins with [5, 0]
【环绕通知中的--->异常通知】:the method 【div】 occurs exception java.lang.ArithmeticException: / by zero
【环绕通知中的--->后置通知】:-----------------end.----------------------
Exception in thread "main" org.springframework.aop.AopInvocationException: Null return value from advice does not match primitive return type for: public abstract int com.qcc.beans.aop.ArithmeticCalculator.div(int,int)
 at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:219)
 at com.sun.proxy.$Proxy7.div(Unknown Source)
 at com.qcc.beans.aop.Main.main(Main.java:15)

从以上发现,返回通知和异常通知不会同时出现;不管是否发生异常,后置通知都会正常打印。

补充:基于@Aspect实现AOP的两种方式

第一种:

package com.anno;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME) //什么时候运行和结束
@Target( {ElementType.METHOD , ElementType.TYPE}) //能放在哪里.
public @interface AnnotationTwo {
}
package com.anno;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
@Aspect
@Component
@EnableAspectJAutoProxy
public class AspectAnnotationTOW {
 @Pointcut("@annotation(AnnotationTwo)")
 public void aspectTow(){

 }

 @Around("aspectTow()")
 public void aspectservice(ProceedingJoinPoint joinPoint){
 System.out.println(111);
 try {
 Object proceed = joinPoint.proceed();
 } catch (Throwable e) {
 e.printStackTrace();
 }
 }

}

第二种:

package com.testAnnotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; 

@Retention(RetentionPolicy.RUNTIME) //什么时候运行和结束
@Target( {ElementType.METHOD , ElementType.TYPE}) //能放在哪里.
public @interface AnnotationOfTest {
 public String say();
 public long howLong() default Long.MAX_VALUE;
}
package com.testAnnotation;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
import com.rabbitmq.tools.jsonrpc.ProcedureDescription;
@Component
@EnableAspectJAutoProxy
@Aspect
public class AspectAnnotation {
 @Around("@annotation(annotationOfTest)")
 public Object interceptor(ProceedingJoinPoint pjp, AnnotationOfTest annotationOfTest ) {
 String say = annotationOfTest.say();
 long howlong = annotationOfTest.howLong();
 try {
 System.out.println(say+"\t"+howlong);
 Object object = pjp.proceed();
 return object;
 } catch (Throwable e) {
 e.printStackTrace();
 }
 return null;
 }
 }

配置文件:

<?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:util="http://www.springframework.org/schema/util"
 xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop"
 xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
 xsi:schemaLocation="
 http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
 http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.3.xsd
 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
 http://www.springframework.org/schema/context
 http://www.springframework.org/schema/context/spring-context-4.3.xsd">

 <context:component-scan base-package="com.testAnnotation"></context:component-scan>
 <context:component-scan base-package="com.service"></context:component-scan>
 <context:component-scan base-package="com.anno"></context:component-scan>
 <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy> 

 </beans>
service:
package com.service;
import java.util.Date;
import org.springframework.stereotype.Service;
import com.anno.AnnotationTwo;
import com.testAnnotation.AnnotationOfTest;

@Service
public class service {
 @AnnotationTwo
 public void go()
 {
 System.out.println(new Date().toLocaleString());
 }
}

项目结构:

测试:

package com.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.service.service;
import com.testAnnotation.AnnotationOfTest;

public class Test {
 public static void main(String[] args) {
 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
 service bean = applicationContext.getBean(service.class);
 bean.go();
 }
}

所需要的包:

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。如有错误或未考虑完全的地方,望不吝赐教。

(0)

相关推荐

  • 基于spring@aspect注解的aop实现过程代码实例

    @AspectJ 作为通过 Java 5 注释注释的普通的 Java 类,它指的是声明 aspects 的一种风格.通过在你的基于架构的 XML 配置文件中包含以下元素,@AspectJ 支持是可用的. 第一步:编写切面类 package com.dascom.hawk.app.web.tool; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.l

  • 基于Spring AOP @AspectJ进阶说明

    @AspectJ可以使用切点函数定义切点,我们还可以使用逻辑运算符对切点进行复核运算得到复合的切点,为了在切面中重用切点,我们还可以对切点进行命名,以便在其他的地方引用定义过的切点. 当一个连接点匹配多个切点时,需要考虑织入顺序的问题,此外一个重要的问题是如何再增强中访问连接点上下文的信息. Waiter接口: package com.yyq.aspectJAdvanced; public interface Waiter { void greetTo(String name); void se

  • 谈谈Spring AOP中@Aspect的高级用法示例

    前言 本文主要跟大家分享介绍了关于Spring AOP中@Aspect的高级用法,下面话不多说了,来随着小编一起看看详细的介绍吧. 1 切点复合运算 支持在切点定义中加入以下运算符进行复合运算: 运算符 说明 && 与运算. ! 非运算. || 或运算. 2 切点命名 一般情况下,切点是直接声明在需要增强方法处,这种切点的声明方式称为匿名切点,匿名切点只能在声明处被使用 . 如果希望在其它地方可以重用这个切点,我们可以通过 @Pointcut 注解及切面类方法来命名它. public cl

  • 在Spring 中使用@Aspect 控制自定义注解的操作

    Spring 中使用@Aspect 控制自定义注解 看这篇介绍@Aspect 1.定义系统日志注解类 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface SysLog { String value() default ""; } 2.定义切面处理类 package com.kxs.common.aspect; import com.google.gso

  • 聊聊注解@Aspect的AOP实现操作

    Spring只支持XML方式而没有实现注解的方式(也叫AspectJ方式)的AOP,所以要使用@Aspect注解,只能引入AspectJ相关的 jar 包 aopalliance-1.0.jar 和 aspectjweaver.jar,这个坑把我给坑惨了. 使用步骤如下: 1.引入相关jar包 2.Spring的配置文件 applicationContext.xml 中引入context.aop对应的命名空间:配置自动扫描的包,同时使切面类中相关方法中的注解生效,需自动地为匹配到的方法所在的类生

  • Spring-基于Spring使用自定义注解及Aspect实现数据库切换操作

    实现思路 重写Spring的AbstractRoutingDataSource抽象类的determineCurrentLookupKey方法. 我们来看下Spring-AbstractRoutingDataSource的源码 AbstractRoutingDataSource获取数据源之前会先调用determineCurrentLookupKey方法查找当前的lookupKey. Object lookupKey = determineCurrentLookupKey(); DataSource

  • Spring学习通过AspectJ注解方式实现AOP操作

    目录 Spring注解AspectJ操作AOP 一.被增强类 二.增强类 三.进行通知的配置 1. spring 配置文件中,开启扫描. 2. 使用注解创建 User 和 UserProxy 对象 3. 在增强类上使用注解 @Aspect 4. spring配置,开启生成代理对象 5. 配置不同类型的通知 四.抽取相同切入点 五.多个增强类的优先级 Spring注解AspectJ操作AOP 一.被增强类 新建一个被增强的类 User,下面有个 add() 方法. package com.ping

  • 使用SpringBoot AOP 记录操作日志、异常日志的过程

    平时我们在做项目时经常需要对一些重要功能操作记录日志,方便以后跟踪是谁在操作此功能:我们在操作某些功能时也有可能会发生异常,但是每次发生异常要定位原因我们都要到服务器去查询日志才能找到,而且也不能对发生的异常进行统计,从而改进我们的项目,要是能做个功能专门来记录操作日志和异常日志那就好了, 当然我们肯定有方法来做这件事情,而且也不会很难,我们可以在需要的方法中增加记录日志的代码,和在每个方法中增加记录异常的代码,最终把记录的日志存到数据库中.听起来好像很容易,但是我们做起来会发现,做这项工作很繁

  • Spring使用AspectJ的注解式实现AOP面向切面编程

    1.认识Spring AOP 1.1 AOP的简介 AOP:面向切面编程,相对于OOP面向对象编程. Spring的AOP的存在目的是为了解耦.AOP可以让一组类共享相同的行为.在OOP中只能通过继承类和实现接口,来使代码的耦合度增强,而且类的继承只能为单继承,阻碍更多行为添加到一组类上,AOP弥补了OOP的不足. 1.2 AOP中的概念 切入点(pointcut): 切入点(pointcut):在哪些类.哪些方法上切入. 通知(advice):在方法前.方法后.方法前后做什么. 切面(aspe

  • 使用Spring的注解方式实现AOP实例

    spring对AOP的实现提供了很好的支持.下面我们就使用Spring的注解来完成AOP做一个例子. 首先,为了使用Spring的AOP注解功能,必须导入如下几个包.aspectjrt.jar,aspectjweaver.jar,cglib-nodep.jar.然后我们写一个接口 package com.bird.service; public interface PersonServer { public void save(String name); public void update(S

  • Spring实战之使用注解实现声明式事务操作示例

    本文实例讲述了Spring实战之使用注解实现声明式事务操作.分享给大家供大家参考,具体如下: 一 配置文件 <?xml version="1.0" encoding="GBK"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xm

  • springboot注解Aspect实现方案

    目录 目标 实现 POM 一些实体类 CheckedParam ProcessDbModel 测试用的入参对象 一些工具类 Base64Util SpringBootBeanUtil ProcessBeanUtil CheckedTransmitableUtil PrivateTransmitableUtil 一些Bean PostProcess TestCheckPostProcess Aspect注解 切面类 CheckedAop 线程池配置 持久化service 审批用的service 测

  • spring boot注解方式使用redis缓存操作示例

    本文实例讲述了spring boot注解方式使用redis缓存操作.分享给大家供大家参考,具体如下: 引入依赖库 在pom中引入依赖库,如下 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> &l

  • mybatis3使用@Select等注解实现增删改查操作

    1.需要的jar包 2.目录树 3.具体代码 一.需要的jar包 第一个:mybatis的jar包 第二个:mysql数据的驱动 二.目录树 三.具体代码 使用框架,配置文件先行! conf.xml:(配置 登录数据库,映射文件) <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN

随机推荐