Java反射机制

反射

1. 反射的定义

  • 反射即反向探知,有点类似考古学家根据发掘的物品来探知以前的事情
  • 在Java程序运行状态中:
    1. 对于给定的一个类(Class)对象,可以获得这个类(Class)对象的所有属性和方法;
    2. 对于给定的一个对象( new ClassName < ? extends Object> ),都能够调用它的任意一个属性和方法

这种动态获取类的内容以及动态调用对象的方法和获取属性的机制,就叫做JAVA的反射机制。

2. 反射的优缺点

优点:

  • 增加程序的灵活性,避免固有逻辑写死到程序中
  • 代码相对简洁,可以提高程序的复用性
public interface Ball {
    public void playBall();
}

class BasketBall implements Ball {
    @Override
    public void playBall() {
        System.out.println("一起打篮球。。。。");
    }
}
class FootBall implements Ball {
    @Override
    public void playBall() {
        System.out.println("一起踢足球。。。。");
    }
}
class BallMain {
    public static void main(String[] args) throws Exception {
        System.out.println(getNewInstanceByKey("BasketBall"));
        System.out.println(getNewInstanceByKey("FootBall"));
    }
    public static Ball getInstanceByKey (String key) {
        if ("BasketBall".equals(key)) {
            return new BasketBall();
        } else if ("FootBall".equals(key)) {
            return new FootBall();
        }
        // 如果要增加排球、羽毛球、、、很多球,下面还要继续判断,创建实例吗?
        return null;
    }
    public static Ball getNewInstanceByKey (String key) throws Exception {
        String basePackage = "com.elian.reflex";
        Ball ball = null;
        Class<?> clazz = Class.forName(basePackage + "." + key);
        ball = (Ball) clazz.newInstance();
        return ball;
    }
}

缺点:

  • 相比于直接调用,反射有比较大的性能消耗
  • 内部暴露和安全隐患

反射到底慢在哪里?

  1. 调用了native方法
  2. 每次new Instance都会做安全检查 比较耗时

3. 深入class内部

image-20220228181503535

3.1 基本操作

1) 获取类对象的4种方式

public class ReflexTest {
    public static void main(String[] args) throws Exception {
        Class<Person> p1 = Person.class;
        Class<?> p2 = Class.forName("com.elian.reflex.Person");
        Class<? extends Person> p3 = new Person().getClass();
        Class<?> p4 = ReflexTest.class.getClassLoader().loadClass("com.elian.reflex.Person");
    }
}

2) 基本操作

// 获取类的相关结构
int modifiers = p1.getModifiers();  // 获取类修饰符
p1.getPackage() // 获取包名
p1.getName() // 获取全路径名
p1.getSuperclass() // 获取父类
p1.getClassLoader() // 获取类加载器
p1.getSimpleName() // 获取类名
p1.getInterfaces() // 获取类型所有实现的接口
p1.getAnnotations() // 获取所有注解

3) 字段的操作

public static void main(String[] args) throws Exception {
    // 获取Person对象
    Class<Person> clazz = Person.class;
    Person person = clazz.newInstance();

    Field[] clazzFields = clazz.getFields(); // 只能获取当前类及父类public的字段
    Field[] declaredFields = clazz.getDeclaredFields(); // 可以获取当前类中所有的字段

    for (Field field :
         clazzFields) {
        System.out.println(field.getModifiers() + " " + field.getName());
    }
    System.out.println( "========" );
    for (Field field :
         declaredFields) {
        System.out.println(field.getModifiers() + " " + field.getName());
    }

    Field nameField = clazz.getDeclaredField("name");
    nameField.setAccessible(true); // 设置权限,private的字段可以设置值
    nameField.set(person, "Elian");
        
    // 对静态字段进行操作
    Field addressField = clazz.getDeclaredField("address");
    addressField.set(null, "北京通州");
}

4) 对方法的操作

    public static void main(String[] args) throws Exception {
        Person person = new Person();
        Class<Person> clazz = Person.class;
        Method[] methods = clazz.getMethods();  // 获取当前类及父类中所有的public修饰的方法
        Method[] declaredMethods = clazz.getDeclaredMethods(); // 获取本类中所有方法
        for (Method method :
                methods) {
            System.out.println( method.getModifiers() + " " + method.getName() );
        }
        System.out.println( "================" );
        for (Method method :
                declaredMethods) {
            System.out.println( method.getModifiers() + " " + method.getName() );
        }

        Method jump = clazz.getDeclaredMethod("jump");
        jump.invoke(person);

        Field address = clazz.getDeclaredField("address");
        address.setAccessible(true);
        address.set(null, "北京通州");

        Method getAddress = clazz.getDeclaredMethod("getAddress");
        getAddress.setAccessible(true);
        System.out.println(getAddress.invoke(null));
    }

5) 对构造器的 操作

public static void main(String[] args) throws Exception {
    Class<Person> clazz = Person.class;
    Constructor<?>[] constructors = clazz.getConstructors();
    Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors();
    for (Constructor constructor :
         constructors) {
        System.out.println(constructor.getModifiers() + " " + constructor.getName());
    }
    System.out.println("===============");
    for (Constructor constructor :
         declaredConstructors) {
        System.out.println(constructor.getModifiers() + " " + constructor.getName());
    }

    // 创建实例两种方式
    // 1. 直接通过newInstance创建对象
    Person person1 = clazz.newInstance();

    // 2. 获取对应的Constructor对象获取实例
    Constructor<Person> declaredConstructor = clazz.getDeclaredConstructor(String.class);
    declaredConstructor.setAccessible(true);
    Person person2  = declaredConstructor.newInstance("Elian");
    System.out.println(person2.getName());
}

漏洞:对于单例的对象来说,能够调用私有的构造器创建新的对象,比较危险,所以应在代码中控制对象的创建。

public class SinglePerson {
    private static final SinglePerson instance = new SinglePerson();

    public static SinglePerson getInstance() {
        return instance;
    }

    private SinglePerson() {
        if (instance != null)
            throw new RuntimeException( "The instance of SinglePerson has been created and cannot be created it again" );
    }

    public static void main(String[] args) {
        SinglePerson s1 = SinglePerson.getInstance();
        SinglePerson s2 = SinglePerson.getInstance();

        System.out.println(s1);
        System.out.println(s2);
    }
}

3.2 反射的使用场景

  1. jdbc的封装
  2. Spring中的IoC
  3. jdbcTemplate
  4. MyBatis

4. SpringIoC中反射的应用

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        prepareRefresh();

        // 创建Ioc容器对象 BeanFactory 同时解析配置文件
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        prepareBeanFactory(beanFactory);
        try {
            postProcessBeanFactory(beanFactory);
            invokeBeanFactoryPostProcessors(beanFactory);
            registerBeanPostProcessors(beanFactory);
            initMessageSource();
            initApplicationEventMulticaster();

            onRefresh();
            registerListeners();
            // 单例对象的实例化
            finishBeanFactoryInitialization(beanFactory);
            finishRefresh();
        }
        catch (BeansException ex) {
            if (logger.isWarnEnabled()) {
                logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
            }
            destroyBeans();
            cancelRefresh(ex);
            throw ex;
        }

        finally {
            resetCommonCaches();
        }
    }
}

'==========================='
    
public abstract class  BeanUtils {
    //....
    
    public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException {
        Assert.notNull(ctor, "Constructor must not be null");
        try {
            ReflectionUtils.makeAccessible(ctor);
            return ctor.newInstance(args);
        }
        catch (InstantiationException ex) {
            throw new BeanInstantiationException(ctor, "Is it an abstract class?", ex);
        }
        catch (IllegalAccessException ex) {
            throw new BeanInstantiationException(ctor, "Is the constructor accessible?", ex);
        }
        catch (IllegalArgumentException ex) {
            throw new BeanInstantiationException(ctor, "Illegal arguments for constructor", ex);
        }
        catch (InvocationTargetException ex) {
            throw new BeanInstantiationException(ctor, "Constructor threw exception", ex.getTargetException());
        }
    }
    
    // ....
}
posted @ 2022-02-28 22:42  coderElian  阅读(48)  评论(0)    收藏  举报