26、根據你的項目經驗,Spring框架的哪些地方是你不喜歡的?你認爲Spring有缺陷嗎?html
Spring框架是一個爲Java應用程序的開發提供了綜合、普遍的基礎性支持的Java平臺。Spring幫助開發者解決了開發中基礎性的問題,使得開發人員能夠專一於應用程序的開發。Spring框架自己亦是按照設計模式精心打造,這使得咱們能夠在開發環境中安心的集成Spring框架,沒必要擔憂Spring是如何在後臺進行工做的。前端
Spring框架至今已集成了20多個模塊。這些模塊主要被分以下圖所示的核心容器、數據訪問/集成,、Web、AOP(面向切面編程)、工具、消息和測試模塊。java
更多信息:Spring 框架教程。程序員
下面列舉了一些使用Spring框架帶來的主要好處:web
控制反轉是應用於軟件工程領域中的,在運行時被裝配器對象來綁定耦合對象的一種編程技巧,對象之間耦合關係在編譯時一般是未知的。在傳統的編程方式中,業務邏輯的流程是由應用程序中的早已被設定好關聯關係的對象來決定的。在使用控制反轉的狀況下,業務邏輯的流程是由對象關係圖來決定的,該對象關係圖由裝配器負責實例化,這種實現方式還能夠將對象之間的關聯關係的定義抽象化。而綁定的過程是經過「依賴注入」實現的。面試
控制反轉是一種以給予應用程序中目標組件更多控制爲目的設計範式,並在咱們的實際工做中起到了有效的做用。spring
依賴注入是在編譯階段還沒有知所需的功能是來自哪一個的類的狀況下,將其餘對象所依賴的功能對象實例化的模式。這就須要一種機制用來激活相應的組件以提供特定的功能,因此依賴注入是控制反轉的基礎。不然若是在組件不受框架控制的狀況下,框架又怎麼知道要建立哪一個組件?數據庫
在Java中依然注入有如下三種實現方式:apache
Spring中的 org.springframework.beans
包和 org.springframework.context
包構成了
Spring
框架
IoC
容器的基礎。
編程
BeanFactory 接口提供了一個先進的配置機制,使得任何類型的對象的配置成爲可能。ApplicationContex
接口對
BeanFactory
(是一個子接口)進行了擴展,在BeanFactory的基礎上添加了其餘功能,好比與Spring的AOP更容易集成,也提供了處理message resource的機制(用於國際化)、事件傳播以及應用層的特別配置,好比針對Web應用的WebApplicationContext。
org.springframework.beans.factory.BeanFactory
是Spring IoC容器的具體實現,用來包裝和管理前面提到的各類bean。BeanFactory接口是Spring IoC 容器的核心接口。
BeanFactory 能夠理解爲含有bean集合的工廠類。BeanFactory 包含了種bean的定義,以便在接收到客戶端請求時將對應的bean實例化。
BeanFactory還能在實例化對象的時生成協做類之間的關係。此舉將bean自身與bean客戶端的配置中解放出來。BeanFactory還包含了bean生命週期的控制,調用客戶端的初始化方法(initialization methods)和銷燬方法(destruction methods)。
從表面上看,application context如同bean factory同樣具備bean定義、bean關聯關係的設置,根據請求分發bean的功能。但application context在此基礎上還提供了其餘的功能。
如下是三種較常見的 ApplicationContext 實現方式:
一、ClassPathXmlApplicationContext:從classpath的XML配置文件中讀取上下文,並生成上下文定義。應用程序上下文從程序環境變量中取得。
ApplicationContext context = new ClassPathXmlApplicationContext(「bean.xml」);
二、FileSystemXmlApplicationContext :由文件系統中的XML配置文件讀取上下文。
ApplicationContext context = new FileSystemXmlApplicationContext(「bean.xml」);
三、XmlWebApplicationContext:由Web應用的XML文件讀取上下文。
將Spring配置到應用開發中有如下三種方式:
在Spring框架中,依賴和服務須要在專門的配置文件來實現,我經常使用的XML格式的配置文件。這些配置文件的格式一般用<beans>
開頭,而後一系列的
bean
定義和專門的應用配置選項組成。
SpringXML配置的主要目的時候是使全部的Spring組件均可以用xml文件的形式來進行配置。這意味着不會出現其餘的Spring配置類型(好比聲明的方式或基於Java Class的配置方式)
Spring的XML配置方式是使用被Spring命名空間的所支持的一系列的XML標籤來實現的。Spring有如下主要的命名空間:context、beans、jdbc、tx、aop、mvc和aso。
<beans> <!-- JSON Support --> <bean name="viewResolver" class="org.springframework.web.servlet.view.BeanNameViewResolver"/> <bean name="jsonTemplate" class="org.springframework.web.servlet.view.json.MappingJackson2JsonView"/> <bean id="restTemplate" class="org.springframework.web.client.RestTemplate"/> </beans>
下面這個web.xml僅僅配置了DispatcherServlet,這件最簡單的配置便能知足應用程序配置運行時組件的需求。
<web-app> <display-name>Archetype Created Web Application</display-name> <servlet> <servlet-name>spring</servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>spring</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
Spring對Java配置的支持是由@Configuration註解和@Bean註解來實現的。由@Bean註解的方法將會實例化、配置和初始化一個新對象,這個對象將由Spring的IoC容器來管理。@Bean聲明所起到的做用與<bean/> 元素相似。被@Configuration所註解的類則表示這個類的主要目的是做爲bean定義的資源。被@Configuration聲明的類能夠經過在同一個類的內部調用@bean方法來設置嵌入bean的依賴關係。
最簡單的@Configuration 聲明類請參考下面的代碼:
@Configuration public class AppConfig { @Bean public MyService myService() { return new MyServiceImpl(); } }
對於上面的@Beans配置文件相同的XML配置文件以下:
<beans> <bean id="myService" class="com.howtodoinjava.services.MyServiceImpl"/> </beans>
上述配置方式的實例化方式以下:利用AnnotationConfigApplicationContext 類進行實例化
public static void main(String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class); MyService myService = ctx.getBean(MyService.class); myService.doStuff(); }
要使用組件組建掃描,僅需用@Configuration進行註解便可:
@Configuration @ComponentScan(basePackages = "com.howtodoinjava") public class AppConfig { ... }
在上面的例子中,com.acme包首先會被掃到,而後再容器內查找被@Component 聲明的類,找到後將這些類按照Sring bean定義進行註冊。
若是你要在你的web應用開發中選用上述的配置的方式的話,須要用AnnotationConfigWebApplicationContext 類來讀取配置文件,能夠用來配置Spring的Servlet監聽器ContrextLoaderListener或者Spring MVC的DispatcherServlet。
<web-app> <!-- Configure ContextLoaderListener to use AnnotationConfigWebApplicationContext instead of the default XmlWebApplicationContext --> <context-param> <param-name>contextClass</param-name> <param-value> org.springframework.web.context.support.AnnotationConfigWebApplicationContext </param-value> </context-param> <!-- Configuration locations must consist of one or more comma- or space-delimited fully-qualified @Configuration classes. Fully-qualified packages may also be specified for component-scanning --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>com.howtodoinjava.AppConfig</param-value> </context-param> <!-- Bootstrap the root application context as usual using ContextLoaderListener --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- Declare a Spring MVC DispatcherServlet as usual --> <servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- Configure DispatcherServlet to use AnnotationConfigWebApplicationContext instead of the default XmlWebApplicationContext --> <init-param> <param-name>contextClass</param-name> <param-value> org.springframework.web.context.support.AnnotationConfigWebApplicationContext </param-value> </init-param> <!-- Again, config locations must consist of one or more comma- or space-delimited and fully-qualified @Configuration classes --> <init-param> <param-name>contextConfigLocation</param-name> <param-value>com.howtodoinjava.web.MvcConfig</param-value> </init-param> </servlet> <!-- map all requests for /app/* to the dispatcher servlet --> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>/app/*</url-pattern> </servlet-mapping> </web-app>
Spring在2.5版本之後開始支持用註解的方式來配置依賴注入。能夠用註解的方式來替代XML方式的bean描述,能夠將bean描述轉移到組件類的內部,只須要在相關類上、方法上或者字段聲明上使用註解便可。註解注入將會被容器在XML注入以前被處理,因此後者會覆蓋掉前者對於同一個屬性的處理結果。
註解裝配在Spring中是默認關閉的。因此須要在Spring文件中配置一下才能使用基於註解的裝配模式。若是你想要在你的應用程序中使用關於註解的方法的話,請參考以下的配置。
<beans> <context:annotation-config/> <!-- bean definitions go here --> </beans>
在 <context:annotation-config/>標籤配置完成之後,就能夠用註解的方式在Spring中向屬性、方法和構造方法中自動裝配變量。
下面是幾種比較重要的註解類型:
Spring Bean的生命週期簡單易懂。在一個bean實例被初始化時,須要執行一系列的初始化操做以達到可用的狀態。一樣的,當一個bean不在被調用時須要進行相關的析構操做,並從bean容器中移除。
Spring bean factory 負責管理在spring容器中被建立的bean的生命週期。Bean的生命週期由兩組回調(call back)方法組成。
Spring框架提供瞭如下四種方式來管理bean的生命週期事件:
使用customInit()
和 customDestroy()
方法管理
bean
生命週期的代碼樣例以下:
<beans> <bean id="demoBean" class="com.howtodoinjava.task.DemoBean" init-method="customInit" destroy-method="customDestroy"></bean> </beans>
Spring容器中的bean能夠分爲5個範圍。全部範圍的名稱都是自說明的,可是爲了不混淆,仍是讓咱們來解釋一下:
全局做用域與Servlet中的session做用域效果相同。
更多內容請參考 : Spring Bean Scopes。
在Spring框架中,不管什麼時候bean被使用時,當僅被調用了一個屬性。一個明智的作法是將這個bean聲明爲內部bean。內部bean能夠用setter注入「屬性」和構造方法注入「構造參數」的方式來實現。
好比,在咱們的應用程序中,一個Customer類引用了一個Person類,咱們的要作的是建立一個Person的實例,而後在Customer內部使用。
public class Customer { private Person person; //Setters and Getters } public class Person { private String name; private String address; private int age; //Setters and Getters }
<bean id="CustomerBean" class="com.howtodoinjava.common.Customer"> <property name="person"> <!-- This is inner bean --> <bean class="com.howtodoinjava.common.Person"> <property name="name" value="lokesh" /> <property name="address" value="India" /> <property name="age" value="34" /> </bean> </property> </bean>
Spring框架並無對單例bean進行任何多線程的封裝處理。關於單例bean的線程安全和併發問題須要開發者自行去搞定。但實際上,大部分的Spring bean並無可變的狀態(好比Serview類和DAO類),因此在某種程度上說Spring的單例bean是線程安全的。若是你的bean有多種狀態的話(好比 View Model 對象),就須要自行保證線程安全。
最淺顯的解決辦法就是將多態bean的做用域由「singleton」變動爲「prototype」。
Spring提供瞭如下四種集合類的配置元素:
下面看一下具體的例子:
<beans> <!-- Definition for javaCollection --> <bean id="javaCollection" class="com.howtodoinjava.JavaCollection"> <!-- java.util.List --> <property name="customList"> <list> <value>INDIA</value> <value>Pakistan</value> <value>USA</value> <value>UK</value> </list> </property> <!-- java.util.Set --> <property name="customSet"> <set> <value>INDIA</value> <value>Pakistan</value> <value>USA</value> <value>UK</value> </set> </property> <!-- java.util.Map --> <property name="customMap"> <map> <entry key="1" value="INDIA"/> <entry key="2" value="Pakistan"/> <entry key="3" value="USA"/> <entry key="4" value="UK"/> </map> </property> <!-- java.util.Properties --> <property name="customProperies"> <props> <prop key="admin">admin@nospam.com</prop> <prop key="support">support@nospam.com</prop> </props> </property> </bean> </beans>
第一種方法是使用以下面代碼所示的<props> 標籤:
<bean id="adminUser" class="com.howtodoinjava.common.Customer"> <!-- java.util.Properties --> <property name="emails"> <props> <prop key="admin">admin@nospam.com</prop> <prop key="support">support@nospam.com</prop> </props> </property> </bean>
也可用」util:」命名空間來從properties文件中建立出一個propertiesbean,而後利用setter方法注入bean的引用。
在Spring框架中,在配置文件中設定bean的依賴關係是一個很好的機制,Spring容器還能夠自動裝配合做關係bean之間的關聯關係。這意味着Spring能夠經過向Bean Factory中注入的方式自動搞定bean之間的依賴關係。自動裝配能夠設置在每一個bean上,也能夠設定在特定的bean上。
下面的XML配置文件代表瞭如何根據名稱將一個bean設置爲自動裝配:
<bean id="employeeDAO" class="com.howtodoinjava.EmployeeDAOImpl" autowire="byName" />
除了bean配置文件中提供的自動裝配模式,還可使用@Autowired
註解來自動裝配指定的
bean
。在使用
@Autowired
註解以前須要在按照以下的配置方式在
Spring
配置文件進行配置纔可使用。
<context:annotation-config />
也能夠經過在配置文件中配置AutowiredAnnotationBeanPostProcessor
達到相同的效果。
<bean class ="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>
配置好之後就可使用@Autowired
來標註了。
@Autowired public EmployeeDAOImpl ( EmployeeManager manager ) { this.manager = manager; }
在Spring框架中共有5種自動裝配,讓咱們逐一分析。
在產品級別的應用中,IoC容器可能聲明瞭數十萬了bean,bean與bean之間有着複雜的依賴關係。設值註解方法的短板之一就是驗證全部的屬性是否被註解是一項十分困難的操做。能夠經過在<bean>中設置「dependency-check」來解決這個問題。
在應用程序的生命週期中,你可能不大願意花時間在驗證全部bean的屬性是否按照上下文文件正確配置。或者你寧肯驗證某個bean的特定屬性是否被正確的設置。即便是用「dependency-check」屬性也不能很好的解決這個問題,在這種狀況下,你須要使用@Required
註解。
須要用以下的方式使用來標明bean的設值方法。
public class EmployeeFactoryBean extends AbstractFactoryBean<Object> { private String designation; public String getDesignation() { return designation; } @Required public void setDesignation(String designation) { this.designation = designation; } //more code here }
RequiredAnnotationBeanPostProcessor
是Spring中的後置處理用來驗證被@Required
註解的bean屬性是否被正確的設置了。在使用RequiredAnnotationBeanPostProcesso
來驗證bean
屬性以前,首先要在IoC
容器中對其進行註冊:
<bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor" />
可是若是沒有屬性被用 @Required
註解過的話,後置處理器會拋出一個BeanInitializationException
異常。
@Autowired註解對自動裝配什麼時候何處被實現提供了更多細粒度的控制。@Autowired
註解能夠像
@Required
註解、構造器同樣被用於在
bean
的設值方法上自動裝配
bean
的屬性,一個參數或者帶有任意名稱或帶有多個參數的方法。
好比,能夠在設值方法上使用@Autowired
註解來替代配置文件中的
<property>
元素。當
Spring
容器在
setter
方法上找到
@Autowired
註解時,會嘗試用
byType 自動裝配。
固然咱們也能夠在構造方法上使用@Autowired
註解。帶有@Autowired
註解的構造方法意味着在建立一個bean時將會被自動裝配,即使在配置文件中使用<constructor-arg>
元素。
public class TextEditor { private SpellChecker spellChecker; @Autowired public TextEditor(SpellChecker spellChecker){ System.out.println("Inside TextEditor constructor." ); this.spellChecker = spellChecker; } public void spellCheck(){ spellChecker.checkSpelling(); } }
下面是沒有構造參數的配置方式:
<beans> <context:annotation-config/> <!-- Definition for textEditor bean without constructor-arg --> <bean id="textEditor" class="com.howtodoinjava.TextEditor"> </bean> <!-- Definition for spellChecker bean --> <bean id="spellChecker" class="com.howtodoinjava.SpellChecker"> </bean> </beans>
@Qualifier
註解意味着能夠在被標註
bean
的字段上能夠自動裝配。
Qualifier註解能夠用來取消Spring不能取消的bean應用。
下面的示例將會在Customer的person屬性中自動裝配person的值。
public class Customer { @Autowired private Person person; }
下面咱們要在配置文件中來配置Person類。
<bean id="customer" class="com.howtodoinjava.common.Customer" /> <bean id="personA" class="com.howtodoinjava.common.Person" > <property name="name" value="lokesh" /> </bean> <bean id="personB" class="com.howtodoinjava.common.Person" > <property name="name" value="alex" /> </bean>
Spring
會知道要自動裝配哪一個person bean麼?不會的,可是運行上面的示例時,
會拋出下面的異常:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [com.howtodoinjava.common.Person] is defined: expected single matching bean but found 2: [personA, personB]
要解決上面的問題,須要使用 @Quanlifier
註解來告訴Spring容器要裝配哪一個bean:
public class Customer { @Autowired @Qualifier("personA") private Person person; }
請注意如下明顯的區別:
ObjectCurrentlyInCreationException異常,由於在B對象被建立以前A對象是不能被建立的,反之亦然。因此Spring用設值注入的方法解決了循環依賴的問題,因對象的設值方法是在對象被建立以前被調用的。
public class AllApplicationEventListener implements ApplicationListener < ApplicationEvent > { @Override public void onApplicationEvent(ApplicationEvent applicationEvent) { //process event } }
Spring的ApplicationContext
提供了支持事件和代碼中監聽器的功能。
咱們能夠建立bean用來監聽在ApplicationContext
中發佈的事件。ApplicationEven
t類和在ApplicationContext
接口
中處理的事件,若是一個bean實現了ApplicationListener
接口,當一個ApplicationEvent
被髮布之後,bean會自動被通知。
Spring 提供瞭如下5中標準的事件:
除了上面介紹的事件之外,還能夠經過擴展ApplicationEvent
類來開發自定義的事件。
public class CustomApplicationEvent extends ApplicationEvent { public CustomApplicationEvent ( Object source, final String msg ) { super(source); System.out.println("Created a Custom event"); } }
爲了監聽這個事件,還須要建立一個監聽器:
public class CustomEventListener implements ApplicationListener < CustomApplicationEvent > { @Override public void onApplicationEvent(CustomApplicationEvent applicationEvent) { //handle event } }
以後經過applicationContext接口的publishEvent()方法來發布自定義事件。
CustomApplicationEvent customEvent = new CustomApplicationEvent(applicationContext, "Test message"); applicationContext.publishEvent(customEvent);
在FileSystemResource
中須要給出spring-config.xml
文件在你項目中的相對路徑或者絕對路徑。在ClassPathResource
中spring會在ClassPath中自動搜尋配置文件,因此要把ClassPathResource
文件放在ClassPath下。
若是將spring-config.xml
保存在了src文件夾下的話,只需給出配置文件的名稱便可,由於src文件夾是默認。
簡而言之,ClassPathResource在環境變量中讀取配置文件,FileSystemResource在配置文件中讀取配置文件。
Spring框架中使用到了大量的設計模式,下面列舉了比較有表明性的:
JmsTemplate
, JpaTemplate。
DispatcherServlet
來對請求進行分發。
BeanFactory
/ ApplicationContext
接口的核心理念。
2六、根據你的項目經驗,Spring框架的哪些地方是你不喜歡的?你認爲Spring有缺陷嗎?
2七、選擇使用Spring框架的緣由(Spring框架爲企業級開發帶來的好處有哪些)? 答:能夠從如下幾個方面做答: - 非侵入式:支持基於POJO的編程模式,不強制性的要求實現Spring框架中的接口或繼承Spring框架中的類。 - IoC容器:IoC容器幫助應用程序管理對象以及對象之間的依賴關係,對象之間的依賴關係若是發生了改變只須要修改配置文件而不是修改代碼,由於代碼的修改可能意味着項目的從新構建和完整的迴歸測試。有了IoC容器,程序員不再須要本身編寫工廠、單例,這一點特別符合Spring的精神"不要重複的發明輪子"。 - AOP(面向切面編程):將全部的橫切關注功能封裝到切面(aspect)中,經過配置的方式將橫切關注功能動態添加到目標代碼上,進一步實現了業務邏輯和系統服務之間的分離。另外一方面,有了AOP程序員能夠省去不少本身寫代理類的工做。 - MVC:Spring的MVC框架是很是優秀的,從各個方面均可以甩Struts 2幾條街,爲Web表示層提供了更好的解決方案。 - 事務管理:Spring以寬廣的胸懷接納多種持久層技術,而且爲其提供了聲明式的事務管理,在不須要任何一行代碼的狀況下就可以完成事務管理。 - 其餘:選擇Spring框架的緣由還遠不止於此,Spring爲Java企業級開發提供了一站式選擇,你能夠在須要的時候使用它的部分和所有,更重要的是,你甚至能夠在感受不到Spring存在的狀況下,在你的項目中使用Spring提供的各類優秀的功能。