反射与自定义注解底层实现原理

反射与自定义注解底层实现原理

一、反射机制

1. 什么是反射

  1. Oracle 官方对反射的解释是:
    Reflection enables Java code to discover information about the fields, methods and constructors of loaded classes, and to use reflected fields, methods, and constructors to operate on their underlying counterparts, within security restrictions.
    The API accommodates applications that need access to either the public members of a target object (based on its runtime class) or the members declared by a given class. It also allows programs to suppress default reflective access control.
  2. 简而言之,通过反射,我们可以在运行时获得程序或程序集中每一个类型的成员和成员的信息。程序中一般的对象的类型都是在编译期就确定下来的,而 Java 反射机制可以动态地创建对象并调用其属性,这样的对象的类型在编译期是未知的。所以我们可以通过反射机制直接创建对象,即使这个对象的类型在编译期是未知的。

(1)Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法。本质是JVM得到class对象之后,再通过class对象进行反编译,从而获取对象的各种信息。
(2)Java属于先编译再运行的语言,程序中对象的类型在编译期就确定下来了,而当程序在运行时可能需要动态加载某些类,这些类因为之前用不到,所以没有被加载到JVM。通过反射,可以在运行时动态地创建对象并调用其属性,不需要提前在编译期知道运行的对象是谁。

2. 反射机制的优缺点

1、优点:在运行时获得类的各种内容,进行反编译,对于Java这种先编译再运行的语言,能够让我们很方便的创建灵活的代码,这些代码可以在运行时装配,无需在组件之间进行源代码的链接,更加容易实现面向对象。
2、缺点:
(1)反射会消耗一定的系统资源,因此如果不需要动态地创建一个对象,那么就不需要用反射;
(2)反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题。

3. 反射的用途

1.通过反射机制访问java对象的属性,方法,构造方法等
3. JDBC加载驱动连接 class.forname

Class.forName("com.mysql.jdbc.Driver"); // 动态加载mysql驱动
  1. Spring容器框架IOC实例化对象
 <bean id="mayikt" class="com.mayikt.UserEntity" />
  1. 自定义注解生效(反射+Aop)
  2. 第三方核心的框架 mybatis orm

4. 反射技术的使用

Class类 代表类的实体,在运行的Java应用程序中表示类和接口
Field类 代表类的成员变量(成员变量也称为类的属性)
Method类 代表类的方法
Constructor类 代表类的构造方法
1.getField、getMethod和getCostructor方法可以获得指定名字的域、方法和构造器。
2.getFields、getMethods和getCostructors方法可以获得类提供的public域、方法和构造器数组,其中包括超类的共有成员。
3.getDeclatedFields、getDeclatedMethods和getDeclaredConstructors方法可以获得类中声明的全部域、方法和构造器,其中包括私有和受保护的成员,但不包括超类的成员。

4.1 反射常用的API

        // 1.第一种获取class方式
        UserEntity userEntity = new UserEntity();
        Class userClass = userEntity.getClass();
        // 默认执行无参构造函数
        UserEntity user2 = (UserEntity) userClass.newInstance();
        System.out.println(user2==userEntity);
        // 2.第二种方式 直接获取class
        Class userClass = UserEntity.class;
        UserEntity user2 = (UserEntity) userClass.newInstance();
        System.out.println(user2);
       // 3.第三种方式 类的完整路径地址
       Class<?> aClass = Class.forName("com.demo.entity.UserEntity");
       UserEntity user3 = (UserEntity) aClass.newInstance();
       System.out.println(user3);
4.1.1 运行期间,一个类,只有一个Class对象产生
import com.demo.entity.UserEntity;

public class Test01 {
    public static void main(String[] args) throws IllegalAccessException, InstantiationException, ClassNotFoundException {
        // 1.第一种获取class方式
        UserEntity userEntity = new UserEntity();
        Class userClass1 = userEntity.getClass();
        // 2.第二种方式 直接获取class
        Class userClass2 = UserEntity.class;
        // 3.第三种方式 类的完整路径地址
        Class<?> userClass3 = Class.forName("com.demo.entity.UserEntity");
        System.out.println(userClass3);
        System.out.println(userClass1==userClass2);
        System.out.println(userClass1==userClass3);

    }
}

4.1.2 反射执行构造函数
4.1.2.1 执行无参数构造函数
Class<?> userClass3 = Class.forName("com.demo.entity.UserEntity");
// 1.默认执行无参构造函数
UserEntity userEntity = (UserEntity) userClass3.newInstance();
4.1.2.1 执行有参数构造函数
Constructor<?> constructor = userClass3.getConstructor(String.class, Integer.class);
UserEntity userEntity2 = (UserEntity) constructor.newInstance("demo", 22);
System.out.println(userEntity2.toString());
4.1.3 反射执行给属性赋值
4.1.3.1 反射执行给公有属性赋值
        // 1.给共有属性赋值
        Class<?> aClass = Class.forName("com.demo.entity.UserEntity");
        Field userNameField = aClass.getField("pubUserName");
        UserEntity userEntity = (UserEntity) aClass.newInstance();
        userNameField.set(userEntity, "demo");
        System.out.println(userEntity.pubUserName);

4.1.3.2 反射执行给私有属性赋值
Class<?> aClass = Class.forName("com.demo.entity.UserEntity");
UserEntity userEntity = (UserEntity) aClass.newInstance();
Field userName = aClass.getDeclaredField("userName");
// 设置允许访问私有属性
userName.setAccessible(true);
userName.set(userEntity, "demo");
System.out.println(userEntity.getUserName());

注意:
xception in thread "main" java.lang.IllegalAccessException: Class com.demo.test.Test03 can not access a member of class com.demo.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.demo.test.Test03.main(Test03.java:28)
解决办法:

// 设置允许访问私有属性
userName.setAccessible(true);
4.1.4 反射执行调用方法
4.1.4.1 反射调用公有方法
Class<?> aClass = Class.forName("com.demo.entity.UserEntity");
UserEntity userEntity = (UserEntity) aClass.newInstance();
Method demo = aClass.getDeclaredMethod("demo");
demo.invoke(userEntity);

4.1.4.2 反射调用私有方法
// 1.使用反射调用共有方法
Class<?> aClass = Class.forName("com.demo.entity.UserEntity");
UserEntity userEntity = (UserEntity) aClass.newInstance();
Method demo = aClass.getDeclaredMethod("demo");
// 2.设置允许调用私有方法
demo.setAccessible(true);
demo.invoke(userEntity);

注意:
Exception in thread "main" java.lang.IllegalAccessException: Class com.demo.test.Test04 can not access a member of class com.demo.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.Method.invoke(Method.java:491) at com.demo.test.Test04.main(Test04.java:23)
解决办法:

// 2.设置允许调用私有方法
demo.setAccessible(true);

4.1.4.3 反射调用方法传递参数
Class<?> aClass = Class.forName("com.demo.entity.UserEntity");
UserEntity userEntity = (UserEntity) aClass.newInstance();
Method demo = aClass.getDeclaredMethod("sum", Integer.class, Integer.class);
// 2.设置允许调用私有方法
demo.setAccessible(true);
Integer result = (Integer) demo.invoke(userEntity, 1, 2);
System.out.println(result);
4.1.4.4 通过反射越过泛型检查
        ArrayList<String> strings = new ArrayList<String>();
        strings.add("demo");
//        strings.add(1);
        Class<? extends ArrayList> aClass = strings.getClass();
        Method add = aClass.getDeclaredMethod("add", Object.class);
        Object invoke = add.invoke(strings, 1);
        System.out.println(strings.size());
//        strings.forEach((t) -> {
//            System.out.println(t);
//        });


二、注解

1. 注解概念

1.1 什么是注解

注解用来给类声明附加额外信息,可以标注在类、字段、方法等上面,编译器、JVM以及开发人员等都可以通过反射拿到注解信息,进而做一些相关处理

SpringBoot 全部都是采用注解化

1.2 常用注解

@Override 只能标注在子类覆盖父类的方法上面,有提示的作用
@Deprecated 标注在过时的方法或类上面,有提示的作用
@SuppressWarnings(“unchecked”) 标注在编译器认为有问题的类、方法等上面,用来取消编译器的警告提示,警告类型有serial、unchecked、unused、all

1.3 元注解

元注解用来在声明新注解时指定新注解的一些特性
@Target 指定新注解标注的位置,比如类、字段、方法等,取值有ElementType.Method等
@Retention 指定新注解的信息保留到什么时候,取值有RetentionPolicy.RUNTIME等
@Inherited 指定新注解标注在父类上时可被子类继承

1.4 注解的Target

TYPE:类、接口(包括注解类型)和枚举的声明
FIELD:字段声明(包括枚举常量)
METHOD:方法声明
PARAMETER:参数声明
CONSTRUCTOR:构造函数声明
LOCAL_VARIABLE:本地变量声明
ANNOTATION_TYPE:注解类型声明
PACKAGE:包声明
TYPE_PARAMETER:类型参数声明,JavaSE8引进,可以应用于类的泛型声明之处
TYPE_USE:JavaSE8引进,此类型包括类型声明和类型参数声明

2. 获取注解信息

         // 1.获取当前类上的注解
        Class<?> aClass = Class.forName("com.mayikt.entity.UserEntity");
//        MayiktName declaredAnnotation = aClass.getDeclaredAnnotation(MayiktName.class);
//        System.out.println(declaredAnnotation);
        // 2.获取当前方法上的注解
//        Method userNameMethod = aClass.getDeclaredMethod("getUserName");
//        MayiktName declaredAnnotation = userNameMethod.getDeclaredAnnotation(MayiktName.class);
//        System.out.println(declaredAnnotation);
        // 3.获取字段上的注解
        Field pubUserName = aClass.getDeclaredField("pubUserName");
        final MayiktName declaredAnnotation = pubUserName.getDeclaredAnnotation(MayiktName.class);
        System.out.println(declaredAnnotation);
        // 4.获得构造方法注解
        Constructor<TestAnnotation> constructors = clazz.getConstructor(new Class[] {});// 先获得构造方法对象
        MyConstructorAnnotation myConstructorAnnotation = constructors.getAnnotation(MyConstructorAnnotation.class);// 拿到构造方法上面的注解实例
        System.out.println(myConstructorAnnotation.desc() + "+" + myConstructorAnnotation.uri());

3. 注解如何生效

实际项目中 注解想生效通过反射+aop机制

4. 注解实现案例

4.1 自定义限流注解

对我们接口实现 限流 比如 每s 只能访问1次 或者每s 访问两次。

4.1.1 Maven
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.1.RELEASE</version>
</parent>
<dependencies>
    <!--  springboot 整合web组件-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
        <version>18.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>

</dependencies>

4.1.2 使用谷歌的guava例子
    /**
     * 每秒生成2.0个令牌
     * //
     */
//    private RateLimiter rateLimiter = RateLimiter.create(2.0);
    @GetMapping("/get")
//    @DemoCurrentLimit(name = "get", token = 1)
    public String get() {
//        boolean result = rateLimiter.tryAcquire();
//        if (!result) {
//            return "当前访问人数过多,请稍后重试!";
//        }
        return "my is get";
    }

4.1.3 封装自定义注解限流框架
4.1.3.1 整合自定义注解
4.1.3.2 整合AOP实现接口限流
package com.demo.service.aop;

import com.google.common.util.concurrent.RateLimiter;
import com.demo.service.annotation.DemoCurrentLimit;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.util.concurrent.ConcurrentHashMap;

@Aspect
@Component
public class CurrentLimitAop {
    /**
     * 每秒生成1.0个令牌 每s产生10 token
     */
//    private RateLimiter rateLimiter = RateLimiter.create(1);
    private ConcurrentHashMap<String, RateLimiter> rateLimiters = new ConcurrentHashMap();

    @Around(value = "@annotation(com.demo.service.annotation.DemoCurrentLimit)")
    public Object around(ProceedingJoinPoint joinPoint) {
        try {
            //获取拦截的方法名
            Signature sig = joinPoint.getSignature();
            //获取拦截的方法名
            MethodSignature methodSignature = (MethodSignature) sig;
            // 判断方法上是否有加上该注解,如果有加上注解则限流
            DemoCurrentLimit demoCurrentLimit =
                    methodSignature.getMethod().getDeclaredAnnotation(DemoCurrentLimit.class);
            if (demoCurrentLimit == null) {
                // 执行目标方法
                return joinPoint.proceed();
            }
            // 获取注解上的name
            String name = demoCurrentLimit.name();
            // 获取注解上的token
            double token = demoCurrentLimit.token();
            RateLimiter rateLimiter = rateLimiters.get(name);
            if (rateLimiter == null) {
                rateLimiter = RateLimiter.create(token);
                rateLimiters.put(name, rateLimiter);
            }
            // 开始限流
            boolean result = rateLimiter.tryAcquire();
            if (!result) {
                return "当前访问人数过多,请稍后重试!";
            }
            return joinPoint.proceed();
        } catch (Throwable throwable) {
            return "系统出现了错误!";
        }
    }
}

@Before(value = "@annotation(com.demo.service.annotation.DemoCurrentLimit)")
public void before() {
    System.out.println("---------------前置通知-----------------");
}

@AfterReturning(value = "@annotation(com.demo.service.annotation.DemoCurrentLimit)")
public void afterReturning() {
    System.out.println("---------------后置通知-----------------");
}

4.1.3.3 使用效果
package com.demo.service;

import com.google.common.util.concurrent.RateLimiter;
import com.demo.service.annotation.DemoCurrentLimit;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MemberService {
    /**
     * 每秒生成2.0个令牌
     * //
     */
//    private RateLimiter rateLimiter = RateLimiter.create(2.0);
    @GetMapping("/get")
//    @DemoCurrentLimit(name = "get", token = 1)
    public String get() {
//        boolean result = rateLimiter.tryAcquire();
//        if (!result) {
//            return "当前访问人数过多,请稍后重试!";
//        }
        return "my is get";
    }

    @GetMapping("/add")
    @DemoCurrentLimit(name = "add", token = 5)
    public String add() {
        return "my is add";
    }

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

Spring aop 有非常多通知:前置、后置、环绕通知
通知执行顺序:环绕通知、前置通知、目标方法、后置通知

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

超级码里喵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值