Java反射机制
Java反射机制指的是在Java程序运行状态中,对于任何一个类,都可以获得这个类的所有属性和方法;对于给定的一个对象,都能够调用它的任意一个属性和方法。这种动态获取类的内容以及动态调用对象的方法称为反射机制。
作用:
- 可以在程序运行时对类和对象进行操作,提高程序的灵活性和扩展性
- 反射机制是构建框架技术的基础所在,使用反射可以避免将代码写死在框架中
反射机制相关的类:
- Class对象:一个类的class对象
- Constructor对象:一个类的构造器对象
- Method对象:一个类的方法对象
- Field对象:一个类的属性对象
获取类的Class对象
Boolean flag = true;
Class booleanClass = flag.getClass();
try {
Class booleanClass1 = Class.forName("java.lang.Boolean");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Class booleanClass2 = Boolean.class;
Class booleanClass3 = Boolean.TYPE;
class对象的相关方法
// 创建当前class对象类的一个实例(Boolean)
try {
Boolean is = (Boolean) booleanClass.newInstance();
} catch (IllegalAccessException | InstantiationException e) {
e.printStackTrace();
}
// 将obj cast成一个class对象的实例(如果不是同一个类会出现ClassCastException)
booleanClass.cast(false);
booleanClass.getInterfaces(); // 获取当前类的接口的class对象
booleanClass.getSuperclass();
booleanClass.getAnnotation(Override.class);
booleanClass.getClassLoader();// 获取类加载器
booleanClass.getPackage();
booleanClass.getName();// 获取类的全名
booleanClass.getSimpleName();// 获取类名
booleanClass.getModifiers();// 获取类修饰符
通过class对象获取类的Constructor对象
try {
// 获取公有构造方法
Constructor constructor = booleanClass.getConstructor();
// 获取所有的公有构造方法
Constructor [] constructors = booleanClass.getConstructors();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
Constructor对象的相关方法:
constructor.getModifiers();
constructor.getName();
constructor.getParameters();
constructor.getAnnotations();
constructor.setAccessible(true);// 将方法的限定符设置为public
constructor.newInstance();
通过class对象获取类的Method对象
/**
* flag.booleanValue()
* */
// 获取指定方法名的method对象(arg:方法名,arg2:方法参数类型class)
Method methodBooleanValue = booleanClass.getDeclaredMethod("booleanValue",null);
// 获取所有的公有方法
Method [] methods = booleanClass.getDeclaredMethods();
Method对象的相关方法:
// 设置public
methodBooleanValue.setAccessible(true);
Object flag2 = booleanClass.newInstance();
// (arg1:当前method所属对象的实例对象,arg2:当前method的参数值),调用flag2对象的booleanValue方法,参数是null
methodBooleanValue.invoke(flag2,null);
通过class对象获取类的Field对象
// arg1 : 属性名称
Field field = booleanClass.getDeclaredField("serialVersionUID");
Field [] fields = booleanClass.getDeclaredFields();
Field对象的相关方法:
// (arg1 :当前field对象所属class对象的实例对象),获取对象的field属性值
String uid = (String) field.get(flag2);
// (arg1:同上,arg2:需要设置的属性的值),设置arg1实例对象的field对象的属性值
field.set(field,"-3665804199014368530L");
// 判断field属性值是否和传入的obj的field属性值相等
field.equals(true);
Demo
Person Class
public class Person {
private String name;
public Person(String name) {
this.name = name;
}
public Person() {
}
public void eat(){
System.out.println("im eating");
}
private void shit(){
System.out.println("im shitting");
}
}
反射Person类
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
Person person = new Person("tony");
// 获取class对象
Class personClass = person.getClass(); // 带实例生成
Constructor constructorWithNoParams = personClass.getConstructor();
Constructor constructorWithParams = personClass.getConstructor(String.class);
// 通过constructor对象生成Person实例
Person reflectPerson = (Person) constructorWithNoParams.newInstance();
reflectPerson.eat();
// 通过反射调用拉屎方法
Method methodShit = personClass.getDeclaredMethod("shit",null);
System.out.println("methodShit modifiers before setAccessible:"+methodShit.getModifiers());
methodShit.setAccessible(true);
System.out.println("methodShit modifiers after setAccessible:"+methodShit.getModifiers());
methodShit.invoke(person,null);
// 通过反射调用吃饭方法
Method methodEat = personClass.getDeclaredMethod("eat",null);
System.out.println("methodEat modifiers:"+methodEat.getModifiers());
// 获取私有field
Field fieldName = personClass.getDeclaredField("name");
fieldName.setAccessible(true);
System.out.println("fieldName get -> person before set:"+fieldName.get(person));
fieldName.set(person,"new tony");
System.out.println("fieldName get -> person after set:"+fieldName.get(person));
}
在运行结果第三行,我们发现setAccessible并不会改变modifiers的值,但是会影响他的访问,如果不设置为true 会抛出IllegalAccessException异常;
注意直接get和getDeclared的区别
get:获得某个类的所有的公共(public)的字段,包括父类中的字段。
getDeclared:获得某个类的所有声明的字段,即包括public、private和proteced,但是不包括父类的申明字段。
代理模式
定义:
给目标对象设置一个代理对象,并由代理对象控制目标对象的引用
目的:
- 间接访问目标,防止直接访问带来不必要的问题
- 通过代理对象对原有的业务增强
静态代理模式:
代理类,被代理对象,需求对象,业务接口;被代理类提供某种服务,需求对象需要这种服务,但是不能直接访问,需要借助代理类访问;
下面举个中介卖房子的例子:
定义业务接口
public interface SaleHouse {
void saleHouse(String s);
}
定义代理类和被代理类(他们都要实现业务接口,因为他们都要提供服务给买房子的人)
public class Seller implements SaleHouse {
@Override
public void saleHouse(String s) {
System.out.println("我是卖家,我要卖房子");
}
}
public class Agent implements SaleHouse {
private Seller seller; // 代理类持有被代理对象的引用
public Agent(Seller seller) {
this.seller = seller;
}
@Override
public void saleHouse(String s) {
System.out.println("我是中介代理");
seller.saleHouse(s);
}
}
我们真是的购房者通过中介购买房子
public static void main(String[] args) {
Seller seller = new Seller();
Agent agent = new Agent(seller);
agent.saleHouse("房子的要求");
}
这样就完成了需求对象通过代理对象到真实对象的访问
上面介绍的代理模式是静态代理模式,这种代理模式严重违反了设计模式的开闭原则:
- 扩展能力差
- 可维护性差
如果这个时候又有一个人来卖车子,这是中介Agent对象又需要实现一个卖车接口,卖车人对象,当被代理对象越来越多,代码越来越臃肿,这种方式肯定是不可取的;
动态代理模式:
- Proxy:用于代理类创建代理对象
- InvocationHandler(interface):代理类实现接口
动态代理类
public class DynamicAgent implements InvocationHandler {
// 代理类持有的真实对象的引用
private Object seller;
// 设置代理的真实对象
public void setSeller(Object seller) {
this.seller = seller;
}
// 获取动态代理对象(1:被代理对象的类加载器,2:被代理对象实现的接口,)
public Object getProxy() {
return Proxy.newProxyInstance(seller.getClass().getClassLoader(), seller.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
System.out.println("我是中介");
// 通过反射调用被代理对象的业务方法
Object result = method.invoke(seller, objects);
return result;
}
}
此时我们有两个类需要代理
interface SaleCar {
void saleCar();
}
class CarSeller implements SaleCar {
@Override
public void saleCar() {
System.out.println("我是卖车人,我要卖车");
}
}
public interface SaleHouse {
void saleHouse(String s);
}
class HouseSeller implements SaleHouse {
@Override
public void saleHouse(String s) {
System.out.println("我是卖房者,我要卖房子");
}
}
通过代理类去代理两个实际类
// 创建动态代理对象(中介公司)
DynamicAgent dynamicAgent = new DynamicAgent();
// 第一个被代理的卖房子的人
SaleHouse houseSeller = new HouseSeller();
// 设置被代理的对象是卖房者
dynamicAgent.setSeller(houseSeller);
// 获取一个代理对象Proxy(中介),服务卖房者 注意:代理对象只能强转为接口
SaleHouse houseAgent = (SaleHouse) dynamicAgent.getProxy();
houseAgent.saleHouse("卖房子");
// 第二个被代理卖车的人
SaleCar carSeller = new CarSeller();
// 设置中介公司的被代理人
dynamicAgent.setSeller(carSeller);
// 分配一个中介去卖车
SaleCar carAgent = (SaleCar) dynamicAgent.getProxy();
carAgent.saleCar();
创建一个代理类实现InvocationHandler 接口,创建一个业务类,一个业务接口,业务类实现业务接口,把业务类传给代理类,通过代理类生成一个Proxy代理对象,通过这个代理对象去调用业务接口的方法;
例子举完了我觉得有必要画一个思维导图帮助理解记忆
动态代理原理:
newProxyInstance():
public static Object newProxyInstance(ClassLoader loader,
Class>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* Look up or generate the designated proxy class.
*/
// 1.通过classLoader和interfaces获取代理类的class对象
Class> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
// 2.通过class对象获取constructor对象
final Constructor> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
// 3.通过constructor对象返回一个instance实例
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
三步走:
- 1.通过classLoader和interfaces获取代理类的class对象
Class> cl = getProxyClass0(loader, intfs)
- 2.通过class对象获取constructor对象
final Constructor> cons = cl.getConstructor(constructorParams)
- 3.通过constructor对象返回一个instance实例
return cons.newInstance(new Object[]{h})
下面重点讲解第一步:getProxyClass0();
private static Class> getProxyClass0(ClassLoader loader,
Class>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// If the proxy class defined by the given loader implementing
// the given interfaces exists, this will simply return the cached copy;
// otherwise, it will create the proxy class via the ProxyClassFactory
return proxyClassCache.get(loader, interfaces);
}
在Proxy类的内部有一个proxyClassCache成员变量,Proxy内部使用的缓存机制,这是一个WeakCache对象,具体实现是一个ConcurrentMap;如果根据提供的类加载器和接口数组能在缓存中找到代理类就直接返回该代理类,否则会调用ProxyClassFactory工厂去生成代理类。这里用到的缓存是二级缓存,它的一级缓存key是根据类加载器生成的,二级缓存key是根据接口数组生成的;
两个重点:
- 缓存机制
- 如何创建代理类
WeakCache缓存机制:
WeakCache
public V get(K key, P parameter) {
Objects.requireNonNull(parameter);
expungeStaleEntries();
Object cacheKey = CacheKey.valueOf(key, refQueue);
// lazily install the 2nd level valuesMap for the particular cacheKey
ConcurrentMap
ProxyClassFactory:
类变量
// prefix for all proxy class names
private static final String proxyClassNamePrefix = "$Proxy";
// next number to use for generation of unique proxy class names
private static final AtomicLong nextUniqueNumber = new AtomicLong();
apply()
@Override
public Class> apply(ClassLoader loader, Class>[] interfaces) {
Map, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
for (Class> intf : interfaces) {
/*
* Verify that the class loader resolves the name of this
* interface to the same Class object.
*/
Class> interfaceClass = null;
try {
interfaceClass = Class.forName(intf.getName(), false, loader);
} catch (ClassNotFoundException e) {
}
if (interfaceClass != intf) {
throw new IllegalArgumentException(
intf + " is not visible from class loader");
}
/*
* Verify that the Class object actually represents an
* interface.
*/
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
/*
* Verify that this interface is not a duplicate.
*/
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
}
String proxyPkg = null; // package to define proxy class in
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
/*
* Record the package of a non-public proxy interface so that the
* proxy class will be defined in the same package. Verify that
* all non-public proxy interfaces are in the same package.
*/
for (Class> intf : interfaces) {
int flags = intf.getModifiers();
if (!Modifier.isPublic(flags)) {
accessFlags = Modifier.FINAL;
String name = intf.getName();
int n = name.lastIndexOf('.');
String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
if (proxyPkg == null) {
proxyPkg = pkg;
} else if (!pkg.equals(proxyPkg)) {
throw new IllegalArgumentException(
"non-public interfaces from different packages");
}
}
}
if (proxyPkg == null) {
// if no non-public proxy interfaces, use com.sun.proxy package
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
/*
* Choose a name for the proxy class to generate.
*/
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
/*
* Generate the specified proxy class.
*/
// 代理类的java字节码(字节流形式)
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
// 通过native方法将java字节码的二进制流转为Class对象
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
/*
* A ClassFormatError here means that (barring bugs in the
* proxy class generation code) there was some other
* invalid aspect of the arguments supplied to the proxy
* class creation (such as virtual machine limitations
* exceeded).
*/
throw new IllegalArgumentException(e.toString());
}
}
}
private static native Class> defineClass0(ClassLoader loader, String name,
byte[] b, int off, int len);
在apply方法中我们发现byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags)
这行代码生成代理类的字节码文件,ProxyGenerator是由编译器创建的类;最后调用defineClass0
native方法将流转变为Class对象,然后反射,method,invoke。。。
通过constructor.newInstance()创建代理实例对象时传入了h(invocationHander)对象,在代理实例类中会调用h.invoke();
由native方法生成的class对象,本质是Proxy的子类继承了代理类的业务接口,其内部在接口的实现中,调用了h.invoke();和静态代理的代理类和被代理带实现了同一个接口是一个道理,
public final class $Proxy0 extends Proxy implements SaleHouse{
...
// Proxy生成的动态代理类(实现业务接口:在实现中调用invoke())
public final void saleHouse(String paramString){
try {
this.h.invoke(this,m3,new Object[]{paramString});
return;
} catch (RuntimeException e){
}
}
...
}
m3是saleHouse方法的method对象;