注解、反射+动态代理剖析

自定义注解

springboot 框架中定义了大量的注解,为什么加上注解之后就能实现配置了。比如@Autowired, 实现 DI 的依赖注入。

比如 RestController 为什么能够返回 JSON 类型的数据,并且接受 HTTP 请求呢? 都是注解的形式,底层使用了动态代理+反射的方式实现的。

创建自定义的注解,是@interface ,上面有 target 和 retenetion,定义了作用的目标和作用的时间。

@Target(ElementType.METHOD) //作用的目标
@Retention(RetentionPolicy.RUNTIME) //作用的时间
public @interface  LogAnnotation {
    public String  methodName() default "";
}

在方法上面使用注解

@LogAnnotation(methodName = "print")
public  void  print(){
     System.out.println("Hello World");
}

使用反射机制来处理注解。反射可以扫描所有的类。在这个里面可以做一些详细得到操作,比如获取方法的一些参数等。


public class AnnotationProcessor {

    public void process() throws NoSuchMethodException {
        Method method = test.class.getMethod("print");  //反射拿到方法  判断方法是否是注解修饰的,可以拿到注解的参数信息
        //如果是注解修饰的
        if (method.isAnnotationPresent(LogAnnotation.class)) {
            LogAnnotation annotation = method.getAnnotation(LogAnnotation.class);
            String methodName = annotation.methodName();
            System.out.println(methodName);
        }
    }
    public static void main(String[] args) {
        AnnotationProcessor annotationProcessor = new AnnotationProcessor();
        try {
            annotationProcessor.process();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }
}

反射

反射是 Java 框架的灵魂,通过反射可以获取任意一个类所有属性和方法,还可以调用这些属性和方法。

spring、mybatis 这些底层都是用了大量的反射机制。

想要通过反射获取到动态的信息 ,获取到class 对象

alt

四种方式 ,通过 类名.class, 第二个通过 Class.Forname(),第三个通过对象调用 getClass(),最后通过类加载器传入类的路径也可以获取 Class 对象。

Class<?> aClass = Class.forName("反射.User");
User o = (User) aClass.newInstance();
o.dance();

//通过Java的对象获取Class对象
User user = new User("Tom");
Class<? extends User> aClass1 = user.getClass();
aClass1.getDeclaredMethod("dance").invoke(user);

//通过类名获取
Class<User> reflectUserClass = User.class;
Field name = reflectUserClass.getDeclaredField("name");
name.setAccessible(true);
User user2 = (User) reflectUserClass.newInstance();
name.set(user2, "Tom");
System.out.println(name.get(user2)); // 应该传入 user2 对象

// 通过ClassLoader类加载器实现类的加载
Class<?> aClass2 = ClassLoader.getSystemClassLoader().loadClass("反射.User");
User user3 = (User) aClass2.newInstance();
user3.dance();

代理模式

代理模式就是 用代理对象来替代真实的对象的访问,在不改变原来对象的前提下面,.提供额外的功能.AOP,事务管理等基底层都是基于代理去实现的.

alt

静态代理

普通的静态代理对象

public interface SmsSerivce {
    public void send();
}
public class SmsSerivceImpl implements SmsSerivce{
    @Override
    public void send() {
        System.out.println("aaa");
    }
}

Proxy 代理类,实现相同的结构,将原来的对象传进来进行增强.

public class SMSProxy implements SmsSerivce{

    // 传入smsSerivce对象,要对sms进行增强的
    private SmsSerivce smsSerivce;
    public SMSProxy(SmsSerivce smsSerivce) {
        this.smsSerivce = smsSerivce;
    }
    @Override
    public void send() {
        System.out.println("代理之前设置");
        smsSerivce.send();
        System.out.println("Proxy: SMS sent");
    }
    public static void main(String[] args) {
        SmsSerivce smsSerivce1=new SmsSerivceImpl();
        SMSProxy proxy=new SMSProxy(smsSerivce1);
        proxy.send();
    }
}

alt

缺点:这样要为每一个都去写一个代理类,静态代理较为麻烦.但是写起来比较简单,底层框架更多的使用动态代理。

动态代理

在 Java 中动态代理主要是有两个核心类 InvocationHanlder 和 Proxy 类,这两个是核心类.

Proxy 是用来生成一个代理类对象的,InvocationHandler 是对代理对象的增强.

alt

package com.cl.jvppeteerdemo.静态代理;

import java.lang.reflect.Proxy;

public class JDKProxyFactory {

    public  static  Object test(Object target){
        return Proxy.newProxyInstance(
            // target通带代理类
            target.getClass().getClassLoader(),
            //被代理类实现的接口,可以有很多
            target.getClass().getInterfaces(),
            //实现invovationHandler的对象
            new DebugInvocationHandler(target)
        );
    }

    public static void main(String[] args) {
        SmsSerivce smsSerivce  = (SmsSerivce) JDKProxyFactory.test(new SmsSerivceImpl());
        smsSerivce.send();
    }
}

 package 代理设计模式.动态代理;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class CarInvocationHandler implements InvocationHandler {
    //目标对象
    private Object target;

    public CarInvocationHandler(Object target) {
        this.target = target;
    }
    //invoke方法 传入对象  method方法  和 参数
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //拿到代理对象的属性
        Field name = target.getClass().getDeclaredField("name");
        name.setAccessible(true);
        System.out.println(name.get(target)+"正在被购买");
        //调用这个mthod方法
        method.invoke(target);
        return  null;
    }
}

alt

JDK 动态代理存在的问题

只能代理实现接口的类, 如果这个类没有实现接口那么就不能实现代理。

为了解决这个问题,使用了 CGLIB 动态代理机制来避免.

CGLIB 是基于 ASM 的字节码生成库,允许我们在运行的时候对字节码进行修改和动态生成.

CGLIB 通过继承的方式实现,SpringAOP 底层就是基于 CJLIB 实现的.

定义一个接口接触 Callback 类,重写拦截方法. intercept,用来拦截增强被代理类的方法.

alt

alt

alt

#动态代理##反射#
牛牛的面试专栏 文章被收录于专栏

牛牛的面试专栏,希望自己在25年可以拿到一份大厂的SP Offer 你的点赞和收藏都是我持续更新的动力

全部评论

相关推荐

点赞 评论 收藏
分享
评论
4
5
分享

创作者周榜

更多
牛客网
牛客企业服务