Java反射学习——定义、操作、单例漏洞、IOC应用

2022-12-02 09:34:48 浏览数 (2)

1.反射的定义

​ 反向探知,在程序运行过程中动态的获取类的相关属性

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

简洁明了的介绍一下反射的优缺点:

优点 ​ 增加程序的灵活性,避免固有逻辑写死到程序中 ​ 代码相对简洁,可以提高程序的复用性

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

反射到底慢在哪里?

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

反射的使用场景:

​ 1.jdbc封装

​ 2.SpringIOC

​ 3.JdbcTemplate

​ 4.Mybatis

​ …

2.反射的操作

2.1基本操作

2.1.1.获取类对象的四种方式

假设我们有一个实体类 User

代码语言:javascript复制
	// 获取类对象的四种方式
	Class<User> class1 = User.class;
	Class<?> class2 = Class.forName("类路径名");
	Class<? extends User> class3 = new User().getClass();
	Class<?> class4 = Demo03.class.getClassLoader().loadClass("类路径名");
2.1.2.基本信息操作
代码语言:javascript复制
System.out.println(clazz1.getModifiers()); // 获取类的修饰符(二进制的int值)
System.out.println(clazz1.getPackage());//获取包名
System.out.println(clazz1.getName());//获取类名
System.out.println(clazz1.getSuperclass());//获取父类
System.out.println(clazz1.getClassLoader());//获取类加载器
System.out.println(clazz1.getSimpleName());//获取简单名称
System.out.println(clazz1.getInterfaces().length); // 获取类型实现的所有的接口
System.out.println(clazz1.getAnnotations().length);//获取注解信息

值得一提的是获取类修饰符这个方法的返回值是一个int类型的数据,其解析请看我的这一篇文章 Java反射机制——getModifiers()方法的返回值.

2.2字段的操作

新建一个User类

利用Field对类的字段进行操作

代码语言:javascript复制
   /**
     * Field操作
     * @param args
     */
    public static void main(String[] args) throws Exception {
        Class<User> userClass = User.class;
        // 获取User对象
        User user = userClass.newInstance();
        // 获取类型中定义的字段 公有的字段以及父类中公有的字段
        //getFields()与getDeclaredFields()方法的区别
        Field[] fields1 = userClass.getFields();
        for(Field f:fields1){
            System.out.println(f.getModifiers()   " "   f.getName());
        }
        System.out.println("--------------------");
        // 可以获取私有的字段  只能够获取当前类中
        Field[] fields2 = userClass.getDeclaredFields();
        for(Field f:fields2){
            System.out.println(f.getModifiers()   " "   f.getName());
        }

        // 获取name字段对应的Field
        Field nameField = userClass.getDeclaredField("name");
        // 如果要修改私有属性信息那么我们需要放开权限
        nameField.setAccessible(true);
        nameField.set(user,"小明");
        System.out.println(user.getName());
        // 如果对静态属性赋值
        Field addressField = userClass.getDeclaredField("address");
        addressField.set(null,"湖南长沙");
        System.out.println(User.address);

    }

2.3 类中的方法操作

还是那个User类

代码语言:javascript复制
    public static void main(String[] args) throws Exception {
        User user = new User();
        Class<User> userClass = User.class;
        // 可以获取当前类及其父类中的所有的共有的方法
        Method[] methods = userClass.getMethods();
        for (Method m : methods) {
            System.out.println(m.getModifiers()   " "   m.getName());
        }
        System.out.println("**********");
        // 获取本类中的所有的方法 包括私有的
        Method[] declaredMethods = userClass.getDeclaredMethods();
        for (Method m:declaredMethods){
            System.out.println(m.getModifiers()   " "   m.getName());
        }
        Method jumpMethod = userClass.getDeclaredMethod("jump");
        // 放开私有方法的调用
        jumpMethod.setAccessible(true);
        jumpMethod.invoke(user);
        Method sayMethod = userClass.getDeclaredMethod("say", String.class);
        // 静态方法调用
        sayMethod.invoke(null,666");
    }

2.4 构造器的操作

代码语言:javascript复制
    /**
     * 构造器的操作
     * @param args
     */
    public static void main(String[] args) throws Exception {
        Class<User> userClass = User.class;
        // 获取所有的公有的构造器
        Constructor<?>[] constructors = userClass.getConstructors();
        for (Constructor c:constructors){
            System.out.println(c.getModifiers()   " "   c.getName() );
        }
        System.out.println("************************");
        // 获取所有的构造器
        Constructor<?>[] declaredConstructors = userClass.getDeclaredConstructors();
        for (Constructor c:declaredConstructors){
            System.out.println(c.getModifiers()   " "   c.getName() );
        }
        // 1.直接通过newInstance创建对象
        User user = userClass.newInstance();
        // 2.获取对应的Construcator对象获取实例
        Constructor<User> declaredConstructor = userClass.getDeclaredConstructor(String.class, String.class);
        // 私有的构造器调用需要放开权限
        declaredConstructor.setAccessible(true);
        System.out.println(declaredConstructor.newInstance("小明","男"));


    }

3.单例的漏洞

产生的原因是:反射可以调用私有的构造器造成的

代码语言:javascript复制
public class PersonSingle {

    private static PersonSingle instance;

    private PersonSingle(){
        if(instance != null){
            throw new RuntimeException("实例已经存在了,不允许再创建...");
        }
    }

    public static PersonSingle getInstance(){
        if(instance == null){
            instance = new PersonSingle();
        }
        return instance;
    }
}

解决方案:在私有构造其中加入逻辑判断结合RuntimeException处理即可

代码语言:javascript复制
    public static void main(String[] args) throws Exception {
        PersonSingle p1 = PersonSingle.getInstance();
        PersonSingle p2 = PersonSingle.getInstance();
        PersonSingle p3 = PersonSingle.getInstance();
        System.out.println(p1);
        System.out.println(p2);
        System.out.println(p3);
        // 通过反射获取实例
        Constructor<? extends PersonSingle> declaredConstructor = p1.getClass().getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        System.out.println( declaredConstructor.newInstance());

    }

4.反射的应用 SpringIOC

IOC 控制反转 就是一种设计思想,容器 管理对象

代码语言:javascript复制
try {
    		// 创建对应IOC容器对象
            DefaultListableBeanFactory beanFactory = this.createBeanFactory();
            beanFactory.setSerializationId(this.getId());
            this.customizeBeanFactory(beanFactory);
    		// 配置文件中的<bean> 会被解析封装为一个 BeanDefinition 
            this.loadBeanDefinitions(beanFactory);
            Object var2 = this.beanFactoryMonitor;
            synchronized(this.beanFactoryMonitor) {
                this.beanFactory = beanFactory;
            }
        } catch (IOException var5) {
            throw new ApplicationContextException("I/O error parsing bean definition source for "   this.getDisplayName(), var5);
        }
代码语言:javascript复制
// 加载配置问题  SAX
Document doc = this.doLoadDocument(inputSource, resource);
// 配置文件解析 BeanDefinition
return this.registerBeanDefinitions(doc, resource);
代码语言:javascript复制
public void refresh() throws BeansException, IllegalStateException {
        Object var1 = this.startupShutdownMonitor;
        synchronized(this.startupShutdownMonitor) {
            this.prepareRefresh();
            // 创建IOC容器对象 BeanFactory 同时解析配置文件
            ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
            this.prepareBeanFactory(beanFactory);

            try {
                this.postProcessBeanFactory(beanFactory);
                this.invokeBeanFactoryPostProcessors(beanFactory);
                this.registerBeanPostProcessors(beanFactory);
                this.initMessageSource();
                this.initApplicationEventMulticaster();
                this.onRefresh();
                this.registerListeners();
                // 单例对象的实例化
                this.finishBeanFactoryInitialization(beanFactory);
                this.finishRefresh();
            } catch (BeansException var9) {
                if (this.logger.isWarnEnabled()) {
                    this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: "   var9);
                }

                this.destroyBeans();
                this.cancelRefresh(var9);
                throw var9;
            } finally {
                this.resetCommonCaches();
            }

        }
    }
代码语言:javascript复制
    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 var3) {
            throw new BeanInstantiationException(ctor, "Is it an abstract class?", var3);
        } catch (IllegalAccessException var4) {
            throw new BeanInstantiationException(ctor, "Is the constructor accessible?", var4);
        } catch (IllegalArgumentException var5) {
            throw new BeanInstantiationException(ctor, "Illegal arguments for constructor", var5);
        } catch (InvocationTargetException var6) {
            throw new BeanInstantiationException(ctor, "Constructor threw exception", var6.getTargetException());
        }
    }

0 人点赞