反射
基本概念
1、每个java类运行时都在JVM里表现为一个class对象
2、现在知道的是一个Class类(类型)的对象,如何通过该类型获取和操作与之对应的类成员、类方法。
3、Java反射是指在运行时动态地获取类的信息并操作类的成员(字段、方法、构造函数等)。通过反射,可以在运行时检查类的属性和方法,并且可以在运行时创建对象、调用方法、访问和修改字段等。
4、Class类与java.lang.reflect类库一起对反射技术进行了全力的支持。
核心API
在反射包中,我们常用的类主要有Constructor类表示的是Class 对象所表示的类的构造方法,利用它可以在运行时动态创建对象、Field表示Class对象所表示的类的成员变量,通过它可以在运行时动态修改成员变量的属性值(包含private)、Method表示Class对象所表示的类的成员方法,通过它可以动态调用对象的方法(包含private)
测试类信息--点击查看代码
public class TestReflect {
private Integer num;
private String name;
public TestReflect(String s) {
System.out.println("执行带参构造函数");
}
public TestReflect() {
System.out.println("执行默认构造函数");
}
}
1、获取Class对象
类名.class; 类型.getClass(); Class.forName("类名")
2、获取构造函数创建实例
Class<TestReflect> cl = TestReflect.class;
// 使用默认构造函数创建实例
cl.newInstance();
// 获取所有构造函数
for (Constructor constructor : cl.getDeclaredConstructors()) {
System.out.println(constructor.toGenericString());
}
3、获取成员变量
点击查看代码
for (Field f : cl.getDeclaredFields()) {
if (f.getType() == Integer.class) {
try {
f.setAccessible(true);
boolean access = f.isAccessible();
f.set(obj, 1);
f.setAccessible(access);
} catch (Exception e) {
System.out.println("赋值异常");
}
}
if (f.getType() == String.class) {
try {
f.setAccessible(true);
boolean access = f.isAccessible();
f.set(obj, "hello");
f.setAccessible(access);
} catch (Exception e) {
System.out.println("赋值异常");
}
}
}
4、调用方法
点击查看代码
for (Method m : cl.getDeclaredMethods()) {
try {
m.invoke(obj);
} catch (Exception e) {
}
}
核心作用与示例
作用
1、程序运行期间的自我检查:反射可以在运行时检查类、接口、字段和方法的信息。这是一种在运行时检查类型信息和成员信息的机制。
2、框架设计:许多Java框架(如Spring和Hibernate)使用反射来实现自动化和灵活的配置。反射使得框架可以在运行时动态地创建对象、调用方法和设置属性。
3、单元测试:反射也常被用于单元测试中,通过反射访问和操作私有方法和字段,以便进行更全面的测试。
4、序列化和反序列化:在Java中,对象可以被序列化为一串字节,然后可以反序列化回原来的对象。这个过程也使用了反射。
灵活性:反射使得代码可以在运行时改变其行为。例如,它可以用来实现设计模式,如工厂模式和代理模式,或者用来在运行时实现接口。
示例
框架设计
spring框架自动织入能力
假设有一个UserService接口和一个UserServiceImpl实现类:
public interface UserService {
void addUser(User user);
}
public class UserServiceImpl implements UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void addUser(User user) {
userDao.addUser(user);
}
}
在Spring框架中,可以使用注解或XML配置来指定依赖关系。下面是使用注解的方式:
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
public void addUser(User user) {
userDao.addUser(user);
}
}
在这个例子中,通过@Autowired注解将UserDao对象注入到UserServiceImpl类中的userDao字段。Spring框架在运行时会通过反射来实例化UserServiceImpl类,并通过反射来获取userDao字段,并将UserDao对象注入到该字段。
通过反射,Spring框架可以在运行时动态地获取类的信息并操作类的成员,从而实现依赖注入的功能。通过注解或配置,Spring框架可以在运行时通过反射来查找和创建依赖对象,并将依赖对象注入到需要依赖的类中。
手动练习实现织入能力
通过反射能力,模拟实现spring的依赖注入功能。
1、汽车类定义
点击查看代码
package com.demo.reflect;
import java.util.Objects;
/**
* @Descrpition TODO
* @Author liutai
* @Date 2023/9/10 17:34
**/
class Car {
@MyAutoWire
Driver driver;
public void drive() {
if (Objects.nonNull(driver)) {
System.out.println("姓名:" + driver.getName() + ", 年龄:" + driver.getAge() + ", driving this car");
} else {
System.out.println("please set a driver firstly");
}
}
}
点击查看代码
package com.demo.reflect;
/**
* @Descrpition TODO
* @Author liutai
* @Date 2023/9/10 17:34
**/
class Driver {
private String name;
private String age;
public Driver(String name, String age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public String getAge() {
return age;
}
}
3、织入注解
点击查看代码
package com.demo.reflect;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.FIELD;
@Retention(RetentionPolicy.RUNTIME)
@Target(value={ FIELD})
public @interface MyAutoWire {
}
4、通过反射为汽车类注入一个司机
点击查看代码
package com.demo.reflect;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
/**
* @Descrpition TODO
* @Author liutai
* @Date 2023/9/10 17:21
**/
public class TestAutoWire {
public static void main(String[] args) {
Car car = new Car();
car.drive();
// 开始自动注入drivier
Field[] fields = car.getClass().getDeclaredFields();
for (Field f : fields) {
for (Annotation ano : f.getAnnotations()) {
if (ano instanceof MyAutoWire) {
// 如果当前变量使用了@MyAutoWire注解,那么使用反射注入变量信息
boolean access = f.isAccessible();
f.setAccessible(true);
try {
Constructor objConstruct = Class.forName("com.demo.reflect.Driver").getDeclaredConstructor(String.class, String.class);
Driver driver = (Driver) objConstruct.newInstance("xiaoming", "6");
f.set(car, driver);
f.setAccessible(access);
} catch (Exception e) {
}
}
}
}
// 再次执行car的drive方法
car.drive();
}
}
// 执行结果
please set a driver firstly
姓名:xiaoming, 年龄:6, driving this car
动态代理
动态代理常用于AOP(面向切面编程)和RPC(远程过程调用)等场景。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class DynamicProxyExample {
public static void main(String[] args) {
// 创建目标对象
UserService userService = new UserServiceImpl();
// 创建代理对象
UserService proxy = (UserService) createProxy(userService);
// 调用代理对象的方法
proxy.addUser("Alice");
}
// 创建代理对象的方法
public static Object createProxy(Object target) {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(), // 类加载器
target.getClass().getInterfaces(), // 接口数组
new InvocationHandler() { // InvocationHandler对象
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 在调用目标方法之前执行一些操作
System.out.println("Before invoking method: " + method.getName());
// 调用目标方法
Object result = method.invoke(target, args);
// 在调用目标方法之后执行一些操作
System.out.println("After invoking method: " + method.getName());
return result;
}
}
);
}
}
// 目标接口
interface UserService {
void addUser(String name);
}
// 目标实现类
class UserServiceImpl implements UserService {
public void addUser(String name) {
System.out.println("Adding user: " + name);
}
}
在这个示例中,我们首先定义了一个目标接口UserService和一个目标实现类UserServiceImpl。然后,通过createProxy()方法使用反射创建代理对象。在InvocationHandler的invoke()方法中,我们可以在调用目标方法之前和之后执行一些操作。最后,我们使用代理对象调用目标方法addUser()。
运行示例代码,输出如下:
Before invoking method: addUser
Adding user: Alice
After invoking method: addUser
从输出结果可以看出,在调用代理对象的addUser()方法时,会先执行invoke()方法中的操作,然后再调用目标方法addUser(),最后再执行invoke()方法中的操作。
通过这种方式,我们可以使用反射来动态地创建代理对象,并在代理对象的方法调用前后执行一些操作,从而实现动态代理的功能。动态代理常用于AOP(面向切面编程)和RPC(远程过程调用)等场景,可以在不修改目标类的情况下,对目标类的方法进行增强或拦截。