注解和反射的理解

反射

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

  2. Java属于先编译再运行的语言,程序中对象的类型在编译期就确定下来了,而当程序在运行时可能需要动态加载某些类,这些类因为之前用不到,所以没有被加载到JVM。

  3. 通过反射,可以在运行时动态地创建对象并调用其属性,不需要提前在编译期知道运行的对象是谁。

  4. 在运行时期,一个类只有一个Class对象产生

反射的优缺点

优点:可以在运行时获得类的各种内容,进行反编译,对于Java这种先编译再运行的特点,能够让我们很方便的创建灵活的代码,这些代码可以在运行时进行装配,不用在组件之间进行代码之间的链接。

缺点:反射会消耗一定的系统资源,假如不需要动态的创建对象的时候,就不要使用反射;反射调用方法的时候可以忽略权限的检查,因此可能会破坏封装性而导致安全问题

反射的用途

反编译:.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

反射常用的API

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

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

Method类 代表类的方法

Constructor类 代表类的构造方法

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

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

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

获取Class的方式

        // 获取Class的方式
        User user = new User();
        Class<? extends User> aClass3 = user.getClass();  // 方法1
        User user1 = aClass3.newInstance();
        
        Class<User> userClass = User.class;          // 方法2
        User user5 = userClass.newInstance();

        Class<?> aClass = Class.forName("com.study.entity.User");  // 方法2
        User user8 = (User) aClass.newInstance();
        
        // 都是获取的同一个类的字节码文件
        System.out.println(aClass == userClass);

        // 通过构造器进行赋值
        Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(String.class, Integer.class);
        User user2 = (User) declaredConstructor.newInstance("消化", 198);
        System.out.println(user2);


	  /**
         * Class.forName(xxx.xx.xx) 返回的是一个类,
         * .newInstance() 后才创建一个对象
         * Class.forName(xxx.xx.xx);的作用是要求JVM查找并加载指定的类
         */
        // Class.forName:返回与给定的字符串名称相关联类或接口的Class对象。 (class com.study.entity.User)
        Class<?> aClass2 = Class.forName(User.class.getName());
        // 获取setAge这个方法 (public void com.study.entity.User.setAge(java.lang.Integer) )
        Method setAge = aClass2.getDeclaredMethod("setAge", Integer.class);
        // 开启能直接操作私有属性的权限
        setAge.setAccessible(true);
        // 创建一个实例对象,默认执行无参构造函数 (User(name=null, age=null))
        User user4 = (User) aClass2.newInstance();
        // invoke() 重要方法,框架底层的调用都是使用反射来创建对象
        setAge.invoke(user4,199);
        System.out.println(user4.toString());

注解

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

元注解

元注解用来在声明新注解时指定新注解的一些特性

@Target 指定新注解标注的位置,比如类、字段、方法等,取值有ElementType.Method等

@Retention 指定新注解的信息保留到什么时候,取值有 RetentionPolicy.RUNTIME (指定新注解保留到程序运行时期)等

@Inherited 指定新注解标注在父类上时可被子类继承

@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LoginToking {
}
public static void main(String[] args) throws Exception{
        Class<?> aClass = Class.forName(User.class.getName());
        /*---------------------------获取属性上是否加了某个注解----------------------------------------------*/

        // 获取到属性上是否加了某个注解:先使用getDeclaredField获取到这个方法,然后再根据getDeclaredAnnotation获取注解
        // 类.class : java里面有一个class类,通过它可以实例一个对象,不用用new来创建对象
        LoginToking name = aClass.getDeclaredField("name").getDeclaredAnnotation(LoginToking.class);
        System.out.println(name.toString());
        System.out.println(name.annotationType());

        /*---------------------------获取方法上是否加了某个注解--------------------------*/
        Method method = aClass.getDeclaredMethod("getName");
        LoginToking declaredAnnotation1 = method.getDeclaredAnnotation(LoginToking.class);
        System.out.println(declaredAnnotation1.toString());

        /*---------------------------获取类上是否加了某个注解--------------------------*/
        LoginToking declaredAnnotation = aClass.getDeclaredAnnotation(LoginToking.class);
        System.out.println(declaredAnnotation.toString());
    }

使用AOP+反射+自定义注解实现限流框架

1. 定义一个注解,设置好作用域和属性
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface LimitRate {

    String name() default "";

    // 每秒访问的次数
    double token() default 20;
}
2. 编写AOP配置类
@Component
@Aspect
public class LimitAOP {
    // 创建一个具有指定稳定吞吐量(QPS 2/s)
//    private RateLimiter rateLimiter = RateLimiter.create(2.0);

    // 定义一个ConcurrentHashMap来存放每次请求不同的限流规则
    private ConcurrentHashMap<String, RateLimiter> rateLimiters = new ConcurrentHashMap();

      // @Before      在切点方法之前执行
      //@After         在切点方法之后执行
      //@AfterReturning 切点方法返回后执行
    //@AfterThrowing 切点方法抛异常执行
    //@Around     属于环绕增强,能控制切点执行前,执行后

    @Around(value = "@annotation(com.example.demo.annotation.LimitRate)")
    public Object around(ProceedingJoinPoint joinPoint){

        // 获取拦截的方法名
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature)signature;
        LimitRate declaredAnnotation = methodSignature.getMethod().getDeclaredAnnotation(LimitRate.class);

        // 获取该注解的name和token参数
        String name = declaredAnnotation.name();
        double token = declaredAnnotation.token();

        RateLimiter rateLimiter = rateLimiters.get(name);
        if (rateLimiter == null) {
            rateLimiter = RateLimiter.create(token);
            rateLimiters.put(name, rateLimiter);
        }
        // 开始进行限流
        boolean b = rateLimiter.tryAcquire();
        if(!b){
            return "当前访问人数过多,请稍后再试";
        }
        Object proceed = null;
        try {
            proceed = joinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        System.out.println(proceed);
        return proceed;
    }

}
3. 将要进行限流的方法上加上自定义的注解
@RestController
public class MemberController {

    @GetMapping("/get")
    @LimitRate(name = "get",token = 1)
    public String get(){
        return "index";
    }

    @GetMapping("/get1")
    @LimitRate(name = "get1",token = 3)
    public String get1(){
        return "index1";
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值