一、 反射是什么?
允许程序在运行时查询和操作对象的类型信息。通过反射,程序能够在运行时获取对象的类定义信息,如类的名称、方法、字段、注解等,并且可以动态地调用对象的方法或访问其字段,而无需在编译时具体知道对象的类。本质是JVM得到class对象之后,再通过class对象进行反编译,从而获取对象的各种信息。
Java属于先编译再运行的语言,程序中对象的类型在编译期就确定下来了,而当程序在运行时可能需要动态加载某些类,这些类因为之前用不到,所以没有被加载到JVM。通过反射,可以在运行时动态地创建对象并调用其属性,不需要提前在编译期知道运行的对象是谁。
二、为什么反射机制能实现动态获取?
1. Class对象: 在Java中,每个类都有一个对应的Class对象,可以通过类名或对象实例的getClass()方法获取。Class对象包含了类的结构信息,如类的方法、属性、构造函数等。
2. 获取类的信息: 反射机制允许在运行时通过Class对象来获取类的各种信息,如方法、属性、注解等。通过Class对象的方法,如getMethod()、getField()等,可以获取类的方法、属性等的信息。
3. 调用方法和访问属性: 反射机制允许在运行时动态地调用类的方法、访问类的属性。通过Method对象的invoke()方法,可以调用类的方法;通过Field对象的get()、set()方法,可以访问类的属性。
4. 实例化对象: 反射机制还可以在运行时动态地实例化对象。通过Class对象的newInstance()方法,可以创建类的实例。
三、 反射的用途
很多流行的Java框架,如Spring、Spring Boot和MyBatis等,都广泛使用了反射机制和动态代理。
1. 在Spring和Spring Boot中,通过依赖注入和面向切面编程(AOP),框架能够在运行时动态地管理和配置组件。这通常涉及到在运行时创建对象、调用对象的方法以及处理依赖关系,其中反射机制发挥了关键作用。Spring的核心容器使用反射来实现依赖注入,动态地创建和管理bean。同时,Spring AOP利用动态代理来实现切面的织入,以便在方法调用前后执行额外的逻辑。
2. 在MyBatis中,动态代理也是其实现原理的关键部分。在Mybatis的开发过程中,程序员更加关注 Mapper接口中的方法以及 xxxMapper.xml文件的编写。但是我们仅仅只是写了一个方法名和Sql语句,并且接口是不能被实例化的,那么Mybatis是如何通过 Mapper接口来执行对应的Sql语句呢?
答:MyBatis使用动态代理来创建Mapper接口的实现类,从而避免了手动编写SQL语句和结果集的映射代码。通过动态代理,MyBatis能够在运行时根据Mapper接口的方法动态地生成SQL并执行数据库操作。具体来说,通过 sqlSession.getMapper(UserMapper.class) 方法获取 Mapper 接口的实例时,实际上是通过动态代理生成了一个符合 Mapper 接口定义的实现类,并将其返回给调用者。这样可以在运行时动态地生成 SQL 语句,实现与 Mapper 接口方法的映射,从而将 Mapper 接口与实际的 SQL 实现解耦。
下面是一个简单的示例,演示了如何在MyBatis中使用XML文件定义SQL语句:
(1)创建XML文件:首先,创建一个XML文件,通常以.xml为后缀,用于定义SQL映射。在该文件中,可以定义SQL语句以及参数映射、结果映射等信息。
<!-- UserMapper.xml -->
<mapper namespace="com.example.mapper.UserMapper"><select id="getUserById" resultType="com.example.model.User">SELECT * FROM users WHERE id = #{id}</select>
</mapper>
(2)创建Mapper接口:然后,创建一个Java接口,用于与XML文件进行关联。该接口中定义的方法名称和XML文件中定义的SQL语句的id相对应。
// UserMapper.java
package com.example.mapper;import com.example.model.User;public interface UserMapper {User getUserById(int id);
}
(3)配置MyBatis:在MyBatis的配置文件中,通常是mybatis-config.xml,配置MyBatis扫描XML文件的位置,并将其与对应的Mapper接口进行关联。
<!-- mybatis-config.xml -->
```xml
```xml
```xml
```xml
(4)使用Mapper接口:最后,在应用程序中通过MyBatis的SqlSession对象来获取Mapper接口的实例,并调用定义的方法执行SQL语句。
// 使用Mapper接口执行SQL语句
SqlSession sqlSession = ...; // 获取SqlSession对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.getUserById(123);
3. Java 中的一大利器 注解 的实现也用到了反射。为什么使用 Spring 的时候 ,一个@Component注解就声明了一个类为 Spring Bean 呢?为什么你通过一个 @Value注解就读取到配置文件中的值呢?究竟是怎么起作用的呢?
答:在Spring框架中,@Component注解用于标识一个类作为Spring管理的组件,也就是所谓的Spring Bean。当Spring容器启动时,它会扫描类路径下的所有组件,并根据注解的配置将它们实例化为Bean,然后将它们管理起来。这样,我们就可以在应用程序中通过依赖注入的方式来使用这些Bean。
而@Value注解用于从外部配置文件中读取值,并将这些值注入到标记了该注解的字段或方法参数中。通过在配置文件中设置对应的键值对,Spring在启动时会解析配置文件,并将对应的值注入到使用了@Value注解的位置。
4.JDBC(Class.forName导致类加载)
如果只是希望一个类的静态代码块执行,其它代码不执行,可以使用:
Class.forName("完整类名");
这个方法的执行会导致类加载,类加载时,静态代码块执行。
还有其他用途就不一一列举了。
四、反射的重要类和要注意的问题:
性能问题: 反射操作通常比非反射代码慢,因为它需要在运行时解析相关的类信息。
安全问题: 使用反射可以访问和修改类的私有成员,这可能会破坏封装性,导致安全隐患。
复杂性: 反射代码通常比直接代码复杂,可能难以阅读和维护。