仿照Spring自己实现有各种通知的AOP,AOP实现的步骤分解
一、需求:
自己实现AOP 2.0:实现Spring AOP,有环绕通知、前置通知、后置通知、返回通知、异常通知等。
已实现:①通过动态代理+通知的注解类,实现了前置通知、后置通知等各种通知;②切点(在需要通知的方法上加注解);③切面(同②);
未实现:①通知的格式没写成可配置的; ②切点、切面没抽取成一个更方便配置的切面类;③其他。
【自己实现AOP 1.0版本(简易版):https://www.cnblogs.com/laipimei/p/11137250.html】
二、思路整理:
1.涉及的角色:
①被代理类;
②被代理类要实现的接口;
③代理类;
④动态创建“代理类的对象”的类;
⑤注解类:
a. 切面注解类,注解在类上:
@Aspect
b. 各种通知注解,注解在方法上:
@Before
@AfterReturning
@After
@AfterThrowing
@Around
⑥IOC容器:BeanFactory(自己实现IOC容器:https://www.cnblogs.com/laipimei/p/11205510.html)。
2.实现步骤:
(1)被代理类、被代理类的接口、通知的注解类的创建;
(2)创建一个“动态代理类”,并把“被代理类的实例”传给该代理类;在该动态代理类的invoke()方法中,实现前置通知、后置通知等各种通知,也是在该invoke()方法中调用、执行真正的代理类要执行的那个方法。
(3)创建一个可以动态创建“代理类的实例”的类,通过该类的getProxyInstance(Object obj)方法可以得到一个动态代理类的实例。
(4)给方法加通知注解,该方法的实例须已交由IOC容器管理的;
(5)遍历BeanFactory,找出方法上有@通知注解的bean,为这些bean生成代理类对象(步骤:MyProxy3.getProxyInstance(Object obj))
(6)用代理类的实例去替代BeanFactory中的被代理类的实例
三、代码实现:
被代理类的接口:
- public interface SuperMan {
- int add(int a, int b);
- int divide(int a, int b);
- }
被代理类:
- package MyIOCAndMyAop.bean;
- import MyIOCAndMyAop.Annotations.After;
- import MyIOCAndMyAop.Annotations.AfterReturning;
- import MyIOCAndMyAop.Annotations.AfterThrowing;
- import MyIOCAndMyAop.Annotations.Around;
- import MyIOCAndMyAop.Annotations.Aspect;
- import MyIOCAndMyAop.Annotations.Before;
- import MyIOCAndMyAop.Annotations.MyComponent;
- @Aspect//切面注解类,加了该注解就表示被注解的类的实例需要做动态代理。
- @MyComponent//自定义注解类,有该注解就表示被注解类交由自定义IOC容器管理了。
- public class Student implements SuperMan {
- @After
- @AfterReturning
- @Before
- @AfterThrowing
- @Override
- public int add(int a, int b) {
- System.out.println("--> a + b = " + (a + b));
- return a + b;
- }
- @Around
- @Override
- public int divide(int a, int b) {
- return a/b;
- }
- }
注解类:
- package MyIOCAndMyAop.Annotations;
- import java.lang.annotation.ElementType;
- import java.lang.annotation.Retention;
- import java.lang.annotation.RetentionPolicy;
- import java.lang.annotation.Target;
- /**
- * 扫描BeanFactory,找出方法上有@Aspect注解的bean,为其创建代理类对象,并替代原bean。
- */
- @Target(ElementType.TYPE)
- @Retention(RetentionPolicy.RUNTIME)
- public @interface Aspect {
- }
- package MyIOCAndMyAop.Annotations;
- import java.lang.annotation.ElementType;
- import java.lang.annotation.Retention;
- import java.lang.annotation.RetentionPolicy;
- import java.lang.annotation.Target;
- /**
- * 前置通知 */
- @Target(ElementType.METHOD)
- @Retention(RetentionPolicy.RUNTIME)
- public @interface After {
- }
- package MyIOCAndMyAop.Annotations;
- import java.lang.annotation.ElementType;
- import java.lang.annotation.Retention;
- import java.lang.annotation.RetentionPolicy;
- import java.lang.annotation.Target;
- /**
- * 返回通知(方法正常执行完,才执行的通知)
- */
- @Target(ElementType.METHOD)
- @Retention(RetentionPolicy.RUNTIME)
- public @interface AfterReturning {
- }
- package MyIOCAndMyAop.Annotations;
- import java.lang.annotation.ElementType;
- import java.lang.annotation.Retention;
- import java.lang.annotation.RetentionPolicy;
- import java.lang.annotation.Target;
- /**
- * 后置通知
- */
- @Target(ElementType.METHOD)
- @Retention(RetentionPolicy.RUNTIME)
- public @interface Before {
- }
- package MyIOCAndMyAop.Annotations;
- import java.lang.annotation.ElementType;
- import java.lang.annotation.Retention;
- import java.lang.annotation.RetentionPolicy;
- import java.lang.annotation.Target;
- /**
- * 异常通知
- */
- @Target(ElementType.METHOD)
- @Retention(RetentionPolicy.RUNTIME)
- public @interface AfterThrowing {
- }
- package MyIOCAndMyAop.Annotations;
- import java.lang.annotation.ElementType;
- import java.lang.annotation.Retention;
- import java.lang.annotation.RetentionPolicy;
- import java.lang.annotation.Target;
- /**
- * 环绕通知:around==>并不常用,但功能最强大。
- */
- @Target(ElementType.METHOD)
- @Retention(RetentionPolicy.RUNTIME)
- public @interface Around {
- }
动态代理类:
- class MyInvocationHandler3 implements InvocationHandler {
- private Object object;// 被代理类
- private Object invoke;
- public void setObject(Object object) {
- this.object = object;
- }
- /**
- * 动态代理:实现了环绕通知、前置通知、后置通知等通知。
- */
- @Override
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- // 入参的类型的处理,返回被代理对象真正要执行的那个方法:
- Method declaredMethod = handleArgs(method);
- // 环绕通知:
- Boolean bool = false;
- if (null != declaredMethod.getAnnotation(MyIOCAndMyAop.Annotations.Around.class)) {
- bool = true;
- }
- aroundInform(declaredMethod, bool, method, args);
- // 前置通知、后置通知、返回通知、异常通知等:
- try {
- if (null != declaredMethod.getAnnotation(MyIOCAndMyAop.Annotations.Before.class)) {
- System.out.println(declaredMethod.getName() + " begings with : " + declaredMethod.getParameters());
- }
- //通过放射,真正执行被代理对象的方法:
- invoke = method.invoke(object, args);
- if (null != declaredMethod.getAnnotation(MyIOCAndMyAop.Annotations.AfterReturning.class)) {
- System.out.println(declaredMethod.getName() + " ends with : " + invoke);
- }
- } catch (Exception e) {
- if (null != declaredMethod.getAnnotation(MyIOCAndMyAop.Annotations.AfterThrowing.class)) {
- System.out.println(declaredMethod.getName() + " occurs exception : " + e);
- }
- } finally {
- if (null != declaredMethod.getAnnotation(MyIOCAndMyAop.Annotations.After.class)) {
- System.out.println(declaredMethod.getName() + " ends.");
- }
- }
- return invoke;
- }
- /**
- * 入参的类型的处理,这个方法很重要。
- * * @return 被代理对象真正要执行的那个方法
- * @param method 被代理对象的接口中声明的被代理方法
- * @throws NoSuchMethodException
- * @throws SecurityException
- */
- public Method handleArgs(Method method) throws NoSuchMethodException, SecurityException {
- Class<?>[] parameterTypes = method.getParameterTypes();
- switch (parameterTypes.length) {
- case 1:
- System.out.println("parameterTypes.length = 1 : " + parameterTypes[0]);
- return object.getClass().getDeclaredMethod(method.getName(), parameterTypes[0]);
- case 2:
- System.out.println("parameterTypes.length = 2 : " + parameterTypes[0] + " ; " + parameterTypes[1]);
- return object.getClass().getDeclaredMethod(method.getName(), parameterTypes[0], parameterTypes[1]);
- case 3:
- System.out.println("parameterTypes.length = 3 : " + parameterTypes[0] + " ; " + parameterTypes[1] + " ; "
- + parameterTypes[2]);
- return object.getClass().getDeclaredMethod(method.getName(), parameterTypes[0], parameterTypes[1],
- parameterTypes[2]);
- default:
- System.out.println("parameterTypes.length = 0 : " + parameterTypes.length);
- return object.getClass().getDeclaredMethod(method.getName());
- }
- }
- /**
- * 环绕通知
- *
- * @param declaredMethod 被代理对象的被代理方法
- * @param bool
- * @param method 被代理对象的接口中声明的被代理方法
- * @param args 被代理方法的声明的入参
- */
- private void aroundInform(Method declaredMethod, Boolean bool, Method method, Object[] args) {
- if (bool) {
- try {
- System.out.println(declaredMethod.getName() + " begings with : " + declaredMethod.getParameters());
- invoke = method.invoke(object, args);
- System.out.println(declaredMethod.getName() + " ends with : " + invoke);
- } catch (Exception e) {
- System.out.println(declaredMethod.getName() + " occurs exception : " + e);
- } finally {
- System.out.println(declaredMethod.getName() + " ends.");
- }
- }
- }
- }
动态创建“代理类的对象”的类:
- class MyProxy3 {
- /**
- * 动态的创建一个代理类的对象.
- *
- * MyProxy动态创建的“代理类的对象”:
- * class A implements Subject{
- * private Handler handler;
- * public void test() {
- * //获得到当前方法名:
- * handler.invoke();
- * }
- * }
- */
- public static Object getProxyInstance(Object obj) {
- MyInvocationHandler3 handler = new MyInvocationHandler3();
- handler.setObject(obj);
- return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), handler);
- }
- /**
- * 对于有@InOutLog注解的,用代理类的bean来替代BeanFactory中的被代理类的bean。
- * 这一步很重要,因为当执行到bean.method(),执行的就一定是bean对应的method()方法,
- * 如果此时没有用代理类对象去替换,那么执行的就是没有InOutLog的原来的那个方法。
- */
- public static void updateBean(String completeClassName, Object object) {
- MyIOC.updateBeanFromBeanFactory(completeClassName, getProxyInstance(object));// (全类名,代理类的bean)
- }
- }
①扫描BeanFactory,找出方法上有@InOutLog注解的bean,为其创建代理类对象,并替代原bean。②使用测试:
- public class MyAOP3 {
- public static void main(String[] args) {
- String completeClassName1 = "MyIOCAndMyAop.bean.Student";
- Object bean = MyIOC.getBean(completeClassName1);
- SuperMan superMan = (SuperMan) bean;
- superMan.add(2, 3);
- superMan.divide(10, 5);
- }
- static {
- init();
- }
- public static void init() {
- updateBeanFromBeanFactory();
- }
- /**
- * 扫描BeanFactory,找出方法上有@Aspect注解的bean,为其创建代理类对象,并替代原bean。
- */
- public static void updateBeanFromBeanFactory() {
- for (Map.Entry<String, Object> entry : MyIOC.getBeanFactory().entrySet()) {
- if (null != entry.getValue().getClass().getDeclaredAnnotation(Aspect.class)) {
- MyProxy3.updateBean(entry.getKey(), entry.getValue());
- }
- }
- }
- }
仿照Spring自己实现有各种通知的AOP,AOP实现的步骤分解的更多相关文章
- Spring AOP前置通知实例说明AOP相关概念
今天又看了下韩顺平的SpringAOP的讲解,讲解的很透彻.仿照视频自己使用下前置通知. 一.引出问题 有个接口TestServiceInter,有两个实现方法TestService和Test2Ser ...
- Spring详解(五)------AspectJ 实现AOP
上一篇博客我们引出了 AOP 的概念,以及 AOP 的具体实现方式.但是为什么要这样实现?以及提出的切入点表达式到底该怎么理解? 这篇博客我们通过对 AspectJ 框架的介绍来详细了解. 1.什么是 ...
- Spring 学习——Spring AOP——AOP配置篇Advice(有参数传递)
声明通知Advice 配置方式(以前置通知为例子) 方式一 <aop:config> <aop:aspect id="ikAspectAop" ref=" ...
- Spring 学习——Spring AOP——AOP配置篇Advice(无参数传递)
声明通知Advice 配置方式(以前置通知为例子) 方式一 <aop:config> <aop:aspect id="ikAspectAop" ref=" ...
- Spring详解(六)------AspectJ 实现AOP
上一篇博客我们引出了 AOP 的概念,以及 AOP 的具体实现方式.但是为什么要这样实现?以及提出的切入点表达式到底该怎么理解? 这篇博客我们通过对 AspectJ 框架的介绍来详细了解. 1.什么是 ...
- Spring 学习——Spring AOP——AOP配置篇Aspect、Pointcut
Schena——based AOP 声明 Spring所有的切面和通知器都必须放在一个<aop:config>标签内,可以同时配置多个<aop:config>元素. 每一个&l ...
- Spring 学习——Spring AOP——AOP概念篇
AOP AOP的定义:AOP,Aspect Oriented Programming的缩写,意为面向切面编程,是通过预编译或运行期动态代理实现程序功能处理的统一维护的一种技术 实现方式 预编译 Asp ...
- spring框架之AspectJ的XML方式完成AOP的开发
1. 步骤一:创建JavaWEB项目,引入具体的开发的jar包 * 先引入Spring框架开发的基本开发包 * 再引入Spring框架的AOP的开发包 * spring的传统AOP的开发的包 * sp ...
- spring框架(2)— 面相切面编程AOP
spring框架(2)— 面相切面编程AOP AOP(Aspect Oriented Programming),即面向切面编程. 可以说是OOP(Object Oriented Programming ...
随机推荐
- matlab 工具函数 —— axnote(在坐标轴上写文本内容)
function axnote(string) font_size = get(0, 'DefaultAxesFontSize'); if 1 h1 = text(0.99, 0.05, string ...
- 多线程——继承Thread类别
详细java此前使用多线程,让我们来看看下面的问题. 什么是多线程 简单的理解成:cpu"同一时候"运行多个任务,这就是多线程. (究其本质,当涉及到进程和线程的概念.上面 ...
- Sync Framework Toolkit 开源库
Sync Framework Toolkit构建在Sync Framework 2.1之上,使用OData在所有平台或客户端——包括Windows Phone 7.Silverlight.Window ...
- SourceTree 免注册使用
sourcetree安装的时候,需要注册.但是这个注册需要FQ,所以我们需要绕过注册 1. 找到目录:C:\Users\用户\AppData\Local\Atlassian\SourceTree 2. ...
- WPF 4 动态覆盖图标(Dynamic Overlay Icon)
原文:WPF 4 动态覆盖图标(Dynamic Overlay Icon) 在<WPF 4 开发Windows 7 任务栏>一文中我们学习了任务栏的相关开发内容,同时也对覆盖图标 ...
- List集合去重方式及效率对比
List集合相信大家在开发过程中几乎都会用到.有时候难免会遇到集合里的数据是重复的,需要进行去除.然而,去重方式有好几种方式,你用的是哪种方式呢?去重方式效率是否是最高效.最优的呢?今天就给大家讲解一 ...
- 基于Go语言快速构建RESTful API服务
In this post, we will not only cover how to use Go to create a RESTful JSON API, but we will also ta ...
- 中资收购美新半导体,为何能通过CFIUS审查(其实是国内公司,技术水平国内领先,和国际仍有差距)
日前,华灿光电发布公告称,旗下子公司和谐光电收购美国美新半导体终获得CFIUS审查通过.这是中资收购豪威科技.图芯科技等公司之后,再次从美国成功收购半导体企业.而且本次中资收购美新半导体还是在特朗普上 ...
- 零元学Expression Blend 4 - Chapter 28 ListBox的基本运用与更改预设样式
原文:零元学Expression Blend 4 - Chapter 28 ListBox的基本运用与更改预设样式 本章将先教大家认识ListBox的基本运用与更改预设样式 本章将先教大家认识List ...
- 【Windows10 IoT开发系列】API 移植工具
原文:[Windows10 IoT开发系列]API 移植工具 Windows 10 IoT Core 中是否提供你的当前 Win32 应用程序或库所依赖的 API? 如果不提供,是否存在可使用的等效 ...