<tbody id="j99e4"></tbody>

<dd id="j99e4"></dd>

  • <button id="j99e4"><object id="j99e4"></object></button>
      1. <th id="j99e4"></th>
        <button id="j99e4"><acronym id="j99e4"></acronym></button><rp id="j99e4"><object id="j99e4"><input id="j99e4"></input></object></rp>
        當前位置:首頁 > 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

        亚洲人成图偷偷小说_亚洲图片小说激情综合_国产精品亚洲自在线播放页码_久久综合亚洲色hezyo国产
        <tbody id="j99e4"></tbody>

        <dd id="j99e4"></dd>

      2. <button id="j99e4"><object id="j99e4"></object></button>
          1. <th id="j99e4"></th>
            <button id="j99e4"><acronym id="j99e4"></acronym></button><rp id="j99e4"><object id="j99e4"><input id="j99e4"></input></object></rp>