文章目录
- 1.基本介绍
- 1.什么是AOP?
- 2.示意图
- 3.五种通知
- 2.AOP快速入门
- 1.入门案例
- 1.导入jar包
- 2.Cal.java 接口
- 3.CalImpl.java 实现类
- 4.CalAspect.java 切面类
- 5.beans06.xml 开启注解功能
- 6.测试
- 2.细节说明
- 3.AOP理解(重点)
- 1.使用场景
- 2.代理对象理解
- 3.AOP的实现方式
- 4.简化的AOP实现方式
- 4.课后练习
- 1.题目
- 2.答案
- 1.UsbInterface.java
- 2.Camera.java
- 3.Phone.java
- 4.Aspect.java
- 5.beans06.xml
- 6.测试
- 3.切入表达式
- 1.基本介绍
- 2.举例说明
- 3.注意事项和细节(重点)
- 1.切入表达式格式
- **注意:**
- 2.细节
- 3.Proxy和CGlib的区别
- 4.JoinPoint常用方法
- 1.基本介绍
- 2.代码实例(使用普通对象)
- 1.Car.java
- 2.Aspect02.java
- 3.beans06.xml
- 4.test.java
- 5.返回通知获取结果
- 代码实例
- 修改Aspect02.java
- 6.异常通知获取异常
- 代码实例
- 1.修改Car.java
- 2.修改Aspect02.java
- 7.环绕通知
- 代码实例
- 1.Dog.java
- 2.Aspect03.java
- 3.beans06.xml
- 4.Test.java
- 8.切入表达式重用
- 代码实例
- 1.Car.java
- 2.Aspect02.java
- 3.beans06.xml
- 4.测试
- 9.AOP多切面优先级问题
- 解释
- 10.根据xml配置AOP
- 代码实例
- 1.UsbInterface.java
- 2.Camera.java
- 3.Aspect.java
- 4.beans07.xml
- 5.测试
- 11.课后练习
- 1.注解实现
- 1.Cal.java
- 2.CalImpl.java
- 3.Aspect_ann.java
- 4.beans06.xml
- 5.测试
- 2.xml实现
- 1.Cal.java
- 2.CalImpl.java
- 3.Aspect_xml.java
- 4.beans08.xml
- 5.测试
1.基本介绍
1.什么是AOP?
2.示意图
3.五种通知
2.AOP快速入门
1.入门案例
1.导入jar包
2.Cal.java 接口
package com.sxs.spring.aop.aspectj; /** * 计算数量的接口 * * @author 孙显圣 * @version 1.0 */ public interface Cal { public double getSub(double num1, double num2); public double getSum(double num1, double num2); }
3.CalImpl.java 实现类
package com.sxs.spring.aop.aspectj; import org.springframework.stereotype.Component; /** * @author 孙显圣 * @version 1.0 */ @Component //注解创建bean对象 public class CalImpl implements Cal { @Override public double getSub(double num1, double num2) { System.out.println("方法内部打印:" + (num1 - num2)); return num1 - num2; } @Override public double getSum(double num1, double num2) { System.out.println("方法内部打印:" + (num1 + num2)); return num1 + num2; } }
4.CalAspect.java 切面类
package com.sxs.spring.aop.aspectj; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; import java.lang.reflect.Method; import java.util.Arrays; /** * 切面类 * * @author 孙显圣 * @version 1.0 */ @Component //自动创建bean对象 @Aspect //切面类 public class CalAspect { /** * @param joinPoint 保存了要切入的方法的信息 * @Before 前置通知 * execution(。。。) 切入表达式,表明要切入的方法,格式:格式:访问修饰符+返回类型 全类名 方法名(参数类型) */ @Before(value = "execution(public double com.sxs.spring.aop.aspectj.CalImpl.getSub(double, double))") public void before(JoinPoint joinPoint) { //获取方法签名 Signature signature = joinPoint.getSignature(); System.out.println("方法执行开始-日志-方法名-" + signature.getName() + "-参数" + Arrays.asList(joinPoint.getArgs())); } /** * @param joinPoint 保存了要切入的方法的信息 * @AfterReturning 返回通知 */ @AfterReturning(value = "execution(public double com.sxs.spring.aop.aspectj.CalImpl.getSub(double, double))") public void afterReturning(JoinPoint joinPoint) { Signature signature = joinPoint.getSignature(); System.out.println("方法执行正常结束-日志-方法名-" + signature.getName() ); } /** * @param joinPoint * @AfterThrowing 异常通知 */ @AfterThrowing(value = "execution(public double com.sxs.spring.aop.aspectj.CalImpl.getSub(double, double))") public void throwing(JoinPoint joinPoint) { Signature signature = joinPoint.getSignature(); System.out.println("方法出现异常-日志-方法名-" + signature.getName() ); } /** * @param joinPoint * @After 后置通知 */ @After(value = "execution(public double com.sxs.spring.aop.aspectj.CalImpl.getSub(double, double))") public void after(JoinPoint joinPoint) { Signature signature = joinPoint.getSignature(); System.out.println("方法最终执行完毕-日志-方法名-" + signature.getName() ); } }
5.beans06.xml 开启注解功能
6.测试
package com.sxs.spring.aop.aspectj; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * @author 孙显圣 * @version 1.0 */ public class AopAspectjTest { public static void main(String[] args) { ApplicationContext ioc = new ClassPathXmlApplicationContext("beans06.xml"); //注意:这里需要使用接口类型来获取代理对象,然后使用代理对象来执行方法,才能实现切面编程 Cal bean = ioc.getBean(Cal.class); bean.getSub(1,22); } }
2.细节说明
3.AOP理解(重点)
1.使用场景
需要对接口对象/普通对象的方法进行额外的操作而不想修改源代码时使用
2.代理对象理解
从接口类型或者接口对象id获取的bean对象的运行类型都是代理对象,编译类型是接口类型,最终执行方法的对象是代理对象
一个代理对象对应一个接口对象,在只有一个接口对象的时候才能使用类型来获取
3.AOP的实现方式
- 编写接口
- 编写实现类,使用注解自动创建bean对象
- 编写切面类,使用注解
- 自动创建bean对象
- 标识切面类
- 通知 + 切入表达式(切点)
- 编写xml文件
- 扫描普通注解
- 开启aop注解功能
- 使用方式
- 使用id或者类型获取针对接口的代理对象
- 使用代理对象执行方法
4.简化的AOP实现方式
- 接口,接口对象(注解)
- 切面(注解),切面对象(注解)
- 通知 + 切点
- 使用方式
- 使用id或者类型获取针对接口的代理对象
- 使用代理对象执行方法
4.课后练习
1.题目
2.答案
1.UsbInterface.java
package com.sxs.spring.aop.homework01; /** * @author 孙显圣 * @version 1.0 */ public interface UsbInterface { public void work(); }
2.Camera.java
package com.sxs.spring.aop.homework01; import org.springframework.stereotype.Component; /** * @author 孙显圣 * @version 1.0 */ @Component public class Camera implements UsbInterface{ @Override public void work() { System.out.println("相机正在工作。。。。。。"); } }
3.Phone.java
package com.sxs.spring.aop.homework01; import org.springframework.stereotype.Component; /** * @author 孙显圣 * @version 1.0 */ @Component public class Phone implements UsbInterface{ @Override public void work() { System.out.println("手机正在工作。。。。。。"); } }
4.Aspect.java
package com.sxs.spring.aop.homework01; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; /** * @author 孙显圣 * @version 1.0 */ @Component @org.aspectj.lang.annotation.Aspect public class Aspect { @Before(value = "execution(* com.sxs.spring.aop.homework01.*.*(..))") public void before(JoinPoint joinPoint) { Signature signature = joinPoint.getSignature(); System.out.println("日志-方法执行前-方法名-" + signature.getName()); } }
5.beans06.xml
6.测试
package com.sxs.spring.aop.homework01; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * @author 孙显圣 * @version 1.0 */ public class test { public static void main(String[] args) { ApplicationContext ioc = new ClassPathXmlApplicationContext("beans06.xml"); //通过id获取代理对象 UsbInterface bean1 = (UsbInterface) ioc.getBean("camera"); //执行方法 bean1.work(); //通过id获取代理对象 UsbInterface bean2 = (UsbInterface) ioc.getBean("phone"); //执行方法 bean2.work(); } }
3.切入表达式
1.基本介绍
2.举例说明
3.注意事项和细节(重点)
1.切入表达式格式
*(访问修饰符(空格)返回类型)(空格)*(全类名/接口名/简单类名/简单接口名).*(方法名)(..)(参数类型) 简称: 星 星点星(点点)
注意:
- 访问修饰符和返回类型可以使用单个*****来表示任意的,必须满足中间有空格,比如 public * …
- 使用简单类名/简单接口名则必须与切面类在同一个包下
- 如果配置了接口类型,则包括接口对象的所有方法
2.细节
3.Proxy和CGlib的区别
- Proxy是针对接口的代理对象,可以执行接口的所有方法
- CGlib是针对父类的代理对象,可以执行该类的所有法
4.JoinPoint常用方法
1.基本介绍
2.代码实例(使用普通对象)
1.Car.java
package com.sxs.spring.aop.joinpoint; import org.springframework.stereotype.Component; /** * @author 孙显圣 * @version 1.0 */ @Component public class Car { public void run() { System.out.println("Car在运行!"); } }
2.Aspect02.java
package com.sxs.spring.aop.joinpoint; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.AfterReturning; import org.springframework.stereotype.Component; /** * @author 孙显圣 * @version 1.0 */ @Component @org.aspectj.lang.annotation.Aspect public class Aspect02 { //返回通知 @AfterReturning(value = "execution(* Car.run())") public void afterReturning(JoinPoint joinPoint) { System.out.println("方法执行完毕!"); Signature signature = joinPoint.getSignature(); System.out.println("name=" + signature.getName()); System.out.println("simpleName=" + signature.getDeclaringType().getSimpleName()); System.out.println("该方法所属类的类名=" + signature.getDeclaringTypeName()); System.out.println("参数=" + joinPoint.getArgs()); } }
3.beans06.xml
4.test.java
package com.sxs.spring.aop.joinpoint; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * @author 孙显圣 * @version 1.0 */ public class test { public static void main(String[] args) { //获取针对父类的代理对象 ApplicationContext ioc = new ClassPathXmlApplicationContext("beans06.xml"); Car bean = ioc.getBean(Car.class); bean.run(); } }
5.返回通知获取结果
代码实例
修改Aspect02.java
package com.sxs.spring.aop.joinpoint; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.AfterReturning; import org.springframework.stereotype.Component; /** * @author 孙显圣 * @version 1.0 */ @Component @org.aspectj.lang.annotation.Aspect public class Aspect02 { /** * 返回通知获取结果 * * @param joinPoint * @param res 参数名必须与returning的一致 */ @AfterReturning(value = "execution(* Car.run())", returning = "res") public void afterReturning(JoinPoint joinPoint, Object res) { System.out.println("返回结果为=" + res); } }
6.异常通知获取异常
代码实例
1.修改Car.java
2.修改Aspect02.java
/** * 异常通知获取异常 * * @param joinPoint * @param throwable 参数名必须与throwing一致 */ @AfterThrowing(value = "execution(* Car.run())", throwing = "throwable") public void afterThrowing(JoinPoint joinPoint, Throwable throwable) { System.out.println("异常通知获取异常:" + throwable); }
7.环绕通知
代码实例
1.Dog.java
package com.sxs.spring.aop.doArround; import org.springframework.stereotype.Component; /** * @author 孙显圣 * @version 1.0 */ @Component public class Dog { public String sayHi(String name) { System.out.println(name + "wang,wang!"); return name + "wang,wang!"; } }
2.Aspect03.java
package com.sxs.spring.aop.doArround; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.stereotype.Component; import java.util.Arrays; import java.util.List; /** * 环绕通知 * * @author 孙显圣 * @version 1.0 */ @Component @Aspect public class Aspect03 { /** * @param joinPoint 连接点对象 * @return 返回结果 */ @Around(value = "execution(public String sayHi(String))") public Object doArround(ProceedingJoinPoint joinPoint) { Object result = null; String methodName = joinPoint.getSignature().getName(); try { Object[] args = joinPoint.getArgs(); List list = Arrays.asList(args); //1.前置通知 System.out.println("AOP环绕通知-前置通知-方法名=" + methodName + "-参数-" + list); //执行方法 result = joinPoint.proceed(); //2.返回通知 System.out.println("AOP环绕通知-返回通知-方法名=" + methodName + "-结果为=" + result); } catch (Throwable e) { //3.异常通知 System.out.println("AOP环绕通知-异常通知-方法名=" + methodName + "-异常=" + e); } finally { //4.后置通知 System.out.println("AOP环绕通知-后置通知-方法名=" + methodName); } return result; } }
3.beans06.xml
4.Test.java
package com.sxs.spring.aop.doArround; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * @author 孙显圣 * @version 1.0 */ public class Test { public static void main(String[] args) { //读取配置文件获取容器 ApplicationContext ioc = new ClassPathXmlApplicationContext("beans06.xml"); //获取针对父类的代理对象 Dog bean = ioc.getBean(Dog.class); //执行方法 bean.sayHi("小白"); } }
8.切入表达式重用
代码实例
1.Car.java
package com.sxs.spring.aop.joinpoint; import org.springframework.stereotype.Component; /** * @author 孙显圣 * @version 1.0 */ @Component public class Car { public void run() { System.out.println("Car在运行!"); } }
2.Aspect02.java
package com.sxs.spring.aop.joinpoint; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; /** * @author 孙显圣 * @version 1.0 */ @Component @org.aspectj.lang.annotation.Aspect public class Aspect02 { //定义切点函数,实现切入表达式重用 @Pointcut(value = "execution(* Car.run())") //设置切入表达式 public void pointCut() { } /** * 前置通知 * * @param joinPoint */ @Before(value = "pointCut()") public void before(JoinPoint joinPoint) { System.out.println("前置通知"); } /** * 返回通知获取结果 * * @param joinPoint * @param res 参数名必须与returning的一致 */ @AfterReturning(value = "pointCut()", returning = "res") public void afterReturning(JoinPoint joinPoint, Object res) { System.out.println("返回结果为=" + res); } /** * 异常通知获取异常 * * @param joinPoint * @param throwable 参数名必须与throwing一致 */ @AfterThrowing(value = "pointCut()", throwing = "throwable") public void afterThrowing(JoinPoint joinPoint, Throwable throwable) { System.out.println("异常通知获取异常:" + throwable); } /** * 后置通知 * @param joinPoint */ @After(value = "pointCut()") public void after(JoinPoint joinPoint) { System.out.println("后置通知"); } }
3.beans06.xml
4.测试
package com.sxs.spring.aop.joinpoint; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * @author 孙显圣 * @version 1.0 */ public class test { public static void main(String[] args) { //获取针对父类的代理对象 ApplicationContext ioc = new ClassPathXmlApplicationContext("beans06.xml"); Car bean = ioc.getBean(Car.class); bean.run(); } }
9.AOP多切面优先级问题
解释
- 多个切面同时切一个方法
- 可以给切面类配置Order注解,数值越小,前置通知先执行
- 执行完前置通知之后执行目标方法
- 然后按照相反的顺序执行多个切面的返回通知、异常通知、最终通知
10.根据xml配置AOP
代码实例
1.UsbInterface.java
package com.sxs.spring.aop.xml; /** * @author 孙显圣 * @version 1.0 */ public interface UsbInterface { public void work(); }
2.Camera.java
package com.sxs.spring.aop.xml; /** * @author 孙显圣 * @version 1.0 */ public class Camera implements UsbInterface { @Override public void work() { System.out.println("相机正在工作。。。。。。"); } }
3.Aspect.java
package com.sxs.spring.aop.xml; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; import java.util.Arrays; /** * @author 孙显圣 * @version 1.0 */ public class Aspect { /** * @param joinPoint 保存了要切入的方法的信息 * @Before 前置通知 * execution(。。。) 切入表达式,表明要切入的方法,格式:访问修饰符+返回类型 全类名 方法名(参数类型) */ public void before(JoinPoint joinPoint) { //获取方法签名 Signature signature = joinPoint.getSignature(); System.out.println("方法执行开始-日志-方法名-" + signature.getName() + "-参数" + Arrays.asList(joinPoint.getArgs())); } /** * @param joinPoint 保存了要切入的方法的信息 * @AfterReturning 返回通知 */ public void afterReturning(JoinPoint joinPoint, Object res) { Signature signature = joinPoint.getSignature(); System.out.println("方法执行正常结束-日志-方法名-" + signature.getName() ); } /** * @param joinPoint * @AfterThrowing 异常通知 */ public void throwing(JoinPoint joinPoint, Throwable throwable) { Signature signature = joinPoint.getSignature(); System.out.println("方法出现异常-日志-方法名-" + signature.getName() ); } /** * @param joinPoint * @After 后置通知 */ public void after(JoinPoint joinPoint) { Signature signature = joinPoint.getSignature(); System.out.println("方法最终执行完毕-日志-方法名-" + signature.getName() ); }}
4.beans07.xml
5.测试
package com.sxs.spring.aop.xml; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * @author 孙显圣 * @version 1.0 */ public class Test { public static void main(String[] args) { ApplicationContext ioc = new ClassPathXmlApplicationContext("beans07.xml"); //获得针对接口的代理对象 UsbInterface bean = ioc.getBean(UsbInterface.class); bean.work(); } }
11.课后练习
1.注解实现
1.Cal.java
package com.sxs.spring.aop.homework02; /** * @author 孙显圣 * @version 1.0 */ public interface Cal { public void cal1(int n); public void cal2(int n); }
2.CalImpl.java
package com.sxs.spring.aop.homework02; import org.springframework.stereotype.Component; /** * @author 孙显圣 * @version 1.0 */ @Component public class CalImpl implements Cal{ /** * 计算1到n的和 * @param n */ @Override public void cal1(int n) { int sum = 0; for (int i = 1; i sum += i; } System.out.println("1到n的和=" + sum); } /** * 计算1乘到n * @param n */ @Override public void cal2(int n) { int accumulate = 1; for (int i = 1; i accumulate *= i; } System.out.println("1到n的积=" + accumulate); } } //切入表达式重用 @Pointcut(value = "execution(* CalImpl.*(..))") public void pointCut() { } //前置通知 @Before(value = "pointCut()") public void before(JoinPoint joinPoint) { //获取目前的毫秒数 long start = System.currentTimeMillis(); //获取函数名 String name = joinPoint.getSignature().getName(); System.out.println(name + "开始时间=" + start); } //返回通知 @AfterReturning(value = "pointCut()") public void afterReturning(JoinPoint joinPoint) { //获取目前的毫秒数 long end = System.currentTimeMillis(); //获取函数名 String name = joinPoint.getSignature().getName(); System.out.println(name + "结束=" + end); } } public static void main(String[] args) { ApplicationContext ioc = new ClassPathXmlApplicationContext("beans06.xml"); Cal bean = ioc.getBean(Cal.class); bean.cal1(5); System.out.println("==========================="); bean.cal2(5); } } public void cal1(int n); public void cal2(int n); } /** * 计算1到n的和 * @param n */ @Override public void cal1(int n) { int sum = 0; for (int i = 1; i sum += i; } System.out.println("1到n的和=" + sum); } /** * 计算1乘到n * @param n */ @Override public void cal2(int n) { int accumulate = 1; for (int i = 1; i accumulate *= i; } System.out.println("1到n的积=" + accumulate); } } //前置通知 public void before(JoinPoint joinPoint) { //获取目前的毫秒数 long start = System.currentTimeMillis(); //获取函数名 String name = joinPoint.getSignature().getName(); System.out.println(name + "开始时间=" + start); } //返回通知 public void afterReturning(JoinPoint joinPoint) { //获取目前的毫秒数 long end = System.currentTimeMillis(); //获取函数名 String name = joinPoint.getSignature().getName(); System.out.println(name + "结束=" + end); } } public static void main(String[] args) { ApplicationContext ioc = new ClassPathXmlApplicationContext("beans08.xml"); //获取针对接口的代理对象 Cal proxy = ioc.getBean("calImpl", Cal.class); proxy.cal1(100); System.out.println("========================="); proxy.cal2(5); } }