关于Class
类加载的几种方式
new Xxx()
public class OrderService {
public static List<String> names;
}
public class UserService {
static {
System.out.println("UserService 静态代码块被执行");
OrderService.names = new ArrayList<>();
OrderService.names.add("GY");
OrderService.names.add("TLX");
}
}
public class TestClient {
public static void main(String[] args) {
// 采用new 创建对象的方式 加载 UserService类
UserService userService = new UserService();
// 静态代码块被执行,OrderService的静态属性names被初始化
System.out.println(OrderService.names);
}
}
classLoader.loadClass(String className)
public class TestClient {
public static void main(String[] args) throws ClassNotFoundException {
ClassLoader classLoader = TestClient.class.getClassLoader();
// 这里不会去解析(链接)这个类,只是加载Class到JVM,也就是说不会去初始化类的静态资源(静态代码块)
classLoader.loadClass("cls.loadclasstest.UserService");
// 看具体打印结果,UserService类的 static {} 并没有执行
System.out.println(OrderService.names);
}
}
Class.forName(String className)
public class TestClient {
public static void main(String[] args) throws ClassNotFoundException {
// 默认会去初始化类,初始化类的静态资源,执行类的 static {}
Class<?> cls1 = Class.forName("cls.loadclasstest.UserService");
// 使用如下方式,则不会初始化类的静态资源
// Class.forName("cls.loadclasstest.UserService", false, TestClient.class.getClassLoader());
System.out.println(OrderService.names);
}
}
具体使用
Spring框架中的IOC,根据XML中配置的目标类地址,加载得到对应的Class;
根据包扫描路径,扫描到class文件,加载各个Class到程序。最终通过Class生成对应实例Bean交给IOC管理;
得到Class cls对象之后
public interface IUserService {}
public class AbstractUserService implements IUserService {}
public class UserService extends AbstractUserService {}
判断Class是不是接口
isInterface
public class ClientTest {
public static void main(String[] args) throws ClassNotFoundException {
Class<?> c1 = Class.forName("cls.clstest.IUserService");
Class<?> c2 = Class.forName("cls.clstest.AbstractUserService");
Class<?> c3 = Class.forName("cls.clstest.UserService");
System.out.println(c1.isInterface());// true
System.out.println(c2.isInterface());// false
System.out.println(c3.isInterface());// false
}
}
获取Class实现的接口Class[]
getInterfaces
Class<?>[] interfaces1 = c2.getInterfaces();
// 只可以获取到该类直接实现的接口,无法获取到父类实现的接口
Class<?>[] interfaces2 = c3.getInterfaces();
判断Class是不是被某个注解修饰了
isAnnotationPresent
// 定义一个注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Controller {
String name();
String[] values();
}
@Controller(name="userService", values = {"abc","edf"})
public class UserService {}
public class ClientTest {
public static void main(String[] args) throws ClassNotFoundException {
Class<?> c3 = Class.forName("cls.clstest.UserService");
// 判断某个Class对象上是否加了某个注解
boolean annotationPresent = c3.isAnnotationPresent(Controller.class);
System.out.println(annotationPresent); // true
Class<?> c2 = Class.forName("cls.clstest.AbstractUserService");
annotationPresent = c2.isAnnotationPresent(Controller.class);
System.out.println(annotationPresent); // false
}
}
获取被修饰注解中的属性
getAnnotation
Class<?> c3 = Class.forName("cls.clstest.UserService");
// 获取 UserService类上的Controller注解对象
Controller annotation = c3.getAnnotation(Controller.class);
// 从注解对象中获取属性
String name = annotation.name();// userService
String[] values = annotation.values();// ["abc","edf"]
System.out.println(name);
for (String value : values) {
System.out.println(value);
}
具体应用
基于指定基础包扫描路径、注解模式实现的Spring IOC,底层实则就用到了根据Class对象,获取其上修饰的注解(对象),判断该类的实例是否是需要IOC进行管理的。并且可以根据Class之上的注解(对象),获取注解中的属性值,以用于实现如何构建当前Class的Bean(比如指定其在IOC中的Bean的名称、是否为单例、或用于Bean对象的一些成员属性的初始化赋值以用于后续业务操作)。
获取构造
Constructor也是一种类型,具体构造函数也是对象
public class UserService {
private String name;
private Integer age;
public UserService(String name, Integer age) {
this.name = name;
this.age = age;
}
public UserService(String name) {
this.name = name;
}
public UserService() {
}
}
public class ClientTest {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class<?> c3 = Class.forName("cls.clstest.UserService");
// 根据参数类型获取构造函数
Constructor<?> constructor1 = c3.getConstructor(String.class, Integer.class);
// 使用构造函数创建对象
UserService userService = (UserService) constructor1.newInstance("hello", 18);
// 获取所有构造函数
Constructor<?>[] constructors = c3.getConstructors();
// 遍历构造函数
for (Constructor<?> constructor : constructors) {
// 获取该构造函数的 参数类型集
Class<?>[] parameterTypes = constructor.getParameterTypes();
System.out.println("------------");
for (Class<?> parameterType : parameterTypes) {
// 打印参数类型
System.out.println(parameterType.getName());
}
}
}
}
具体应用体现
Spring IOC初始化过程中通过构造方法注入属性,生成Bean
<bean id="******" class="***.***.***">
<constructor-arg type="java.lang.String" value="***" />
</bean>
<!--或者-->
<bean id="******" class="***.***.***">
<!--这里实则是根据Class中匹配构造函数的参数的名称获取对应构造方法-->
<!-- 具体参见 : https://blog.csdn.net/qq_31803503/article/details/80990821 -->
<constructor-arg name="**" value="***" />
<constructor-arg name="***" value="****"/>
</bean>
附加内容如下:
Class<?> c3 = Class.forName("cls.clstest.UserService");
// 根据参数类型获取构造函数
Constructor<?> constructor1 = c3.getConstructor(String.class, Integer.class);
// 获取构造函数参数对象Parameter
Parameter[] parameters = constructor1.getParameters();
for (Parameter parameter : parameters) {
// 打印参数名称
System.out.println(parameter.getName()); // 当前这种获取参数的方式,打印的参数名称跟实际定义的名称不服
}
如上这中方式获取的构造函数参数的名称,没有具体意义,并不是具体的name、age, Spring使用ASM字节码操作框架来获取方法参数的名称 。
具体参见:https://blog.csdn.net/qq_31803503/article/details/80990821
创建对象
用Constructor来创建对象
// 比如通过获取无参构造来构建对象,要求目标Class得有可访问的无参构造
Class<?> c3 = Class.forName("cls.clstest.UserService");
Constructor<?> constructor2 = c3.getConstructor();
UserService o = (UserService) constructor2.newInstance();
如果构造不可访问,如:
public class UserService {
// 私有无参构造外部不可访问
private UserService() {}
private String name;
private Integer age;
public UserService(String name, Integer age) {
this.name = name;
this.age = age;
}
}
则需要暴力靠近
Class<?> c3 = Class.forName("cls.clstest.UserService");
// 获取声明过的构造(不管是否可访问)
Constructor<?> constructor2 = c3.getDeclaredConstructor();
// 暴力靠近
constructor2.setAccessible(true);
// 通过当前Constructor构建对象
UserService o = (UserService) constructor2.newInstance();
如果获取的是带参构造,则需按类型进行传参构建对象
Class<?> c3 = Class.forName("cls.clstest.UserService");
// 获取可访问的构造函数对象
Constructor<?> constructor1 = c3.getConstructor(String.class, Integer.class);
// 按照参数类型对应传参构建实例
UserService userService = (UserService) constructor1.newInstance("hello", 18);
cls.newInstance构建
Class<?> c3 = Class.forName("cls.clstest.UserService");
// 这里底层实则也是调用的无参数构造,要求目标类Class提供可访问的无参构造
UserService o1 = (UserService) c3.newInstance();
应用体现
Spring IOC 中的各个Service、Controller、Component、Configuration...对象,都是通过反射执行构造生成的(除去代理对象);
Method方法
// 目标类
public class UserService {
// 私有方法
private String f1(String s) {
System.out.println("private f1");
return s;
}
// 静态方法
public static Integer f2(Integer i1, Integer i2) {
System.out.println("static f2");
return i1 + i2;
}
// 无参方法
public String f3() {
System.out.println("public f3 no args");
return "f3";
}
// 带参方法
public String f3(String name, Integer age) {
System.out.println("public f3 has args");
return "f3 " + name + " " + age;
}
}
获取Class中Method对象
Class<?> c3 = Class.forName("cls.clstest.UserService");
// 获取已声明的方法(包含私有不可访问的方法),按名称获取方法
Method f3_1 = c3.getDeclaredMethod("f3");// 获取无参方法f3
Method f3_2 = c3.getDeclaredMethod("f3", String.class, Integer.class);// 获取带参方法
// 获取所有方法
Method[] methods = c3.getDeclaredMethods();
for (Method method : methods) {
// 方法名称
System.out.println(method.getName());
// 方法所在类
System.out.println(method.getDeclaringClass().getName());// 即 cls.clstest.UserService
// 方法返回类型
System.out.println(method.getReturnType().getName());
// 方法参数类型集合
Class<?>[] parameterTypes = method.getParameterTypes();
for (Class<?> parameterType : parameterTypes) {
// 打印方法参数类型名称
System.out.println(parameterType.getName());
}
System.out.println("----------------------");
}
执行Method
执行私有普通方法
Class<?> c3 = Class.forName("cls.clstest.UserService");
// 构建执行方法的对象
UserService userService = new UserService();
// 获取私有方法Method对象需要使用getDeclaredMethod
Method f1 = c3.getDeclaredMethod("f1", String.class);
// 执行私有方法前,需要暴力靠近
f1.setAccessible(true);
// 执行
f1.invoke(userService,"haha");
执行静态方法
Class<?> c3 = Class.forName("cls.clstest.UserService");
Method f2 = c3.getMethod("f2", Integer.class, Integer.class);
// 静态方法使用Class对象去执行
Object invoke = f2.invoke(c3, 1, 1);
System.out.println(invoke);
执行普通无参方法
Class<?> c3 = Class.forName("cls.clstest.UserService");
// 如果需要获取带参方法,则需要将参数类型一并传入
Method f3 = c3.getMethod("f3");
UserService userService = new UserService();
Object invoke = f3.invoke(userService);
System.out.println(invoke);
获取Method上面的注解
// 自定义注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestMapping {
String[] value() default {};
}
@Controller(name = "userController", values = {"aaa","bbb"})
public class UserController {
@Autowired
private UserService userService;
// 在方法上加上注解
@RequestMapping("/test")
public String f() {
String s = userService.f3();
return s;
}
}
Class<?> controllerClass = Class.forName("cls.clstest.UserController");
// 根据方法名称获取Method对象,这里该方法没有参数
Method f = controllerClass.getMethod("f");
// 判断Method上是否包含某个注解
boolean annotationPresent = f.isAnnotationPresent(RequestMapping.class);
System.out.println(annotationPresent);// true
if(annotationPresent) {
// 获取注解对象
RequestMapping annotation = f.getAnnotation(RequestMapping.class);
// 获取注解对象中的参数
String[] value = annotation.value();
for (String s : value) {
System.out.println(s); // /test
}
}
应用体现
- 在RPC框架中,服务提供者在接收到请求数据后,从协议数据中获取方法名称、参数类型、以及客户端请求参数、及注册中心的具体服务Bean的信息,从服务提供端容器中获取最终执该方法的对象,然后用反射进行执行,获取执行结果,响应服务调用者;
- 在Spring MVC中通过获取具体Method对象上的注解,解析注解对象中的属性值,最终生成映射关系,在具体客户端请求时,通过url及一些信息最终定位到具体用于处理该请求业务的Method方法,然后从IOC中获取到该Controller对象,然后利用反射进行执行,获取业务请求结果,响应客户端;
Field属性
获取Field对象
public class UserController {
@Autowired
private UserService userService;
}
Class<?> controllerClass = Class.forName("cls.clstest.UserController");
// 根据属性名称,获取已声明属性(可以获取私有属性)
// controllerClass.getField("userService"); // 这样则无法获取私有属性
Field userServiceField = controllerClass.getDeclaredField("userService");
// 获取所有属性--数组
Field[] declaredFields = controllerClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
// 属性名称
System.out.println(declaredField.getName());
// 属性类型
System.out.println(declaredField.getType());
}
执行Field的对应Get、Set方法
执行属性Get方法
类中不需要提供该属性对应的get、set方法
Class<?> controllerClass = Class.forName("cls.clstest.UserController");
Field userServiceF = controllerClass.getDeclaredField("userService");
// 暴力靠近,让属性的get、set方法可以执行
userServiceF.setAccessible(true);
// 构建目标对象
UserController userController = new UserController();
// 反射执行Field的get方法
UserService userService1 = (UserService) userServiceF.get(userController);
// 执行目标对象的userService这个成员属性的get方法
System.out.println(userService1);// 这里因为没有设置对象的属性,所以为null
执行属性Set方法
Class<?> controllerClass = Class.forName("cls.clstest.UserController");
UserController userController = new UserController();
// 按属性名称获取属性Field对象
Field userServiceF = controllerClass.getDeclaredField("userService");
userServiceF.setAccessible(true);
// 反射执行Filed的set方法
userServiceF.set(userController,new UserService());
// 反射执行Field的get方法
UserService userService1 = (UserService) userServiceF.get(userController);
System.out.println(userService1);// 打印对象中的属性
获取Field上面的注解
// 自定义注解
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
}
Class<?> controllerClass = Class.forName("cls.clstest.UserController");
Field userServiceF = controllerClass.getDeclaredField("userService");
// 判断Field上是否被某个注解修饰
boolean b = userServiceF.isAnnotationPresent(Autowired.class);
System.out.println(b);// true
if(b) {
Autowired annotation = userServiceF.getAnnotation(Autowired.class);
}
拓展内容:
Class<?> controllerClass = Class.forName("cls.clstest.UserController");
Field userServiceF = controllerClass.getDeclaredField("userService");
// 判断Field上是否有@Autowired
boolean b = userServiceF.isAnnotationPresent(Autowired.class);
System.out.println(b);
if(b) {
// 如果当前字段被@Autowired修饰,Spring IOC 初始化——动态注入环节,则会考虑针对当前字段做自动注入操作
// 获取字段的类型
Class<?> type = userServiceF.getType();
// 从IOC已经构建的Bean容器中根据类型type获取Bean
// 这里我自己new一个,用于测试
UserService userService = new UserService();
// 同样需要从IOC容器中根据当前Class——controllerClass,获取到当前Controller对象,我同样new一个用于测试
UserController userController = new UserController();
// 获取到Bean之后,接下来执行Field的set方法,实现自动注入
userServiceF.setAccessible(true);
// 动态注入
userServiceF.set(userController, userService);
}
应用体现
Spring IOC加载过程中,会用到反射操作Field,实现Bean之间的动态注入等功能;