实习笔记—— AOP开发II(AOP中Advice的类型)

实习笔记—— AOP开发II(AOP中Advice的类型),第1张

系列文章目录

实习笔记 —— Spring基础
实习笔记 —— IOC反转控制(xml配置文件 + 注解)
实习笔记 —— AOP开发I


文章目录
  • 系列文章目录
  • 一、AOP中Advice的类型的XML配置文件形式
  • 二、AOP中Advice的类型的基本注解形式
  • 三、AOP中Advice的类型的切入点注解形式
  • 四、总结


一、AOP中Advice的类型的XML配置文件形式

先回顾昨天学的AOP中的相关术语,体会Advice的含义:

实习时介绍了AOP中Advice的五种类型,我们将这五种类型综合起来学习。

1.before advice前置通知: 在执行切点前的通知,可以判断是否可以执行该切点。

2.after-returning advice后置通知: 在运行完切点完未返回之前,进行通知。

3.around advice环绕通知: 包裹一个方法的执行,这是一个功能强大的通知方式。可以在方法执行前或者执行后around advice,也可以用来判断是否往下执行当前的切点,或者返回切点的值。或者抛出一个异常。

4.after throwing advice异常通知: 在运行完切点时抛出异常进行的通知。

5.After advice最终通知:执行完该切点后,进行的通知。

以具体项目为例:(创建项目第一步先导包)

①声明接口:

②声明接口的实现类:

③创建切面类,将所有新添加的需求对应的方法写在这里:

package com.high.demo1;

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

//最开始导错包了import org.aopalliance.intercept.Joinpoint;

// 切面类,存放用户要增加的方法
public class MyAspectXML {
	
	// Advice:通知,增强;甲方要求新增加的方法或需求(如显示用户身份需求)
//	public void checkPri() {
//		System.out.println("进行身份验证........");
//	} 
	// ctrl + alt + down
	
	// 1.前置通知
	public void checkPri(JoinPoint jointJoinpoint) {
		System.out.println("save方法的前置通知:进行身份验证........" + jointJoinpoint);
	}
	
	// 2.后置通知
	public void writeLog(Object result) {
		System.out.println("delete方法的后置通知:Be recorded....." + result);
	}
	
	// 3.环绕通知:性能监控
	// ProceedingJoinPoint是指被增加的接入点find()
	public Object aroundOne(ProceedingJoinPoint joinPoint) throws Throwable {
		System.out.println("环绕通知前.....");
		// 调用了此方法对应的切入点 find()方法,基本都要写,用于体现“环绕”
		Object proceed = joinPoint.proceed();		
		System.out.println("环绕通知后前.....");
		
		return proceed;
	}
	
	// 4.异常抛出通知
	// 参数: Throwable
	public void afterThrow(Throwable ex) {
		System.out.println("Error appeared." + ex.getMessage());
	}
	
	// 5.最终通知 —— 相当于异常中的 finally代码块
	public void afterLast() {
		System.out.println("This is the final Advice.");
	}
}

④编写XML配置文件:


<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" 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.xsd">
        
	
	
 	<bean name="productDao" class="com.high.demo1.ProductDaoImpl">bean>      
 	       
   	
   	<bean name="myAspect" class="com.high.demo1.MyAspectXML">bean>  
   	 
   	
   	
   	<aop:config>
   			
   		<aop:pointcut id="pointcut1" expression="execution(* com.high.demo1.ProductDaoImpl.save(..))"/>
   		<aop:pointcut id="pointcut2" expression="execution(* com.high.demo1.ProductDaoImpl.delete(..))"/>
   		<aop:pointcut id="pointcut3" expression="execution(* com.high.demo1.ProductDaoImpl.find(..))"/>
   		<aop:pointcut id="pointcut4" expression="execution(* com.high.demo1.ProductDaoImpl.update(..))"/>
   		<aop:pointcut id="pointcut5" expression="execution(* com.high.demo1.ProductDaoImpl.find(..))"/>
   		
   		
   		<aop:aspect ref="myAspect">
   			
   			
   			<aop:before method="checkPri" pointcut-ref="pointcut1"/>
   			
   			<aop:after-returning method="writeLog" pointcut-ref="pointcut2" returning="result"/>
   			
   			<aop:around method="aroundOne" pointcut-ref="pointcut3"/>
   			
   			<aop:after-throwing method="afterThrow" pointcut-ref="pointcut4" throwing="ex"/>
   			
   			<aop:after method="afterLast" pointcut-ref="pointcut5"/>
   		aop:aspect>
   	aop:config>
beans>

⑤编写测试类:

package com.high.demo1;

import javax.annotation.Resource;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

// AOP入门 
// 书写类注解,类层级,所以写在类外面
// 整合JUnit4
@RunWith(SpringJUnit4ClassRunner.class)
// 加载XML配置文件(严格按照格式书写,不要添加任何空格等字符)
@ContextConfiguration("classpath:applicationContext.xml")

public class SpringDemo {
	
	// 1.未使用注解整合JUnit
//	@Test
//	public void demo() {
//		ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
//		ProductDaoImpl product = (ProductDaoImpl)applicationContext.getBean("productDao");
//		
//		product.save();
//		product.update();
//		product.delete();
//		product.find();
//	}
	
	// 2.整合后
	// 想用哪个类就注入哪个类,交给spring管理
	
	// 对私有属性的引入
	@Resource(name = "productDao")
	private ProductDao productDao;
		
	@Test
	public void demo2() {
		productDao.save();
		productDao.update();
		productDao.delete();
		productDao.find();
	}
}

在正常无Advice的情况下,测试类的输出结果为:

在为各个方法添加新需求后:体会五种类型的Advice的作用

二、AOP中Advice的类型的基本注解形式

核心:
在配置文件中启用注解功能:

<aop:aspectj-autoproxy>aop:aspectj-autoproxy>

①创建类(应该是接口及其实现类,偷懒了):

②编写配置文件:
由于使用注解形式弱化了配置文件的作用,所以配置文件精简了许多

③最核心的部分:编写切面类

package com.high.demo;

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;

// 切面类
// AOP注解
@Aspect
public class MyAspectDemo {
	
	// 1.前置增强
	@Before(value = "execution(* com.high.demo.OrderDao.save(..))")
	public void checkPri(JoinPoint joinPoint) {
		System.out.println("save方法的前值增强:权限检测..." + joinPoint);
	}
	
	// 2.后置增强
	@AfterReturning(value = "execution(* com.high.demo.OrderDao.find(..))", returning = "result")
	public void afterReturn(String result) {
		System.out.println("find方法的后置增强..." + result);
	}
	
	// 3.环绕增强
	@Around(value = "execution(* com.high.demo.OrderDao.delete(..))")
	public Object aroundOne(ProceedingJoinPoint joinPoint) throws Throwable {
		System.out.println("delete方法的环绕前通知");
		// 获取接入点
		Object target = joinPoint.proceed();
		System.out.println("delete方法的环绕后通知");
		
		return target;
	}
	
	// 4.异常通知
	@AfterThrowing(value = "execution(* com.high.demo.OrderDao.update(..))", throwing="ex")
	public void afterThrowing(Throwable ex) {
		System.out.println("update方法的异常通知:Error appeared: " + ex.getMessage());
	}
	
	// 5.最终通知
	@After(value = "execution(* com.high.demo.OrderDao.update(..))")
	public void afterAdvice() {
		System.out.println("This is the After Advice of method update.");
	}
}

④测试类:
测试类完全使用注解形式了,比之前的先获取配置文件再使用对象及其方法的形式简便了一些

测试结果:

当解除 update 方法的注释 i = 1 / 0 后,代码中出现 0 作分母的错误,此时异常通知就开始起作用了(update 方法未能成功执行,而针对update 方法的最终通知无论如何都会执行)

三、AOP中Advice的类型的切入点注解形式

这是最简便的书写方式了,相对于标题二中的基本注解形式,进一步简化代码,声明切入点的注解,使得不必再对方法的每个新增需求创建切入点,而是对每个原有方法声明切入点,大大减少了需要声明的切入点的个数。

在切面类中写@Aspect注解,并在类中用注解进行切入点的声明(这是注解方式相较于XML配置文件方式简练的点,不再需要在XML配置文件中单独声明切入点并编写织入过程了)

其实实体类、测试类、配置文件相对于标题二下的内容都没有修改,只是在切面类中增加了切入点的注解:

①加入切入点注解的切面类写法:

package com.high.demo2;

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.aspectj.lang.annotation.Pointcut;

@Aspect // 最开始给忘喽
public class MyAspect {
	// AOP切入点的注解,提供更大的灵活性,方便解耦
	@Pointcut(value = "execution(* com.high.demo2.UserDao.save(..))")
	private void pointcut1() {}; //此处是一个方法,不是变量
	
	@Pointcut(value = "execution(* com.high.demo2.UserDao.find(..))")
	private void pointcut2() {};
	
	@Pointcut(value = "execution(* com.high.demo2.UserDao.delete(..))")
	private void pointcut3() {};
	
	@Pointcut(value = "execution(* com.high.demo2.UserDao.update(..))")
	private void pointcut4() {};
	
	
	// 1.前置通知
	@Before(value = "MyAspect.pointcut1()")
	public void checkPri(JoinPoint joinPoint) {
		System.out.println("切入点注解测试:save方法的前置通知; " + joinPoint);
	}
	
	// 2.后置通知
	@AfterReturning(value = "MyAspect.pointcut2()", returning = "result")
	public void afterOne(String result) {
		System.out.println("切入点注解测试:find方法的后置通知; " + result);
	}
	
	// 3.环绕通知
	@Around(value = "MyAspect.pointcut3()")
	public Object aroundOne(ProceedingJoinPoint joinPoint) throws Throwable {
		System.out.println("切入点注解测试:delete方法的环绕通知(前);");
		// 获取接入点
		Object target = joinPoint.proceed();
		System.out.println("切入点注解测试:delete方法的环绕通知(后);");
		
		return target;
	}
	
	// 4.异常通知
	@AfterThrowing(value = "MyAspect.pointcut4()", throwing = "ex")
	public void afterThrowing(Throwable ex) {
		System.out.println("切入点注解测试:update方法的异常通知; " + ex.getMessage());
	}
	
	// 5.最终通知
	@After(value = "MyAspect.pointcut4()")
	public void afterAdvice() {
		System.out.println("切入点注解测试:update方法的最终通知;");
	}
}

②测试结果:
(尽管实体类等没有变化,但在切面类书写 Advice 时在熟练之后对各输出语句内容进行了自由发挥,整体代码自认为更容易理解了。)

四、总结

1.后置通知在return语句前输出。

2.异常通知是报错时才会出现的通知,after-throwing 中的after 是指在执行后才能发现异常。
(同时发现:在项目spring03中测试时,按照测试类调用方法的顺序,update执行错误后剩余的delete和find不执行了。)

3.关于最终通知,若异常测试对应的方法(切入点)出错,最终通知会显示在异常测试报错之前,再报错。

4.若所有的新增需求甲方都不要了,我们要改哪些代码?
——在配置文件中将切面类相关的代码删除,保留

<bean name="productDao" class="com.high.demo1.ProductDaoImpl">bean>

仍可在测试类中以XML配置文件形式使用spring框架对对象进行 *** 作。

5.声明切入点时的expression属性写法(spring的切入点表达式写法):

6.注解有两种:类层面的注解 + 方法/属性层面(类内部)的注解。

7.spring注重团队开发与规则,便于大规模使用。类比自家饭菜与连锁店,自家饭菜一家一个味,但不利于形成规模。

8.代码能力相对容易掌握,但排错能力需要经验与锻炼。日常的节奏是写一段代码,报错,排错,完善,再往下写。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/729357.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-27
下一篇 2022-04-27

发表评论

登录后才能评论

评论列表(0条)

保存