Spring 核心組件工作原理簡析


 

Spring Framework 的核心組件有三個: Spring Core,Spring Context 和 Spring Beans,它們奠定了 Spring 的基礎並撐起了 Spring 的框架結構。Spring 的其它功能特性例如 Web、AOP、JDBC 等都是在其基礎上發展實現的。 
I. Bean 組件 
Spring 使用工廠模式來管理程序中使用的對象(Bean),Bean 工廠最上層的接口為 BeanFactory,簡單來看,工廠就是根據需要返回相應的 Bean 實例。

public interface BeanFactory {
    //... Object getBean(String name); } 
  • 1
  • 2
  • 3
  • 4
  • 5

在工廠模式中,在工廠的實現類中生成 Bean 返回給調用客戶端,這就要求客戶端提供生成自己所需類實例的工廠類,增加客戶負擔。Spring 結合控制反轉和依賴注入為客戶端提供所需的實例,簡化了客戶端的操作。具體的實現方式大致如下。

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable { /** Map of bean definition objects, keyed by bean name */ private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>; public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition){ //... } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

beanDefinitionMap 作為具體的 Bean 容器,Spring 創建的對象實例保存其中。客戶端需要時,使用工廠的 getBean 方法去試圖得到相應的實例,如果實例已存在,則返回該實例;如果實例不存在,則首先產生相應實例並通過 registerBeanDefinition 方法將其保存在 beanDefinitionMap 中(Lazy Initialization),然后返回該實例給客戶端。

Spring Bean 工廠的繼承關系

 

Spring Bean 工廠的繼承關系

Spring 中的相關代碼包是 org.springframework.beans

 

beanDefinitionMap 並不直接保存實例本身,而是將實例封裝在 BeanDefinition 對象后進行保存。BeanDefinition 包含了實例的所有信息,其簡化版的定義如下。

public class BeanDefinition { private Object bean; private Class<?> beanClass; private String beanClassName; // Bean 屬性字段的初始化值 private BeanPropertyValues beanPropertyValues; //... }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

Spring Bean 工廠生產 Bean 時 
1) 先將實例的類型參數保存到 beanClass 和 beanClassName,將需要初始化的字段名和值保存到 beanPropertyValues 中,這個過程 Spring 通過控制反轉來實現,本文第二小節將予以簡要說明 
2) 生成 bean 實例,並利用反射機制將需要初始化的字段值寫入 bean 實例,將實例保存在 bean 中,完成 BeanDefinition 的構建。 
假設我們已經完成了步驟 1) 的操作,之后的過程用代碼表述如下所示。

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition){ //生成 bean 實例,並完成初始化 Object bean = createBean(beanDefinition); //將 bean 實例保存在 beanDefinition 中 beanDefinition.setBean(bean); //將 beanDefinition 實例保存在 Spring 容器中 beanDefinitionMap.put(beanName, beanDefinition); } protected Object createBean(BeanDefinition beanDefinition) { try{ Object bean = beanDefinition.getBeanClass().newInstance(); try { setBeanPropertyValues(bean, beanDefinition); } catch (NoSuchFieldException | SecurityException | IllegalArgumentException e) { e.printStackTrace(); } return bean; }catch(InstantiationException e){ e.printStackTrace(); }catch(IllegalAccessException e){ e.printStackTrace(); } return null; } protected void setBeanPropertyValues(Object bean, BeanDefinition beanDefinition) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException{ for(PropertyValue pv : beanDefinition.getBeanPropertyValues().getBeanPropertyValues()){ Field beanFiled = bean.getClass().getDeclaredField(pv.getName()); beanFiled.setAccessible(true); beanFiled.set(bean, pv.getValue()); } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

II. Context 組件 
一般地,傳統的程序設計中,無論是使用工廠創建實例,或是直接創建實例,實例調用者都要先主動創建實例,而后才能使用。控制反轉(Inverse of Control) 將實例的創建過程交由容器實現,調用者將控制權交出,是所謂控制反轉。 
依賴注入(Dependence Injection) 在控制反轉的基礎上更進一步。如果沒有依賴注入,容器創建實例並保存后,調用者需要使用 getBean(String beanName) 才能獲取到實例。使用依賴注入時,容器會將 Bean 實例自動注入到完成相應配置的調用者,供其進一步使用。

Context 組件借助上述的控制反轉和依賴注入,協助實現了 Spring 的 Ioc 容器。下面我們以一個 Service 類作為所需的 Bean 實例進行說明。實際應用中,我們會需要 Spring 管理很多 Bean 實例。

public class SampleService { private String service; public String getService() { return service; } public void setService(String service) { this.service= service; } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

在程序運行過程中,需要一個 SampleService ,我們不讓調用者 new 一個實例,而是在配置文件中表明該 SampleService 的實例交由 Spring 容器進行管理,並指定其初始化參數。配置文件即資源,其內容如下。

<?xml version="1.0" encoding="UTF-8"?> <beans> <bean name="sampleService " class="com.service.SampleService "> <property name="service" value="This is a service"></property> </bean> </beans>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Spring Core 組件提供 ResourceLoader 接口,便於讀入 xml 文件或其他資源文件。其核心功能代碼應該提供如下方法。

public class ResourceLoader { public Resource getResource(String location){ URL resource = this.getClass().getClassLoader().getResource(location); return new UrlResource(resource); } } // UrlResource 的功能代碼 public class UrlResource implements Resource { private final URL url; public UrlResource(URL url){ this.url = url; } @Override public InputStream getInputStream() throws IOException { URLConnection urlConnection = url.openConnection(); urlConnection.connect(); return urlConnection.getInputStream(); } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

即加載資源文件,並以數據流的形式返回。Context 根據資源中的定義,生成相應的 bean 並保存在容器中,bean 的名字是 sampleService ,供程序進一步使用。這樣就完成了控制反轉的工作。接下來就需要把 sampleService 注入到需要使用它的地方,亦即完成依賴注入操作。 
現在假設 SampleController 中使用 SampleService 的對象,Spring 提供三種依賴注入的方式,構造器注入、setter 注入和注解注入。

public class SampleController { /** * 3. 注解注入 **/ /* @Autowired */ private SampleService sampleService; /** * 1. 構造器注入 **/ public SampleController(SampleService sampleService){ this.sampleService = sampleService; } //無參構造函數 public SampleController(){} // 類的核心功能 public void process(){ System.out.println(sampleService.getService()); } /** * 2. setter 注入 **/ /*public void setService(SampleService service) { this.service= service; }*/ }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

三種注入方式在配置文件中對應不同的配置方式,在前面 xml 文件的基礎上,我們可以分別實現這三種注入方式。需要注意的是,這里 SampleController 也是使用 Spring 的 Ioc 容器生成管理的。

<?xml version="1.0" encoding="UTF-8"?> <beans> <bean name="sampleService " class="com.service.SampleService "> <property name="service" value="This is a service"></property> </bean> <!-- 1. 構造器注入方式為SampleContorller 的 bean 注入 SampleService --> <bean name="sampleContorller" class="com.controller.SampleContorller"> <!-- index 是構造方法中相應參數的順序 --> <constructor-arg index="0" ref="sampleService"></constructor-arg> </bean> <!-- 2. setter 注入方式為SampleContorller 的 bean 注入 SampleService --> <!-- <bean name="sampleContorller" class="com.controller.SampleContorller"> <property name="sampleService " ref="sampleService"></property> </bean> --> <!-- 3. 注解注入方式為SampleContorller 的 bean 注入 SampleService --> <!-- <bean name="sampleContorller" class="com.controller.SampleContorller"> <!-- 不需要配置,Spring 自動按照類型注入相應的 bean --> </bean> --> </beans>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27


免責聲明!

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



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