目录
一、AOP概念
AOP解决的问题
AOP中关键性概念
如何实现AOP
二、AOP使用
准备工作
前置通知
后置通知
编辑
环绕通知
异常通知
过滤通知(适配器)
三、总结
一、AOP概念
AOP:即面向切面编程
AOP解决的问题
解决了需求的改变,造成了原有没必要改变的代码,需要去改变它;
比如:书籍的增删改,本身只需要完成增删改即可,这时如果需要添加日志功能,那么需要在原有的代码基础上,去修改添加日志功能,受牵连的方法就三个(add/edit/del)
AOP中关键性概念
连接点(Joinpoint):程序执行过程中明确的点,如方法的调用,或者异常的抛出.
目标(Target):被通知(被代理)的对象
注1:完成具体的业务逻辑
通知(Advice):非核心的业务代码,在某个特定的连接点上执行的动作,同时Advice也是程序代码的具体实现,例如一个实现日志记录的代码(通知有些书上也称为处理)
注2:完成切面编程
代理(Proxy):将通知应用到目标对象后创建的对象(代理=目标+通知),
注3:只有代理对象才有AOP功能,而AOP的代码是写在通知的方法里面的
切入点(Pointcut):多个连接点的集合,定义了通知应该应用到那些连接点。
(也将Pointcut理解成一个条件 ,此条件决定了容器在什么情况下将通知和目标组合成代理返回给外部程序)
适配器(Advisor):相当于正则表达式,适配器=通知(Advice)+切入点(Pointcut)
如何实现AOP
连接点(Joinpoint):程序执行过程中明确的点,如方法的调用,或者异常的抛出.
目标(Target):被通知(被代理)的对象
注1:完成具体的业务逻辑
通知(Advice):非核心的业务代码,在某个特定的连接点上执行的动作,同时Advice也是程序代码的具体实现,例如一个实现日志记录的代码(通知有些书上也称为处理)
注2:完成切面编程
代理(Proxy):将通知应用到目标对象后创建的对象(代理=目标+通知),
注3:只有代理对象才有AOP功能,而AOP的代码是写在通知的方法里面的
切入点(Pointcut):多个连接点的集合,定义了通知应该应用到那些连接点。
(也将Pointcut理解成一个条件 ,此条件决定了容器在什么情况下将通知和目标组合成代理返回给外部程序)
适配器(Advisor):相当于正则表达式,适配器=通知(Advice)+切入点(Pointcut)
目标对象只负责业务逻辑代码
通知对象负责AOP代码,这二个对象都没有AOP的功能,只有代理对象才有
二、AOP使用
准备工作
BookBiz.java
package com.maomao.aop.biz; public interface BookBiz { // 购书 public boolean buy(String userName, String bookName, Double price); // 发表书评 public void comment(String userName, String comments); }
异常类 PriceException.java
package com.maomao.aop.exception; 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); } }
BookBizImpl.java
package com.maomao.aop.biz.impl; import com.maomao.aop.biz.BookBiz; import com.maomao.aop.exception.PriceException; public class BookBizImpl implements BookBiz { 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); } }
前置通知
实现org.springframework.aop.MethodBeforeAdvice接口
买书、评论前加系统日志
MyMethodBeforeAdvice.java
package com.maomao.aop.advice; import java.lang.reflect.Method; import java.util.Arrays; import org.springframework.aop.MethodBeforeAdvice; public class MyMethodBeforeAdvice implements MethodBeforeAdvice{ @Override public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable { // 目标对象的类名 String clzName=arg2.getClass().getName(); // 当前调用的方法是 String methdName=arg0.getName(); // 当前调用方法所传递参数 String args=Arrays.toString(arg1); System.out.println("【系统日志】:"+clzName+"."+methdName+"被调用,传递的参数为:"+args); } }
spring-context.xml
com.maomao.aop.biz.BookBiz
myBefore
测试
package com.maomao.aop.demo; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.maomao.aop.biz.BookBiz; public class Demo1 { @SuppressWarnings("resource") public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(); BookBiz bean=(BookBiz)context.getBean("bookBiz"); bean.buy("aa", "abc", 99.9); bean.comment("aa", "123123"); } }
后置通知
实现org.springframework.aop.AfterReturningAdvice接口
买书返利(存在bug)
每调用一次buy方法打印:“[销售返利][时间]返利3元。”
MyAfterReturningAdvice.java
package com.maomao.aop.advice; import java.lang.reflect.Method; import java.util.Arrays; import org.springframework.aop.AfterReturningAdvice; public class MyAfterReturningAdvice implements AfterReturningAdvice { public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { String targetName = target.getClass().getName(); String methodName = method.getName(); String params = Arrays.toString(args); String msg = "【返利通知:返利3元】:正在调用" + targetName + "." + methodName + ",携带的参数:" + params + ";目标对象所调用的方法的返回值:" + returnValue; System.out.println(msg); } }
spring-context.xml
运行结果
环绕通知
org.aopalliance.intercept.MethodInterceptor
类似拦截器,会包括切入点,目标类前后都会执行代码。
MyMethodInterceptor.java
package com.maomao.aop.advice; import java.lang.reflect.Method; import java.util.Arrays; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; public class MyMethodInterceptor implements MethodInterceptor { public Object invoke(MethodInvocation invocation) throws Throwable { Object target = invocation.getThis(); Method method = invocation.getMethod(); Object[] args = invocation.getArguments(); String targetName = target.getClass().getName(); String methodName = method.getName(); String params = Arrays.toString(args); String msg = "【环绕通知】:正在调用->" + targetName + "." + methodName + ",携带的参数:" + params; System.out.println(msg); Object returnValue = invocation.proceed(); String msg2 = "【环绕通知】:目标对象所调用的方法的返回值:" + returnValue; System.out.println(msg2); return returnValue; } }
spring-context.xml
异常通知
org.springframework.aop.ThrowsAdvice
出现异常执行系统提示,然后进行处理。价格异常为例
MyThrowsAdvice.java
package com.maomao.aop.advice; import org.springframework.aop.ThrowsAdvice; import com.maomao.aop.exception.PriceException; public class MyThrowsAdvice implements ThrowsAdvice { public void afterThrowing(PriceException ex ) { System.out.println("【异常通知】:价格输入错误,购买失败,请重新购买"); } }
spring-context.xml
在原来的测试类改价格
Demo1.java
package com.maomao.aop.demo; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.maomao.aop.biz.BookBiz; public class Demo1 { @SuppressWarnings("resource") public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("/spring-context.xml"); // BookBiz bean=(BookBiz)context.getBean("bookBiz"); BookBiz bean=(BookBiz)context.getBean("bookProxy"); bean.buy("aa", "abc", -99.9); bean.comment("aa", "123123"); } }
过滤通知(适配器)
org.springframework.aop.support.RegexpMethodPointcutAdvisor
处理买书返利的bug
com.maomao.aop.biz.BookBiz
MyBefore MyAfterPlus MyMethod MyThrows
三、总结
AOP面向切面编程
主要作用:将核心的业务功能与非核心的业务功能进行分离;
将核心的业务功能写到目标对象中将非核心的业务功能写到通知中
专业名词:通知、连接点、目标对象、切入点、代理、适配器
日常开发通常的应用场景
事物管理、日志功能
事物的开启:前置通知
事物的提交:后置通知
事物的回滚:异常通知