<p id="g32nn"></p>
    1. <acronym id="g32nn"><strong id="g32nn"></strong></acronym>
      <pre id="g32nn"></pre>

      <table id="g32nn"><option id="g32nn"></option></table>
          當前位置:首頁 > IT技術 > 編程語言 > 正文

          Java反射自定義注解底層設計原理
          2022-09-06 22:42:29


          文章目錄

          1.什么是反射、反射優缺點
          2.反射的用途/反射應用場景
          3.反射調用方法/給屬性賦值
          4.反射如何越過泛型檢查
          5.什么是注解/注解生效的原理
          6.自定義注解實現API接口限流框架

          一、反射
          1. 反射概念

          使用反射機制可以動態獲取當前class的信息 比如方法的信息、注解信息、方法的參數、屬性等。
          .java 源代碼 編譯.class 類加載器 jvm 字節碼

          2. 反射機制的優缺點

          優點:提供開發者能夠更好封裝框架實現擴展功能。
          缺點:
          (1)反射會消耗一定的系統資源,因此如果不需要動態地創建一個對象,那么就不需要用反射;
          (2)反射調用方法時可以忽略權限檢查,因此可能會破壞封裝性而導致安全問題。

          3. 反射的用途

          反編譯:.class–>.java
          1.通過反射機制訪問java對象的屬性,方法,構造方法等
          2. JDBC加載驅動連接 class.forname
          Class.forName(“com.mysql.jdbc.Driver”); // 動態加載mysql驅動
          3. Spring容器框架IOC實例化對象

          <bean id="mayikt" class="com.mayikt.UserEntity" />

          4.自定義注解生效(反射+Aop)
          5.第三方核心的框架 mybatis orm

          4. 反射技術的使用

          Class類 代表類的實體,在運行的Java應用程序中表示類和接口
          Field類 代表類的成員變量(成員變量也稱為類的屬性)
          Method類 代表類的方法
          Constructor類 代表類的構造方法
          1.getField、getMethod和getCostructor方法可以獲得指定名字的域、方法和構造器。
          2.getFields、getMethods和getCostructors方法可以獲得類提供的public域、方法和構造器數組,其中包括超類的共有成員。
          3.getDeclatedFields、getDeclatedMethods和getDeclaredConstructors方法可以獲得類中聲明的全部域、方法和構造器,其中包括私有和受保護的成員,但不包括超類的成員。

          5. 反射常用的Api

          (1)Object–>getClass
          (2)任何數據類型(包括基本的數據類型)都有一個“靜態”的class屬性
          (3)通過class類的靜態方法:forName(String className)(最常用)
          Class<?> aClass = Class.forName(“com.mayikt.entity.UserEntity”);

          <p>
          * 第1種:獲取class UserEntity.class
          * 第2種:獲取class Class.forName("類的全路徑");
          * 第3種:new UserEntity().getClass()
          */
          @Test
          public void objCreateTest() throws InstantiationException, IllegalAccessException, ClassNotFoundException {
          Class<UserEntity> userClass1 = UserEntity.class;
          //默認執行無參構造函數
          UserEntity userEntity1 = userClass1.newInstance();
          System.out.println(userEntity1);

          //2.類的的完成路徑 報名+類名
          Class<?> userClass2 = Class.forName("com.gblfy.elk.entity.UserEntity");
          System.out.println(userClass1 == userClass2);

          //3.new UserEntity().getClass()
          UserEntity userEntity2 = new UserEntity();
          Class userClass3 = userEntity2.getClass();

          System.out.println(userClass1 == userClass3);//true
          System.out.println(userEntity1 == userEntity2);//false
          }

          運行期間,一個類,只有一個Class對象產生

          6. 反射執行構造函數

          執行無參數構造函數和執行有參數構造函數

          () throws InstantiationException, IllegalAccessException, ClassNotFoundException, InvocationTargetException, NoSuchMethodException {
          // //2.類的的完成路徑 報名+類名
          Class<?> userClass2 = Class.forName("com.gblfy.elk.entity.UserEntity");
          // //默認執行無參構造函數
          UserEntity userEntity = (UserEntity) userClass2.newInstance();
          System.out.println(userEntity);

          //執行有參構造函數
          Constructor<?> declaredConstructor = userClass2.getDeclaredConstructor(String.class, Integer.class);
          UserEntity userEntity2 = (UserEntity) declaredConstructor.newInstance("mayikt", 22);
          System.out.println(userEntity2);
          }
          7. 反射執行給屬性賦值

          反射執行給公有屬性賦值和反射執行給私有屬性賦值

          /**
          * 反射如何給屬性賦值
          */
          @Test
          public void evaluationTest() throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException {
          Class<?> userClass2 = Class.forName("com.gblfy.elk.entity.UserEntity");
          UserEntity userEntity2 = (UserEntity) userClass2.newInstance();

          //給公有屬性賦值
          Field publicName = userClass2.getDeclaredField("publicName");
          publicName.set(userEntity2, "mayikt");
          System.out.println(userEntity2.getPublicName());

          //給私有屬性賦值
          Field userName = userClass2.getDeclaredField("userName");
          //設置訪問私有屬性權限
          userName.setAccessible(true);
          userName.set(userEntity2, "mayikt2");
          System.out.println(userEntity2.getUserName());
          }
          注意:
          xception in thread "main" java.lang.IllegalAccessException: Class com.mayikt.test.Test03 can not access a member of class com.mayikt.entity.UserEntity with modifiers "private"
          at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
          at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:296)
          at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:288)
          at java.lang.reflect.Field.set(Field.java:761)
          at com.mayikt.test.Test03.main(Test03.java:28)
          解決辦法:
          // 設置允許訪問私有屬性
          userName.setAccessible(true);
          8. 反射執行調用方法

          反射調用公有方法

          () throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException, InvocationTargetException, NoSuchMethodException {
          Class<?> userClass2 = Class.forName("com.gblfy.elk.entity.UserEntity");
          //創建類實例
          Object o = userClass2.newInstance();
          //獲取公有和私有方法
          Method method = userClass2.getDeclaredMethod("mayikt");
          //設置訪問私有方法權限
          method.setAccessible(true);
          method.invoke(o);
          }

          反射調用私有方法和反射調用方法傳遞參數

          //使用反射機制調用私有有參方法
          @Test
          public void methodCarryParamTest() throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException, InvocationTargetException, NoSuchMethodException {
          Class<?> userClass2 = Class.forName("com.gblfy.elk.entity.UserEntity");
          //創建類實例
          Object o = userClass2.newInstance();
          //獲取公有和私有方法
          Method method = userClass2.getDeclaredMethod("sum", Integer.class, Integer.class);
          //設置訪問私有方法權限
          method.setAccessible(true);
          Integer result = (Integer) method.invoke(o, 1, 5);
          System.out.println(result);
          }
          二、注解
          2.1. 注解概念

          什么是注解
          注解用來給類聲明附加額外信息,可以標注在類、字段、方法等上面,編譯器、JVM以及開發人員等都可以通過反射拿到注解信息,進而做一些相關處理

          SpringBoot 全部都是采用注解化

          2.2. 常用注解
          @Override     只能標注在子類覆蓋父類的方法上面,有提示的作用
          @Deprecated 標注在過時的方法或類上面,有提示的作用
          @SuppressWarnings("unchecked")
          2.3. 元注解
          元注解用來在聲明新注解時指定新注解的一些特性
          @Target 指定新注解標注的位置,比如類、字段、方法等,取值有ElementType.Method等
          @Retention 指定新注解的信息保留到什么時候,取值有RetentionPolicy.RUNTIME等
          @Inherited 指定新注解標注在父類上時可被子類繼承
          2.4. 常用注解
          (ElementType.METHOD) // 指定新注解可以標注在方法上
          @Retention(RetentionPolicy.RUNTIME) // 指定新注解保留到程序運行時期
          @Inherited // 指定新注解標注在父類上時可被子類繼承
          public @interface MayiktName {
          public String name();
          }
          2.5. 注解的Target
          TYPE:類、接口(包括注解類型)和枚舉的聲明
          FIELD:字段聲明(包括枚舉常量)
          METHOD:方法聲明
          PARAMETER:參數聲明
          CONSTRUCTOR:構造函數聲明
          LOCAL_VARIABLE:本地變量聲明
          ANNOTATION_TYPE:注解類型聲明
          PACKAGE:包聲明
          TYPE_PARAMETER:類型參數聲明,JavaSE8引進,可以應用于類的泛型聲明之處
          TYPE_USE:JavaSE8引進,此類型包括類型聲明和類型參數聲明
          2.6. 獲取注解信息
          package com.gblfy.elk.annotate;

          import java.lang.annotation.*;

          /**
          * ElementType.TYPE 注解在類上生效
          * ElementType.METHOD 注解在方法上生效
          * ElementType.FIELD 注解在屬性上生效
          * Retention 加此注解,反射才可以獲取
          * Inherited 子類可以繼承
          */
          @Target({ElementType.METHOD, ElementType.TYPE, ElementType.FIELD,ElementType.ANNOTATION_TYPE})
          @Retention(RetentionPolicy.RUNTIME)
          @Inherited
          public @interface MayiktName {
          }
          package com.gblfy.elk.entity;

          import com.gblfy.elk.annotate.MayiktName;

          @MayiktName
          public class UserEntity {

          private String userName;
          private Integer userAge;

          @MayiktName
          public String publicName;

          public UserEntity() {
          System.out.println("執行無參構造函數");
          }

          public UserEntity(String userName, Integer userAge) {
          System.out.println("執行有參構造函數");
          this.userName = userName;
          this.userAge = userAge;
          }

          public String getUserName() {
          return userName;
          }

          public void setUserName(String userName) {
          this.userName = userName;
          }

          public Integer getUserAge() {
          return userAge;
          }

          public void setUserAge(Integer userAge) {
          this.userAge = userAge;
          }

          public String getPublicName() {
          return publicName;
          }

          public void setPublicName(String publicName) {
          this.publicName = publicName;
          }

          @Override
          public String toString() {
          final StringBuffer sb = new StringBuffer("UserEntity{");
          sb.append("userName='").append(userName).append(''');
          sb.append(", userAge=").append(userAge);
          sb.append('}');
          return sb.toString();
          }

          @MayiktName
          private void mayikt() {
          System.out.println("mayikt");
          }

          @MayiktName
          private Integer sum(Integer a, Integer b) {
          return a + b;
          }
          }
          /**
          * 注解聯練習
          *
          * @author gblfy
          * @date 2022-03-13
          */
          public class AnnotateCase {

          //判斷某類上是否加上@MayiktName注解
          @Test
          public void classAnnotateTest() throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException {
          //加載類
          Class<?> userClass = Class.forName("com.gblfy.elk.entity.UserEntity");
          // 1.獲取當前類上的注解
          MayiktName declaredAnnotation = userClass.getDeclaredAnnotation(MayiktName.class);
          System.out.println(declaredAnnotation);
          }

          //判斷指定方法上是否加上@MayiktName注解
          @Test
          public void methodAnnotateTest() throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException {
          //加載類
          Class<?> userClass = Class.forName("com.gblfy.elk.entity.UserEntity");
          //創建類實例
          Object o = userClass.newInstance();
          //獲取指定mayikt方法
          Method method = userClass.getDeclaredMethod("mayikt");
          //獲取該方法上的注解,有則返回,無則返回null
          MayiktName mayiktName = method.getDeclaredAnnotation(MayiktName.class);
          System.out.println(mayiktName);
          }

          //判斷某屬性上是否加上@MayiktName注解
          @Test
          public void fieldAnnotateTest() throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, NoSuchFieldException {
          //加載類
          Class<?> userClass = Class.forName("com.gblfy.elk.entity.UserEntity");
          // 1.獲取屬性上的注解
          Field publicName = userClass.getDeclaredField("publicName");
          MayiktName mayiktName = publicName.getDeclaredAnnotation(MayiktName.class);
          System.out.println(mayiktName);
          }
          }
          2.7. 注解如何生效

          實際項目中 注解想生效通過反射+aop機制

          2.8. 注解實現案例

          自定義限流注解

          對我們接口實現 限流 比如 每s 只能訪問1次 或者每s 訪問兩次。
          Maven

          <dependency>
          <groupId>com.alibaba</groupId>
          <artifactId>fastjson</artifactId>
          <version>1.2.79</version>
          </dependency>
          <dependency>
          <groupId>com.google.guava</groupId>
          <artifactId>guava</artifactId>
          <version>22.0</version>
          </dependency>
          <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-aop</artifactId>
          </dependency>

          <dependency>
          <groupId>org.apache.commons</groupId>
          <artifactId>commons-lang3</artifactId>
          <version>3.12.0</version>
          </dependency>
          <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-web</artifactId>
          </dependency>

          使用谷歌的guava例子

          package com.gblfy.elk.controller;

          import com.gblfy.elk.annotate.GblfyStreamLimit;
          import com.google.common.util.concurrent.RateLimiter;
          import org.springframework.web.bind.annotation.GetMapping;
          import org.springframework.web.bind.annotation.RestController;

          @RestController
          public class SreamLimitController {

          /**
          * 每秒生成1.0個令牌
          * 滑動窗口、令牌桶、漏桶算法實現
          */
          private RateLimiter rateLimiter = RateLimiter.create(1.0);

          @GetMapping("/get")
          public String get() {
          System.out.println("-----------------執行目標方法-----------------");

          boolean result = rateLimiter.tryAcquire();
          if (!result) {
          return "當前訪問人數過多,請稍后重試!";
          }
          return "my is get";
          }

          @GetMapping("/add")
          public String add() {
          return "my is add";
          }
          }
          2.09. 封裝自定義注解限流框架

          整合自定義注解

          package com.gblfy.elk.annotate;

          import java.lang.annotation.ElementType;
          import java.lang.annotation.Retention;
          import java.lang.annotation.RetentionPolicy;
          import java.lang.annotation.Target;

          /**
          * 自定義請求限流注解
          *
          * @author gblfy
          * @date 2022-03-13
          */
          @Target(ElementType.METHOD)
          @Retention(RetentionPolicy.RUNTIME)
          public @interface GblfyStreamLimit {
          /**
          * 限流名稱
          *
          * @return
          */
          String name() default "";

          /**
          * 限流次數,默認限流頻次 1秒/20次
          *
          * @return
          */
          double limitNum() default 20.0;
          }
          2.10. 整合Aop實現接口限流
          package com.gblfy.elk.aop;

          import com.gblfy.elk.annotate.GblfyStreamLimit;
          import com.google.common.util.concurrent.RateLimiter;
          import org.aspectj.lang.JoinPoint;
          import org.aspectj.lang.ProceedingJoinPoint;
          import org.aspectj.lang.Signature;
          import org.aspectj.lang.annotation.*;
          import org.aspectj.lang.reflect.MethodSignature;
          import org.springframework.stereotype.Component;

          import java.util.concurrent.ConcurrentHashMap;

          @Aspect
          @Component
          public class StreamLimitAop {

          //并發map儲存
          private ConcurrentHashMap<String, RateLimiter> rateLimiterStrategy = new ConcurrentHashMap();

          /**
          * 只要在方法上添加該自定義限流注解,就會被AOP環繞通知攔截
          *
          * @param joinPoint
          * @return
          */
          @Around(value = "@annotation(com.gblfy.elk.annotate.GblfyStreamLimit)")
          public Object around(ProceedingJoinPoint joinPoint) {

          try {
          //獲取攔截的方法名
          Signature sig = joinPoint.getSignature();
          //獲取攔截的方法名
          MethodSignature methodSignature = (MethodSignature) sig;
          // 判斷方法上是否有加上該注解,如果有加上注解則限流
          GblfyStreamLimit gblfyStreamLimit =
          methodSignature.getMethod().getDeclaredAnnotation(GblfyStreamLimit.class);
          if (gblfyStreamLimit == null) {
          // 執行目標方法
          return joinPoint.proceed();
          }
          // 1.獲取注解上的限流名稱(name)
          String name = gblfyStreamLimit.name();

          // 2.獲取注解上的limitNum(限流次數),實現對不同的方法限流策略不一樣的效果
          double limitNum = gblfyStreamLimit.limitNum();
          RateLimiter rateLimiter = rateLimiterStrategy.get(name);
          if (rateLimiter == null) {
          //3.動態匹配并創建不同的限流策略
          rateLimiter = RateLimiter.create(limitNum);
          rateLimiterStrategy.put(name, rateLimiter);
          }
          // 開始限流
          boolean result = rateLimiter.tryAcquire();
          if (!result) {
          return "當前訪問人數過多,請稍后重試!";
          }
          return joinPoint.proceed();
          } catch (Throwable throwable) {
          return "系統出現了錯誤!";
          }
          }


          /**
          * 前置通知
          */
          @Before(value = "@annotation(com.gblfy.elk.annotate.GblfyStreamLimit)")
          public void before() {
          System.out.println("----------------------前置通知----------------------");
          }


          /**
          * 后置通知
          */
          @AfterReturning(value = "@annotation(com.gblfy.elk.annotate.GblfyStreamLimit)")
          public void AfterReturning() {
          System.out.println("----------------------后置通知----------------------");
          }

          /**
          * 異常通知
          *
          * @param point
          */
          @AfterThrowing(value = "@annotation(com.gblfy.elk.annotate.GblfyStreamLimit)", throwing = "e")
          public void serviceAspect(JoinPoint point, Exception e) {
          System.out.println("----------------------異常通知----------------------");
          }
          }
          2.11. 案例
          package com.gblfy.elk.controller;

          import com.gblfy.elk.annotate.GblfyStreamLimit;
          import org.springframework.web.bind.annotation.GetMapping;
          import org.springframework.web.bind.annotation.RestController;

          @RestController
          public class SreamLimitController {


          @GetMapping("/get2")
          @GblfyStreamLimit(name = "get2", limitNum = 1.0)
          public String get2() {
          System.out.println("-----------------執行目標方法-----------------");
          return "my is get";
          }

          @GetMapping("/add")
          public String add() {
          return "my is add";
          }
          }

          ??http://127.0.0.1:8080/get?? http://127.0.0.1:8080/my


          本文摘自 :https://blog.51cto.com/g

          97久久久久人妻精品专区_国产成人精品视频导航_国产色诱视频在线播放网站_97午夜理论电影影院
          <p id="g32nn"></p>
          1. <acronym id="g32nn"><strong id="g32nn"></strong></acronym>
            <pre id="g32nn"></pre>

            <table id="g32nn"><option id="g32nn"></option></table>