Java反射机制和动态代理

前言:

反射机制是java的高级特性之一,而且也是基础,那么反射有什么应用场景呢,当然平常写业务逻辑开发一般是用不到的,它的应用是在框架里面,所以看源码,学习框架不懂这个代码都看不懂,所以还是要掌握它。

反射是什么?

java反射是在运行状态下,对于任意一个类,都能知道这个类的所有属性和方法,对于任意一个对象,都能调用它的任意一个方法和属性,这种动态获取信息以及动态调用队形的功能成为java语言的反射机制。

Class

  • 提到反射就不能不提Class了,Class可以说是反射能够实现的基础,这里说的Class和class关键字不是一个东西,class关键字是在声明一个java类的时候用的,而Class是javaJDK提供的一个类

  • 对于每一个类,Java虚拟机都会初始化一个Class类型的实例,每当我们编写并且编译一个新创建的类就会产生一个对象的Class对象,并且这个Class对象会被报存到同名的.class文件中,当我们new出一个实例或者引用静态变量时,Java虚拟机中类加载器会将对应的Class对象加载到JVM中,然后JVM会根据这个类型信息的Class对象来创建我们需要的实力对象或者提供静态变量的引用值。

类的结构信息组成

  • AnnotatedElement接口:该接口提供了获取注解信息的一些方法,即所有的参数、方法成员变量、构造方法都能通过反射获取到注解的相关信息

  • package类:对应Java包 的一些信息。

  • Parameter类:对应构造方法或者成员方法的参数类型,如参数访问限制,参数类型等

  • AccessibleObject类:该类是成员变量。成员方法、构造方法的父类。提供了一些public private等修饰符的入口检查

  • GenericDeclaration类:提供泛型的一些信息

  • Field类:对应的是成员变量

  • Executable类:可执行的、由两个子类、成员方法和构造方法,提供了两种方法公共的必要进行的一些操作,比如获取方法参数、获取方法名称、获取方法访问修饰符等等。

  • Method类:对应方法的类,提供了累的一些方法信息

  • Constructor类:对应构造方法的类,提供了构造方法的一些信息

  • Class类:包含了类的所有信息

    一般我们常用的一般都是Field、Method Constructor这三个类

反射的使用

获取到java中要反射类的字节码有三种方法
  1. 任意一个类都有一个隐士的静态成员变量class
    Class c=Statudent.class;
    
  2. 通过getClass方法获得
    Class c2=mStatudent.getClass();
    
  3. 通过Class.forname();
    Class c=Class.forName("包名+类名");
    
得到Class对象 我们就可以获取这个类中任意一个方法或者属性
  1. 获取该类中指定的属性
    Class studentClass=student.getClass();
    Fiele ageField=studentClass.getDeclaredField("age);
    ageField.setAccessible(true);
    System.out.println("student的年龄"+student.getAge());                               
    
  2. 获取类中所有的字段包括父类的
    Class studentClass=student.getClass();
    Field[] fieldArray=studentClass.getFields();
    for(int i=-;i
  3. 获取该类所有的字段不包括父类的
    Class studentClass=student.getClass();
    Field[] fieldArray=studentClass.getDeclaredFields();//主要是这个方法的区别
    for(int i=-;i
  4. 修改非静态的方法和属性需要一个要修改的类的对象
    Student student=new Student(20);
    Class studentClass=student.getClass();
    Field ageField=studentClass.getDeclaredField("age);
    ageField.setAccessible(trye);
    ageField.set(student,30);
    System.out.println("当前学生通过反射修改后的年龄是"+student.getAge());
    

    上面需要注意的是如果字段是非public的 需要在访问该字段之前设置setAccessible(true),那么对于一个final字段是否可以通过反射修改它的值呢,答案是肯定的,前提是在访问该字段之前要取消该字段的访问权限,但是如果该字段即被static修饰又被final修饰,那么是无法修改的。

  5. 修改静态的字段

    和修改非静态字段相比,修改类的静态字段就要轻松的多,因为静态字段是属于类所有的,所以在修改静态字段的时候就不需要在传递一个该类的实例对象了

    Field nameField=studentClass.getDeclaredField("name);
    nameField.setAccessible(true);
    nameField.set(null,"demo");
    System.out.println("反射修改静态字段  修改的名字是"+nameField.get(null));
    
通过反射获取并且调用类中的方法

Class类提供了4中方式获取类中的方法,其实和获取Field一样

  1. getMethod(String name,Class[] params) 使用特定的参数类型,获得name名字相同的公共方法
  2. Method[] getMethods() 获取类的所有公共方法 有父类的
  3. Method getDeclaredMethod(String name,Class[] params);可以获取私有的方法通过特定的参数类型。
  4. Method[] getDeclaredMethods() 获得类声明的所有方法 不包括父类的

和前面设置属性一样,method 有invoke(Object obj,Object ...args)这个方法,通过这个方法,可以调用任意一个类的任意一个方法,当然 如果要是调用的静态方法,那么第一个参数就直接填null。

反射机制的优缺点

  • 优点:就是可以实现动态创建对象和编译,强大的灵活性,并且代码简洁明了,
  • 缺点:对性能有影响,使用反射是一种解释操作,而且还绕过了源码,会干扰原来的内部逻辑

代理

代理是一种常见的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问,代理类负责委托类预处理,以及进行被委托类执行后的处理。

如果需要委托类来处理某一个业务,那么我就可以先在代理类中统一处理然后在调用具体实现类。

分类:
  1. 静态代理:由开发人员创建代理类,在对其进行编译,代理类内部是持有真实类的对象的。在程序运行钱代理类的.class文件就已经存在了
  2. 动态代理:在程序运行时使用反射机制动态创建一个代理类。根据jdk的源码可以看到
动态代理:
//先定义接口
interface Subject{
    void say(String name);
}

//这是真实处理对象
class RealSubject implements Subject{
    
    public void say(String name){
        System.out.println("我的姓名是"+name);
    }
}
//下面是一个动态代理类,

class CustomInvocationHandler implements Invocationhandler{
    
    Object  subObj;
    public CustomInvocationHandler(Object obj){
        this.subObj=obj;
    }
    
    public Object getProxyInstance(){
        rerutn Proxy.newProxyInstance(subObj.getClass.getClassLoader(),subObj.getClass.getInterfaces,this);
    }
    
    public Object invoke(Object proxy,Method method,Object[] args){
       return method.invoke(subObj,args);
    }
}


//   下面开始使用动态代理类
RealSubject realObj=new RealSubject();
CustomInvocationHandler mInvocation=new CustomInvocationHandler(realObj);
Subject proxyObj=mInvocation.getProxyInstance();//得到代理对象 转成 
proxyObj.say();

jdk中生成动态代理的源码:

ProxyUtils.generateClassFile(aafactory.getClass(),employee1.getClass().getSimpleName();ProxyUtils.generateClassFile(bbToolsFactory.getClass(),employee2.getClass().getSimpleName());
Method[] methods = aafactory.getClass().getMethods();
for(Method method:methods) {
  System.out.println(method.getName());//打印方法名称
 }
public class ProxyUtils {

    public static void generateClassFile(Class clazz,String proxyName){
        /*ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);*/
        byte[] proxyClassFile =ProxyGenerator.generateProxyClass(
                proxyName, new Class[]{clazz});
        String paths = clazz.getResource(".").getPath();
        System.out.println(paths);
        FileOutputStream out = null;

        try {
            out = new FileOutputStream(paths+proxyName+".class");
            out.write(proxyClassFile);
            out.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

你可能感兴趣的:(Java反射机制和动态代理)