Spring的反射機制和依賴注入


 

我們知道,Spring中大量使用了反射機制,那么究竟是什么地方使用了呢?

spring的一大核心概念是注入,

但是,這存在的一個前提就是類是由spring管理起來的。

反射是根據className生成一個具體的實例

這是一個很實用的思想。

比如:當我們需要根據傳進來的參數的類型,選擇具體的實現類時,

反射機制就能很好的解決問題。

然而,一般我們使用反射機制,創建的代理類是根據構造函數實例化的。

而不是從spring容器中注入 。

這樣就會導致一個問題,無法在創建的代理類中實現注入功能。

當然,如果你一定要使用的話,系統會提示空指針錯誤。

這個時候,如果把反射創建的類由spring注入就可以有效的解決這個問題 。

這樣也存在一個問題。

就是獲得spring的ApplicationContext.

如果我們重新獲得一遍的話,

這樣就是對系統資源極大的浪費。

這樣我們可以聲明一個靜態變量將ApplicationContext保存起來

    // 聲明一個靜態變量保存
    public void setApplicationContext(ApplicationContext contex)
            throws BeansException {
        MyApplicationContextUtil.context = contex;
    }

並將其用spring容器管理起來。

這樣的話,我們就可以很輕松的獲得ApplicationContext,而不需要消耗太多的系統資源。

從而,很簡單的,

當我們的實現類全部繼承一個相同的接口時,

我們的接口便可以通過反射初始化。

從而,創建不同的具體實現類。

同時,因為所有的類都是通過spring管理起來的。

很明顯,在創建的實現類中也是可以使用spring的注入。

而不是有空指針錯誤。

 

一  反射源頭Class類

對類的概念我們已經非常熟悉了。比如可以有Student這個類,Person這個類。但是我們要知道,有一個叫Class的類,它是反射的源頭。

 

正常方式:通過完整的類名—>通過new實例化—>取得實例化對象

反射方式:實例化對象—>getClass()方法—>通過完整的類名

 

一個簡單的例子:

 

package cn.classes;

public class OneClass {

}


package cn.test;

import cn.classes.OneClass;

public class Test {
    public static void main(String[] args) {
        OneClass c = new OneClass();
        System.out.println(c.getClass().getName());
   }
}

輸出結果:cn.classes.OneClass


我們需要使用反射,就要獲得Class這個類,有三種方法:

package cn.classes;

public class OneClass {

}


import cn.classes.OneClass;

public class Test {
public static void main(String[] args) {
     Class<?> c1 = null;
     Class<?> c2 = null;
     Class<?> c3 = null;

     try 
    {
         // 方法一:forName(重要)
         c1 = Class.forName("cn.classes.OneClass");
    } 
    catch (ClassNotFoundException e) 
    {
         e.printStackTrace();
    }
     // 方法二
     c2 = new OneClass().getClass();
  
     // 方法三
     c3 = OneClass.class;

     System.out.println(c1.getName());
     System.out.println(c2.getName());
     System.out.println(c3.getName());
   }
}

輸出結果:cn.classes.OneClass

 

 

二 利用Class這個類實例化類

 

①無參構造

package cn.classes;

public class Person {
    private String name;
    private int age;

     .............省略getter,setter..............

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

}


package cn.test;

import cn.classes.Person;

public class Test
{
    // 這樣做必須在類中有一個空構造方法
    public static void main(String[] args)
    {
              Class<?> c = null;
               try
               {
                      c = Class.forName("cn.classes.Person");
                      Person p = (Person)c.newInstance();
                      p.setName("xy");
                      p.setAge(20);
                      System.out.println(p);
               } 
              catch (Exception e)
              {
                      e.printStackTrace();
               }  
     }
}

 

②有參構造

package cn.classes;

public class Person
{
    private String name;
    private int age;

    .............省略getter,setter..............    

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


package cn.test;

import java.lang.reflect.Constructor;

import cn.classes.Person;

public class Test
{
    // 如果沒有一個空構造方法
    public static void main(String[] args)
    {
          Class<?> c = null;
          try
          {
                  c = Class.forName("cn.classes.Person");
                  Constructor<?>[] cons = c.getConstructors();
                  Person p = (Person)cons[0].newInstance("xy",20);
                  System.out.println(p);
          } 
         catch (Exception e)
         {
                e.printStackTrace();
         }  
    }
}

 

三 Spring中使用Class實例化

bean.xml
<bean id="id" class="com.xy.Student" />

 

Spring將采用的代碼創建代碼Java實例
Class c = Class.forName("com.xy.Student");
Object bean = c.newInstance();

 

 

四 Class類調用方法

package cn.classes;

public class Person
{
    public void add()
    {
           System.out.println("add");
    }

    public void addWithParameters(String name, int age)
    {
            System.out.println("add帶參數方法" + name + age);
    }
}


package cn.test;

import java.lang.reflect.Method;

public class Test
{
    public static void main(String[] args)
    {
             Class<?> c1 = null;
             try
             {

                   c1 = Class.forName("cn.classes.Person");

                   // 不帶參數的方法調用
                   Method m = c1.getMethod("add");
                   m.invoke(c1.newInstance());

 

                   // 帶參數方法調用
                   Method m1 = c1.getMethod("addWithParameters", String.class, int.class);
                   m1.invoke(c1.newInstance(), "xy", 22);
            }
            catch (Exception e)
            {
                   e.printStackTrace();
            }
    }
}

 

 

五 Class獲得getter,setter方法

Class這個類可以獲得類的很多信息,比如獲得該類的接口,構造函數,屬性,方法等。我們來看如何獲得getter,setter方法。

package cn.classes;

public class Person
{
    private String name;
    private int age;

    省略getter,setter  

 }

 

package cn.test;

import java.lang.reflect.Method;

public class Test
{
    public static void main(String[] args)
    {
           Class<?> c1 = null;
           Object obj = null;
           try
           {
                     c1 = Class.forName("cn.classes.Person");
                     obj = c1.newInstance();
                     setter(obj, "name", "xy", String.class);
                     setter(obj, "age", 20, int.class);
                     getter(obj, "name");
                     getter(obj, "age");
           }
           catch (Exception e)
            {
                    e.printStackTrace();
            }
   }

   /**
    * @param obj:要操作的對象
    * @param att:要操作的屬性
    * @param value:要設置的屬性內容
    * @param type:要設置的屬性類型
    */
    public static void setter(Object obj, String att, Object value, Class<?> type)
    {
         try
         {
            // 得到setter方法
          Method m = obj.getClass().getMethod("set" + initStr(att), type);
          m.invoke(obj, value);
         }
         catch (Exception e)
         {
          e.printStackTrace();
         }
   }

   /**
    * @param obj:要操作的對象
    * @param att:要操作的屬性
    */
   public static void getter(Object obj, String att)
   {
        try
        {
               // 得到getter方法
               Method m = obj.getClass().getMethod("get" + initStr(att));
               System.out.println(m.invoke(obj));
        }
       catch (Exception e)
       {
               e.printStackTrace();
       }
   }

   public static String initStr(String oldStr)
   {
         String newStr = oldStr.substring(0, 1).toUpperCase() + oldStr.substring(1);
         return newStr;
   }
}

 

六 Spring調用getter,setter方法

我們以setter注入例子

bean.xml
<bean id="id" class="com.xy.Student">
    <property name="stuName" value="xy" />
</bean>


Spring將采用的代碼創建代碼Java實例,並注入值:
Class c = Class.forName("com.xy.Student");
Object bean = c.newInstance();


通過一些操作獲取對stuName對應的setter方法名
String setname = "set" + "StuName";
Method method = c.getMehod(setname,String.Class);
method.invoke(bean,"xy");

這樣就完成了最基本的注入操作。當然,Spring還可以通過構造函數進行注入。這樣就參考第二點有參構造的Class的使用。

Class還可以訪問Annotation,這樣就Spring使用注解的時候,可以完成注入的功能


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM