一、需求:

  自己实现AOP:1.0版本:在某个方法上加"@InOutLog"注解,那么执行到该方法时,方法的前面、后面会输出日志信息。

  【自己实现AOP 2.0版本(实现Spring的有前置通知、后置通知、返回通知等各种通知的AOP):https://www.cnblogs.com/laipimei/p/11163377.html

二、思路整理:

  1.涉及的角色:

    ①被代理类;

    ②被代理类要实现的接口;

    ③代理类;

    ④动态创建“代理类的对象”的类;

    ⑤注解类(注解在方法上);

    ⑥IOC容器:BeanFactory(自己实现IOC容器:https://www.cnblogs.com/laipimei/p/11205510.html)。

  2.实现步骤:

    (1)被代理类、被代理类的接口、InOutLog注解类的创建;

    (2)创建一个“动态代理类”,并把“被代理类的实例”传给该代理类;在该动态代理类的invoke()方法中,实现日志的输出,也是在该invoke()方法中调用、执行真正的代理类要执行的那个方法。

    (3)创建一个可以动态创建“代理类的实例”的类,通过该类的getProxyInstance(Object obj)方法可以得到一个动态代理类的实例。
    (4)给方法加通知注解,该方法的实例须已交由IOC容器管理的;
    (5)遍历BeanFactory,找出方法上有@InOutLog注解的bean,为这些bean生成代理类对象(步骤:MyProxy3.getProxyInstance(Object obj));

    (6)用代理类的实例去替代BeanFactory中的被代理类的实例;

三、代码实现:

被代理类的接口:

package MyIOCAndMyAop.bean;

public interface Subject {
void test();
}

被代理类:

 1 package MyIOCAndMyAop.bean;
2
3 import MyIOCAndMyAop.Annotations.InOutLog;
4 import MyIOCAndMyAop.Annotations.MyAutowired;
5 import MyIOCAndMyAop.Annotations.MyComponent;
6
7 /**
8 * 被代理类
9 */
10 @MyComponent
11 public class Person implements Subject{
12
13 @MyAutowired
14 private Student student;
15
16 @InOutLog
17 public void test(){
18 System.out.println(this + ".test() : " + student);
19 }
20 }

InOutLog注解类:

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 InOutLog { }

动态代理类:

public class MyInvocationHandler2 implements InvocationHandler{
private Object object;//被代理类
private Object invoke;
public void setObject(Object object) {
this.object = object;
} /**
* 在BeanFactory中,方法上有@InOutLog注解,则生成动态代理方法
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//这里做判断,看是否需要做下面的输出
Boolean bool = false;
//!!!注意,要用被代理类的对象去判断其method方法上是否有@InOutLog注解,而不是用入参method对象(该method对象是被代理类的接口的)
//怎么处理入参的类型:见MyAOP2.这里没有做入参处理,可能会报方法找不到异常,注意!!!
Method declaredMethod = object.getClass().getDeclaredMethod(method.getName());
if(null != declaredMethod.getAnnotation(InOutLog.class)) {
System.out.println("我是日志输出~~~start~~~");
bool = true;
} invoke = method.invoke(object, args); //这里做判断,同上
if(bool) {
System.out.println("我是日志输出~~~end~~~");
System.out.println("------------------------------------------------------------------------------");
}
return invoke;
}
}

动态创建“代理类的对象”的类:

public class MyProxy2 {

    /**
* 动态的创建一个代理类的对象
* MyProxy动态创建的“代理类的对象”:
* class A implements Subject{
* private Handler handler;
* public void test() {
* //获得到当前方法名
* handler.invoke();
* }
* }
*/
public static Object getProxyInstance(Object obj) {
MyInvocationHandler2 handler = new MyInvocationHandler2();
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 MyAOP2 {
public static void main(String[] args) {
/**
* 使用步骤:
* ① 給指定类的某个方法加@InOutLog注解;
* ② 通过BeanFactory或的该类的实例;
* ③ 执行bean.method();
* 效果:method()方法的前后有了log的输出。
*/
String completeClassName = "MyIOCAndMyAop.bean.Person";
Object bean = MyIOC.getBean(completeClassName);
Subject person = (Subject)bean;
person.test();
} static {
init();
} public static void init() {
updateBeanFromBeanFactory();
} /**
* 扫描BeanFactory,找出方法上有@InOutLog注解的bean,为其创建代理类对象,并替代原bean。
*/
public static void updateBeanFromBeanFactory() {
for(Map.Entry<String, Object> entry : MyIOC.getBeanFactory().entrySet()) {
for(Method method : entry.getValue().getClass().getDeclaredMethods()) {
if(null != method.getAnnotation(InOutLog.class)) {
MyProxy2.updateBean(entry.getKey(), entry.getValue());
}
}
}
}
}

最新文章

  1. 原生js arguments 用法
  2. BZOJ 2822: [AHOI2012]树屋阶梯
  3. linux初始化配置-----网络配置
  4. IEE分月表改造
  5. 问题解决——在STL的queue中使用自定义类
  6. 从实用主义深入理解c++虚函数
  7. 黄聪:VPS实现自动定时备份网站数据以及Mysql数据库到百度云同步盘
  8. c创建win窗口
  9. MySQL在线备份与恢复工具 --&gt; Xtrabackup
  10. [SAM4N学习笔记]UART的使用
  11. CI框架篇之模型篇--AR操作(2)
  12. 如何实现MySQL随机查询数据与MySQL随机更新数据?
  13. SQL Server :理解GAM和SGAM页
  14. 我的第一个python web开发框架(7)——本地部署前端访问服务器
  15. 【Luogu1393】动态逆序对(CDQ分治)
  16. Kafka生产者-向Kafka中写入数据
  17. Android Demo---实现从底部弹出窗口
  18. Java 学习笔记 (八) Java 变量
  19. C++STL模板库适配器之queue队列
  20. MySQL系列:性能优化

热门文章

  1. WPF-WPF BitmapEffect (按钮凹凸效果)
  2. C# ?和??使用讲解
  3. ASP.NET Core 配置 Entity Framework Core - ASP.NET Core 基础教程 - 简单教程,简单编程
  4. Win32 键盘事件 - 击键消息、字符消息、插入符号(光标)
  5. C# 操作XML文档 使用XmlDocument类方法
  6. Nginx支持LInux的软链接或硬链接
  7. 服务器做RAID10
  8. jquery 用json设置css
  9. thinkphp5 的一些笔记
  10. C#高性能大容量SOCKET并发(十一):编写上传客户端