java 注解取返回值 java 注解实现原理

admin2024-06-11  3

什么是反射

(1)Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法。本质是JVM得到class对象之后,再通过class对象进行反编译,从而获取对象的各种信息。

(2)Java属于先编译再运行的语言,程序中对象的类型在编译期就确定下来了,而当程序在运行时可能需要动态加载某些类,这些类因为之前用不到,所以没有被加载到JVM。通过反射,可以在运行时动态地创建对象并调用其属性,不需要提前在编译期知道运行的对象是谁。

使用反射机制可以动态获取当前class的信息 比如方法的信息、注解信息、方法的参数、属性等;

.java 源代码 编译.class 类加载器 jvm 字节码

反射机制的优缺点

第三方框架—创建对象 不是直接new 反射机制创建

目的:提供开发者能够更好封装框架实现扩展功能。

在一个类中 定义了一个私有属性/方法 反射机制私有属性

1、优点:在运行时获得类的各种内容,进行反编译,对于Java这种先编译再运行的语言,能够让我们很方便的创建灵活的代码,这些代码可以在运行时装配,无需在组件之间进行源代码的链接,更加容易实现面向对象。

2、缺点:

(1)反射会消耗一定的系统资源,因此如果不需要动态地创建一个对象,那么就不需要用反射;

(2)反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题。

反射的用途

反编译:.class–>.java

1.通过反射机制访问java对象的属性,方法,构造方法等

\3. JDBC加载驱动连接 class.forname

Class.forName(“com.mysql.jdbc.Driver”); // 动态加载mysql驱动

\4. Spring容器框架IOC实例化对象

\5. 自定义注解生效(反射+Aop)

\6. 第三方核心的框架 mybatis orm

反射技术的使用

Class类 代表类的实体,在运行的Java应用程序中表示类和接口

Field类 代表类的成员变量(成员变量也称为类的属性)

Method类 代表类的方法

Constructor类 代表类的构造方法

1.getField、getMethod和getCostructor方法可以获得指定名字的域、方法和构造器。

2.getFields、getMethods和getCostructors方法可以获得类提供的public域、方法和构造器数组,其中包括超类的共有成员。

3.getDeclatedFields、getDeclatedMethods和getDeclaredConstructors方法可以获得类中声明的全部域、方法和构造器,其中包括私有和受保护的成员,但不包括超类的成员。

反射常用的Api

(1)Object–>getClass

(2)任何数据类型(包括基本的数据类型)都有一个“静态”的class属性

(3)通过class类的静态方法:forName(String className)(最常用)

Class<?> aClass = Class.forName(“com.mayikt.entity.UserEntity”);

// 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.mayikt.entity.UserEntity");
UserEntity user3 = (UserEntity) aClass.newInstance();
System.out.println(user3);

运行期间,一个类,只有一个Class对象产生
验证:

import com.mayikt.entity.UserEntity;

/**
 * @author 余胜军
 * @ClassName Test01
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.mayikt.entity.UserEntity");
        System.out.println(userClass3);
        System.out.println(userClass1==userClass2);
        System.out.println(userClass1==userClass3);

    }
}

反射执行构造函数
执行无参数构造函数

Class<?> userClass3 = Class.forName("com.mayikt.entity.UserEntity");
// 1.默认执行无参构造函数
UserEntity userEntity = (UserEntity) userClass3.newInstance();

执行有参数构造函数

Constructor<?> constructor = userClass3.getConstructor(String.class, Integer.class);
UserEntity userEntity2 = (UserEntity) constructor.newInstance("mayikt", 22);
System.out.println(userEntity2.toString());

反射执行给属性赋值
反射执行给公有属性赋值

// 1.给共有属性赋值
//        Class<?> aClass = Class.forName("com.mayikt.entity.UserEntity");
//        Field userNameField = aClass.getField("pubUserName");
//        UserEntity userEntity = (UserEntity) aClass.newInstance();
//        userNameField.set(userEntity, "mayikt");
//        System.out.println(userEntity.pubUserName);

反射执行给私有属性赋值

Class<?> aClass = Class.forName("com.mayikt.entity.UserEntity");
UserEntity userEntity = (UserEntity) aClass.newInstance();
Field userName = aClass.getDeclaredField("userName");
// 设置允许访问私有属性
userName.setAccessible(true);
userName.set(userEntity, "mayikt");
System.out.println(userEntity.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);

反射执行调用方法
反射调用公有方法

Class<?> aClass = Class.forName("com.mayikt.entity.UserEntity");
UserEntity userEntity = (UserEntity) aClass.newInstance();
Method mayikt = aClass.getDeclaredMethod("mayikt");
mayikt.invoke(userEntity);

反射调用私有方法

// 1.使用反射调用共有方法
Class<?> aClass = Class.forName("com.mayikt.entity.UserEntity");
UserEntity userEntity = (UserEntity) aClass.newInstance();
Method mayikt = aClass.getDeclaredMethod("mayikt");
// 2.设置允许调用私有方法
mayikt.setAccessible(true);
mayikt.invoke(userEntity);
Exception in thread "main" java.lang.IllegalAccessException: Class com.mayikt.test.Test04 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.Method.invoke(Method.java:491)
	at com.mayikt.test.Test04.main(Test04.java:23)
解决办法:

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

反射调用方法传递参数.

Class<?> aClass = Class.forName("com.mayikt.entity.UserEntity");
UserEntity userEntity = (UserEntity) aClass.newInstance();
Method mayikt = aClass.getDeclaredMethod("sum", Integer.class, Integer.class);
// 2.设置允许调用私有方法
mayikt.setAccessible(true);
Integer result = (Integer) mayikt.invoke(userEntity, 1, 2);
System.out.println(result);

通过反射越过泛型检查
泛型用在编译期,编译过后泛型擦除(消失掉),所以是可以通过反射越过泛型检查的

ArrayList<String> strings = new ArrayList<String>();
strings.add("mayikt");
//        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);
//        });

注解

注解概念

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

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

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

常用注解

@Target(ElementType.METHOD) // 指定新注解可以标注在方法上
 @Retention(RetentionPolicy.RUNTIME) // 指定新注解保留到程序运行时期
 @Inherited // 指定新注解标注在父类上时可被子类继承
 public @interface MayiktName {
 public String name();
 }


自定义注解 运行 :反射+aop
注解的Target

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

获取注解信息

// 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());

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

注解实现案例
自定义限流注解

对我们接口实现 限流 比如 每s 只能访问1次 或者每s 访问两次。
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>

使用谷歌的guava例子

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

封装自定义注解限流框架

整合自定义注解

整合Aop实现接口限流

package com.mayikt.service.aop;

import com.google.common.util.concurrent.RateLimiter;
import com.mayikt.service.annotation.MayiktCurrentLimit;
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;

/**
 * @author songchuanfu
 * @ClassName ExtTransactionalAop
@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.mayikt.service.annotation.MayiktCurrentLimit)")
    public Object around(ProceedingJoinPoint joinPoint) {
        try {
            //获取拦截的方法名
            Signature sig = joinPoint.getSignature();
            //获取拦截的方法名
            MethodSignature methodSignature = (MethodSignature) sig;
            // 判断方法上是否有加上该注解,如果有加上注解则限流
            MayiktCurrentLimit mayiktCurrentLimit =
                    methodSignature.getMethod().getDeclaredAnnotation(MayiktCurrentLimit.class);
            if (mayiktCurrentLimit == null) {
                // 执行目标方法
                return joinPoint.proceed();
            }
            // 获取注解上的name
            String name = mayiktCurrentLimit.name();
            // 获取注解上的token
            double token = mayiktCurrentLimit.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.mayikt.service.annotation.MayiktCurrentLimit)")
public void before() {
    System.out.println("---------------前置通知-----------------");
}
@AfterReturning(value = "@annotation(com.mayikt.service.annotation.MayiktCurrentLimit)")
public void afterReturning() {
    System.out.println("---------------后置通知-----------------");
}

使用效果

package com.mayikt.service;

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

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

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

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

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

1.需要自定义一个限流注解;
2.限流注解如何生效?
如何判断方法上是否有加上该限流注解呢?
Spring Aop
当前
方法前后做拦截
Aop 前置、后置、环绕通知等

Aop 环绕通知 在环绕通知
环绕通知方法

{
 // 1.使用反射机制判断该请求方法上是否有加上
 限流 注解;
 // 2.如果该方法上有加上该注解 则调用限流api框架}


Spring aop 有非常多通知
前置、后置、环绕通知

为什么需要使用环绕通知 来处理判断限流呢
环绕通知非常的灵活控制目标方法是否执行。

环绕通知
前置通知
目标方法
后置通知

异常通知
前置通知 如何触发
如果在环绕通知中调用了目标方法,则会先执行前置通知 在执行目标方法。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明原文出处。如若内容造成侵权/违法违规/事实不符,请联系SD编程学习网:675289112@qq.com进行投诉反馈,一经查实,立即删除!