java中的反射與應用

摘要:本文主要講解了反射的基礎語法、反射在動態代理中的應用,動態代理主要講解了JDK動態代理和Cglib的動態代理。兩者的區別是JDK是面向接口的編程,Cglib是面向方法的編程,都有各自的應用場景。

1、什麼反射?

Java程序在運行時,可以獲取類的相關信息,可以動態調用對象的方法機制。

類比:類是所有對象的抽象,類對象class是對所有類的抽象。(個人觀點)

2、反射的使用

反射應用的前提是獲取類所對應的類對象,一個類能且只能產生一個類對象。假設在包com.smart.reflect下存在一個Student類,獲取類對象的三個方法

(1) Class studentClass=  Class.forName("com.smart.reflect.Student"); 推薦使用

(2) Student student = new Student (); Class studentClass = student.getClass();

(3)  Class studentClass = Student.class;

拿到類對象之後,就可以獲取類及其父類的相關信息,屬性,方法(構造方法和普通方法),來看下主要信息

(1)Class 描述類本身

(1.1)獲取類的修飾符:studentClass.getModifies()

(1.2)獲取類的名字:studentClass.getSimpleName();studentClass.getName();

   (1.3)   獲取父類的信息:Class superClass = studentClass,getSuperClass()

(1.4)獲取接口信息:Class[] interface = studentClass.getInterfaces();

創建對象

Object object =  studentClass.newInstance(),調用無參構造函數;studentClass.newInstance()

(2)Package 包信息

         獲取包的相關信息:Package p = studentClass.getPackage();

(3)Field 屬性信息

  (3.1)獲取 屬性:Field field = studentClass.getField(屬性名); Field field = studentClass.getDeclareField(屬性名); 

  (3.2)獲取屬性的類型:Class fclass = field.getType();

    (3.3)  爲屬性賦值:field.set(對象,值)

 (3.4)取屬性的值:field.get(對象)

 (3.5) 獲取所有公有的屬性: Field[] field = studentClass.getFields()

 (3.6) 獲取所有的屬性: Field[] field = studentClass.getDeclaredFields()

注意:訪問私有屬性 field.setAccessible(true);

   (3.7) 獲取所有內部類:Class[] innerClass = studentClass.getClasses()

(4)Method 方法信息

 (4.1)獲取 方法:Method method = studentClass.getMethod(方法名,類型); Method method = studentClass.getDeclareMethod(方法名,類型); 

(4.2)獲取方法的信息:返回值類型,方法名,參數列的類型,異常類型

 (4.3) 獲取所有公有的方法: Method[] method= studentClass.getMethods()

 (4.4) 獲取所有的方法: Method[] method = studentClass.getDeclaredMethods()

(4.5)有參數:method.invoke(對象,參數); 無參數:method.invoke(對象);

  注意:訪問私有屬性 method.setAccessible(true);

(5)Constructor 用來描述類中的構造方法

(6)Annotation 描述類中的註解

3、反射的應用

3.1 動態代理

3.1.1.靜態代理之Helloworld

(1)創建目標接口

 

interface Service{
       void sayHello();
   } 

(2)實現目標接口

 

class RealService implements Service{
        @Override
        public void sayHello() {
            System.out.println("hello, world!");
        }

    }

(3)創建代理類,實現了Service接口

 

class StaticProxy implements Service{

        private Service realService;
        public StaticProxy(Service service){
            this.realService = service;
        }
        @Override
        public void sayHello() {
            realService.sayHello();
        }

    }

代理類中注入了Service接口的對象,實例化時只需要注入Service的實現類就好。

(4)創建測試程序

3.1.2.動態代理-JDK

 

(1)創建目標接口

 

interface Service{
       void sayHello();
   } 

(2)實現目標接口

class RealService implements Service{
        @Override
        public void sayHello() {
            System.out.println("hello, world!");
        }

    }

(3)創建代理類

 

public class DynamicProxyHanlder implements InvocationHandler {
    private Object obj;
    public DynamicProxyHanlder(Object realObj){
        this.obj = realObj;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        method.invoke(obj, args);
        return null;
    }
}

創建動態代理,主要是實現InvocationHandler接口,重寫了invoke方法,其中obj表示要真實代理的類。

(4)創建測試程序

 

public class Client {

    public static void main(String[] args) {
        Service realService = new ServiceImpl();
        InvocationHandler proxyHandler = new DynamicProxyHanlder(realService);
        Service proxyService = 
                (Service) Proxy.newProxyInstance(
                        Service.class.getClassLoader(),
                        new Class<?>[]{Service.class}, 
                        proxyHandler);
        proxyService.sayHello();
        
    }
}

代理類傳入的參數是:被代理接口的類加載器,類類型的數組,代理類。

3.1.3.動態代理-Cglib

(1)創建目標方法類

 

class Service{
        public void sayHello(){
            System.out.println("hello,world");
        }
    }

(2)創建代理類

 

class SimpleInterceptor implements MethodInterceptor {

        @Override
        public Object intercept(Object object, Method method,
                Object[] args, MethodProxy proxy) throws Throwable {
            Object result = proxy.invokeSuper(object, args);
            return result;
        }

    }

實現的是MethodInterceptorie接口中的intercept的方法

(3)創建測試程序

 

public class Main {

    private static <T> T getProxy(Class<T> clazz){
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(new SimpleInterceptor());

        return (T)enhancer.create();
    }

    public static void main(String[] args) {
        Service proxyService = getProxy(Service.class);
        proxyService.sayHello();
    }
}

總結:靜態代理和動態代理-JDK是面向接口編程的,而動態代理-cglib是面向方法的編程。

動態代理涉及到類加載器的加載,具體的文章請參考:https://www.cnblogs.com/hiyujie/p/wo-xueJava1ClassLoader-yu-shuang-qin-wei-tuo-mo-sh.html

4、類加載器和反射的關係

      類加載器將字節碼文件加載如JVM。

5、反射實例 (簡化版的SpringIOC的實現)

5.1創建Person類:

/**
 *
 * @author smart 2019/3/23
 */
public class Person {

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    private String name;

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    private Integer age;
}

5.2 創建簡化版的Spring處理類

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Scanner;

/**
 *
 * @author smart 2019/3/23
 */
public class MySpring {
    public Object getBean(String packagePath){
        Object result = null;
        Scanner scanner = new Scanner(System.in);
        try{
            //創建類
           Class<?> classObject = Class.forName(packagePath);
           result = classObject.newInstance();

           //獲取類的屬性
            Field[] fields = classObject.getDeclaredFields();
            for (Field field: fields) {
                //獲取屬性名
                String name = field.getName();
                String firstLetter = name.substring(0,1).toUpperCase();
                String otherLetter = name.substring(1);
                StringBuilder propertiesMethod = new StringBuilder("set");
                propertiesMethod.append(firstLetter).append(otherLetter);
                Class fieldClass = field.getType();
                Method method = classObject.getMethod(propertiesMethod.toString(),fieldClass);
                //參數可以從文件中讀取或者從註解中讀取
                System.out.println("請輸入參數");
                String param = scanner.nextLine();
                Constructor con = fieldClass.getConstructor(String.class);
                method.invoke(result,con.newInstance(param));
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return result ;
    }
}

5.3測試及結果


/**
 *
 * @author smart 2019/3/23
 */
public class MainTest {
    public static void main(String[] args) {
        MySpring mySpring = new MySpring();
        Person person = (Person) mySpring.getBean("Person");
        System.out.println(person);
    }
}
測試結果:

請輸入參數
12222
請輸入參數
12
Person{name='12222', age=12}

5.4 利用註解的方式注入值

(1)自定義註解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 *
 * @author smart 2019/3/23
 */
@Target(value = {ElementType.CONSTRUCTOR,ElementType.FIELD,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnoation {
    String value();
}

(2)修改Person類

/**
 *
 * @author smart 2019/3/23
 */
public class Person {

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @MyAnnoation("smart")
    private String name;

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @MyAnnoation("12")
    private Integer age;
}

(3)修改MySpring類

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Scanner;

/**
 *
 * @author smart 2019/3/23
 */
public class MySpring {
    public Object getBean(String packagePath){
        Object result = null;
        Scanner scanner = new Scanner(System.in);
        try{
            //創建類
           Class<?> classObject = Class.forName(packagePath);
           result = classObject.newInstance();
           //獲取類的屬性
            Field[] fields = classObject.getDeclaredFields();
            for (Field field: fields) {
                //獲取屬性名
                String name = field.getName();
                String firstLetter = name.substring(0,1).toUpperCase();
                String otherLetter = name.substring(1);
                StringBuilder propertiesMethod = new StringBuilder("set");
                propertiesMethod.append(firstLetter).append(otherLetter);
                Class fieldClass = field.getType();
                Method method = classObject.getMethod(propertiesMethod.toString(),fieldClass);
                //參數可以從文件中讀取或者從註解中讀取
                Annotation annotation = field.getAnnotation(MyAnnoation.class);
                Constructor con = fieldClass.getConstructor(String.class);
                method.invoke(result,con.newInstance(((MyAnnoation) annotation).value()));
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return result ;
    }
}

(4)測試結果

Person{name='smart', age=12}

備註:調用註解的裏的value方法,可以通過如下的代碼:‘

’Annotation annotation = field.getAnnotation(MyAnnoation.class);

Class annotationClass = annotation.getClass();

Method method = annotationClass.getMethod("value");

String value = method.invoke(annotationClass);

 

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