動(dòng)態(tài)代理
調(diào)用者—>代理—>對象
動(dòng)態(tài)代理就是無侵入式的給代碼增加新的功能,通過接口保證后面的對象和代理需要實(shí)現(xiàn)同一個(gè)接口,接口中就是被代理的所有方法,代理里面就是對象要被代理的方法。
為什么需要代理
因?yàn)橐粋€(gè)對象覺得自己身上的功能太多,就會(huì)將一部分功能代理出去,對象中什么方法想要被代理,在代理中必須有對應(yīng)的方法,一般會(huì)定義一個(gè)接口,接口中的方法是想要被代理的方法
代理的詳細(xì)實(shí)現(xiàn)過程
首先是有一個(gè)需要被代理的對象,然后這個(gè)對象中有哪個(gè)方法想要被代理,創(chuàng)建一個(gè)接口,接口中的方法就是這個(gè)對象想要被代理的方法,在這個(gè)對象中需要實(shí)現(xiàn)這個(gè)接口,然后重寫這幾個(gè)方法。這時(shí)候就需要一個(gè)代理類,這個(gè)代理類,這個(gè)代理類主要是用到里面的代理方法,這個(gè)方法的作用是給一個(gè)對象創(chuàng)建一個(gè)代理,將需要被代理的對象傳入其中,然后返回的是代理的對象。在這個(gè)方法里面,需要調(diào)用一個(gè)方法,就是java.util.reflect.Proxy
類里面的一個(gè)方法,由于這個(gè)方法是靜態(tài)方法,所以可以直接使用類名進(jìn)行調(diào)用,public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
在這個(gè)方法中,第一個(gè)參數(shù)是指定哪一個(gè)類加載器去加載生成的代理類,第二個(gè)參數(shù)是指定的接口,第三個(gè)參數(shù)是用于指定生成的代理對象要干什么,一般創(chuàng)建代理對象不會(huì)調(diào)用到第三個(gè)參數(shù),后面只有通過代理對象進(jìn)行方法回調(diào)的時(shí)候才會(huì)調(diào)用第三個(gè)參數(shù),在執(zhí)行這個(gè)第三個(gè)參數(shù)時(shí),會(huì)重寫invoke方法進(jìn)行代理方法的實(shí)現(xiàn),傳入invoke方法有三個(gè)參數(shù),第一個(gè)參數(shù)是傳入代理的對象,第二個(gè)參數(shù)是傳入需要運(yùn)行的就是原本對象的方法,第三個(gè)參數(shù)就是傳入這個(gè)方法的參數(shù)。在返回的時(shí)候,返回的是傳入這個(gè)方法的invoke方法傳回的值。
代碼詳情
- 首先是定義一個(gè)方法,里面是所有的功能,這里定義了一個(gè)明星類
public class BigStar implements Star {
private String name;
public BigStar() {
}
public BigStar(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String sing(String name) {
System.out.println(this.name + "正在唱" + name);
return "謝謝";
}
@Override
public void dance() {
System.out.println(this.name + "跳舞");
}
}
- 其次是一個(gè)需要代理的方法
/**
* 我們可以將所有想要被代理的方法定義在接口當(dāng)中
*/
public interface Star {
// 唱歌
String sing(String name);
// 跳舞
void dance();
}
- 然后是一個(gè)代理類
public class ProxyUtil {
/**
* 這個(gè)方法的作用:給明星的一個(gè)對象創(chuàng)建一個(gè)代理
* @param bigStar 被代理的明星對象
* @return 給明星創(chuàng)建的代理
*/
public static Star createProxy(BigStar bigStar){
/**
* java.util.reflect.Proxy類:提供了為對象產(chǎn)生代理對象的方法
* public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
* 參數(shù)一:用于指定用哪個(gè)類加載器去加載生成的代理類
* 參數(shù)二:指定接口,就是這個(gè)接口用于指定生成的代理有哪些方法
* 參數(shù)三:用于指定生成的代理對象要干什么
*/
Star star = (Star) Proxy.newProxyInstance(
ProxyUtil.class.getClassLoader(),
new Class[]{Star.class},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/**
* 參數(shù)一:代理的對象
* 參數(shù)二:要運(yùn)行的方法
* 參數(shù)三:調(diào)用方法時(shí)傳遞的實(shí)參
*/
if("sing".equals(method.getName())){
System.out.println("給話筒");
} else if ("dance".equals(method.getName())) {
System.out.println("準(zhǔn)備場地");
}
return method.invoke(bigStar, args);
}
}
);
return star;
}
}
- 測試代理
public class Test {
public static void main(String[] args) {
BigStar bigStar = new BigStar("坤哥");
Star prosy = ProxyUtil.createProxy(bigStar);
String res = prosy.sing("雞你太美");
System.out.println(res);
// ProxyUtil.createProxy(bigStar).dance();
}
}
反射
反射概念
反射就是可以在運(yùn)行時(shí)分析類和執(zhí)行類的能力,通過反射可以獲得任意一個(gè)類的屬性和方法,還可以調(diào)用這些類的屬性和方法。比如下面這張圖片。
反射中常用的方法
在反射中,我們可以使用三種方式調(diào)用類的字節(jié)碼文件,分別是Class.forName(全類名),類名.class,對象.getClass();在這三種調(diào)用類的字節(jié)碼的過程中,第一種一般市用在類加載過程中的,第二種經(jīng)常作為參數(shù)進(jìn)行調(diào)用,第三種一般是創(chuàng)建對象之后,然后使用對象名進(jìn)行調(diào)用。當(dāng)我們獲取到類的字節(jié)碼文件之后,就可以通過類的字節(jié)碼文件去調(diào)用類中的構(gòu)造方法,成員變量和成員方法以及他們相應(yīng)的信息。構(gòu)造方法(Constructor),使用getConstructors
可以獲取到所有的公共的構(gòu)造方法,使用getDeclaredConstructors
可以獲取所有的構(gòu)造方法,使用不加s的可以用來獲取單個(gè)的構(gòu)造方法,但是需要傳入構(gòu)造方法中傳入的參數(shù)類型,比如Stirng就需要傳入String.class
,int就需要傳入int.class
,Declared
是用來控制是否查找公共方法,當(dāng)我們獲取到某個(gè)對象之后,可以使用getModifiers
獲取修飾這個(gè)對象的權(quán)限修飾符(但是一般獲取到的都是數(shù)字比如下圖),使用getParameters
獲取這個(gè)對象中所有傳入?yún)?shù)的類型,然后使用newInstance
去構(gòu)造這個(gè)對象,但是一般private
修飾的方法會(huì)報(bào)錯(cuò),需要使用setAccessible(true)
消除掉當(dāng)前方法的修飾權(quán)限。
文章來源:http://www.zghlxwxcb.cn/news/detail-666028.html
反射雖然給與了我們在運(yùn)行時(shí)分析類和操作類的能力,但是也增加了安全性問題,可以通過setAccessible(true)方法設(shè)置對象可被強(qiáng)制訪問,可以破壞單例的封裝性文章來源地址http://www.zghlxwxcb.cn/news/detail-666028.html
所有代碼
- 獲取類字節(jié)碼文件
/**
* Java反射
* 反射允許對類的成員變量,成員方法,構(gòu)造方法的信息進(jìn)行編程訪問
* 可以獲取到這三者的權(quán)限修飾符,名字,返回值類型??梢詫Λ@取到的屬性賦值,或者獲取已經(jīng)有的值。
* 可以獲取到成員方法返回的值,還可以獲取到方法拋出的異常,方法上的注解
* 可以運(yùn)行獲取出來的方法
*
* 首先要獲取.class字節(jié)碼文件
* 獲取class對象的三種方式:
* 1. Class.forName("全類名"); <源代碼階段使用>
* 2. 類名.class <類加載階段使用>
* 3. 對象.getClass(); <運(yùn)行階段使用>
*/
public class ReflectDemo1 {
public static void main(String[] args) throws ClassNotFoundException {
Student student = new Student();
// 最常見的一種
System.out.println(Class.forName("oop.reflectdemo.reflect.Student"));
// 一般當(dāng)作參數(shù)進(jìn)行傳遞
System.out.println(Student.class);
// 當(dāng)只有這個(gè)類的對象才可以使用
System.out.println(student.getClass());
}
}
- 獲取類中的構(gòu)造方法,屬性,以及成員方法信息
/**
* Java反射
* 常見的獲取構(gòu)造方法,成員變量,成員方法的方法
* Java中Constructor描述構(gòu)造方法,F(xiàn)ield描述成員變量,Method描述成員方法
*/
public class ReflectDemo2 {
public static void main(String[] args) throws Exception {
// 獲取字節(jié)碼文件對象
Class clazz = Class.forName("oop.reflectdemo.reflect.Student");
// 獲取所有公共的的構(gòu)造方法
Constructor[] constructor = clazz.getConstructors();
for (Constructor constructor1 : constructor) {
System.out.println(constructor1);
}
// 獲取所有的構(gòu)造方法
Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
for (Constructor declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor);
}
/**
* 獲取單個(gè)構(gòu)造方法
* 后面括號(hào)里面需輸入這個(gè)構(gòu)造方法的傳入值類型
* 一般getConstructor只能獲取到public修飾的方法,而要獲取到所有權(quán)限修飾的方法必須用getDeclaredConstructor;
*
*/
clazz.getConstructor(); // 獲取無參構(gòu)造
clazz.getConstructor(String.class); // 獲取傳入?yún)?shù)為String類型的構(gòu)造方法
Constructor declaredConstructor = clazz.getDeclaredConstructor(String.class, int.class);// 獲取傳入?yún)?shù)為int的構(gòu)造方法
// 獲取修飾這個(gè)對象的權(quán)限修飾符
int modifiers = declaredConstructor.getModifiers();
System.out.println(modifiers);
// 獲取這個(gè)對象中所有傳入?yún)?shù)的類型
declaredConstructor.getParameters();
declaredConstructor.setAccessible(true);
Student tom = (Student) declaredConstructor.newInstance("Tom", 21);
System.out.println(tom);
/**
* 操作成員變量
*/
// 獲取public修飾的所有的成員變量
Field[] fields = clazz.getFields();
for (Field field : fields) {
System.out.println(field);
}
// 獲取所有成員變量
Field[] declaredField = clazz.getDeclaredFields();
for (Field field : declaredField) {
System.out.println(field);
}
// 返回單個(gè)的成員變量對象
Field name = clazz.getDeclaredField("name");
System.out.println(name);
// 獲取權(quán)限修飾符
int modifiers1 = name.getModifiers();
System.out.println(modifiers1);
// 獲取名字
String name1 = name.getName();
System.out.println(name1);
// 獲取類型
Class<?> type = name.getType();
System.out.println(type);
// 獲取成員變量記錄的值
Student s = new Student("Tom", 32);
name.setAccessible(true);
Object value = name.get(s);
System.out.println(value);
// 修改對象里面的值
name.set(s,"Jerry");
System.out.println(s.getName());
/**
* 操作成員方法
*/
// 獲取所有的公共方法
Method[] methods = clazz.getMethods();
// 獲取所有的方法
Method[] declaredMethods = clazz.getDeclaredMethods();
/**
* 獲取方法進(jìn)行調(diào)用
*/
// 獲取單個(gè)方法, 傳入方法名,后面跟著方法傳入的參數(shù)類型
Method eat = clazz.getMethod("eat", String.class);
// 獲取方法的權(quán)限修飾符
int modifiers2 = eat.getModifiers();
// 獲取方法的形參
Parameter[] parameters = eat.getParameters();
// 獲取方法拋出的異常
Class<?>[] exceptionTypes = eat.getExceptionTypes();
// 方法運(yùn)行
Student student = new Student();
eat.setAccessible(true);
eat.invoke(student,"漢堡");
}
}
到了這里,關(guān)于Java動(dòng)態(tài)代理、反射的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!