热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Spring:AOP面向切面编程——实现通知的三种方法

通知:前置通知:运行目标方法前时,运行的通知;后置通知:运行目标方法后时,运行的通知;异常通知:运行目标方法发生异常时,运行的通知;环绕通知:在环绕通知中可以定义,前置通知、后置通

通知:

  前置通知:运行目标方法前时,运行的通知;

  后置通知:运行目标方法后时,运行的通知;

  异常通知:运行目标方法发生异常时,运行的通知;

  环绕通知:在环绕通知中可以定义,前置通知、后置通知、异常通知和最终通知,比较全面;

  最终通知:运行方法后,都会运行的通知;

让我们用一张图来更加好的理解这几个通知:

Spring: AOP 面向切面编程——实现通知的三种方法

一、通过接口实现通知

  前置通知:继承MethodBeforeAdvice接口,并重写before()方法;

  后置通知:继承AfterReturningAdvice接口,并重写afterReturning()方法;

  异常通知:继承ThrowsAdvice接口,无重写方法;

  环绕通知:继承MethodInterceptor接口,并重写invoke()方法;

  最终通知;

 如何来写一个通知?

  a. 需要的jar包;

    aopaliance.jar         aspectjweaver.jar

  b. 编写业务方法和需要的通知; 

         在下面的这个代码中,我们使用的业务方法是 addStudent(), 我们使用的通知是 前置通知;

  c. 在applicationContext.xml中配置相关内容;

   举例: 

       
	
	 
		
	
	
	  
	
         
          

 目前在applicationContext.xml中的关于通知的相关配置大概就是这样,现在还不理解没事,目前只是举一个栗子;

我们来使用继承接口来实现一个前置通知:

  a.jar包导一下;

  b.编写业务类和前置通知类;

  业务类:

  Student类:

public class Student {
	private String stuName;
	private int stuAge;
	private String stuClass;
	private Course course;
	
	public Student() {}

	public String getStuName() {
		return stuName;
	}

	public void setStuName(String stuName) {
		this.stuName = stuName;
	}

	public int getStuAge() {
		return stuAge;
	}

	public void setStuAge(int stuAge) {
		this.stuAge = stuAge;
	}

	public String getStuClass() {
		return stuClass;
	}

	public void setStuClass(String stuClass) {
		this.stuClass = stuClass;
	}

	
	public Course getCourse() {
		return course;
	}

	public void setCourse(Course course) {
		this.course = course;
	}

	@Override
	public String toString() {
		return "Student [stuName=" + stuName + ", stuAge=" + stuAge + ", stuClass=" + stuClass + ", course=" + course
				+ "]";
	}
}

  StudentDao类:

import org.kay.entity.Student;

public interface StudentDao {
	public void removeStudent(Student stu);
	public void deleteStudent();
}

  StudentDaoImpl类:

import org.kay.entity.Student;

public class StudentDaoImpl implements StudentDao{
	public void addStudent(Student student) {
		System.out.println("增加学生...");
	}

	@Override
	public void deleteStudent() {
		// TODO Auto-generated method stub
		System.out.println("删除学生...");
	}

	@Override
	public void removeStudent(Student stu) {
		System.out.println("移动学生...");
	}
}

  StudentService类:

import org.kay.entity.Student;

public interface StudentService {
	public void removeStudent(Student stu);
}

  StudentServiceImpl类:

import org.kay.dao.StudentDao;
import org.kay.entity.Student;
import org.kay.service.StudentService;

public class StudentServiceImpl implements StudentService{

	StudentDao stuDao;
	
	public void setStuDao(StudentDao stuDao) {
		this.stuDao = stuDao;
	}

	@Override
	public void removeStudent(Student stu) {
		// TODO Auto-generated method stub
		//stuDao = null;  // 进行异常通知的测试代码。空指针异常。
		stuDao.removeStudent(stu);
	}	
}

  前置通知类:

import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;

public class MyBeforeAdvice implements MethodBeforeAdvice{

	@Override
	public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
		System.out.println("这是一个前置通知!");
		System.out.println("Method:"+arg0+", Object[]:"+arg1+", Object:"+arg2);
	}
}

  编写好了相关的类,我们现在只要将他们配置一下就行了;

  c.在applicationContext.xml中配置相关内容;


	
	
		
	
	
	
	
		
		
	 

  测试:   全部都编写和配置好了之后,我们写一个Test类来进行一下测试;

import org.kay.dao.StudentDao;
import org.kay.dao.StudentDaoImpl;
import org.kay.entity.CollectionDemo;
import org.kay.entity.Student;
import org.kay.service.StudentService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
	
	public static void beforeAdvice() {
		ApplicationContext cOntext= new ClassPathXmlApplicationContext("applicationContext.xml");
		StudentService stuService = (StudentService)context.getBean("studentService");
		Student stu = new Student();
		stuService.removeStudent(stu);
	}
	
	public static void main(String[] args) {
		
		beforeAdvice();	
	}
}

  结果: 

Spring: AOP 面向切面编程——实现通知的三种方法

我们来使用继承接口来实现一个后置通知:

 添加一下后置通知类:

import java.lang.reflect.Method;
import org.springframework.aop.AfterReturningAdvice;

public class MyAfterRunningAdvice implements AfterReturningAdvice{

	@Override
	public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable {
		// TODO Auto-generated method stub
		System.out.println("这是一个 后置通知!");
	}
}

业务类就使用上面的类;

在applicationContext.xml中配置相关内容;

       
	
	
		
		
	

接着就是进行测试:

    public static void afterRunningAdvice() {
		ApplicationContext cOntext= new ClassPathXmlApplicationContext("applicationContext.xml");
		StudentService stuService = (StudentService)context.getBean("studentService");
		Student stu = new Student();
		stuService.removeStudent(stu);
	}

 这个后置通知测试的代码跟前置通知测试的代码一样,在SpringIoc容器中拿到studentService。进行studentService方法的调用。

     结果:

Spring: AOP 面向切面编程——实现通知的三种方法

我们来使用继承接口来实现一个异常通知:

 编写一个异常通知类:

import java.lang.reflect.Method;
import org.springframework.aop.ThrowsAdvice;

public class MyThrowAdvice implements ThrowsAdvice{
	
	public void afterThrowing(Method method, Object[] args, Object target, Exception ex) {
		System.out.println("这是一个异常通知!");
	}
}

业务类还是跟上面一样;         但是我们注意一下就是必须要发生异常才进行异常通知中的内容;

所以需要改变一些代码,就是将 stuDao == null; 给一个 空指针异常

@Override
	public void removeStudent(Student stu) {
		// TODO Auto-generated method stub
		stuDao = null;  // 进行异常通知的测试代码。空指针异常。
		stuDao.removeStudent(stu);
	}	

在applicationContext.xml中配置相关内容;

     
	
		
		
	

接着就是进行测试:

    public static void throwAdvice() {
		ApplicationContext cOntext= new ClassPathXmlApplicationContext("applicationContext.xml");
		StudentService stuService = (StudentService)context.getBean("studentService");
		Student stu = new Student();
		stuService.removeStudent(stu);
	}

结果:

Spring: AOP 面向切面编程——实现通知的三种方法

我们来使用继承接口来实现一个环绕通知:

 编写一个环绕通知类:

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class MyAroundAdvice implements MethodInterceptor{

	@Override
	public Object invoke(MethodInvocation invocation) throws Throwable {
		//方法体一
		Object result = null;
		try {
			System.out.println("【环绕通知】中的前置通知!!!");

			System.out.println("这个是一个【环绕通知】!!!");
			result = invocation.proceed(); // 控制目标方法的执行,XXXStudent();
			//result 就是目标方法的返回值。
			
			System.out.println("【环绕通知】中的前后置通知!!!");
			
		}catch(Exception e) {
			System.out.println("【环绕通知】中的异常通知!!!");
		}
		
		return result;
	}
}

业务类还是跟上面一样;   

在applicationContext.xml中配置相关内容;

     
	
		
		
	

接着就是进行测试:

       public static void aroundAdvice() {
		ApplicationContext cOntext= new ClassPathXmlApplicationContext("applicationContext.xml");
		StudentService stuService = (StudentService)context.getBean("studentService");
		Student stu = new Student();
		stuService.removeStudent(stu);
	}

结果:

1、当没有发生异常的结果:

Spring: AOP 面向切面编程——实现通知的三种方法

 

2、当发生异常时的结果:

Spring: AOP 面向切面编程——实现通知的三种方法

二、通过注解实现通知

前置通知:  注解为   @Before

后置通知:  注解为   @AfterReturning

异常通知:  注解为   @AfterThrowing

环绕通知:  注解为   @Around

最终通知:  注解为   @After

使用注解进行通知的配置的话,与接口有一点不同,利用注解的话只需要在applicationContext.xml中开启注解对AOP的支持就行


两个注意点:

  @Aspect 声明该类是一个通知;

  @Component("annotationAdvice")  将AnnotationAdvice纳入到SpringIoc容器中。   

其实  @Component("annotationAdvice")    等价于  

如果用注解将通知纳入到SpringIoc容器中去的话,需要在applicationContext.xml文件中设置扫描器;

   

我们来使用注解来实现前置通知通知、后置、异常通知、环绕通知和最终通知:

编写一个普通的类:

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;

@Component("annotationAdvice") // 将AnnotationAdvice纳入到SpringIoc容器中。
@Aspect // 此类是一个通知类
public class AnnotationAdvice {
	
	@Before("execution(* org.kay.service.Impl.StudentServiceImpl.*(..))")
	public void myBefore() {
		System.out.println("<注解---前置通知!!>");
	}
	
	@AfterReturning(pointcut="execution(* org.kay.service.Impl.StudentServiceImpl.*(..))", returning="returningValue")
	public void myAfterReturning(JoinPoint jp, Object returningValue) {
		System.out.println("<注解---后置通知!!>, 目标对象:" + jp.getTarget()+"\n" + jp.getArgs() + "\n" + jp.getKind()+" 返回值:" + returningValue);
	}

	@AfterThrowing("execution(* org.kay.service.Impl.StudentServiceImpl.*(..))")
	public void myThrows() {
		System.out.println("<注解---异常通知!!>");
	}
	
	//环绕通知
	@Around("execution(* org.kay.service.Impl.StudentServiceImpl.*(..))")
	public void myAround(ProceedingJoinPoint jp) {
		
		try {
			//前置
			System.out.println("<注解---环绕通知---前置通知>");
			
			jp.proceed();
			
			//后置
			System.out.println("<注解---环绕通知---后置通知>");
		}catch(Throwable e) {
			//异常
			System.out.println("<注解---环绕通知---异常通知>");
		}finally{
			//最终
			System.out.println("<注解---环绕通知---最终通知>");
		}
	}
	
	//最终通知
	@After("execution(* org.kay.service.Impl.StudentServiceImpl.*(..))")
	public void myAfter() {
		System.out.println("<注解---最终通知!!>");
	}
	
}

业务类还是以前的一样的;

在applicationContext.xml中配置

	

测试:

    public static void annotationAdvice() {
		ApplicationContext cOntext= new ClassPathXmlApplicationContext("applicationContext.xml");
		StudentService stuService = (StudentService)context.getBean("studentService");
		Student stu = new Student();
		stuService.removeStudent(stu);
	}

结果:(大致的内容有了就行!)

1、没有异常:

Spring: AOP 面向切面编程——实现通知的三种方法

2、发生异常:

Spring: AOP 面向切面编程——实现通知的三种方法

三、通过配置实现通知

 基于Schema配置;类似于接口的方式在applicationContext.xml中进行配置。

先编写一个普通的类:

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;

public class ConfigSchemaAdvice {
	
	public void myConfigBefore() {
		System.out.println("Schema---前置通知...");
	}
	
	public void myConfigAfterReturning(JoinPoint jp, Object returningValue) {
		System.out.println("Schema---后置通知..." + "返回值: " + returningValue);
	}
	
	public void myConfigThrows(JoinPoint jp, NullPointerException e) {
		System.out.println("Schema---异常通知..." + e.getMessage());
	}
	
	public void myConfigFinally() {
		System.out.println("Schema---最终通知...");
	}
	
	public Object myConfigAround(ProceedingJoinPoint jp) {
		Object result = null;
		try {
			System.out.println("Schema《环绕》 --- 前置通知¥¥¥");

			result = jp.proceed();//执行方法。
			System.out.println("目标函数: " + jp.getTarget());
			
			System.out.println("Schema《环绕》 --- 后置通知¥¥¥");
		}catch(Throwable e) {
			System.out.println("Schema《环绕》 --- 异常通知¥¥¥");
		}finally {
			System.out.println("Schema《环绕》 --- 最终通知¥¥¥");
		}
		return result;
	}
}

因为这个是一个普通的类,而且没有加注解;所以我们需要在applicationContext.xml中进行配置,使它先纳入SpringIoc容器中,在将他变成一个通知类;

       	
    

测试:

     public static void schemaAdvice() {
		ApplicationContext cOntext= new ClassPathXmlApplicationContext("applicationContext.xml");
		StudentService stuService = (StudentService)context.getBean("studentService");
		Student stu = new Student();
		stuService.removeStudent(stu);
	}

 结果:

Spring: AOP 面向切面编程——实现通知的三种方法

其他:

如果需要获取目标对象信息:

  注解 和 Schema : JoinPoint ;

  接口: Method method, Object[] args, Object target ; 

注解和Schema的不同之处:

  注解使用的是在普通类进行注解使它变成通知类;

  而Schema是在applicationContext.xml中进行了配置,使普通类变成了通知类。

其中关于获取目标对象信息的参数都没有介绍感兴趣的可以看一下网易云 颜群老师的Spring课程,是我的Spring启蒙老师。^.^ !

颜群老师其他的课程也讲的很不错哦!

https://study.163.com/course/introduction.htm?courseId=1005991005&_trace_c_p_k2_=f16e22940de942cca0b8a16fff6e1788

 


推荐阅读
  • 在Spring框架中,基于Schema的异常通知与环绕通知的实现方法具有重要的实践价值。首先,对于异常通知,需要创建一个实现ThrowsAdvice接口的通知类。尽管ThrowsAdvice接口本身不包含任何方法,但开发者需自定义方法来处理异常情况。此外,环绕通知则通过实现MethodInterceptor接口来实现,允许在方法调用前后执行特定逻辑,从而增强功能或进行必要的控制。这两种通知机制的结合使用,能够有效提升应用程序的健壮性和灵活性。 ... [详细]
  • 如何在Java中高效构建WebService
    本文介绍了如何利用XFire框架在Java中高效构建WebService。XFire是一个轻量级、高性能的Java SOAP框架,能够简化WebService的开发流程。通过结合MyEclipse集成开发环境,开发者可以更便捷地进行项目配置和代码编写,从而提高开发效率。此外,文章还详细探讨了XFire的关键特性和最佳实践,为读者提供了实用的参考。 ... [详细]
  • 本文作为“实现简易版Spring系列”的第五篇,继前文深入探讨了Spring框架的核心技术之一——控制反转(IoC)之后,将重点转向另一个关键技术——面向切面编程(AOP)。对于使用Spring框架进行开发的开发者来说,AOP是一个不可或缺的概念。了解AOP的背景及其基本原理,对于掌握这一技术至关重要。本文将通过具体示例,详细解析AOP的实现机制,帮助读者更好地理解和应用这一技术。 ... [详细]
  • Spring注解开发指南:@Resource与@Component详解 ... [详细]
  • 本文探讨了如何在 Google Sheets 中通过自定义函数实现 AJAX 调用。具体介绍了编写脚本的方法,以便在电子表格中发起 AJAX 请求,从而实现数据的动态获取与更新。这种方法不仅简化了数据处理流程,还提高了工作效率。 ... [详细]
  • 本文详细探讨了Java集合框架的使用方法及其性能特点。首先,通过关系图展示了集合接口之间的层次结构,如`Collection`接口作为对象集合的基础,其下分为`List`、`Set`和`Queue`等子接口。其中,`List`接口支持按插入顺序保存元素且允许重复,而`Set`接口则确保元素唯一性。此外,文章还深入分析了不同集合类在实际应用中的性能表现,为开发者选择合适的集合类型提供了参考依据。 ... [详细]
  • 使用 MyEclipse 和 TestNG 测试框架在 Java 中高效进行单元测试
    通过MyEclipse集成TestNG测试框架,可以在Java开发中高效地进行单元测试。本文介绍了在JDK 1.8.0_121和MyEclipse 10.0离线环境下配置和使用TestNG的具体步骤,帮助开发者提高测试效率和代码质量。 ... [详细]
  • 开发心得:深入探讨Servlet、Dubbo与MyBatis中的责任链模式应用
    开发心得:深入探讨Servlet、Dubbo与MyBatis中的责任链模式应用 ... [详细]
  • Ceph API微服务实现RBD块设备的高效创建与安全删除
    本文旨在实现Ceph块存储中RBD块设备的高效创建与安全删除功能。开发环境为CentOS 7,使用 IntelliJ IDEA 进行开发。首先介绍了 librbd 的基本概念及其在 Ceph 中的作用,随后详细描述了项目 Gradle 配置的优化过程,确保了开发环境的稳定性和兼容性。通过这一系列步骤,我们成功实现了 RBD 块设备的快速创建与安全删除,提升了系统的整体性能和可靠性。 ... [详细]
  • Spring框架入门指南:专为新手打造的详细学习笔记
    Spring框架是Java Web开发中广泛应用的轻量级应用框架,以其卓越的功能和出色的性能赢得了广大开发者的青睐。本文为初学者提供了详尽的学习指南,涵盖基础概念、核心组件及实际应用案例,帮助新手快速掌握Spring框架的核心技术与实践技巧。 ... [详细]
  • 深入解析Tomcat:开发者的实用指南
    深入解析Tomcat:开发者的实用指南 ... [详细]
  • Liferay Portal 中 AutoEscape 构造函数的应用与实例代码解析 ... [详细]
  • 在Laravel中实现PHP对JSON数据的发布与处理 ... [详细]
  • Android目录遍历工具 | AppCrawler自动化测试进阶(第二部分):个性化配置详解
    终于迎来了“足不出户也能为社会贡献力量”的时刻,但有追求的测试工程师绝不会让自己的生活变得乏味。与其在家消磨时光,不如利用这段时间深入研究和提升自己的技术能力,特别是对AppCrawler自动化测试工具的个性化配置进行详细探索。这不仅能够提高测试效率,还能为项目带来更多的价值。 ... [详细]
  • 本文深入探讨了NDK与JNI技术在实际项目中的应用及其学习路径。通过分析工程目录结构和关键代码示例,详细介绍了如何在Android开发中高效利用NDK和JNI,实现高性能计算和跨平台功能。同时,文章还提供了从基础概念到高级实践的系统学习指南,帮助开发者快速掌握这些关键技术。 ... [详细]
author-avatar
风一样的骨头gg
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有