欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Java Spring之AOP切面编程(多个实例分析)

程序员文章站 2022-07-10 18:50:10
文章目录AOP简介AOP中关键性概念案例实战项目层次spring-context.xmlMyThrowsAdvice(异常通知)BookBizImplIBookBizPriceExceptionMyAfterReturningAdvice (前置通知)MyMethodBeforeAdvice (后置通知)MyMethodInterceptor (环绕通知)测试AOP简介AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统...



AOP简介

AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

AOP中关键性概念

  • 连接点(Joinpoint)

    程序执行过程中明确的点,如方法的调用,或者异常的抛出。

  • 目标(Target)

    被通知(被代理)的对象

    注:完成具体的业务逻辑

  • 通知(Advice)
    在某个特定的连接点上执行的动作,同时Advice也是程序代码的具体实现,例如一个实现日志记录的代码(通知有些书上也称为处理)

    注:完成切面编程

  • 代理(Proxy)

    将通知应用到目标对象后创建的对象(代理=目标+通知)

    例子:外科医生+护士

    注:只有代理对象才有AOP功能,而AOP的代码是写在通知的方法里面的

  • 切入点(Pointcut)

    多个连接点的集合,定义了通知应该应用到那些连接点,(也将Pointcut理解成一个条件 ,此条件决定了

    容器在什么情况下将通知和目标组合成代理返回给外部程序)

  • 适配器(Advisor)

    适配器=通知(Advice)+切入点(Pointcut)

图解

aop代码是切入执行的,在调用UserBiz.add()之前它会去spring的上下文中找有没有编号为1的方块,如果有对应的那么就先执行对应编号的,然后执行UserBiz.add()方法,执行完之后它会去找下面的边界的编号看有没有对应的方块如果有就执行。

代理对象其实就相当于一块拼图,目标对象就是那一块大的前后通知就相当于零碎的部分。

Java Spring之AOP切面编程(多个实例分析)
通知的接口

通知类型 解释
前置通知 实现org.springframework.aop.MethodBeforeAdvice接口
后置通知 实现org.springframework.aop.AfterReturningAdvice接口
环绕通知 org.aopalliance.intercept.MethodInterceptor
异常通知 org.springframework.aop.ThrowsAdvice
过滤通知(适配器) org.springframework.aop.support.RegexpMethodPointcutAdvisor
  • 前置通知:在连接点之前执行的通知()
  • 后置通知:在连接点正常完成后执行的通知
  • 环绕通知:包围一个连接点的通知,最大特点是可以修改返回值,由于它在方法前后都加入了自己的逻辑代码,因此功能异常强大,它通过MethodInvocation.proceed()来调用目标方法(甚至可以不调用,这样目标方法就不会执行)
  • 异常通知:出现异常执行系统提示,然后进行处理,价格异常为例

案例实战

项目层次

Java Spring之AOP切面编程(多个实例分析)

spring-context.xml

这里的代理对象bean的属性需要注意一下:

工具类org.springframework.aop.framework.ProxyFactoryBean用来创建一个代理对象,在一般情况下它需

要注入以下三个属性:

  1. proxyInterfaces:代理应该实现的接口列表(List)
  2. interceptorNames:需要应用到目标对象上的通知Bean的名字。(List)
  3. target:目标对象 (Object)
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" default-autowire="byType" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:cache="http://www.springframework.org/schema/cache" 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.3.xsd
		http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache-4.3.xsd"> <!--*********************************AOP*********************************--> <!-- 目标对象 --> <bean id="bookBiz" class="com.liyingdong.aop.biz.impl.BookBizImpl"></bean> <!-- 通知 --> <bean id="myBefore" class="com.liyingdong.aop.advice.MyMethodBeforeAdvice"></bean> <bean id="myAfter" class="com.liyingdong.aop.advice.MyAfterReturningAdvice"></bean> <bean id="myinterceptor" class="com.liyingdong.aop.advice.MyMethodInterceptor"></bean> <bean id="myThrowsAdvice" class="com.liyingdong.aop.advice.MyThrowsAdvice"></bean> <!--过滤通知不需要写类  --> <bean id="myAfter2" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <!-- advice:指定是哪个过滤器上面的--> <property name="advice" ref="myAfter"></property> <!-- pattern:正则匹配 --> <property name="pattern" value=".*buy"></property> </bean> <!-- 由代理工厂来组装目标对象及通知 --> <bean id="bookProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <!--
          1.需要找到目标是谁  
          2.需要找到代理了那个接口(list)
          3.通知(list)
            --> <property name="target" ref="bookBiz"></property> <!--代理应该实现的接口列表(List), 它是一个列表,因为有可能你的实现类不止实现了一个 --> <property name="proxyInterfaces"> <list> <value>com.liyingdong.aop.biz.IBookBiz</value> </list> </property> <!--需要应用到目标对象上的通知Bean的名字。(List)  --> <property name="interceptorNames"> <list> <value>myBefore</value> <!-- <value>myAfter</value> --> <value>myAfter2</value> <value>myinterceptor</value> <value>myThrowsAdvice</value> </list> </property> </bean> </beans> 

MyThrowsAdvice(异常通知)

异常通知处理类用于事务的回滚,注意一下它没有重写的方法,但是你一定要定义afterThrowing这个名字符合的的格式

public void afterThrowing( [Method method,] [Object args,] [Object target,] Throwable throwable );
前面三个参数都是可选的,只有第三个参数是必须的,同时我们还可以在同一个类中定义这个方法的多个版本,如:
public void afterThrowing( MyException1 ex ) {}
public void afterThrowing( MyException2 ex ) {}
具体那个方法被调用则根据具体的Exception来判断,由AOP容器自动识别 执行

 /**
 * 异常通知
 * 注意:它没有重写的方法,但是你一定要定义afterThrowing这个名字符合的的格式
 *
 * 案例:张三向李四转账
 *     biz.transfer(user1,user2)
 *         UserDao.update(user1)
 *         UserDao.update(user2)
 *         
 *   如果在账号过程中张三向李四转账的时候服务器崩了,
 *   张三扣了钱李四却没收到,这是不合理的,
 *   所以可以在此类中做回滚      
 *         
 */ public class MyThrowsAdvice implements ThrowsAdvice { public void afterThrowing(PriceException ex) { System.out.println("价格输入有误,购买失败,请重新输入!!!"); } } 

BookBizImpl

public class BookBizImpl implements IBookBiz { public BookBizImpl() { super(); } public boolean buy(String userName, String bookName, Double price) { // 通过控制台的输出方式模拟购书 if (null == price || price <= 0) { throw new PriceException("book price exception"); } System.out.println(userName + " buy " + bookName + ", spend " + price); return true; } public void comment(String userName, String comments) { // 通过控制台的输出方式模拟发表书评 System.out.println(userName + " say:" + comments); } } 

IBookBiz

public interface IBookBiz { // 购书 public boolean buy(String userName, String bookName, Double price); // 发表书评 public void comment(String userName, String comments); } 

PriceException

public class PriceException extends RuntimeException { public PriceException() { super(); } public PriceException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); } public PriceException(String message, Throwable cause) { super(message, cause); } public PriceException(String message) { super(message); } public PriceException(Throwable cause) { super(cause); } } 

MyAfterReturningAdvice (前置通知)

/**
 * 买书返利(存在bug)
 * @author Administrator
 *
 */ public class MyAfterReturningAdvice implements AfterReturningAdvice { @Override public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { // TODO Auto-generated method stub //获取被调用目标对象的类名 String clzName=target.getClass().getName(); //被调用到的方法名 String methodName = method.getName(); //携带的参数 String params = Arrays.toString(args); System.out.println("【买书返利的后置通知】:"+clzName+"."+methodName+"("+params+")"+"\t目标对象方法掉用后的返回值"+returnValue); } } 

MyMethodBeforeAdvice (后置通知)

/**
 * 买书、评论前加系统日志
 * @author Administrator
 *
 */ public class MyMethodBeforeAdvice implements MethodBeforeAdvice { @Override public void before(Method method, Object[] args, Object target) throws Throwable { // TODO Auto-generated method stub //获取被调用目标对象的类名 String clzName=target.getClass().getName(); //被调用到的方法名 String methodName = method.getName(); //携带的参数 String params = Arrays.toString(args); System.out.println("【买书、评论前加系统日志】:"+clzName+"."+methodName+"("+params+")"); } } 

MyMethodInterceptor (环绕通知)

/**
 * 环绕通知:类似拦截器,会包括切入点,目标类前后都会执行代码。
 * @author Administrator
 *
 */ public class MyMethodInterceptor implements MethodInterceptor { @Override public Object invoke(MethodInvocation invocation) throws Throwable { //获取被调用目标对象的类名 String clzName=invocation.getThis().getClass().getName(); //被调用到的方法名 String methodName = invocation.getMethod().getName(); //携带的参数 String params = Arrays.toString(invocation.getArguments()); //	       sessionFactory.openSession,session.beginTransation System.out.println("【环绕通知】:"+clzName+"."+methodName+"("+params+")"); //获取返回值  Object returnValue = invocation.proceed(); //	       transation.commit(),session.close() System.out.println("【环绕通知】:\t目标对象方法调用后的返回值"+returnValue); return returnValue; } } 

测试

这里测试一下异常通知,我们把价格设置为负数。

public class AopTest { public static void main(String[] args) { ApplicationContext springContext = new ClassPathXmlApplicationContext("/spring-context.xml"); IBookBiz bean = (IBookBiz) springContext.getBean("bookProxy"); System.out.println(bean.getClass()); boolean buy = bean.buy("海王", "富婆如何爱上我", -66d); bean.comment("海王", "6666"); } } 

结果:
Java Spring之AOP切面编程(多个实例分析)
测试前后通知环绕通知。

public class AopTest { public static void main(String[] args) { ApplicationContext springContext = new ClassPathXmlApplicationContext("/spring-context.xml"); IBookBiz bean = (IBookBiz) springContext.getBean("bookProxy"); System.out.println(bean.getClass()); boolean buy = bean.buy("海王", "富婆如何爱上我", 66d); bean.comment("海王", "6666"); } } 

结果:
Java Spring之AOP切面编程(多个实例分析)

本文地址:https://blog.csdn.net/qq_45384482/article/details/107897717

相关标签: aop spring java