什么是反射
程序运行期间动态的创建一个类的实例对象,调用一个类中的成员,往类中添加成员,删除成员等。成员包括(属性、方法)
第一次听说这个概念,可能不理解,看一个案例,通过反射创建一个实例对象,并调用其中的一个方法: 现在有名叫AService、BService、CService三个类,这三个类中分别都有一个show()方法。接下来把其全类名放到一个字符串数组中,随机一个数字,根据下标取出对应的全类名,实例化一个这个类的对象,调用类里面的show()方法
public class AService {
public void show(){
System.out.println("AService中的show方法");
}
}
public class BService {
public void show(){
System.out.println("BService中的show方法");
}
}
public class CService {
public void show(){
System.out.println("CService中的show方法");
}
}
private static void dynamic() throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
String[] objects = new String[]{"com.example.service.AService", "com.example.service.BService", "com.example.service.CService"};
int num = new Random().nextInt(3);
String ref = objects[num];
Class<?> clazz = Class.forName(ref);
Object newInstance = clazz.newInstance();
Method showMethod = clazz.getMethod("show");
showMethod.invoke(newInstance);
}
在这里请问一下,创建实例对象的Object newInstance = clazz.newInstance();的这一行代码你能看出是创建哪个类的对象吗? 显然不知道,更何况我们而没有显示的创建了某个类的实例对象。创建实例对象的Object newInstance = clazz.newInstance();的这一行代码是在程序运行时才执行的。也就是说, 在程序运行的时候,才实例化了某个类的一个对象,然后又通过反射调用了这个实例对象中的方法 在没有使用反射的情况下,这个案例大致上以下这种类似的方法:
private static void nodynamic() {
String[] objects = new String[]{"com.example.service.AService", "com.example.service.BService", "com.example.service.CService"};
int num = new Random().nextInt(3);
String ref = objects[num];
switch (ref) {
case "com.example.service.AService":
AService aService = new AService();
aService.show();
break;
case "com.example.service.BService":
BService bService = new BService();
bService.show();
break;
case "com.example.service.CService":
CService cService = new CService();
cService.show();
break;
default:
System.out.println("没有找到对象");
break;
}
}
不使用反射很明显就复杂了很多。通过这个案例先有一个简单的认识
接下来再通过一个案例加深以下对反射的理解。 在使用Spring的时候(注解方式),如果我们需要将某个类加载到Spring的容器当中,就会在类上面标注@Service、@Component等其它几个注解进行标记,然后指定注解扫描包,然后Spring就会扫描指定包下标记了这些注解的类,并将这些类加载到Spring的容器当中,模拟这个流程来做个案例演示: 步骤一、自定义一个注解@StudyService
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface StudyService {
String value() default "";
}
步骤二、对我们的业务类,加上我们自定义的注解 业务类:
package com.example.service;
import com.example.anntion.StudyService;
@StudyService
public class AService {
public void show(){
System.out.println("AService中的show方法");
}
}
package com.example.service;
public class BService {
public void show(){
System.out.println("BService中的show方法");
}
}
package com.example.service;
import com.example.anntion.StudyService;
@StudyService
public class CService {
public void show(){
System.out.println("CService中的show方法");
}
}
步骤三、我们在容器初始化的时候,我们需要扫描标有service注解的类,然后实例化后放进容器内。大概的代码如下:
List<String> clazzList = new ArrayList<>();
Map<String, Object> ioc = new HashMap<>();
public void scanClass() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
System.out.println("===============开始扫描文件夹下的类文件===============");
File classPath = new File(this.getClass().getClassLoader().getResource("com//example//service").getFile());
for (File file : classPath.listFiles()) {
clazzList.add("com.example.service." + file.getName().replace(".class", ""));
}
System.out.println("===============结束扫描文件夹下的类文件===============");
System.out.println("===============开始扫描标记有@StudyService注解的类,并放到IOC容器当中===============");
for (String data : clazzList) {
Class<?> clazz = Class.forName(data);
if (clazz.isAnnotationPresent(StudyService.class)) {
Object newInstance = clazz.newInstance();
ioc.put(clazz.getSimpleName(), newInstance);
}
}
System.out.println("===============结束扫描标记有@StudyService注解的类===============");
System.out.println("===============查看IOC容器中的bean===============");
for (Map.Entry<String, Object> entry : ioc.entrySet()) {
System.out.println("key=" + entry.getKey() + ";value=" + entry.getValue());
}
}
运行结果:
反射带来的好处
提高了程序的灵活性与扩展性
原理
反射的优缺点
优点
可以在程序运行期间动态的去操作类的成员,动态的创建对象、调用对象中的属性与方法,
缺点
- 如果我们在业务逻辑的代码当中,使用反射进行对象的操作,会模糊程序代码的逻辑,增大了维护的困难
- 反射会消耗CPU资源,影响性能,在使用反射操作的时候,里面会对成员做一些的检查,例如:方法权限,然后获取成员对象,然后再返回拷贝之后的对象
反射的具体应用
- 利用反射进行反编译
- 加载数据库驱动
- Spring将加了@Componet、@Service注解的类加载到IOC容器当中
- Spring中xml的形式配置bean的时候,需要制定类的全类名,这也是通过反射实现的
- …
反射的使用
package com.example.entity;
public class Student {
private final static Integer num = 1;
private Integer id;
public String name;
public Integer age;
public void show(String message){
System.out.println(message);
}
}
private static void getClassMethod() throws ClassNotFoundException {
Class<?> method01Clazz = Class.forName("com.example.entity.Student");
Student student = new Student();
Class<? extends Student> method02Clazz = student.getClass();
Class<Student> method03Clazz = Student.class;
getClassMethod();
}
private static void extracted() throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
Class<?> clazz = Class.forName("com.example.entity.Student");
Student newInstance = (Student) clazz.newInstance();
System.out.println(newInstance);
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
String modifier = Modifier.toString(field.getModifiers());
Class<?> typeClazz = field.getType();
String type = typeClazz.getSimpleName();
String name = field.getName();
System.out.println(modifier + " " + type + " " + name);
}
Method method = clazz.getMethod("show", String.class);
method.invoke(newInstance, "你好啊");
}
本文主要是加深对反射的理解
|