控制反轉(Inversion ofControl,縮寫為IoC)
簡單來說就是當自己需要一個物件的時候不需要自己手動去new一個,而是由其他容器來幫你提供;Spring裡面就是IOC容器。
例如:
在Spring裡面經常需要在Service這個裝配一個Dao,一般是使用@Autowired註解:類似如下
public Class ServiceImpl{
@Autowired
Dao dao;
public void getData(){
dao.getData();
}
在這裡未初始化Dao直接使用是會報出空指標異常的,那麼在Spring裡面的做法就是通過反射來將需要的類幫你載入進來。
下面是一個例子模擬了Spring的DI和IOC
首先寫兩個註解模擬Spring的註解:
Entity註解代表的是Spring的@Service
@Target(ElementType.TYPE) // 類
@Retention(RetentionPolicy.RUNTIME)
public @interface Entity {
}
代表的是Spring裡面的@Autowrid
@Target(ElementType.FIELD) //描述方法的
@Retention(RetentionPolicy.RUNTIME) // 僅執行時保留
public @interface Resources {
}
當註解建立完成之後再建立兩個類:
Rain類代表的是需要從其他地方獲取天氣資料(資料庫或者伺服器)
public class Rain {
public void rain(){
System.out.println("正在下雨"); // 為了方便直接寫了
}
}
Weather類代表的是獲取到的天氣資料
@Entity
public class Weather {
@Resources
Rain rain; // 這裡在後面通過反射直接注入rain
public void weather_rain() {
rain.rain();
}
下面是通過反射來直接注入:
首先遍歷指定的包名:這一步先省略,
首先是建立一個List模擬Spring的bean容器,即將已經裝初始化好的帶有Entity註解的類全部初始化
public class Weather_reflec {
List<Object> objectList ; // 模擬Spring容器
public Weather_reflec() {
objectList= new ArrayList<Object>();
}
// 在這裡其實最好的做法是先找出帶有註解的類,判斷帶有Entity註解再傳入。但是為了方便直接省略了
public void get_ref(Object object) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Class<?> clazz =object.getClass();
if(clazz.isAnnotationPresent(Entity.class)){ //判斷是否帶有Entity註解
Field[] fields =clazz.getDeclaredFields(); //獲取其變數
for(Field field :fields){
if(field.isAnnotationPresent(Resources.class)){ //判斷是否需要注入
Class<?> rainClass=Class.forName(field.getType().getName(),false,Thread.currentThread().getContextClassLoader()); // 這裡先將Rain類載入
field.set(object,rainClass.newInstance()); //賦給rain
objectList.add(object); //最後將已將賦值後的Weather儲存進容器
}
}
}
}
public List<Object> returnList(){
return objectList; //返回容器方便以後使用
}
最後也就是模擬Controller裡面直接使用的
public class WeatherPrediction {
public static void main(String args[]) throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
WeatherPrediction weatherPrediction =new WeatherPrediction();
Weather weather =(Weather)weatherPrediction.springDo();
weather.weather_rain(); // 這裡如果是普通呼叫會報空指標異常,而容器卻為其將rain這個變數賦值了,所以可以正常輸出
}
/*
模擬Spring啟動過程,這一步其實可以單獨寫一個類,這一步是容器該做的,而我們並不需要去管
*/
public Object springDo() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Weather_reflec weather_reflec =new Weather_reflec(); // 啟動的時候就需要載入的
Weather weather =new Weather(); //掃描類註解後new操作然後進行下一步
weather_reflec.get_ref(weather); // 將其類裡面的變數進行new操作並放入容器
Object object =weather_reflec.returnList().get(0);
return object;
}
執行後輸出:正在下雨
在WeatherPrediction裡面並沒有對Rain進行一個new操作但是卻可以使用,這應該是最簡單的一個模擬Spring的IOC例子了,當然Spring的IOC容器比這個強大太多了,比如需要考慮執行緒安全,以及各種的細節問題