Java反射機制
Java反射機制指的是在Java程序運行狀態(tài)中蝌箍,對于任何一個類,都可以獲得這個類的所有屬性和方法牺汤;對于給定的一個對象辽旋,都能夠調(diào)用它的任意一個屬性和方法。這種動態(tài)獲取類的內(nèi)容以及動態(tài)調(diào)用對象的方法稱為反射機制檐迟。
作用:
- 可以在程序運行時對類和對象進行操作补胚,提高程序的靈活性和擴展性
- 反射機制是構建框架技術的基礎所在,使用反射可以避免將代碼寫死在框架中
反射機制相關的類:
- 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對象的相關方法
// 創(chuàng)建當前class對象類的一個實例(Boolean)
try {
Boolean is = (Boolean) booleanClass.newInstance();
} catch (IllegalAccessException | InstantiationException e) {
e.printStackTrace();
}
// 將obj cast成一個class對象的實例(如果不是同一個類會出現(xiàn)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:方法參數(shù)類型class)
Method methodBooleanValue = booleanClass.getDeclaredMethod("booleanValue",null);
// 獲取所有的公有方法
Method [] methods = booleanClass.getDeclaredMethods();
Method對象的相關方法:
// 設置public
methodBooleanValue.setAccessible(true);
Object flag2 = booleanClass.newInstance();
// (arg1:當前method所屬對象的實例對象溶其,arg2:當前method的參數(shù)值),調(diào)用flag2對象的booleanValue方法敦间,參數(shù)是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();
// 通過反射調(diào)用拉屎方法
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);
// 通過反射調(diào)用吃飯方法
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));
}
在運行結果第三行金闽,我們發(fā)現(xiàn)setAccessible并不會改變modifiers的值纯露,但是會影響他的訪問剿骨,如果不設置為true 會拋出IllegalAccessException異常;
注意直接get和getDeclared的區(qū)別
get:獲得某個類的所有的公共(public)的字段埠褪,包括父類中的字段浓利。
getDeclared:獲得某個類的所有聲明的字段,即包括public钞速、private和proteced贷掖,但是不包括父類的申明字段。
代理模式
定義:
給目標對象設置一個代理對象渴语,并由代理對象控制目標對象的引用
目的:
- 間接訪問目標苹威,防止直接訪問帶來不必要的問題
- 通過代理對象對原有的業(yè)務增強
靜態(tài)代理模式:
代理類,被代理對象驾凶,需求對象牙甫,業(yè)務接口掷酗;被代理類提供某種服務,需求對象需要這種服務窟哺,但是不能直接訪問泻轰,需要借助代理類訪問;
下面舉個中介賣房子的例子:
定義業(yè)務接口
public interface SaleHouse {
void saleHouse(String s);
}
定義代理類和被代理類(他們都要實現(xiàn)業(yè)務接口且轨,因為他們都要提供服務給買房子的人)
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("房子的要求");
}
這樣就完成了需求對象通過代理對象到真實對象的訪問
上面介紹的代理模式是靜態(tài)代理模式,這種代理模式嚴重違反了設計模式的開閉原則:
- 擴展能力差
- 可維護性差
如果這個時候又有一個人來賣車子旋奢,這是中介Agent對象又需要實現(xiàn)一個賣車接口泳挥,賣車人對象,當被代理對象越來越多黄绩,代碼越來越臃腫羡洁,這種方式肯定是不可取的;
動態(tài)代理模式:
- Proxy:用于代理類創(chuàng)建代理對象
- InvocationHandler(interface):代理類實現(xiàn)接口
動態(tài)代理類
public class DynamicAgent implements InvocationHandler {
// 代理類持有的真實對象的引用
private Object seller;
// 設置代理的真實對象
public void setSeller(Object seller) {
this.seller = seller;
}
// 獲取動態(tài)代理對象(1:被代理對象的類加載器爽丹,2:被代理對象實現(xiàn)的接口筑煮,)
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("我是中介");
// 通過反射調(diào)用被代理對象的業(yè)務方法
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("我是賣房者粤蝎,我要賣房子");
}
}
通過代理類去代理兩個實際類
// 創(chuàng)建動態(tài)代理對象(中介公司)
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();
創(chuàng)建一個代理類實現(xiàn)InvocationHandler 接口,創(chuàng)建一個業(yè)務類初澎,一個業(yè)務接口秸应,業(yè)務類實現(xiàn)業(yè)務接口,把業(yè)務類傳給代理類碑宴,通過代理類生成一個Proxy代理對象软啼,通過這個代理對象去調(diào)用業(yè)務接口的方法;
例子舉完了我覺得有必要畫一個思維導圖幫助理解記憶
動態(tài)代理原理:
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<Void>() {
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類的內(nèi)部有一個proxyClassCache成員變量祸挪,Proxy內(nèi)部使用的緩存機制,這是一個WeakCache對象贞间,具體實現(xiàn)是一個ConcurrentMap贿条;如果根據(jù)提供的類加載器和接口數(shù)組能在緩存中找到代理類就直接返回該代理類,否則會調(diào)用ProxyClassFactory工廠去生成代理類增热。這里用到的緩存是二級緩存整以,它的一級緩存key是根據(jù)類加載器生成的,二級緩存key是根據(jù)接口數(shù)組生成的峻仇;
兩個重點:
- 緩存機制
- 如何創(chuàng)建代理類
WeakCache緩存機制:
WeakCache<K,P,V>中公黑,K代表key值,P代表參數(shù),V代表存儲的值凡蚜。此類用于緩存{(key奠骄,sub-key)-->value}鍵值對。具體實現(xiàn)是一個ConcurrentMap<Object,ConcurrentMap<Object,Supplier<V>>>(Supplier是一個接口番刊,就一個get方法用于獲得值含鳞,不過是V的包裹類),第一個Object就是key(這里表達式不用K是因為key值可以為null)芹务,第二個Object就是sub-key蝉绷。
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<Object, Supplier<V>> valuesMap = map.get(cacheKey);
if (valuesMap == null) {
ConcurrentMap<Object, Supplier<V>> oldValuesMap
= map.putIfAbsent(cacheKey,
valuesMap = new ConcurrentHashMap<>());
if (oldValuesMap != null) {
valuesMap = oldValuesMap;
}
}
// create subKey and retrieve the possible Supplier<V> stored by that
// subKey from valuesMap
// 調(diào)用apply方法創(chuàng)建代理類
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
Supplier<V> supplier = valuesMap.get(subKey);
Factory factory = null;
while (true) {
if (supplier != null) {
// supplier might be a Factory or a CacheValue<V> instance
V value = supplier.get();
if (value != null) {
return value;
}
}
// else no supplier in cache
// or a supplier that returned null (could be a cleared CacheValue
// or a Factory that wasn't successful in installing the CacheValue)
// lazily construct a Factory
if (factory == null) {
factory = new Factory(key, parameter, subKey, valuesMap);
}
if (supplier == null) {
supplier = valuesMap.putIfAbsent(subKey, factory);
if (supplier == null) {
// successfully installed Factory
supplier = factory;
}
// else retry with winning supplier
} else {
if (valuesMap.replace(subKey, supplier, factory)) {
// successfully replaced
// cleared CacheEntry / unsuccessful Factory
// with our Factory
supplier = factory;
} else {
// retry with current supplier
supplier = valuesMap.get(subKey);
}
}
}
}
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<Class<?>, 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字節(jié)碼(字節(jié)流形式)
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
// 通過native方法將java字節(jié)碼的二進制流轉為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方法中我們發(fā)現(xiàn)byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags)
這行代碼生成代理類的字節(jié)碼文件,ProxyGenerator是由編譯器創(chuàng)建的類枣抱;最后調(diào)用defineClass0
native方法將流轉變?yōu)镃lass對象熔吗,然后反射,method佳晶,invoke桅狠。。轿秧。
通過constructor.newInstance()創(chuàng)建代理實例對象時傳入了h(invocationHander)對象中跌,在代理實例類中會調(diào)用h.invoke();
由native方法生成的class對象,本質(zhì)是Proxy的子類繼承了代理類的業(yè)務接口,其內(nèi)部在接口的實現(xiàn)中菇篡,調(diào)用了h.invoke();和靜態(tài)代理的代理類和被代理帶實現(xiàn)了同一個接口是一個道理漩符,
public final class $Proxy0 extends Proxy implements SaleHouse{
...
// Proxy生成的動態(tài)代理類(實現(xiàn)業(yè)務接口:在實現(xiàn)中調(diào)用invoke())
public final void saleHouse(String paramString){
try {
this.h.invoke(this,m3,new Object[]{paramString});
return;
} catch (RuntimeException e){
}
}
...
}
m3是saleHouse方法的method對象;