反射與代理

概述

         反射機制是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小案例)頗多,

參考:JDK動態代理   Cglib動態代理

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章