首页 > 其他分享 >006 使用动态代理实现自定义注解功能

006 使用动态代理实现自定义注解功能

时间:2022-12-28 13:44:36浏览次数:52  
标签:String 自定义 org example person 006 import 注解 public

问题的提出:自定义一个注解,如@MyLog,当把此注解加在函数上时,该函数的调用会被自动日志。

解题思路:创建函数所在对象的动态代理,当该函数被调用时,在代理中进行日志。

两种方法:方法一使用JDK动态代理,只能对实现了接口的类进行代理;方法二使用CGLiB动态代理,可以对普通类进行代理。

1. 自定义MyLog注解

 1 package org.example.annotation;
 2 
 3 import java.lang.annotation.*;
 4 
 5 @Target(ElementType.METHOD)
 6 @Retention(RetentionPolicy.RUNTIME)
 7 @Documented
 8 public @interface MyLog {
 9     String beginMsg() default "执行开始...";
10     String endMsg() default "执行结束!!!";
11 }

 方法一:使用JDK动态代理。

先定义一个接口:

1 package org.example.business;
2 
3 public interface IPerson {
4     boolean eat(String food);
5 
6     String walk(String start, String finish);
7 
8     void sleep(int seconds);
9 }

再实现上述接口,注意实现类的工厂方法createPerson():

 1 package org.example.business;
 2 
 3 import org.example.annotation.MyLog;
 4 import org.example.proxy.ProxyInvocationHandler;
 5 
 6 import java.util.concurrent.TimeUnit;
 7 
 8 public class Person implements IPerson{
 9 
10     public static IPerson createPerson(){
11         Person person = new Person();
12         //TODO: 此处可以对Person对象进行额外的初始化工作
13         return (IPerson) ProxyInvocationHandler.getProxy(person);
14     }
15     @Override
16     @MyLog(beginMsg = "starting eat", endMsg = "finishing eat")
17     public boolean eat(String food) {
18         System.out.println("the person is eating " + food);
19         return true;
20     }
21 
22     @Override
23     public void sleep(int seconds) {
24         System.out.println("the person is sleeping...");
25         try {
26             TimeUnit.SECONDS.sleep(seconds);
27         }catch (InterruptedException e){
28             e.printStackTrace();
29         }
30     }
31 
32     @Override
33     @MyLog
34     public String walk(String start, String terminal) {
35         System.out.println("the person is walking from " + start + " to " + terminal);
36         return start + "--" + terminal;
37     }
38 }

最后实现InvocationHandler接口:

 1 package org.example.proxy;
 2 
 3 import org.example.annotation.MyLog;
 4 
 5 import java.lang.reflect.InvocationHandler;
 6 import java.lang.reflect.Method;
 7 import java.lang.reflect.Proxy;
 8 import java.time.LocalDateTime;
 9 import java.time.format.DateTimeFormatter;
10 
11 public class ProxyInvocationHandler implements InvocationHandler {
12     private Object target;
13 
14     public ProxyInvocationHandler(Object target){
15         this.target = target;
16     }
17 
18     static boolean equalParamTypes(Class<?>[] params1, Class<?>[] params2) {
19         if (params1.length == params2.length) {
20             for (int i = 0; i < params1.length; i++) {
21                 if (params1[i] != params2[i])
22                     return false;
23             }
24             return true;
25         }
26         return false;
27     }
28 
29     @Override
30     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
31         Object result;
32 
33         MyLog myLog = null;
34         Method[] methodList = target.getClass().getDeclaredMethods();
35         for (Method m : methodList) {
36             if (m.getName().endsWith(method.getName()) &&
37                     m.getReturnType().equals(method.getReturnType()) &&
38                     equalParamTypes(m.getParameterTypes(), method.getParameterTypes())) {
39 
40                         myLog = m.getAnnotation(MyLog.class);
41                         break;
42                 }
43         }
44 
45         if (myLog != null) {
46             System.out.println(myLog.beginMsg() + LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
47             result = method.invoke(target, args);
48             System.out.println(myLog.endMsg() + LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
49         } else {
50             result = method.invoke(target, args);
51         }
52         return result;
53     }
54 
55     public static Object getProxy(Object target){
56         return Proxy.newProxyInstance(
57                 target.getClass().getClassLoader(),
58                 target.getClass().getInterfaces(),
59                 new ProxyInvocationHandler(target)
60         );
61     }
62 
63 }

在Main中测试:

 1 package org.example;
 2 
 3 import org.example.business.IPerson;
 4 import org.example.business.Person;
 5 
 6 public class Main {
 7     public static void main(String[] args) {
 8 
 9         IPerson person = Person.createPerson();
10         person.eat("rice");
11         person.sleep(1);
12         String s = person.walk("shanghai", "beijing");
13         System.out.println(s);
14     }
15 }

输出结果如下:

starting eat2022-12-28T13:23:05.1305902
the person is eating rice
finishing eat2022-12-28T13:23:05.13759
the person is sleeping...
执行开始...2022-12-28T13:23:06.139794
the person is walking from shanghai to beijing
执行结束!!!2022-12-28T13:23:06.1487947
shanghai--beijing

 方法二:使用CGLiB动态代理。

导入CGLiB依赖:

<dependency>
  <groupId>cglib</groupId>
  <artifactId>cglib</artifactId>
  <version>3.3.0</version>
</dependency>

由于 JDK 8 中有关反射相关的功能自从 JDK 9 开始就已经被限制了,需要 Add VM options:

--add-opens java.base/java.lang=ALL-UNNAMED

来开启这种默认不被允许的行为。

定义一个普通类,该类可以不实现某个接口,注意该类的工厂方法createHuman():

 1 package org.example.business;
 2 
 3 import org.example.annotation.MyLog;
 4 import org.example.proxy.MyLogMethodInterceptor;
 5 
 6 import java.util.concurrent.TimeUnit;
 7 
 8 public class Human {
 9 
10     // 由newHuman()创建的对象才能使@MyLog生效
11     public static Human createHuman(){
12         Human human = (Human) MyLogMethodInterceptor.getProxy(Human.class);
13         //TODO: 此处可以对Human对象进行额外的初始化工作
14         return human;
15     }
16 
17     @MyLog(beginMsg = "starting eat", endMsg = "finishing eat")
18     public boolean eat(String food) {
19         System.out.println("the person is eating " + food);
20         return true;
21     }
22 
23     public void sleep(int seconds) {
24         System.out.println("the person is sleeping...");
25         try {
26             TimeUnit.SECONDS.sleep(seconds);
27         }catch (InterruptedException e){
28             e.printStackTrace();
29         }
30     }
31 
32     @MyLog
33     public String walk(String start, String terminal) {
34         System.out.println("the person is walking from " + start + " to " + terminal);
35         return start + "--" + terminal;
36     }
37 }

最后实现MethodInterceptor接口:

 1 package org.example.proxy;
 2 
 3 import net.sf.cglib.proxy.Enhancer;
 4 import net.sf.cglib.proxy.MethodInterceptor;
 5 import net.sf.cglib.proxy.MethodProxy;
 6 import org.example.annotation.MyLog;
 7 
 8 import java.lang.reflect.Method;
 9 import java.time.LocalDateTime;
10 import java.time.format.DateTimeFormatter;
11 
12 public class MyLogMethodInterceptor implements MethodInterceptor {
13     @Override
14     public Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
15         Object result;
16 
17         MyLog myLog = method.getAnnotation(MyLog.class);
18         if (myLog != null) {
19             System.out.println(myLog.beginMsg() + LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
20             result = methodProxy.invokeSuper(target, args);
21             System.out.println(myLog.endMsg() + LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
22         } else {
23             result = methodProxy.invokeSuper(target, args);
24         }
25         return result;
26     }
27 
28     public static Object getProxy(Class<?> clazz){
29         Enhancer enhancer = new Enhancer();
30         enhancer.setClassLoader(clazz.getClassLoader());
31         enhancer.setSuperclass(clazz);
32         enhancer.setCallback(new MyLogMethodInterceptor());
33         return enhancer.create();
34     }
35 
36 }

在Main中测试:

 1 package org.example;
 2 
 3 import org.example.business.Human;
 4 
 5 public class Main {
 6     public static void main(String[] args) {
 7 
 8         Human human = Human.createHuman();
 9         human.eat("bread");
10         human.sleep(1);
11         String t = human.walk("杭州", "合肥");
12         System.out.println(t);
13     }
14 }

输出结果如下:

starting eat2022-12-28T13:36:19.0685967
the person is eating bread
finishing eat2022-12-28T13:36:19.0925967
the person is sleeping...
执行开始...2022-12-28T13:36:20.1026827
the person is walking from 杭州 to 合肥
执行结束!!!2022-12-28T13:36:20.1116888
杭州--合肥

标签:String,自定义,org,example,person,006,import,注解,public
From: https://www.cnblogs.com/java369/p/17009922.html

相关文章

  • Vue自定义hook
    自定义hook函数什么是hook?——本质是一个函数,把setup函数中使用的CompositionAPI进行了封装。类似于vue2.x中的mixin。自定义hook的优势:复用代码,让setup中......
  • 1006.Django模型基础01
    一、Django的ORM简介1.ORM概念:对象关系映射(ObjectRelationalMapping);2.ORM优势:不用直接编写SQL代码,只需像操作对象一样从数据库操作数据。 django模型映射关系模型......
  • SpringBoot - 注入原生注解 Servlet,Filter,Listener
    @ServletComponentScan(basePackages=“com.atguigu.admin”)指定原生Servlet组件都放在那里@WebServlet(urlPatterns=“/my”)直接响应,没有经过Spring的拦截器@WebF......
  • iOS6下自定义UI控件外观效果
    尽管iOS原生的UI控件就已经有很不错的显示效果,但是App开发者仍然希望自己的产品与众不同,所以自定义UI外观成了每个App产品开发必做之事。今天就来做一个在iOS6下实现自定义U......
  • 自定义异常
    自定义异常:       ......
  • ASP.NET 2.0中使用自定义provider (2)
     在teched2005上,有对asp.net2.0的介绍,其中讲到asp.net2.0提供了很多功能,让程序员做少很多东西,这引起了大家的疑惑:asp.net2.0的自定义能力如何?扩......
  • 【WPF】自定义一个自删除的多功能ListBox
    原文地址https://www.cnblogs.com/younShieh/p/17008534.html❤如果本文对你有所帮助,不妨点个关注和推荐呀,这是对笔者最大的支持~❤我需要一个ListBox,他在界面上分为几......
  • SpringBoot - 自定义拦截器HandlerInterceptor
    1.实现HandlerInterceptor接口/***自定义拦截器*/publicclassMyInterceptorimplementsHandlerInterceptor{@OverridepublicbooleanpreHandle(Htt......
  • Vue 封装自定义组件,通过npm install的方式安装使用
    1、新创建一个空的项目,我创建了一个新的项目(common-package-ui)2、在src下创建一个package文件夹用于存放自己的自定义组件,我创建了一个测试文件夹(test),下面创建了一个test......
  • QT打开摄像头(自定义取景器)
    自建取景器.h#ifndefCAMERASURFACE_H#defineCAMERASURFACE_H#include<QAbstractVideoSurface>#include<QObject>classCameraSurface:publicQAbstractVideoS......