一、需求:

  自己实现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实现的步骤分解的更多相关文章

  1. Spring AOP前置通知实例说明AOP相关概念

    今天又看了下韩顺平的SpringAOP的讲解,讲解的很透彻.仿照视频自己使用下前置通知. 一.引出问题 有个接口TestServiceInter,有两个实现方法TestService和Test2Ser ...

  2. Spring详解(五)------AspectJ 实现AOP

    上一篇博客我们引出了 AOP 的概念,以及 AOP 的具体实现方式.但是为什么要这样实现?以及提出的切入点表达式到底该怎么理解? 这篇博客我们通过对 AspectJ 框架的介绍来详细了解. 1.什么是 ...

  3. Spring 学习——Spring AOP——AOP配置篇Advice(有参数传递)

    声明通知Advice 配置方式(以前置通知为例子) 方式一 <aop:config> <aop:aspect id="ikAspectAop" ref=" ...

  4. Spring 学习——Spring AOP——AOP配置篇Advice(无参数传递)

    声明通知Advice 配置方式(以前置通知为例子) 方式一 <aop:config> <aop:aspect id="ikAspectAop" ref=" ...

  5. Spring详解(六)------AspectJ 实现AOP

    上一篇博客我们引出了 AOP 的概念,以及 AOP 的具体实现方式.但是为什么要这样实现?以及提出的切入点表达式到底该怎么理解? 这篇博客我们通过对 AspectJ 框架的介绍来详细了解. 1.什么是 ...

  6. Spring 学习——Spring AOP——AOP配置篇Aspect、Pointcut

    Schena——based AOP 声明 Spring所有的切面和通知器都必须放在一个<aop:config>标签内,可以同时配置多个<aop:config>元素. 每一个&l ...

  7. Spring 学习——Spring AOP——AOP概念篇

    AOP AOP的定义:AOP,Aspect Oriented Programming的缩写,意为面向切面编程,是通过预编译或运行期动态代理实现程序功能处理的统一维护的一种技术 实现方式 预编译 Asp ...

  8. spring框架之AspectJ的XML方式完成AOP的开发

    1. 步骤一:创建JavaWEB项目,引入具体的开发的jar包 * 先引入Spring框架开发的基本开发包 * 再引入Spring框架的AOP的开发包 * spring的传统AOP的开发的包 * sp ...

  9. spring框架(2)— 面相切面编程AOP

    spring框架(2)— 面相切面编程AOP AOP(Aspect Oriented Programming),即面向切面编程. 可以说是OOP(Object Oriented Programming ...

随机推荐

  1. cocos2d-x 显示触摸操作(单击显示效果浪潮,对于视频演示)-绩效转型

    http://blog.csdn.net/hitwhylz/article/details/26042751 首先是显示触摸操作. 在文章最后.对性能进行一些提升改造. 由于要演示我们的作品.使用试玩 ...

  2. WPF刷新界面

    Winform 里有 Application.DoEvents();可刷新! WPF 里没这个,尽管可用委托实现多线程,但是刷新还是不行! 后来找到了 类似App.DoEvents()的方法(): 代 ...

  3. [WPF]有Focus(), 那Unfocus()呢?

    原文:[WPF]有Focus(), 那Unfocus()呢? [WPF]有Focus(), 那Unfocus()呢? 周银辉 我们可以调用Focus()方法,让WPF控件获得焦点, 那我现在不想要焦点 ...

  4. 【msdn wpf forum翻译】获取当前窗口焦点所在的元素

    原文:[msdn wpf forum翻译]获取当前窗口焦点所在的元素 原文地址: http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/6b ...

  5. JS如何为iframe添加onclick事件

    如果页面上有iframe时,鼠标点击在iframe内时,包含iframe的document是不响应任何事件的, 例如: $("#iframe1").click(function() ...

  6. GRPC 1.3.4 发布,Google 高性能 RPC 框架(Java C++ Go)

    GRPC 1.3.4 发布了,GRPC 是一个高性能.开源.通用的 RPC 框架,面向移动和 HTTP/2 设计,是由谷歌发布的首款基于 Protocol Buffers 的 RPC 框架. GRPC ...

  7. 小米手机销量暴跌36% 雷军做错了什么?(人的需求是复杂的,而不是仅仅是一个性价比;要做体验价格比,而不是配置价格比)good

    小米手机销量暴跌36% 雷军做错了什么? 日前,小米科技创始人雷军在美国马萨诸塞州剑桥市出席了第20届哈佛中国论坛开幕式并发表了演讲.在演讲中,雷军说但小米却只用两年半的时间一跃成为了中国第一,世界第 ...

  8. 【Windows10 IoT开发系列】配置篇

    原文:[Windows10 IoT开发系列]配置篇 Windows10 For IoT是Windows 10家族的一个新星,其针对不同平台拥有不同的版本.而其最重要的一个版本是运行在Raspberry ...

  9. Arch Linux 是个 针对 i686 优化的 Linux 发行版(通过可以轻松使用的二进制包系统 - pacman)

    Arch Linux 是个 针对 i686 优化的 Linux 发行版(通过可以轻松使用的二进制包系统 - pacman)Arch 同时也拥有一个类似 ports 的包构建系统(Arch Build ...

  10. Screensiz.es站收集整理了移动端的相关尺寸。

    Screensiz.es站收集整理了移动端的相关尺寸. Screensiz.es 彩蛋爆料直击现场 Screensiz.es站收集整理了移动端的相关尺寸.