概述
反射機制是java中很重要的一環,是與io一樣,屬於java底層性原理,很多框架的開發的十分依賴反射(比如IOC).反射理解對類進行了''解剖''.
代理與反射恰好相反,對類進行了包裝,代理類的行爲,在各類框架中應用十分廣泛(如AOP,RPC).
反射
反射(Reflection)是Java 程序開發語言的特徵之一,它允許運行中的 Java 程序獲取自身的信息,並且可以操作類或對象的內部屬性。(反射)
從基本定義中可以理出關鍵詞:運行中(反射時機),自身(反射對象),內部屬性(反射內容),以下僅對反射對象與內容作舉例說明.(反射入門)
class
package reflection;
@Deprecated
public class ReflectClass{
public String name;
/**get the default name
* @return
*/
public static String getDefaultName(String name){
return name == null ? "hello world" : name;
}
private void getName(){
System.out.println("private excute");
}
}
test
package reflection;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import org.junit.Test;
public class TestReflect {
@Test
/*
* jdk提供的反射相關類 java.lang.Class;//類 java.lang.reflect.Constructor;//構造器
* java.lang.reflect.Field;//屬性 java.lang.reflect.Method;//方法
* java.lang.reflect.Modifier;//修飾符(public private 等)
* java.lang.annotation.Annotation;//註解
*/
public void testBasic() throws Exception {
/*注:對abstract class ,class,interface 都是可以獲取到相關declare信息的,但invoke等一些方法只針對實例而言的
* 此處僅以class爲例(當class繼承class或實現interface,反射的包括繼承或實現部分)
*
* */
/* 獲取類 ----獲取反射對象 */
Class<?> clazz;
// 第一種方式(直接獲取):
// java中每個類型都有class屬性.
clazz = ReflectClass.class;
/*
* //第二種方式(利用classLoader獲取): clazz = Class.forName("ReflectClass");
*
* //第三種方式(實例獲取): ReflectClass r = new ReflectClass(); clazz =
* r.getClass();
*/
/* 反射內容 */
/*
* 屬性 private String name;
*/
Field[] fields = clazz.getDeclaredFields();
System.out.println("reflect field start");
for (Field field : fields) {
System.out.println("name: " + field.getName());// 屬性的名字
System.out.println("modifier: " + Modifier.toString(field.getModifiers()));// 獲得屬性的修飾符,例如public,static等等
System.out.println("type: " + field.getType().getSimpleName());// 屬性的類型的名字
}
System.out.println("****************************");
/*
* 構造器 public ReflectClass(){}
*/
Constructor<?>[] constructors = clazz.getDeclaredConstructors();
System.out.println("reflect constructor start");
for (Constructor<?> constructor : constructors) {
System.out.println("name: " + constructor.getName());// 構造器的名字
System.out.println("modifier: " + Modifier.toString(constructor.getModifiers()));// 獲得構造器修飾符,例如public,static等等
System.out.println("parameters: " + Arrays.toString(constructor.getParameterTypes()));// 構造器參數
}
System.out.println("****************************");
/*
* 方法 public static String getDefaultName(String name)
*/
Method[] methods = clazz.getDeclaredMethods();
System.out.println("reflect method start");
for (Method method : methods) {
System.out.println("name: " + method.getName());// 方法的名字
System.out.println("modifier: " + Modifier.toString(method.getModifiers()));// 獲得方法修飾符,例如public,static等等
System.out.println("parameters: " + Arrays.toString(method.getParameterTypes()));// 方法參數
}
System.out.println("****************************");
System.out.println("reflect annotation start");
Annotation[] annotations = clazz.getDeclaredAnnotations();
for (Annotation annotation : annotations) {
System.out.println("name: " + annotation.toString());
}
System.out.println("****************************");
System.out.println("invoke private start");
/* 注意:對一些private 修飾,可以獲取,但需要進行權限設置(暴力反射)才能執行,以method爲例 */
Method method = clazz.getDeclaredMethod("getName");
System.out.println("name: " + method.getName());
method.setAccessible(true);// 設置訪問權限
method.invoke(new ReflectClass());
/*
* 打印結果 reflect field start name: name modifier: public type: String
****************************
* reflect constructor start name: reflection.ReflectClass modifier:
* public parameters: []
****************************
* reflect method start name: getName modifier: private parameters: []
* name: getDefaultName modifier: public static parameters: [class
* java.lang.String]
****************************
* reflect annotation start name: @java.lang.Deprecated()
****************************
* invoke private start name: getName private excute
*/
}
}
反射獲取類描述,類描述結合代理對象,便可與實現類的相關功能(rpc).
代理
代理:爲其他對象提供一種代理已控制對這個對象的訪問.(代理)
按照代理的創建的時期,代理分靜態代理,動態代理
靜態代理
靜態代理屬於硬編碼,在程序運行之前就代理類已經生成,靜態代理更多的時候作爲一種代理思想而存在.
靜態代理example
被代理對象
package proxy;
public class Student {
public void study(){
System.out.println("hello world");
}
}
代理對象
package proxy;
public class StudentProxy {
private Student student;
public StudentProxy (Student student) {
this.student = student;
}
public void study(){
if (student == null) {
System.out.println("proxy fail");
}
student.study();
}
}
訪問
public static void main(String[] args) {
StudentProxy proxy = new StudentProxy(new Student());
proxy.study();
}
動態代理
動態代理,在程序運行時,運用反射機制動態創建,相比靜態代理,更爲靈活,大多數框架底層使用就是動態代理.此處主要講JDK動態代理與CGLIB代理
JDK動態代理
JDK實現動態代理核心:Proxy代理工具類,InvocationHandler反射實現接口.
JDK動態代理只對實現接口的類有效
增加一個接口
package proxy;
public interface Human{
public void study();
}
接口實現
package proxy;
public class Student implements Human{
public void study(){
System.out.println("hello world");
}
}
client
final Student stu =new Student();
Human s = (Human)Proxy.newProxyInstance(Human.class.getClassLoader(), new Class[] {Human.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result;
result = method.invoke(stu,args);
return result;
}
});
s.study();
CGLIB代理
JDK動態代理的類必須實現接口,對沒有實現接口的類進行代理,使用CGLIB動態代理
基本原理:運行時動態的生成一個被代理類的子類(通過ASM字節碼處理框架實現),子類重寫了被代理類中所有非final的方法。在子類中採用方法攔截的技術攔截所有父類非final方法的調用,順勢植入橫切邏輯。
引入相關包
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.4</version>
</dependency>
目標類
package proxy;
public class CglibTest {
public void doSomething(){
System.out.println("hello");
}
}
client
/*Enhancer類是CGLib中的一個字節碼增強器
* 設置父類CglibTest
* 設置方法攔截器(實現AOP)
* enhancer.create()動態生成CglibTest的子類
* */
Enhancer enhancer =new Enhancer();
enhancer.setSuperclass(CglibTest.class);
//方法攔截器,執行子類方法前執行
enhancer.setCallback(new MethodInterceptor() {
/**
* 重寫方法攔截在方法前和方法後加入業務
* Object obj爲目標對象
* Method method爲目標方法
* Object[] params 爲參數,
* MethodProxy proxy CGlib方法代理對象
*/
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
Object result ;
System.out.println("處理前");
result=proxy.invokeSuper(obj, args);
System.out.println("處理後");
return result;
}
});
CglibTest cglibTest=(CglibTest)enhancer.create();
cglibTest.doSomething();
代理總結
- 相對於動態代理,靜態代理更被認可爲一種思維模式,而動態代理使用反射機制動態創建代理對象
- JDK動態代理(可以用靜態的思維去理解),代理對象和目標對象實現了相同的接口,目標對象作爲代理對象的一個屬性,具體接口實現中,可以在調用目標對象相應方法前後加上其他的業務邏輯處理
- CGLIB是生成一個目標對象的子類,覆蓋目標對象的所有方法(不包括final方法),通過設置方法攔截器,在調用子類的方法時,進行攔截,實現AOP
- JDK動態代理制能針對實現接口的類實行代理,CGLIBE針對類
- 動態代理在AOP,RPC(rpc小案例)頗多,