秦皇岛建设厅网站,鞍山58二手车,斗图在线制作生成器,沈阳有名的设计公司有哪些文章目录 #x1f497;AOP-官方文档#x1f35d;AOP 讲解#x1f35d;AOP APIs #x1f497;动态代理#x1f35d;初始动态代理#x1f35d;动态代理深入#x1f35d;AOP问题提出#x1f4d7;使用土方法解决#x1f4d7; 对土方法解耦-开发最简单的AOP类#x1f4d7;… 文章目录 AOP-官方文档AOP 讲解AOP APIs 动态代理初始动态代理动态代理深入AOP问题提出使用土方法解决 对土方法解耦-开发最简单的AOP类 土方法缺点 Spring AOP基本介绍AOP编程 快速入门注意事项和细节课后作业AOP-切入表达式注意事项和细节 AOP-JoinPoint返回通知获取结果异常通知获取异常信息环绕通知切入点表达式重用切面类执行顺序注意事项和细节 基于XML配置AOP课后作业 引出对Spring底层实现再思考创建maven项目思考一: 原生Spring如何实现依赖注入和(singleton, prototype)思考二: 原生Spring容器如何实现BeanPostProcessor思考三: 原生Spring容器如何实现AOP补充说明 Spring整体架构分析 上文中, 我们学习到了
Spring系列一spring的安装与使用 接下来我们学习, AOP切面编程 AOP-官方文档
AOP 讲解 AOP 讲解: spring-framework-5.3.8\docs\reference\html/index.html
AOP APIs **AOP APIs:** spring-framework-5.3.8\docs\javadoc-api\index.html  
动态代理
初始动态代理
需求说明 1.由Vehicle (交通工具接口, 有一个run方法), 下面有两个类 Car 和 Ship 2.当运行Car对象的 run 方法 和 ship对象的 run 方法时, 输入如下内容 交通工具开始运行了… 轮船在海上航行… 交通工具停止运行了… 交通工具开始运行了… 小汽车在路上跑… 交通工具停止运行了… 解决方案一: 传统方案
//接口, 该接口有run方法
public interface Vehicle {void run();
}public class Car implements Vehicle{Overridepublic void run() {System.out.println(交通工具开始运行了....);System.out.println(小汽车在路上 running....);System.out.println(交通工具停止运行了....);}
}public class Ship implements Vehicle{Overridepublic void run() {System.out.println(交通工具开始运行了....);System.out.println(大轮船在路上 running....);System.out.println(交通工具停止运行了....);}
}public class TestVehicle {Testpublic void run() {//OOP基础java基础Vehicle vehicle new Ship();//动态绑定vehicle.run();}
}来思考一下, 这个解决方案好吗? 代码冗余, 其实就是单个对象的调用, 并没有很好的解决. 解决方案二: 动态代理方式 动态代理解决思路: 在调用方法时, 使用反射机制, 根据方法去决定调用哪个对象方法
public class VehicleProxyProvider {//定义一个属性//target_vehicle 表示真正要执行的对象//该对象实现了Vehicle接口private Vehicle target_vehicle;//构造器public VehicleProxyProvider(Vehicle target_vehicle) {this.target_vehicle target_vehicle;}//编写一个方法, 可以返回一个代理对象//解读//1.这个方法非常重要, 理解有一定难度//2.public Vehicle getProxy() {//得到类加载器ClassLoader classLoader target_vehicle.getClass().getClassLoader();//得到要代理的对象/被执行对象 的接口信息, 底层是通过接口来完成调用Class?[] interfaces target_vehicle.getClass().getInterfaces();//创建InvocationHandler 对象//因为 InvocationHandler 是接口, 所以我们可以通过匿名对象的方式来创建该对象/*** public interface InvocationHandler {* public Object invoke(Object proxy, Method method, Object[] args)* throws Throwable;* }* invoke 方法是将来执行target_vehicle的方法时, 会调用到*/InvocationHandler invocationHandler new InvocationHandler() {/*class VehicleProxyProvider$01 implements InvocationHandler {Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println(交通工具开始运行了....);//这里是我们的反射基础 OOPObject result method.invoke(target_vehicle, args);System.out.println(交通工具停止运行了....);return result;}}InvocationHandler invocationHandler new VehicleProxyProvider$01();*//*** invoke 方法是将来执行我们的target_vehicle的方法时, 会调用到** param proxy 表示代理对象* param method 就是通过代理对象调用方法时, 的那个方法 代理对象.run()* param args 表示调用 代理对象.run(xx) 传入的参数* return 表示 代理对象.run(xx) 执行后的结果.* throws Throwable*/Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println(交通工具开始运行了....);//这里是我们的反射基础 OOP//method 是 public abstract void com.zzw.spring.aop.proxy.Vehicle.run()//target_vehicle 是 Ship对象//args 是 null//这里通过反射动态绑定机制, 就会执行到被代理对象的方法//执行完毕就返回Object result method.invoke(target_vehicle, args);System.out.println(交通工具停止运行了....);return result;}};/*public static Object newProxyInstance(ClassLoader loader,Class?[] interfaces,InvocationHandler h)解读1.Proxy.newProxyInstance() 可以返回一个代理对象2.ClassLoader loader: 类加载器,3.Class?[] interfaces 就是将来要代理的对象的接口信息4.InvocationHandler h 调用处理器/对象, 有一个非常重要的方法invoke*/Vehicle proxy (Vehicle) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);return proxy;}
}public class TestVehicle {Testpublic void proxyRun() {//创建Ship对象Vehicle vehicle new Ship();//创建VehicleProxyProvider对象, 并且我们传入的要代理的对象VehicleProxyProvider vehicleProxyProvider new VehicleProxyProvider(new Car());//获取代理对象, 该对象可以代理执行方法//解读//1.proxy 编译类型Vehicle,//2.运行类型 是代理类型, 即 class com.sun.proxy.$Proxy8Vehicle proxy vehicleProxyProvider.getProxy();System.out.println(proxy的编译类型是 Vehicle);System.out.println(proxy的运行类型是 proxy.getClass());//下面解读/debug怎么执行到 代理对象的 public Object invoke(Object proxy, Method method, Object[] args)//梳理完毕, proxy的编译类型是Vehicle, 运行类型是Proxy class com.sun.proxy.$Proxy8//所以当执行run方法时, 会执行到 代理对象的invoke//如果体现动态 [1.被代理的对象 2.方法]//proxy.run();String result proxy.fly(10000);System.out.println(result result);System.out.println(ok);}debug
动态代理深入
需求说明 1.有一个SmartAnimal 接口, 可以完成简单的加减法, 要求在执行 getSum() 和 getSub() 时, 输出执行前, 执行过程, 执行后的日志结果. 输出内容如下: 日志-方法名-getSum-参数 1.5 4.5 方法内部打印result 6.0 日志-方法名-getSum-结果result 6.0 日志-方法名-getSub-参数 1.4 3.3 方法内部打印result -1.9 日志-方法名-getSub-结果result -1.9 解决方案一: 传统方案
public interface SmartAnimalAble {//求和float getSum(float i, float j);//求差float getSub(float i, float j);
}public class SmartCat implements SmartAnimalAble {Overridepublic float getSum(float i, float j) {System.out.println(日志-方法名-getSum-参数 i j);float result i j;System.out.println(方法内部打印result result);System.out.println(日志-方法名-getSum-结果result (i j));return result;}Overridepublic float getSub(float i, float j) {System.out.println(日志-方法名-getSub-参数 i j);float result i - j;System.out.println(方法内部打印result result);System.out.println(日志-方法名-getSub-结果result (i - j));return result;}
}public class AopTest {Testpublic void run() {SmartAnimalAble smartAnimalAble new SmartCat();smartAnimalAble.getSum(1.5f, 4.5f);System.out.println();smartAnimalAble.getSub(1.4f, 3.3f);}
}解决方案二: 动态代理方式 考虑代理对象调用方法(底层是反射调用)时, 可能出现的异常- [横切关注点]
//可以返回一个动态代理对象, 可以执行SmartCat对象的方法
public class MyProxyProvider {//这是一个属性, 是我们要执行的目标对象//该对象实现了SmartAnimal接口private SmartAnimalAble target_obj;//构造器MyProxyProvider(SmartAnimalAble target_obj) {this.target_obj target_obj;}//编写一个方法, 可以返回一个代理对象//该代理对象可以执行目标对象public SmartAnimalAble getProxy() {//1.得到类加载器ClassLoader classLoader target_obj.getClass().getClassLoader();//2.得到要执行的目标对象的接口信息Class?[] interfaces target_obj.getClass().getInterfaces();//3.创建InvocationHandler 对象InvocationHandler invocationHandler new InvocationHandler() {Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {String name method.getName();//方法名Object result null;try {System.out.println(方法执行前-日志-方法名- name -参数 Arrays.asList(args));//这里从aop的角度看,就是一个横切关注点-前置通知//使用反射调用方法result method.invoke(target_obj, args);System.out.println(方法执行正常结束-日志-方法名- name -结果result result);//这里从aop的角度看, 也是一个横切关注点-返回通知return result;} catch (Exception e) {e.printStackTrace();//如果反射执行方法时, 出现异常, 就会进入到catch{}System.out.println(方法执行异常-日志-方法名- name -异常类型 e.getClass().getName());//这里从aop的角度看, 又是一个横切关注点-异常通知} finally {//不管你是否出现了异常, 最终都会执行到 finally {}//这里从aop的角度看, 还是一个横切关注点-最终通知System.out.println(方法最终结束-日志-方法名- name);}return result;}};//创建代理对象SmartAnimalAble proxy (SmartAnimalAble) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);return proxy;}
}public class AopTest {Testpublic void smartCatTestProxy() {//创建SmartCat对象SmartAnimalAble smartAnimalAble new SmartCat();MyProxyProvider myProxyProvider new MyProxyProvider(smartAnimalAble);//获取代理对象, 该对象可以代理执行方法SmartAnimalAble proxy myProxyProvider.getProxy();System.out.println(proxy的编译类型是 SmartAnimalAble);System.out.println(proxy的运行类型是 proxy.getClass());//proxy的编译类型是SmartAnimal, 运行类型是 Class com.sun.proxy.$Proxy8//所以当执行getSum方法时, 会执行到 代理对象的invokeproxy.getSum(1.2f, 2.4f);System.out.println();proxy.getSub(1.3f, 4.5f);System.out.println(ok);}
}AOP问题提出
在MyProxyProvider.java中, 我们的输出语句功能比较弱, 在实际开发中, 我们希望是以一个方法的形式, 嵌入到真正执行的目标方法前.
如图分析
使用土方法解决
1). 需求分析 使用土方法解决前面的问题, 后面使用Spring的AOP组件完成
2). 先建一个包, 把相关文件拷贝过来, 进行修改完成.
//我们的一个方法, 在目标对象执行前执行
public void before(Method method, Object[] args) {System.out.println(before方法执行前-日志-方法名- method.getName() -参数 Arrays.asList(args));//这里从aop的角度看,就是一个横切关注点-前置通知
}//我们的一个方法, 在目标对象执行后执行
public void after(Method method, Object result) {System.out.println(after方法执行正常结束-日志-方法名- method.getName() -结果result result);//这里从aop的角度看, 也是一个横切关注点-返回通知
}
Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {String name method.getName();//方法名Object result null;before(method, args);//使用反射调用方法result method.invoke(target_obj, args);after(method, result);return result;
} 3). 该方法问题分析: 耦合度高 对土方法解耦-开发最简单的AOP类
public class ZzwAOP {//我们的一个方法, 在目标对象执行前执行public static void before(Method method, Object[] args) {System.out.println(ZzwHsp-方法执行前-日志-方法名- method.getName() -参数 Arrays.asList(args));//这里从aop的角度看,就是一个横切关注点-前置通知}//我们的一个方法, 在目标对象执行后执行public static void after(Method method, Object result) {System.out.println(ZzwHsp-方法执行正常结束-日志-方法名- method.getName() -结果result result);//这里从aop的角度看, 也是一个横切关注点-返回通知}
}Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {String name method.getName();//方法名Object result null;try {//before(method, args);ZzwAOP.before(method, args);//使用反射调用方法result method.invoke(target_obj, args);//after(method, result);ZzwAOP.after(method, result);return result;} catch (Exception e) {}
}土方法缺点
土方法 不够灵活; 土方法 复用性差; 土方法 是一种硬编码 (因为没有注解和反射支撑)
Spring AOP 闪亮登场 - 底层是ASPECTJ
Spring AOP
基本介绍
什么是AOP AOP的全称(aspect oriented programming), 面向切面编程.
第一张图
AOP实现方式 1.基于动态代理的方式 [内置aop实现] 2.使用框架aspectj来实现
AOP编程 快速入门
●说明 1.需要引入核心的aspect包 2.在切面类中声明通知方法 1)前置通知: Before 2)返回通知: AfterReturning 3)异常通知: AfterThrowing 4)后置通知: After 最终通知 5)环绕通知: Around
●需求说明 我们使用aop编程的方式, 来实现手写的动态代理案例效果, 以上一个案例为例进行讲解.
1.导入AOP编程需要的包
2.创建包spring/aop/aspect
package com.zzw.spring.aop.aspectj;public interface SmartAnimalAble {//求和float getSum(float i, float j);//求差float getSub(float i, float j);
}易错点: 不要引入别的包下的SmartAnimalAble, 要引入同包下的SmartAnimalAble
Component //使用Component 当spring容器启动时, 将 SmartDog 注入到容器
public class SmartDog implements SmartAnimalAble {Overridepublic float getSum(float i, float j) {float result i j;System.out.println(方法内部打印result result);return result;}Overridepublic float getSub(float i, float j) {float result i - j;System.out.println(方法内部打印result result);return result;}
}切面类, 类似于我们前面自己写的MyProxyProvider, 但是功能强大很多
Aspect //表示是一个切面类[底层切面编程的支撑(动态代理反射动态绑定)]
Component //会将SmartAnimalAspect注入到容器
public class SmartAnimalAspect {//希望将showBeginLog方法切入到SmartDog-getSum前执行-前置通知/*** 解读* 1. Before 表示是前置通知, 即在我们的目标对象执行方法前执行* 2. value execution(public float com.zzw.spring.aop.aspectj.SmartDog.getSum(float, float))* 指定切入到哪个类的哪个方法 形式是: 访问修饰符 返回类型 全类名.方法名(形参列表)* 3. showBeginLog方法可以理解成就是一个切入方法, 这个方法名是可以由程序员指定的 比如:showBeginLog* 4. JoinPoint joinPoint 在底层执行时, 由AspectJ切面编程框架, 会给该切入方法传入 joinPoint对象* , 通过该方法, 程序员可以获取到 相关信息* param joinPoint*/Before(value execution(public float com.zzw.spring.aop.aspectj.SmartDog.getSum(float, float)))public void showBeginLog(JoinPoint joinPoint) {//通过连接点对象joinPoint, 可以获取方法签名Signature signature joinPoint.getSignature();System.out.println(切面类showBeginLog()-方法执行前-日志-方法名- signature.getName() -参数 Arrays.asList(joinPoint.getArgs()));}//返回通知: 即把showSuccessEndLog方法切入到目标对象方法正常执行完毕后的地方AfterReturning(value execution(public float com.zzw.spring.aop.aspectj.SmartDog.getSum(float, float)))public void showSuccessEndLog(JoinPoint joinPoint, Object res) {Signature signature joinPoint.getSignature();System.out.println(切面类showSuccessEndLog()-方法执行正常结束-日志-方法名- signature.getName());}//异常通知: 即把showExceptionLog方法切入到目标对象方法执行发生异常后的catch{}AfterThrowing(value execution(public float com.zzw.spring.aop.aspectj.SmartDog.getSum(float, float)))public void showExceptionLog(JoinPoint joinPoint) {Signature signature joinPoint.getSignature();System.out.println(切面类showExceptionLog()-方法执行异常-日志-方法名- signature.getName());}//最终通知: 即把showFinallyEndLog方法切入到目标方法执行后, 不管是否发生异常都要执行, finally{}After(value execution(public float com.zzw.spring.aop.aspectj.SmartDog.getSum(float, float)))public void showFinallyEndLog(JoinPoint joinPoint) {Signature signature joinPoint.getSignature();System.out.println(切面类showFinallyEndLog()-方法最终执行完毕-日志-方法名- signature.getName());}
}新建beans08.xml
?xml version1.0 encodingUTF-8?
beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexmlns:contexthttp://www.springframework.org/schema/contextxsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsdcontext:component-scan base-packagecom.zzw.spring.aop.aspectj/!--开启基于注解的AOP功能--aop:aspectj-autoproxy/
/beans测试
public class AopAspectjTest {Testpublic void smartDogTestByProxy() {ApplicationContext ioc new ClassPathXmlApplicationContext(beans08.xml);//这里我们需要通过接口类型来获取到注入的SmartDog对象-就是代理对象SmartAnimalAble smartAnimalAble ioc.getBean(SmartAnimalAble.class);smartAnimalAble.getSum(1, 2);//System.out.println(smartAnimalAble运行类型是 smartAnimalAble.getClass());//class com.sun.proxy.$Proxy16}
}注意事项和细节
1.关于切面类方法命名可以自己规范一下, 比如showBeginLog(), showSuccessEndLog(), showExceptionLog(), showFinallyEndLog()
2.切入表达式的更多配置, 比如使用模糊配置 Before(value“execution(* com.zzw.aop.proxy.SmartDog.*(…))”)
第一个\*表示: 任意修饰符和返回类型 第二个\*表示: 任意方法名 ..表示: 任意形参列表 3.表示所有访问权限, 所有包下的所有类的所有方法, 都会被执行该前置通知方法 Before(value“execution(* *.*(…))”)
4.当spring容器开启了 基于注解的AOP功能 aop:aspectj-autoproxy/, 我们获取注入的对象, 需要以接口的类型来获取, 因为你注入的对象.getClass() 已经是代理类型了
5.当spring容器开启了 基于注解的AOP功能 aop:aspectj-autoproxy/, 我们获取注入的对象, 也可以通过id来获取, 但是也要转成接口类型.
//这里我们需要通过接口类型来获取到注入的SmartDog对象-就是代理对象
SmartAnimalAble smartAnimalAble ioc.getBean(SmartAnimalAble.class);//SmartAnimalAble smartAnimalAble (SmartAnimalAble) ioc.getBean(smartDog);课后作业
1.有接口 UsbInterface (方法 work) 2.实现子类 Phone 和 Camera 实现 UsbInterface 3.请在SmartAnimalAspect 切面类, 写一个方法(可输出日志信息作为前置通知, 在Phone和Camera对象执行work方法前调用 4.其它如返回通知, 异常通知, 后置通知, 也可以加入. 新建包 com.zzw.spring.aop.homework;
接口
public interface UsbInterface {void work(String name);
}实现类
Component //将Phone当作一个组件注入到容器中
public class Phone implements UsbInterface{Overridepublic void work(String name) {System.out.println(name 手机正在工作中....);}
}Component //将Camera对象注入到Spring容器
public class Camera implements UsbInterface{Overridepublic void work(String name) {System.out.println(name 相机正在工作中....);}
}切面类
Aspect
Component
public class SmartAnimalAspect {//希望将showBeginLog切入到Phone/Camera-work() 前执行//前置通知//切入表达式也可以指向接口的方法, 这时切入表达式会对实现了接口的类/对象生效//比如下面我们是对UsbInterface切入, 那么对实现类 Phone/Camera 都生效//Before(value execution(public void *.work(String)))Before(value execution(public void com.zzw.spring.aop.homework.UsbInterface.work(String)))public void showBeginLog(JoinPoint joinPoint) {//通过连接点对象, 可以获取方法签名Signature signature joinPoint.getSignature();System.out.println(切面类showBeginLog()-方法执行前-日志-方法名- signature.getName() -参数 Arrays.asList(joinPoint.getArgs()));}//返回通知AfterReturning(value execution(public void UsbInterface.work(String)))//AfterReturning(value execution(public void com.zzw.spring.aop.homework.*.work(String)))public void showSuccessEndLog(JoinPoint joinPoint) {//通过连接点对象, 可以获取方法名Signature signature joinPoint.getSignature();System.out.println(切面类showSuccessEndLog()-方法执行正常结束-日志-方法名- signature.getName());}//异常通知//AfterThrowing(value execution(public void *.work(String)))AfterThrowing(value execution(public void UsbInterface.work(String)))public void showExceptionLog(JoinPoint joinPoint) {//通过连接点对象, 可以获取方法名Signature signature joinPoint.getSignature();System.out.println(切面类showExceptionLog()-方法执行异常-日志-方法名- signature.getName());}//后置通知//After(value execution(public void *.work(String)))After(value execution(public void UsbInterface.work(String)))public void showFinallyEndLog(JoinPoint joinPoint) {//通过连接点对象, 可以获取方法名Signature signature joinPoint.getSignature();System.out.println(切面类showFinallyEndLog()-方法最终执行完毕-日志-方法名- signature.getName());}
}beans09.xml
?xml version1.0 encodingUTF-8?
beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexmlns:contexthttp://www.springframework.org/schema/contextxmlns:aophttp://www.springframework.org/schema/aopxsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsdcontext:component-scan base-packagecom.zzw.spring.aop.homework/!--开启基于注解的AOP功能--aop:aspectj-autoproxy/
/beans测试类
public class AspAspectjTest {public static void main(String[] args) {ApplicationContext ioc new ClassPathXmlApplicationContext(beans09.xml);UsbInterface phone (UsbInterface) ioc.getBean(phone);phone.work(华为);System.out.println();UsbInterface camera (UsbInterface) ioc.getBean(camera);camera.work(索尼);//System.out.println(phone的运行类型是 phone.getClass());}
}AOP-切入表达式
切入点表达式
1.作用 通过表达式的方式定位一个或多个具体的连接点
2.语法细节 ①切入点表达式的语法格式 execution([权限修饰符] [返回值类型] [简单类名/全类名] [方法名] (参数列表)) ②举例说明
表达式execution(* com.zzw.spring.aop.aspectj.SmartDog.*(..))含义如果SmartAnimalAble是接口, 则表示 接口/类 中声明的所有方法.第一个 * 代表任意修饰符及任意返回值第二个 * 表示任意方法.. 匹配任意数量, 任意类型的参数 (规定.的数量是2个)若目标类, 接口与该切面类在同一个包中可以省略包名
表达式execution(public * SmartDog.*(..))含义SmartDog 接口/类 中的所有公有方法
表达式execution(public double SmartDog.*(..))含义SmartDog 接口/类 中返回double类型数值的方法
表达式execution(public double SmartDog.*(double, ..))含义第一个参数为double类型的方法...匹配任意数量, 任意类型的参数
表达式execution(public double SmartDog.*(double, double))含义参数类型为double, double类型的方法
表达式execution(public double SmartDog.*(double, double))含义参数类型为double, double类型的方法 ③在AspectJ中, 切入点表达式可以通过, ||, ! 等操作符结合起来
表达式execution(* *.add(int, ..)) || execution(* *.sub(int, ..))含义任意类中第一个参数为int类型的add方法或sub方法
注意事项和细节
1.切入表达式也可以指向类的方法, 这时切入表达式会对该类/对象生效 2.切入表达式也可以指向接口的方法, 这时切入表达式会对实现了接口的类/对象生效 3.切入表达式也可以对没有实现接口的类, 进行切入
Component //把Car视为一个组件[对象], 注入到Spring容器
public class Car {public void run() {System.out.println(小汽车 run...);}
}//切面类
Aspect //表示是一个切面类
Component //会将SmartAnimalAspect注入到容器
public class SmartAnimalAspect {//给Car配置一个前置通知Before(value execution(public void Car.run()))public void ok1(JoinPoint joinPoint) {Signature signature joinPoint.getSignature();System.out.println(切面类的ok1()-执行的目标方法- signature.getName());}
}测试
public class AopAspectjTest {Testpublic void test3() {ApplicationContext ioc new ClassPathXmlApplicationContext(beans08.xml);Car car ioc.getBean(Car.class);//说明: car对象仍然是代理对象System.out.println(car的运行类型 car.getClass());//car的运行类型class com.zzw.spring.aop.aspectj.Car$$EnhancerBySpringCGLIB$$5e9a8b7acar.run();}
}4.补充: 动态代理jdk的Proxy和Spring的CGlib
AOP-JoinPoint
1.通过JoinPoint可以获取到调用方法的签名 2.其他常用方法
Aspect //表示是一个切面类
Component //会将SmartAnimalAspect注入到容器
public class SmartAnimalAspect {//给Car配置一个最终通知After(value execution(public void Car.run()))public void ok4(JoinPoint joinPoint) {//演示joinPoint常用的方法joinPoint.getSignature().getName();//获取目标方法名joinPoint.getSignature().getDeclaringType().getSimpleName();//获取目标方法所属类的简单类名joinPoint.getSignature().getDeclaringTypeName();//获取目标方法所属类的类名joinPoint.getSignature().getModifiers();//获取目标方法声明类型(public, private, protected)Object[] args joinPoint.getArgs();//获取传入目标方法的参数, 返回一个数组joinPoint.getTarget();//获取被代理的对象joinPoint.getThis();//获取代理对象自己 }
}返回通知获取结果
切面类
Aspect //表示是一个切面类[底层切面编程的支撑(动态代理反射动态绑定)]
Component //会将SmartAnimalAspect注入到容器
public class SmartAnimalAspect {//返回通知: 即把showSuccessEndLog方法切入到目标对象方法正常执行完毕后的地方//解读//1.如果我们希望把目标方法, 执行的结果, 返回给切入方法//2.可以在 AfterReturning 增加属性, 比如 returning res//3.同时在切入方法增加 Object res//4.注意: returning res 和 Object res 的 res名字一样AfterReturning(value execution(public float com.zzw.spring.aop.aspectj.SmartDog.getSum(float, float)), returning res)public void showSuccessEndLog(JoinPoint joinPoint, Object res) {Signature signature joinPoint.getSignature();System.out.println(切面类showSuccessEndLog()-方法执行正常结束-日志-方法名- signature.getName() 返回的结果是 res);}
}异常通知获取异常信息
如何在异常通知方法中获取异常信息
Component //使用Component 当spring容器启动时, 将 SmartDog 注入到容器
public class SmartDog implements SmartAnimalAble {Overridepublic float getSum(float i, float j) {float result i j;int res 1 / 0;//模拟一个算数异常System.out.println(方法内部打印result result);return result;}
}切面类
Aspect //表示是一个切面类[底层切面编程的支撑(动态代理反射动态绑定)]
Component //会将SmartAnimalAspect注入到容器
public class SmartAnimalAspect {//异常通知: 即把showExceptionLog方法切入到目标对象方法执行发生异常后的catch{}AfterThrowing(value execution(public float com.zzw.spring.aop.aspectj.SmartDog.getSum(float, float)), throwing throwable)public void showExceptionLog(JoinPoint joinPoint, Throwable throwable) {Signature signature joinPoint.getSignature();System.out.println(切面类showExceptionLog()-方法执行异常-日志-方法名- signature.getName() 异常信息 throwable);}
}环绕通知
需求: 如果使用环绕通知完成其它四个通知的功能.
新建SmartAnimalAspect2切面类, 并把SmartAnimalAspect切面类注解注释, 避免干扰; 去掉SmartDog的异常代码.
//切面类
Aspect //表示是一个切面类[底层切面编程的支撑(动态代理反射动态绑定)]
Component //会将SmartAnimalAspect注入到容器
public class SmartAnimalAspect2 {//演示环绕通知的使用//1. Around 表示这是一个环绕通知[可以完成其它四个通知的功能]//2. (value execution(public float getSum(float, float))) 切入点表达式//3. doAround 表示要切入的方法 - 调用的基本结构 try-catch-finallyAround(value execution(public float com.zzw.spring.aop.aspectj.SmartDog.getSum(float, float)))public Object doAround(ProceedingJoinPoint joinPoint) {Object result null;String methodName joinPoint.getSignature().getName();try {//1.相当于前置通知完成的事情Object[] args joinPoint.getArgs();ListObject argList Arrays.asList(args);System.out.println(AOP环绕通知[前置通知]-- methodName 方法开始了--参数有: argList);//在环绕通知中一定要调用joinPoint.proceed()来执行目标方法result joinPoint.proceed();//2.相当于返回通知完成的事情System.out.println(AOP环绕通知[返回通知]-- methodName 方法结束了--结果是: result);} catch (Throwable throwable) {//3.相当于异常通知完成的事情System.out.println(AOP环绕通知[异常通知]-- methodName 方法抛出异常--异常对象: throwable);} finally {//相当于最终通知完成的事情System.out.println(AOP环绕通知[最终通知]-- methodName 方法最终结束了...);}return result;}
}测试
public class AopAspectjTest {Testpublic void testDoAround() {//获取Spring容器ApplicationContext ioc new ClassPathXmlApplicationContext(beans08.xml);SmartAnimalAble smartAnimalAble ioc.getBean(SmartAnimalAble.class);smartAnimalAble.getSum(10, 2);}
}
结果
Connected to the target VM, address: 127.0.0.1:60160, transport: socket
AOP环绕通知[前置通知]--getSum方法开始了--参数有: [10.0, 2.0]
方法内部打印result 12.0
AOP环绕通知[返回通知]--getSum方法结束了--结果是: 12.0
AOP环绕通知[最终通知]--getSum方法最终结束了...切入点表达式重用
为了统一管理切入点表达式, 我们可以使用切入点表达式重用技术
对SmartAnimalAspect.java稍作修改
//切面类, 类似于我们前面自己写的MyProxyProvider, 但是功能强大很多
Aspect //表示是一个切面类[底层切面编程的支撑(动态代理反射动态绑定)]
Component //会将SmartAnimalAspect注入到容器
public class SmartAnimalAspect {//定义一个切入点, 在后面使用时可以直接引用, 提高了复用性Pointcut(value execution(public float com.zzw.spring.aop.aspectj.SmartDog.getSum(float, float)))public void myPointCut() {}//Before(value execution(public float com.zzw.spring.aop.aspectj.SmartDog.getSum(float, float)))//这里我们使用定义好的切入点Before(value myPointCut())public void showBeginLog(JoinPoint joinPoint) {//通过连接点对象joinPoint, 可以获取方法签名Signature signature joinPoint.getSignature();System.out.println(切面类showBeginLog()[使用的myPointCut()]-方法执行前-日志-方法名- signature.getName() -参数 Arrays.asList(joinPoint.getArgs()));}//返回通知: 即把showSuccessEndLog方法切入到目标对象方法正常执行完毕后的地方//AfterReturning(value execution(public float com.zzw.spring.aop.aspectj.SmartDog.getSum(float, float)), returning res)//使用切入点AfterReturning(value myPointCut(), returning res)public void showSuccessEndLog(JoinPoint joinPoint, Object res) {Signature signature joinPoint.getSignature();System.out.println(切面类showSuccessEndLog()-方法执行正常结束-日志-方法名- signature.getName() 返回的结果是 res);}//异常通知: 即把showExceptionLog方法切入到目标对象方法执行发生异常后的catch{}//AfterThrowing(value execution(public float com.zzw.spring.aop.aspectj.SmartDog.getSum(float, float)), throwing throwable)//直接使用切入点表达式AfterThrowing(value myPointCut(), throwing throwable)public void showExceptionLog(JoinPoint joinPoint, Throwable throwable) {Signature signature joinPoint.getSignature();System.out.println(切面类showExceptionLog()-方法执行异常-日志-方法名- signature.getName() 异常信息 throwable);}//最终通知: 即把showFinallyEndLog方法切入到目标方法执行后, 不管是否发生异常都要执行, finally{}//After(value execution(public float com.zzw.spring.aop.aspectj.SmartDog.getSum(float, float)))//直接使用切入点表达式After(value myPointCut())public void showFinallyEndLog(JoinPoint joinPoint) {Signature signature joinPoint.getSignature();System.out.println(切面类showFinallyEndLog()-方法最终执行完毕-日志-方法名- signature.getName());}
}切面类执行顺序
如果同一个方法, 有多个切面在同一个切入点切入, 那么执行的优先级如何控制
●基本语法 import org.springframework.core.annotation.Order; 通过order(valuen) 来控制. n值越小, 优先级越高 ●代码 新建SmartAnimalAspect3.java, 将SmartAnimalAspect2.java注销, 保留SmartAnimalAspect.java
1.如果这两个类不加order注解, 那么执行结果如下
SmartAnimalAspect-切面类showBeginLog()[使用的myPointCut()]-方法执行前-日志-方法名-getSum-参数 [1.0, 2.0]
SmartAnimalAspect3-切面类showBeginLog()-方法执行前-日志-方法名-getSum-参数 [1.0, 2.0]
方法内部打印result 3.0
SmartAnimalAspect3-切面类showSuccessEndLog()-方法执行正常结束-日志-方法名-getSum 返回的结果是3.0
SmartAnimalAspect3-切面类showFinallyEndLog()-方法最终执行完毕-日志-方法名-getSum
SmartAnimalAspect-切面类showSuccessEndLog()-方法执行正常结束-日志-方法名-getSum 返回的结果是3.0
SmartAnimalAspect-切面类showFinallyEndLog()-方法最终执行完毕-日志-方法名-getSum如果两个类加了order注解
Order(value 2)//表示该切面类执行的顺序, value的值越小, 优先级越高
Aspect
Component
public class SmartAnimalAspect {//内容省略
}Order(value 1)//表示该切面类执行的顺序, value的值越小, 优先级越高
Aspect
Component
public class SmartAnimalAspect3 {//内容省略
}那么执行结果如下
SmartAnimalAspect3-切面类showBeginLog()-方法执行前-日志-方法名-getSum-参数 [1.0, 2.0]
SmartAnimalAspect-切面类showBeginLog()[使用的myPointCut()]-方法执行前-日志-方法名-getSum-参数 [1.0, 2.0]
方法内部打印result 3.0
SmartAnimalAspect-切面类showSuccessEndLog()-方法执行正常结束-日志-方法名-getSum 返回的结果是3.0
SmartAnimalAspect-切面类showFinallyEndLog()-方法最终执行完毕-日志-方法名-getSum
SmartAnimalAspect3-切面类showSuccessEndLog()-方法执行正常结束-日志-方法名-getSum 返回的结果是3.0
SmartAnimalAspect3-切面类showFinallyEndLog()-方法最终执行完毕-日志-方法名-getSum注意事项和细节
不能理解成: 优先级高的每个消息通知都先执行. 这个方法调用机制和Filter过滤器链式调用类似 方法调用机制如下图所示
基于XML配置AOP
基本说明 前面我们是通过注解来配置aop的, 在spring中, 我们也可以通过xml的方式来配置AOP.
将SmartAnimalAble.java ,SmartDog.java 拷贝至 spring/aop/xml包下 注意: 不要引错包
切面类
//这是我们开发一个切面类, 但是不用注解, 而是使用xml配置
public class SmartAnimalAspect {public void showBeginLog(JoinPoint joinPoint) {//通过连接点对象joinPoint, 可以获取方法签名Signature signature joinPoint.getSignature();System.out.println(SmartAnimalAspect[XML配置]-切面类showBeginLog()-方法执行前-日志-方法名- signature.getName() -参数 Arrays.asList(joinPoint.getArgs()));}public void showSuccessEndLog(JoinPoint joinPoint, Object res) {Signature signature joinPoint.getSignature();System.out.println(SmartAnimalAspect[XML配置]-切面类showSuccessEndLog()-方法执行正常结束-日志-方法名- signature.getName() 返回的结果是 res);}public void showExceptionLog(JoinPoint joinPoint, Throwable throwable) {Signature signature joinPoint.getSignature();System.out.println(SmartAnimalAspect[XML配置]-切面类showExceptionLog()-方法执行异常-日志-方法名- signature.getName() 异常信息 throwable);}public void showFinallyEndLog(JoinPoint joinPoint) {Signature signature joinPoint.getSignature();System.out.println(SmartAnimalAspect[XML配置]-切面类showFinallyEndLog()-方法最终执行完毕-日志-方法名- signature.getName());}
}beans09_2.xml
?xml version1.0 encodingUTF-8?
beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexmlns:aophttp://www.springframework.org/schema/aopxsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd!--使用XML配置, 完成AOP编程--!--配置一个切面类对象-bean--bean classcom.zzw.spring.aop.xml.SmartAnimalAspect idsmartAnimalAspect/!--配置一个SmartDog对象-bean--bean classcom.zzw.spring.aop.xml.SmartDog idsmartDog/!--配置切面类, 细节: 一定要引入 xmlns:aop--aop:config!--配置切入点表达式--aop:pointcut idmyPointCut expressionexecution(public float com.zzw.spring.aop.xml.SmartDog.getSum(float, float))/!--配置切面的 前置,返回,异常,最终通知--aop:aspect refsmartAnimalAspect order10!--配置前置通知--aop:before methodshowBeginLog pointcut-refmyPointCut/!--配置返回通知--aop:after-returning methodshowSuccessEndLog pointcut-refmyPointCut returningres/!--配置异常通知--aop:after-throwing methodshowExceptionLog pointcut-refmyPointCut throwingthrowable/!--配置最终通知--aop:after methodshowFinallyEndLog pointcut-refmyPointCut/!--配置环绕通知--!--aop:around method--/aop:aspect/aop:config
/beans测试
public class AopAspectjXMLTest {Testpublic void testAspectByXML() {ApplicationContext ioc new ClassPathXmlApplicationContext(beans09_2.xml);SmartAnimalAble smartAnimalAble ioc.getBean(SmartAnimalAble.class);smartAnimalAble.getSum(1, 2);}
}课后作业
1.请编写一个Cal接口 (1)方法 cal1(int n) 计算1 2 ... n (2)方法 cal2(int n) 计算 1 * 2 *...* n
2.实现类 MyCal, 实现Cal的方法
3.请分别使用 注解方式 / XML配置方式 完成AOP编程 (1) 在执行cal1 前打印开始执行的时间, 执行完后打印结束的时间 (2) 在执行cal2 前打印开始执行的时间, 执行完后打印结束的时间 ❶基于注解
接口
public interface Cal {//计算12...nint cal1(int n);//计算1*2*...*nint cal2(int n);
}实现类
Component
public class MyCal implements Cal{Overridepublic int cal1(int n) {int sum 0;for (int i 1; i n; i) {sum i;}return sum;}Overridepublic int cal2(int n) {int res 1;for (int i 1; i n; i) {res * n;}return res;}
}切面类
Aspect //CalAspect是一个切面类
Component //CalAspect作为对象, 注入到Spring容器
public class CalAspect {//注意: 如果目标类和切面类, 在同一个包, 可以省略包名//因为cal1和cal2方法, 都要去输出开始执行时间, 因此使用 MyCal.* 通配符Pointcut(value execution(public int MyCal.*(int)))public void myPointCut() {}//前置通知Before(value myPointCut())public void calStart(JoinPoint joinPoint) {Signature signature joinPoint.getSignature();System.out.println(signature.getName() 执行, 开始执行时间 System.currentTimeMillis());}//返回通知AfterReturning(value myPointCut(), returning res)public void calEnd(JoinPoint joinPoint, Object res) {Signature signature joinPoint.getSignature();System.out.println(signature.getName() 执行结果 res);System.out.println(signature.getName() 执行, 结束时间 System.currentTimeMillis());}
}beans10.xml
?xml version1.0 encodingUTF-8?
beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexmlns:contexthttp://www.springframework.org/schema/contextxmlns:aophttp://www.springframework.org/schema/aopxsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd!--扫描指定的包--context:component-scan base-packagecom.zzw.spring.aop.homework02.annotation_/!--开启基于注解的AOP功能--aop:aspectj-autoproxy/
/beans测试
public class AopAspectjTest {Testpublic void test() {ApplicationContext ioc new ClassPathXmlApplicationContext(beans10.xml);Cal cal ioc.getBean(Cal.class);cal.cal1(10);System.out.println();cal.cal2(10);}
}结果
cal1 执行, 开始执行时间1691395310255
cal1 执行结果55
cal1 执行, 结束时间1691395310256cal2 执行, 开始执行时间1691395310256
cal2 执行结果1410065408
cal2 执行, 结束时间1691395310257❷基于XML配置方法 接口
public interface Cal {//计算12...nint cal1(int n);//计算1*2*...*nint cal2(int n);
}实现类
public class MyCal implements Cal{Overridepublic int cal1(int n) {int sum 0;for (int i 1; i n; i) {sum i;}return sum;}Overridepublic int cal2(int n) {int res 1;for (int i 1; i n; i) {res * ;}return res;}
}切面类
public class CalAspect {//前置通知public void calStart(JoinPoint joinPoint) {Signature signature joinPoint.getSignature();System.out.println(signature.getName() 执行[基于XML配置], 开始执行时间 System.currentTimeMillis());}//返回通知public void calEnd(JoinPoint joinPoint, Object res) {Signature signature joinPoint.getSignature();System.out.println(signature.getName() 执行结果 res);System.out.println(signature.getName() 执行[基于XML配置], 结束时间 System.currentTimeMillis());}
}beans11.xml
?xml version1.0 encodingUTF-8?
beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xmlns:aophttp://www.springframework.org/schema/aopxsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd!--使用XML配置, 完成AOP编程--!--配置一个切面类对象-bean--bean classcom.zzw.spring.aop.homework02.xml.CalAspect idcalAspect/!--配置一个MyCal对象-bean--bean classcom.zzw.spring.aop.homework02.xml.MyCal idmyCal/!--配置切面类, 细节: 一定要引入 xmlns:aop--aop:config!--配置切入点表达式--aop:pointcut idmyPointCut expressionexecution(public int com.zzw.spring.aop.homework02.xml.MyCal.*(int))/!--配置切面的 前置, 返回通知--aop:aspect refcalAspect order10!--配置前置通知--aop:before methodcalStart pointcut-refmyPointCut/!--配置返回通知--aop:after-returning methodcalEnd pointcut-refmyPointCut returningres//aop:aspect/aop:config
/beans测试
public class AopAspectjXMLTest {Testpublic void testMyCalByXML() {ApplicationContext ioc new ClassPathXmlApplicationContext(beans11.xml);Cal cal ioc.getBean(Cal.class);cal.cal1(10);System.out.println();cal.cal2(10);}
}结果
cal1 执行[基于XML配置], 开始执行时间1691396329275
cal1 执行结果55
cal1 执行[基于XML配置], 结束时间1691396329275cal2 执行[基于XML配置], 开始执行时间1691396329275
cal2 执行结果1410065408
cal2 执行[基于XML配置], 结束时间1691396329275引出对Spring底层实现再思考
创建maven项目
1.创建maven项目
2.加入依赖
project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsdmodelVersion4.0.0/modelVersiongroupIdcom.zzw/groupIdartifactIdzzw-spring/artifactIdversion1.0-SNAPSHOT/versiondependencies!--加入spring开发的基本包--dependencygroupIdorg.springframework/groupIdartifactIdspring-context/artifactIdversion5.3.8/version/dependency!--加入spring开发切面编程需要的包--dependencygroupIdorg.springframework/groupIdartifactIdspring-aspects/artifactIdversion5.3.8/version/dependency/dependencies
/project2.项目目录结构, 文件夹对应不上的可以自己 添加或删除.
//就是一个Controller
//也可以使用 Controller
在默认情况下, 我们配置Component,Repository,Controller,Service 是单例
Component
public class UserAction { }//也可以使用 Repository
Component
public class UserDao {public void hi() {System.out.println(UserDao hi()...);}
}//也可以使用 Service
Component
public class UserService {//也可以使用 ResourceAutowiredprivate UserDao userDao;//定义一个属性public void m1() {userDao.hi();}
}src目录下创建beans.xml
?xml version1.0 encodingUTF-8?
beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexmlns:contexthttp://www.springframework.org/schema/contextxsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd!--配置自动扫描的包, 同时引入对应的名称空间--context:component-scan base-packagecom.zzw.spring.component//beans测试1
public class AppMain {public static void main(String[] args) {//测试/看看是否可以得到spring容器中的bean, 同时看看依赖注入是否OKApplicationContext ioc new ClassPathXmlApplicationContext(beans.xml);UserAction userAction ioc.getBean(userAction, UserAction.class);UserAction userAction2 ioc.getBean(userAction, UserAction.class);System.out.println(userAction userAction);System.out.println(userAction2 userAction2);UserDao userDao ioc.getBean(userDao, UserDao.class);System.out.println(userDao userDao);UserService userService ioc.getBean(userService, UserService.class);System.out.println(userService userService);}
}报错: Caused by: java.io.FileNotFoundException: class path resource [beans.xml] cannot be opened because it does not exist, 其错误原因是
实际上 beans.xml 应该在 src/main/resources 下创建
?xml version1.0 encodingUTF-8?
beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexmlns:contexthttp://www.springframework.org/schema/contextxsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd!--配置自动扫描的包, 同时引入对应的名称空间--!--说明1.如果我们是普通的java项目, beans.xml放在src目录下即可2.如果我们是java maven项目, beans.xml放在src/main/resources--context:component-scan base-packagecom.zzw.spring.component//beans此时项目结构图如下所示:
运行结果
userActioncom.zzw.spring.component.UserAction679b62af
userAction2com.zzw.spring.component.UserAction679b62af
userDaocom.zzw.spring.component.UserDao5cdd8682
userServicecom.zzw.spring.component.UserServiced6da883思考一: 原生Spring如何实现依赖注入和(singleton, prototype)
1.Spring底层如何实现: IOC容器创建和初始化 2.Spring底层如何实现: 根据 singleton, prototype来返回bean对象
案例注入默认是单例注入, 标识了注解Scope(value prototype)后代表多例
//在默认情况下, 我们配置Component,Repository,Controller,Service 是单例
//Scope(value prototype) 表示以多例形式, 返回 UserAction bean
//多例是懒加载
//思考: Spring容器底层如何实现
Component
Scope(value prototype)
public class UserAction {}测试2
public class AppMain {public static void main(String[] args) {//测试/看看是否可以得到spring容器中的bean, 同时看看依赖注入是否OKApplicationContext ioc new ClassPathXmlApplicationContext(beans.xml);UserAction userAction ioc.getBean(userAction, UserAction.class);UserAction userAction2 ioc.getBean(userAction, UserAction.class);System.out.println(userAction userAction);System.out.println(userAction2 userAction2);UserDao userDao ioc.getBean(userDao, UserDao.class);System.out.println(userDao userDao);UserService userService ioc.getBean(userService, UserService.class);System.out.println(userService userService);//测试一下当前的依赖注入userService.m1();}
}运行结果
userActioncom.zzw.spring.component.UserAction60bd273d
userAction2com.zzw.spring.component.UserAction121314f7
userDaocom.zzw.spring.component.UserDao130c12b7
userServicecom.zzw.spring.component.UserService5e600dd5
UserDao hi()...思考二: 原生Spring容器如何实现BeanPostProcessor
//编写一个后置处理器
public class MyBeanProcessor implements BeanPostProcessor {/*** 在Bean的 init初始化方法前调用** param bean* param beanName* return* throws BeansException*/public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println(postProcessBeforeInitialization() 被调用... beanName bean bean.getClass());return bean;}/*** 在Bean的 init初始化方法后调用** param bean* param beanName* return* throws BeansException*/public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println(postProcessAfterInitialization() 被调用... beanName bean bean.getClass());return bean;}
}Component
public class UserService {Autowiredprivate UserDao userDao;public void m1() {userDao.hi();}//这里我们需要指定init() 是初始化方法PostConstructpublic void init() {System.out.println(UserService init()...);}
}?xml version1.0 encodingUTF-8?
beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexmlns:contexthttp://www.springframework.org/schema/contextxsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd!--配置自动扫描的包--context:component-scan base-packagecom.zzw.spring.component/!--配置后置处理器--bean classcom.zzw.spring.process.MyBeanProcessor idmyBeanProcessor/
/beans测试3
public class AppMain {public static void main(String[] args) {ApplicationContext ioc new ClassPathXmlApplicationContext(beans.xml);UserAction userAction ioc.getBean(userAction, UserAction.class);UserAction userAction2 ioc.getBean(userAction, UserAction.class);System.out.println(userAction userAction);System.out.println(userAction2 userAction2);UserDao userDao ioc.getBean(userDao, UserDao.class);System.out.println(userDao userDao);UserService userService ioc.getBean(userService, UserService.class);System.out.println(userService userService);//测试一下当前的依赖注入//userService.m1();}
}运行结果
postProcessBeforeInitialization() 被调用... userDao beanclass com.zzw.spring.component.UserDao
postProcessAfterInitialization() 被调用... userDao beanclass com.zzw.spring.component.UserDao
postProcessBeforeInitialization() 被调用... userService beanclass com.zzw.spring.component.UserService
UserService init()...
postProcessAfterInitialization() 被调用... userService beanclass com.zzw.spring.component.UserService
postProcessBeforeInitialization() 被调用... userAction beanclass com.zzw.spring.component.UserAction
postProcessAfterInitialization() 被调用... userAction beanclass com.zzw.spring.component.UserAction
postProcessBeforeInitialization() 被调用... userAction beanclass com.zzw.spring.component.UserAction
postProcessAfterInitialization() 被调用... userAction beanclass com.zzw.spring.component.UserAction
userActioncom.zzw.spring.component.UserAction1ffaf86
userAction2com.zzw.spring.component.UserAction6574a52c
userDaocom.zzw.spring.component.UserDao6c1a5b54
userServicecom.zzw.spring.component.UserService1c7696c6拿掉UserAction的Scope(value “prototype”)注解, 运行结果如下
postProcessBeforeInitialization() 被调用... userAction beanclass com.zzw.spring.component.UserAction
postProcessAfterInitialization() 被调用... userAction beanclass com.zzw.spring.component.UserAction
postProcessBeforeInitialization() 被调用... userDao beanclass com.zzw.spring.component.UserDao
postProcessAfterInitialization() 被调用... userDao beanclass com.zzw.spring.component.UserDao
postProcessBeforeInitialization() 被调用... userService beanclass com.zzw.spring.component.UserService
UserService init()...
postProcessAfterInitialization() 被调用... userService beanclass com.zzw.spring.component.UserService
userActioncom.zzw.spring.component.UserAction71a794e5
userAction2com.zzw.spring.component.UserAction71a794e5
userDaocom.zzw.spring.component.UserDao76329302
userServicecom.zzw.spring.component.UserService5e25a92e知识点: 基于注解的方式配置后置处理器(别忘了扫描包)
//编写一个后置处理器
Component
public class MyBeanProcessor implements BeanPostProcessor {/*** 在Bean的 init初始化方法前调用** param bean* param beanName* return* throws BeansException*/public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println(postProcessBeforeInitialization() 被调用... beanName bean bean.getClass());return bean;}/*** 在Bean的 init初始化方法后调用** param bean* param beanName* return* throws BeansException*/public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println(postProcessAfterInitialization() 被调用... beanName bean bean.getClass());return bean;}
}?xml version1.0 encodingUTF-8?
beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexmlns:contexthttp://www.springframework.org/schema/contextxsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsdcontext:component-scan base-packagecom.zzw.spring.component/context:component-scan base-packagecom.zzw.spring.process/
/beans思考三: 原生Spring容器如何实现AOP
//接口
public interface SmartAnimalAble {float getSum(float i, float j);float getSub(float i, float j);
}Component
public class SmartDog implements SmartAnimalAble{public float getSum(float i, float j) {float res i j;System.out.println(SmartDog getSum() 结果 res);return res;}public float getSub(float i, float j) {float res i - j;System.out.println(SmartDog getSub() 结果 res);return res;}
}//这是一个切面类
Aspect
Component
public class SmartAnimalAspect {//给SmartDog配置前置, 返回, 异常, 最终通知//前置通知Before(value execution(public float com.zzw.spring.aop.SmartDog.getSum(float, float)))public void showBeginLog(JoinPoint joinPoint) {//通过连接点对象joinPoint, 可以获取方法签名Signature signature joinPoint.getSignature();System.out.println(SmartAnimalAspect-切面类showBeginLog()-方法执行前-日志-方法名- signature.getName() -参数 Arrays.asList(joinPoint.getArgs()));}//返回通知: 即把showSuccessEndLog方法切入到目标对象方法正常执行完毕后的地方AfterReturning(value execution(public float com.zzw.spring.aop.SmartDog.getSum(float, float)), returning res)public void showSuccessEndLog(JoinPoint joinPoint, Object res) {Signature signature joinPoint.getSignature();System.out.println(SmartAnimalAspect-切面类showSuccessEndLog()-方法执行正常结束-日志-方法名- signature.getName() 返回的结果是 res);}//异常通知: 即把showExceptionLog方法切入到目标对象方法执行发生异常后的catch{}AfterThrowing(value execution(public float com.zzw.spring.aop.SmartDog.getSum(float, float)), throwing throwable)public void showExceptionLog(JoinPoint joinPoint, Throwable throwable) {Signature signature joinPoint.getSignature();System.out.println(SmartAnimalAspect-切面类showExceptionLog()-方法执行异常-日志-方法名- signature.getName() 异常信息 throwable);}//最终通知: 即把showFinallyEndLog方法切入到目标方法执行后, 不管是否发生异常都要执行, finally{}After(value execution(public float com.zzw.spring.aop.SmartDog.getSum(float, float)))public void showFinallyEndLog(JoinPoint joinPoint) {Signature signature joinPoint.getSignature();System.out.println(SmartAnimalAspect-切面类showFinallyEndLog()-方法最终执行完毕-日志-方法名- signature.getName());}
}?xml version1.0 encodingUTF-8?
beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexmlns:contexthttp://www.springframework.org/schema/contextxmlns:aophttp://www.springframework.org/schema/aopxsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd!--配置自动扫描的包, 同时引入对应的名称空间--context:component-scan base-packagecom.zzw.spring.component/context:component-scan base-packagecom.zzw.spring.aop/!--开启基于注解的AOP功能--aop:aspectj-autoproxy/!--配置后置处理器--bean classcom.zzw.spring.process.MyBeanProcessor idmyBeanProcessor/
/beans测试4
public class AppMain {public static void main(String[] args) {//测试/看看是否可以得到spring容器中的bean, 同时看看依赖注入是否OKApplicationContext ioc new ClassPathXmlApplicationContext(beans.xml);UserAction userAction ioc.getBean(userAction, UserAction.class);UserAction userAction2 ioc.getBean(userAction, UserAction.class);System.out.println(userAction userAction);System.out.println(userAction2 userAction2);UserDao userDao ioc.getBean(userDao, UserDao.class);System.out.println(userDao userDao);UserService userService ioc.getBean(userService, UserService.class);System.out.println(userService userService);//测试一下当前的依赖注入userService.m1();//测试一下AOPSmartAnimalAble smartDog ioc.getBean(SmartAnimalAble.class);System.out.println(smartDog.getSum(1,2));}
}运行结果
postProcessBeforeInitialization() 被调用... userAction beanclass com.zzw.spring.component.UserAction
postProcessAfterInitialization() 被调用... userAction beanclass com.zzw.spring.component.UserAction
postProcessBeforeInitialization() 被调用... userDao beanclass com.zzw.spring.component.UserDao
postProcessAfterInitialization() 被调用... userDao beanclass com.zzw.spring.component.UserDao
postProcessBeforeInitialization() 被调用... userService beanclass com.zzw.spring.component.UserService
UserService init()...
postProcessAfterInitialization() 被调用... userService beanclass com.zzw.spring.component.UserService
postProcessBeforeInitialization() 被调用... smartAnimalAspect beanclass com.zzw.spring.aop.SmartAnimalAspect
postProcessAfterInitialization() 被调用... smartAnimalAspect beanclass com.zzw.spring.aop.SmartAnimalAspect
postProcessBeforeInitialization() 被调用... smartDog beanclass com.zzw.spring.aop.SmartDog
postProcessAfterInitialization() 被调用... smartDog beanclass com.sun.proxy.$Proxy16
userActioncom.zzw.spring.component.UserAction26ceffa8
userAction2com.zzw.spring.component.UserAction26ceffa8
userDaocom.zzw.spring.component.UserDao600b90df
userServicecom.zzw.spring.component.UserService7c8c9a05
UserDao hi()...
SmartAnimalAspect-切面类showBeginLog()-方法执行前-日志-方法名-getSum-参数 [1.0, 2.0]
SmartDog getSum() 结果3.0
SmartAnimalAspect-切面类showSuccessEndLog()-方法执行正常结束-日志-方法名-getSum 返回的结果是3.0
SmartAnimalAspect-切面类showFinallyEndLog()-方法最终执行完毕-日志-方法名-getSum
3.0简析Spring AOP 和 BeanPostProcessor关系
1.AOP实现Spring可以通过给一个类, 加入注解 EnableAspectJAutoProxy 来指定
2.追一下EnableAspectJAutoProxy的源码
Target({ElementType.TYPE})
Retention(RetentionPolicy.RUNTIME)
Documented
Import({AspectJAutoProxyRegistrar.class})
public interface EnableAspectJAutoProxy {boolean proxyTargetClass() default false;boolean exposeProxy() default false;
} public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry); AnnotationAttributes enableAspectJAutoProxy AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);if (enableAspectJAutoProxy ! null) {if (enableAspectJAutoProxy.getBoolean(proxyTargetClass)) {AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);}if (enableAspectJAutoProxy.getBoolean(exposeProxy)) {AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);}}}Nullablepublic static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Nullable Object source) {return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source); }3.看一下AnnotationAwareAspectJAutoProxyCreator的类图 4.解读 1)AOP底层是基于BeanPostProcessor机制的. 2)即在Bean创建好后, 根据是否需要AOP处理, 决定返回代理对象, 还是原生Bean 3)在返回代理对象时, 就可以根据要代理的类和方法来返回 4)机制并不难, 本质就是BeanPostProcessor机制 动态代理技术
补充说明
后置处理器SmartDog类型postProcessBeforeInitialization()class com.zzw.spring.aop.SmartDogpostProcessAfterInitialization()class com.sun.proxy.$Proxy16
为何: SmartDog 在 postProcessBeforeInitialization() 中类型是普通类型, 在postProcessAfterInitialization()中成了代理类型. 其本质原因在: SmartAnimalAspect类是一个切面类, 并且对SmartDog中的方法做了切入, 所以后置处理器处理成了代理对象.
Spring整体架构分析 下乘: Spring系列五手动实现Spring底层机制, 未完待续…