联博开奖_Java反射机制与动态署理
发表时间:2021-01-05 浏览量:84
Java反射机制与动态署理
1. 概述
Java 反射机制与动态署理我们平时写代码可能用得比较少,但在种种常见的框架(Spring、MyBatis 等)中却习以为常。有句话叫“无反射,不框架;无署理,不框架”。
由于以后计划阅读和学习框架的源码,这里先简朴回首反射机制和动态署理(暂不深入剖析实现原理),为后面做些准备。
2. 反射机制
Java 反射机制是在 Java 程序运行状态中,对于随便一个类,都能够知道这个类的所有属性和方式;对于随便一个工具,都能够挪用它的随便方式和属性。这种动态获取信息以及动态挪用工具方式的功效称为 Java 语言的反射机制。
从面向工具的角度来看,我们平时用到的"类"、"组织器"、"属性"、"方式"实在也是一个"类",它们在 JDK 中划分对应 Class、Constructor、Field、Method 类。其中 Class 相当于"类的类",可称为"元类",从这个角度看,我们平时自界说的"类"可以明白为 Class 的一个工具。
简朴来说(小我私家明白),反射机制就是通过 Class、Constructor 等"元类"来操作其他的通俗类(建立工具、挪用方式等)。下面以简朴代码示例。
2.1 准备代码
界说一个通俗的 Java 类 Human,如下:
package com.jaxer.example.reflection;
public class Human {
public String gender;
private int age;
protected void hello() {
}
public void hi() {
}
}
界说一个通俗的 Person 类,继续自 Human,如下:
public class Person extends Human {
// 两个属性
public String name;
private int age;
// 种种修饰符的组织器
public Person() {
}
private Person(String name) {
this.name = name;
}
protected Person(int age) {
this.age = age;
}
Person(String name, int age) {
this.name = name;
this.age = age;
}
// getter, setter 方式
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
// 自界说方式
private void test() {
System.out.println("test is invoked.");
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
该 Person 类界说了 name 和 age 两个成员变量及其 getter/setter 方式,另有四个组织器,划分使用差别的修饰符,自界说了一个 test 方式,重写了 toString 方式。
PS: 这两个类仅供参考,只是为了演示。
2.2 获取 Class
获取 Person 的 Class 工具通常有下面三种方式:
// 方式一:从工具获取
Person person = new Person();
person.getClass();
// 方式二:类名.class
Person.class;
// 方式三:Class.forName
Class<?> aClass = Class.forName("com.jaxer.example.reflection.Person");
/*
这三种方式的获取到的都是:
class com.jaxer.example.reflection.Person
而且可以证实,这三种方式获得的同一个 Class 工具,即同一个 Person 类
*/
获取到了 Class 工具,就可以使用 Class 工具来建立 Person 工具或者做一些其他操作,例如:
// 获取 Class 工具
Class<?> aClass = Class.forName("com.jaxer.example.reflection.Person");
// 使用 Class 建立 Person 工具
System.out.println(aClass.newInstance()); // 建立 Person 工具
// 获取 Person 的父类(Human)
System.out.println("superClass-->" + aClass.getSuperclass());
/* 输出效果:
* Person{name='null', age=0}
* superClass-->class com.jaxer.example.reflection.Human
*/
2.3 获取 Constructor
Class 中获取组织器(Constructor)的方式如下:
-
getConstructors: 获取所有 public 组织器;
-
getDeclaredConstructors: 获取所有组织器(包罗 private 类型);
-
getConstructor(Class… parameterTypes): 获取指定参数的 public 组织器;
- getDeclaredConstructor(Class… parameterTypes): 获取指定参数的组织器(包罗 private 类型)。
测试代码:
private static void testConstructors(Class<?> aClass) throws Exception {
// 1. 获取所有public组织器
Constructor<?>[] constructors = aClass.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println("constructor-->" + constructor);
}
// 2. 获取所有组织器
Constructor<?>[] declaredConstructors = aClass.getDeclaredConstructors();
for (Constructor<?> declaredConstructor : declaredConstructors) {
System.out.println("declaredConstructor-->" + declaredConstructor);
}
// 4. 获取指定的组织器(凭据参数类型)
Constructor<?> constructor = aClass.getDeclaredConstructor(String.class);
// 修改接见权限(用这种方式可以使用 private 类型组织器建立工具)
constructor.setAccessible(true);
// 使用该组织器建立 Person 工具
Object instance = constructor.newInstance("Ace");
System.out.println(instance);
}
输出效果如下:
constructor-->public com.jaxer.example.reflection.Person()
declaredConstructor-->com.jaxer.example.reflection.Person(java.lang.String,int)
declaredConstructor-->protected com.jaxer.example.reflection.Person(int)
declaredConstructor-->private com.jaxer.example.reflection.Person(java.lang.String)
declaredConstructor-->public com.jaxer.example.reflection.Person()
Person{name='Ace', age=0}
2.4 获取 Field
获取 Class 属性(Field)的方式如下:
-
getFields(): 获取 public 属性(包罗父类);
-
getDeclaredFields(): 获取所有属性(包罗 private 类型);
-
getField(String name): 获取指定名称的 public 属性;
- getDeclaredField(String name): 获取指定名称的属性(包罗 private 类型)。
测试代码:
private static void testFields(Class<?> aClass) throws Exception {
// 1. 获取 public 属性(包罗父类的 public 属性)
Field[] fields = aClass.getFields();
for (Field field : fields) {
System.out.println("field-->" + field);
}
// 2. 获取所有属性
Field[] declaredFields = aClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println("declaredField-->" + declaredField);
}
// 4. 获取指定的属性(这里获取 age 属性)
Field age = aClass.getDeclaredField("age");
System.out.println("age-->" + age);
// 给指定工具的属性赋值(private 类型不能赋值,需要修改接见权限)
Object obj = aClass.newInstance();
age.setAccessible(true); // 修改接见权限
age.set(obj, 18); // 设置新值
System.out.println("obj-->" + obj);
}
输出效果:
field-->public java.lang.String com.jaxer.example.reflection.Person.name
field-->public java.lang.String com.jaxer.example.reflection.Human.gender
declaredField-->public java.lang.String com.jaxer.example.reflection.Person.name
declaredField-->private int com.jaxer.example.reflection.Person.age
age-->private int com.jaxer.example.reflection.Person.age
obj-->Person{name='null', age=18}
2.5 获取 Method
获取 Class 方式(Method)的方式如下:
-
getMethods(): 获取所有 pubic 方式(包罗父类及 Object 类);
-
getDeclaredMethods(): 获取所有方式(包罗 private 方式);
-
getMethod(String name, Class… parameterTypes): 获取指定名称和参数的 public 方式;
,
,www.caibao.it采用以太坊区块链高度哈希值作为统计数据,联博以太坊统计数据开源、公平、无任何作弊可能性。联博统计免费提供API接口,支持多语言接入。
- getDeclaredMethod(String name, Class… parameterTypes): 获取指定名称和参数的方式(包罗 private 方式)。
测试代码:
private static void testMethods(Class<?> aClass) throws Exception {
// 1. 获取所有 public 方式(包罗父类及 Object 类)
Method[] methods = aClass.getMethods();
for (Method method : methods) {
System.out.println("method-->" + method);
}
// 2. 获取该类声明的所有方式
Method[] declaredMethods = aClass.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println("declaredMethod-->" + declaredMethod);
}
// 4. 获取指定的方式并挪用
Method method = aClass.getDeclaredMethod("test");
method.setAccessible(true); // 修改接见权限
System.out.println("方式名:" + method.getName());
System.out.println("方式修饰符:" + Modifier.toString(method.getModifiers()));
Object instance = aClass.newInstance();
System.out.println(method.invoke(instance));
}
输出效果:
// getMethods 方式返回效果:
// Person 类的 public 方式:
method-->public java.lang.String com.jaxer.example.reflection.Person.toString()
method-->public java.lang.String com.jaxer.example.reflection.Person.getName()
method-->public void com.jaxer.example.reflection.Person.setName(java.lang.String)
method-->public void com.jaxer.example.reflection.Person.setAge(int)
method-->public int com.jaxer.example.reflection.Person.getAge()
// Human 类的 public 方式:
method-->public void com.jaxer.example.reflection.Human.hi()
// Object 类的 public 方式:
method-->public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
method-->public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
method-->public final void java.lang.Object.wait() throws java.lang.InterruptedException
method-->public boolean java.lang.Object.equals(java.lang.Object)
method-->public native int java.lang.Object.hashCode()
method-->public final native java.lang.Class java.lang.Object.getClass()
method-->public final native void java.lang.Object.notify()
method-->public final native void java.lang.Object.notifyAll()
// getDeclaredMethods 方式返回效果:
declaredMethod-->public java.lang.String com.jaxer.example.reflection.Person.toString()
declaredMethod-->public java.lang.String com.jaxer.example.reflection.Person.getName()
declaredMethod-->public void com.jaxer.example.reflection.Person.setName(java.lang.String)
declaredMethod-->private void com.jaxer.example.reflection.Person.test()
declaredMethod-->public void com.jaxer.example.reflection.Person.setAge(int)
declaredMethod-->public int com.jaxer.example.reflection.Person.getAge()
// 挪用 test 方式:
方式名:private void com.jaxer.example.reflection.Person.test()
方式修饰符:private
test is invoked.
null
框架中常用的反射机制主要是以上那些,这里暂不深究实在现原理,以后有需要再行弥补。下面简要剖析署理相关的内容。
3. 署理
使用署理的主要目的:对目的工具(的方式)举行功效增强,例如 Spring 的 AOP。
既然是对目的工具的方式举行增强,署理工具的方式中一定会挪用目的工具的方式。而且一样平常会在目的工具的方式挪用前后(或者其他时机)做一些其他的处置以到达增强的效果。
署理模式通常分为「静态署理」和「动态署理」,静态署理使用较少;而动态署理常用的有 JDK 动态署理和 CGLib 动态署理。下面简要剖析。
3.1 准备代码
一个通俗的 Java 接口及实在现类如下:
// 接口
public interface UserService {
void save(String name);
}
// 实现类
public class UserServiceImpl implements UserService {
@Override
public void save(String name) {
System.out.println("保留用户名:" + name);
}
}
无论静态署理照样动态署理,都是对该类的 save 方式举行增强。
此处以在该方式执行前后各打印一句话来演示。
3.2 静态署理
主要头脑:建立一个 UserService 接口的实现类 UserServiceProxy(署理工具),而且该类持有 UserServiceImpl 工具(目的工具),在 UserServiceProxy 类的 save 方式中挪用 UserServiceImpl 的 save 方式,示例代码如下:
public class UserServiceProxy implements UserService {
private UserService userService;
public UserServiceProxy(UserService userService) {
this.userService = userService;
}
@Override
public void save(String name) {
System.out.println("---静态署理:方式执行前---");
userService.save(name); // 挪用目的类的方式
System.out.println("---静态署理:方式执行后---");
}
}
测试代码:
private static void testStaticProxy() {
UserService userService = new UserServiceImpl();
UserService proxy = new UserServiceProxy(userService);
proxy.save("jack");
}
/* 运行效果:
---静态署理:方式执行前---
保留用户:jack
---静态署理:方式执行后---
*/
显而易见,使用 UserServiceProxy 可以增强 UserServiceImpl 的 save 方式。但由于代码是牢固的(编码时代写好的),不够天真,因此静态署理使用较少,通常使用动态署理。
PS: 此处代码实现形式可能不尽相同,但思绪相近。
3.3 动态署理
与静态署理相比,动态署理则是在运行时动态天生一个署理类,该类可以对目的工具的方式举行功效增强。动态署理常用的有 JDK 动态署理和 CGLib 动态署理,下面简要剖析。
3.3.1 JDK 动态署理
JDK 的动态署理实现方式:
使用 Proxy 类的 newProxyInstance 方式建立署理工具,使用 InvocationHandler 来实现增强的逻辑(通常建立一个 InvocationHandler 接口的实现类,在其 invoke 方式中实现增强的逻辑)。示例代码如下(仅供参考):
// JDK 署理工厂,作用是天生署理工具
public class JDKProxyFactory {
// 获取 target 的署理工具(其中 target 为目的工具)
public Object getProxy(Object target) {
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), new MyInvocationHandler(target));
}
// 自界说 InvocationHandler
private static class MyInvocationHandler implements InvocationHandler {
// 目的工具
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
// 实现增强逻辑
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("目的类方式执行前-----");
Object result = method.invoke(target, args); // 挪用目的工具的方式
System.out.println("目的类方式执行后-----");
return result;
}
}
}
测试代码:
private static void testJdkProxy() {
UserService userService = new UserServiceImpl(); // 目的工具
JDKProxyFactory jdkProxyFactory = new JDKProxyFactory(); // 署理工厂
// 使用署理工厂天生署理工具
UserService proxy = (UserService) jdkProxyFactory.getProxy(userService);
proxy.save("jack"); // 挪用署理工具的方式(已对目的工具举行增强)
}
/* 运行效果:
目的类方式执行前-----
保留用户:jack
目的类方式执行后-----
*/
实现原理可参考:https://blog.csdn.net/yhl_jxy/article/details/80586785
4.3.3.2 CGLib 动态署理
主要思绪:
建立一个目的类的子类 Enhancer,在子类中设置回调工具(MethodInterceptor),并在回调方式(intercept)中实现对目的工具的增强功效逻辑。示例代码如下:
// CGLIB 工厂,用于天生署理工具
public class CglibProxyFactory {
// 获取署理工具(对目的工具举行增强)
public Object getProxy(Object target) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(new MyMethodInterceptor()); // 设置回调
return enhancer.create();
}
// 自界说方式阻挡 MethodInterceptor
private static class MyMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
System.out.println("cglib方式挪用前");
Object o = proxy.invokeSuper(obj, args); // 挪用目的工具的方式
System.out.println("cglib方式挪用后");
return o;
}
}
}
测试代码:
private static void testCglibProxy() {
UserService userService = new UserServiceImpl();
CglibProxyFactory cglibProxyFactory = new CglibProxyFactory();
UserService proxy = (UserService) cglibProxyFactory.getProxy(userService);
proxy.save("jack");
}
/* 运行效果:
cglib方式挪用前
保留用户:jack
cglib方式挪用后
*/
PS: 使用 CGLib 需要导入第三方 jar 包(cglib 和 asm)。
实现原理可参考:https://blog.csdn.net/yhl_jxy/article/details/80633194
二者主要区别:
JDK 动态署理不依赖第三方库,CGLib 需要依赖第三方库;
若目的工具实现了接口,两种方式都可以使用;若未实现接口,则只能使用 CGLib。
4. 小结
反射机制:简朴来说,反射机制主要是通过 Class、Constructor 等"元类"来操作其他的通俗类,以到达在运行时代动态建立工具、动态挪用方式等目的。
静态署理&动态署理:二者主要区别在于时机,静态署理在编码期已写好署理类,而动态署理则是在运行时代动态天生署理类。
JDK 动态署理&CGLib 动态署理的主要区别:
- JDK 动态署理不依赖第三方库,CGLib 则要依赖第三方库;
JDK 使用 InvocationHandler 接口实现增强逻辑,使用 Proxy.newProxyInstance 天生署理工具;而 CGLib 使用 MethodInterceptor 接口实现增强逻辑,使用 Enhancer 天生署理工具; - 若目的工具实现了接口,两种方式都可以使用;若未实现接口,则只能使用 CGLib。
0
珍藏