一、Spring簡介
- Spring 是個java企業級應用的開源開發框架。Spring主要用來開發Java應用,但是有些擴展是針對構建J2EE平台的web應用。Spring 框架目標是簡化Java企業級應用開發,並通過POJO為基礎的編程模型促進良好的編程習慣。
二、Spring框架組成
- 7個主要模塊如下
- Core(核心容器):核心容器提供 Spring 框架的基本功能。核心容器的主要組件是
BeanFactory
,它是通過工廠模式來實現的。BeanFactory
使用控制反轉 (IOC) 模式將應用程序的配置和依賴性規范與實際的應用程序代碼分開。
三、核心組件詳解 Bean/Context/Core/IOC/AOP/MVC
Spring 框架中的核心組件只有三個:Core、Context 和 Beans,其中Beans 組件最為重要;
Spring 就是面向 Bean 的編程(BOP,Bean Oriented Programming):關鍵點是把對象之間的依賴關系轉而用配置文件來管理,也就是依賴注入機制;
Spring 設計策略類似面向對象編程(Object Oriented Programming),Spring將對象包裝在 Bean 中,進而對這些對象管理以及進行一些額外操作;
三者關系:Bean 中包裝Object,Object中包含數據;Context是Bean 依賴關系的集合;Core 是發現、建立和維護每個 Bean 之間的關系所需要的一些列的工具;
1、Bean 組件在 Spring 的 org.springframework.beans 包下:Bean 的定義、Bean 的創建以及對 Bean 的解析
(1)Bean 的定義,BeanDefinition,類圖如下:
Bean 的定義就是完整的描述了在 Spring 的配置文件中定義的 <bean/> 節點中所有的信息,包括各種子節點。當 Spring 成功解析已定義的一個 <bean/> 節點后,在 Spring 的內部它就回被轉化成 BeanDefinition 對象。以后所有的操作都是對這個對象完成的。
(2)Bean 的創建由工廠模式實現,類圖如下:
其中BeanFactory作為最頂層的一個接口類,它定義了IOC容器的基本功能規范,BeanFactory 有三個子類:ListableBeanFactory、HierarchicalBeanFactory 和AutowireCapableBeanFactory;最終實現所有的接口的默認實現類是DefaultListableBeanFactory;ListableBeanFactory 接口表示這些 Bean 是可列表的,而 HierarchicalBeanFactory 表示的是這些 Bean 是有繼承關系的,也就是每個 Bean 有可能有父 Bean。AutowireCapableBeanFactory 接口定義 Bean 的自動裝配規則。
(3)Bean 的解析,類圖如下:
2、Context 組件在 Spring 的 org.springframework.context 包下:環境構建
Context 相關的類結構圖如下:
ApplicationContext 是 Context 的頂級父類,他除了能標識一個應用環境的基本信息外,他還繼承了擴展了 Context 功能的五個接口;
ApplicationContext 繼承了 BeanFactory(再次Spring 容器中運行的主體對象是 Bean);ApplicationContext 繼承了 ResourceLoader 接口用於訪問到任何外部資源(Core 的功能);
ApplicationContext 的子類主要包含兩個方面:
-
- ConfigurableApplicationContext 表示該 Context 是可修改的,也就是在構建 Context 中用戶可以動態添加或修改已有的配置信息,它下面又有多個子類,其中最經常使用的是可更新的 Context,即 AbstractRefreshableApplicationContext 類。
- WebApplicationContext 顧名思義,就是為 web 准備的 Context 他可以直接訪問到 ServletContext,通常情況下,這個接口使用的少。
Context 作為 Spring 的 Ioc 容器,基本上整合了 Spring 的大部分功能,或者說是大部分功能的基礎:標識一個應用環境;利用 BeanFactory 創建 Bean 對象;保存對象關系表;能夠捕獲各種事件;
3、Core 組件: Spring 的核心組件
Core 組件作為 Spring 的核心組件,包含了很多的關鍵類,重要組成部分就是定義了資源的訪問方式;
Core 相關的類結構圖如下:
Resource 接口繼承了 InputStreamSource 接口,這個接口中有個 getInputStream 方法,返回的是 InputStream 類,所有的資源都被可以通過 InputStream 類來獲取,屏蔽資源提供者;
ResourceLoader 接口實現加載資源,保持資源的加載者的統一,默認實現是 DefaultResourceLoader;
Context 和 Resource 的類關系圖如下:
Context 是把資源的加載、解析和描述工作委托給了 ResourcePatternResolver 類來完成,他相當於一個接頭人,他把資源的加載、解析和資源的定義整合在一起便於其他組件使用;
4、IOC — 貼入源碼注釋均為“英譯漢”,望理解
概念Inversion of Control(控制反轉):把對象之間的依賴關系轉而用配置文件來管理;對組件對象的控制權轉移給外部容器;對象的協作關系由容器來建立。(不再由對象自己來負責)
(1)如何創建 BeanFactory 工廠?
Ioc 容器,實際上是 Context 組件結合其他兩個組件共同構建了一個 Bean 關系網,源碼如下
源碼片段0:ApplicationContext =new FileSystemXmlApplicationContext(xmlPath);
1 /** 2 * Create a new FileSystemXmlApplicationContext, loading the definitions 3 * from the given XML files and automatically refreshing the context. 4 * @param configLocations array of file paths 5 * @throws BeansException if context creation failed 6 */ 7 //構造方法 8 public FileSystemXmlApplicationContext(String... configLocations) throws BeansException { 9 this(configLocations, true, null); 10 } 11 12 //實際調用 13 public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) 14 throws BeansException { 15 super(parent); 16 setConfigLocations(configLocations); 17 if (refresh) { 18 refresh(); 19 } 20 }
源碼片段1:AbstractApplicationContext 的refresh 方法
1 //構建的入口就在 AbstractApplicationContext 類的 refresh 方法中,方法的代碼如下: 2 public void refresh() throws BeansException, IllegalStateException { 3 synchronized (this.startupShutdownMonitor) { 4 //調用容器准備刷新的方法,獲取容器的當時時間,同時給容器設置同步標識
// Prepare this context for refreshing.
5 prepareRefresh(); 6 //告訴子類啟動refreshBeanFactory()方法,Bean定義資源文件的載入從 // Tell the subclass to refresh the internal bean factory. 7 //子類的refreshBeanFactory()方法啟動 --父子工廠均進行刷新 8 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); 9 //為BeanFactory配置容器特性,例如類加載器、事件處理器等 // Prepare the bean factory for use in this context. 10 prepareBeanFactory(beanFactory); 11 try { 12 //為容器的某些子類指定特殊的BeanPost事件處理器
// Allows post-processing of the bean factory in context subclasses.
13 postProcessBeanFactory(beanFactory); 14 //調用所有注冊的BeanFactoryPostProcessor的Bean
// Invoke factory processors registered as beans in the context.
15 invokeBeanFactoryPostProcessors(beanFactory); 16 //為BeanFactory注冊BeanPost事件處理器. // Register bean processors that intercept bean creation. 17 //BeanPostProcessor是Bean后置處理器,用於監聽容器觸發的事件 18 registerBeanPostProcessors(beanFactory); 19 //初始化信息源,和國際化相關. // Initialize message source for this context. 20 initMessageSource(); 21 //初始化容器事件傳播器.
// Initialize event multicaster for this context.
22 initApplicationEventMulticaster(); 23 //調用子類的某些特殊Bean初始化方法 // Initialize other special beans in specific context subclasses. 24 onRefresh(); 25 //為事件傳播器注冊事件監聽器. // Check for listener beans and register them. 26 registerListeners(); 27 //初始化所有剩余的單態Bean. // Instantiate all remaining (non-lazy-init) singletons. 28 finishBeanFactoryInitialization(beanFactory); 29 //初始化容器的生命周期事件處理器,並發布容器的生命周期事件 // Last step: publish corresponding event. 30 finishRefresh(); 31 } 32 catch (BeansException ex) { 33 //銷毀以創建的單態Bean // Destroy already created singletons to avoid dangling resources. 34 destroyBeans(); 35 //取消refresh操作,重置容器的同步標識. // Reset 'active' flag. 36 cancelRefresh(ex); 37 throw ex; 38 } 39 } 40 }
refresh()方法的作用是:在創建IoC容器前,如果已經有容器存在,則需要把已有的容器銷毀和關閉,以保證在refresh之后使用的是新建立起來的IoC容器;第8行ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();以后均為注冊容器的信息源和生命周期事件;
源碼片段2:上述代碼第8行中的obtainFreshBeanFactory方法
1 protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { 2 //這里使用了委派設計模式,父類定義了抽象的refreshBeanFactory()方法,具體實現調用子類容器的refreshBeanFactory()方法 3 refreshBeanFactory(); 4 ConfigurableListableBeanFactory beanFactory = getBeanFactory(); 5 if (logger.isDebugEnabled()) { 6 logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory); 7 } 8 return beanFactory; 9 }
源碼片段3:上述代碼第3行中的refreshBeanFactory方法
1 protected final void refreshBeanFactory() throws BeansException { 2 if (hasBeanFactory()) {//如果已經有容器,銷毀容器中的bean,關閉容器 3 destroyBeans(); 4 closeBeanFactory(); 5 } 6 try { 7 //創建IoC容器 8 DefaultListableBeanFactory beanFactory = createBeanFactory(); 9 beanFactory.setSerializationId(getId()); 10 //對IoC容器進行定制化,如設置啟動參數,開啟注解的自動裝配等 11 customizeBeanFactory(beanFactory); 12 //調用載入Bean定義的方法,主要這里又使用了一個委派模式,在當前類中只定義了抽象的loadBeanDefinitions方法,具體的實現調用子類容器 13 loadBeanDefinitions(beanFactory); 14 synchronized (this.beanFactoryMonitor) { 15 this.beanFactory = beanFactory; 16 } 17 } 18 catch (IOException ex) { 19 throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); 20 } 21 }
先判斷BeanFactory是否存在,如果存在則先銷毀beans並關閉beanFactory,接着創建DefaultListableBeanFactory,並調用loadBeanDefinitions(beanFactory)裝載bean定義;
源碼片段4:上述代碼第13行中的loadBeanDefinitions(beanFactory)方法
1 public abstract class AbstractXmlApplicationContext extends AbstractRefreshableConfigApplicationContext { 2 …… 3 //實現父類抽象的載入Bean定義方法 4 @Override 5 protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { 6 //創建XmlBeanDefinitionReader,即創建Bean讀取器,並通過回調設置到容器中去,容 器使用該讀取器讀取Bean定義資源 7 XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); 8 //為Bean讀取器設置Spring資源加載器,AbstractXmlApplicationContext的 9 //祖先父類AbstractApplicationContext繼承DefaultResourceLoader,因此,容器本身也是一個資源加載器 10 beanDefinitionReader.setResourceLoader(this); 11 //為Bean讀取器設置SAX xml解析器 12 beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); 13 //當Bean讀取器讀取Bean定義的Xml資源文件時,啟用Xml的校驗機制 14 initBeanDefinitionReader(beanDefinitionReader); 15 //Bean讀取器真正實現加載的方法 16 loadBeanDefinitions(beanDefinitionReader); 17 } 18 //Xml Bean讀取器加載Bean定義資源 19 protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { 20 //獲取Bean定義資源的定位 21 Resource[] configResources = getConfigResources(); 22 if (configResources != null) { 23 //Xml Bean讀取器調用其父類AbstractBeanDefinitionReader讀取定位 24 //的Bean定義資源 25 reader.loadBeanDefinitions(configResources); 26 } 27 //如果子類中獲取的Bean定義資源定位為空,則獲取FileSystemXmlApplicationContext構造方法中setConfigLocations方法設置的資源 28 String[] configLocations = getConfigLocations(); 29 if (configLocations != null) { 30 //Xml Bean讀取器調用其父類AbstractBeanDefinitionReader讀取定位 31 //的Bean定義資源 32 reader.loadBeanDefinitions(configLocations); 33 } 34 } 35 //這里又使用了一個委托模式,調用子類的獲取Bean定義資源定位的方法 36 //該方法在ClassPathXmlApplicationContext中進行實現,對於我們 37 //舉例分析源碼的FileSystemXmlApplicationContext沒有使用該方法 38 protected Resource[] getConfigResources() { 39 return null; 40 } …… 41 41}
原文參照: https://www.ibm.com/developerworks/cn/java/j-lo-spring-principle/ (IBM在線圖書,有價值)
https://www.cnblogs.com/ITtangtang/p/3978349.html
(2)Bean生命周期
-
1 <bean id="batchAddLineActiona" class="nc.ui.pgrade_set.action.Pgradepd_setAddLineActiona" init-method="initUI" > 2 <property name="model"><ref bean="batchModel"/></property> 3 <property name="voClassName" value="nc.vo.pgradepd_set.Grade_setVO" /> 4 <property name="exceptionHandler"><ref bean="exceptionHandler" /></property> 5 </bean>
- 概念Bean:一個組件;
- Bean 的標識 (id 和 name),Id 屬性具有唯一性,name 屬性可以指定一個或者多個名稱,第一名稱默認為標識;
- Bean 的 class屬性,具體實現類(注意是實現類,不能是接口)的全路徑包名.類名,在 Spring 配置文件中 class 屬性指明 Bean 的來源,也就是 Bean 的實際路徑,它指向一個實體類;
- Bean 的作用域 scope,
- Singleton( 單例 ):表示Spring IoC容器中只會存在一個共享的bean實例;
- non-Singleton(也稱 prototype):每次對該bean請求(將其注入到另一個bean中,或者調用getBean())時都會創建一個新的bean實例;
- 僅在基於web的Spring ApplicationContext情形下有效:request:針對每一個 HTTP 請求都會產生一個新的 Bean,請求結束時銷毀;
- 僅在基於web的Spring ApplicationContext情形下有效:session :針對某個HTTP Session都會產生一個新的 Bean,HTTP Session最終被廢棄時銷毀;
- 僅在基於web的Spring ApplicationContext情形下有效:global session :全局的HTTP Session中(基於portlet的web應用),一個bean定義對應一個實例;
- 概念Inversion of Control(控制反轉):對組件對象的控制權轉移給外部容器;對象的協作關系由容器來建立。(不再由對象自己來負責)
1)Bean實例化
實例化的三種方式:構造器實例化、靜態工廠方式實例化、實例工廠方式實例化;
2)屬性注入
IOC注入,根據Bean的定義信息進行屬性配置;
3)自身方法、Bean級生命周期接口和方法、容器級生命接口和方法、工廠后處理器接口偶和方法、
BeanNameAware接口,調用它實現的setBeanName(String beanId)方法,此處傳遞的是Spring配置文件中Bean的ID;
BeanFactoryAware接口,調用它實現的setBeanFactory(),傳遞的是Spring工廠本身(可以用這個方法獲取到其他Bean);
BeanPostProcessors,調用postProcessBeforeInstantiation(Class<?>c,String beanName) 方法;
Spring配置文件中配置了init-method屬性,自動調用其配置的初始化方法;
BeanPostProcessors,調用postProcessAfterInstantiation(Object bean,String beanName) 方法;
4)此時實例化的Bean就可以使用了
此時,Bean已經可以被應用系統使用,並且將保留在BeanFactory中直到它不在被使用;
5)銷毀接口和方法
關閉容器時,DisposableBean接口,執行destroy()方法;
關閉容器時,Spring配置文件中配置了destroy-method屬性,自動調用其配置的銷毀方法;
(3)依賴注入的幾種方式
注解注入:Autowired、Resource、Qualifier、Service、Controller、Repository、Component;
Autowired是自動注入,代替setter和getter方法;Repository類似Autowired(按類型等),但是它是按照名稱注入;Qualifier和Autowired配合使用,指定bean的名稱(接口多實現時接口聲明變量確定實現類);Service,Controller,Repository分別標記類是Service層類,Controller層類,數據存儲層的類;一般注入的是DAO實現類和Service的接口;
set注入:定義私有變量,借助setXXX方法設置屬性值;XML文件的<property>標簽聲明(name,value或ref);
構造器注入:帶有參數的構造函數注入;
工廠注入:靜態工廠的方法注入,實例工廠的方法注入;
5、AOP(Aspect Oriented Programming),即面向切面編程
(1)代理模式
代理模式:提供了對目標對象另外的訪問方式;即通過代理對象訪問目標對象。
代理模式的關鍵點是:代理對象與目標對象,代理對象是對目標對象的擴展,並會調用目標對象;
代理模式優點:在目標對象已經實現的功能操作的基礎上,增加額外的功能操作,拓展目標對象的功能;(不修改已經寫好的代碼,運用代理模式實現新功能)
靜態代理模式:代理對象需要與目標對象實現一樣的接口
1 // 接口 2 public interface IUserDao { 3 void save(); 4 } 5 6 /** 7 * 接口實現, 目標對象 8 */ 9 public class UserDao implements IUserDao { 10 public void save() { 11 System.out.println("----已經保存數據!----"); 12 } 13 } 14 /** 15 * 代理對象,靜態代理 16 */ 17 public class UserDaoProxy implements IUserDao{ 18 //接收保存目標對象 19 private IUserDao target; 20 public UserDaoProxy(IUserDao target){ 21 this.target=target; 22 } 23 24 public void save() { 25 System.out.println("開始事務..."); 26 target.save();//執行目標對象的方法 27 System.out.println("提交事務..."); 28 } 29 } 30 /** 31 * 測試類 32 */ 33 public class App { 34 public static void main(String[] args) { 35 //目標對象 36 UserDao target = new UserDao(); 37 38 //代理對象,把目標對象傳給代理對象,建立代理關系 39 UserDaoProxy proxy = new UserDaoProxy(target); 40 41 proxy.save();//執行的是代理的方法 42 } 43 }
JDK動態代理模式:代理對象不需要實現接口,但目標對象必須實現接口
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )
其中,在Proxy類中是靜態方法,且接收的三個參數依次為:
ClassLoader loader:
指定當前目標對象使用類加載器,獲取加載器的方法是固定的;Class<?>[] interfaces:
目標對象實現的接口的類型,使用泛型方式確認類型;InvocationHandler h:
事件處理,執行目標對象的方法時,會觸發事件處理器的方法,會把當前執行目標對象的方法作為參數傳入;
1 /** 2 * 創建動態代理對象 3 * 動態代理不需要實現接口,但是需要指定接口類型 4 */ 5 public class ProxyFactory{ 6 //維護一個目標對象 7 private Object target; 8 public ProxyFactory(Object target){ 9 this.target=target; 10 } 11 12 //給目標對象生成代理對象 13 public Object getProxyInstance(){ 14 return Proxy.newProxyInstance( 15 target.getClass().getClassLoader(), 16 target.getClass().getInterfaces(), 17 new InvocationHandler() { 18 @Override 19 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 20 System.out.println("開始事務2"); 21 //執行目標對象方法 22 Object returnValue = method.invoke(target, args); 23 System.out.println("提交事務2"); 24 return returnValue; 25 } 26 } 27 ); 28 } 29 } 30 /** 31 * 測試類 32 */ 33 public class App { 34 public static void main(String[] args) { 35 // 目標對象 36 IUserDao target = new UserDao(); 37 // 【原始的類型 class cn.itcast.b_dynamic.UserDao】 38 System.out.println(target.getClass()); 39 40 // 給目標對象,創建代理對象 41 IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance(); 42 // class $Proxy0 內存中動態生成的代理對象 43 System.out.println(proxy.getClass()); 44 45 // 執行方法 【代理對象】 46 proxy.save(); 47 } 48 }
Cglib動態代理:子類代理,目標對象不需要實現接口,根據目標對象構建一個子類對象從而實現對目標對象功能的擴展;
1 /** 2 * 目標對象,沒有實現任何接口 3 */ 4 public class UserDao { 5 public void save() { 6 System.out.println("----已經保存數據!----"); 7 } 8 } 9 /** 10 * Cglib子類代理工廠 11 * 對UserDao在內存中動態構建一個子類對象 12 */ 13 public class ProxyFactory implements MethodInterceptor{ 14 //維護目標對象 15 private Object target; 16 17 public ProxyFactory(Object target) { 18 this.target = target; 19 } 20 21 //給目標對象創建一個代理對象 22 public Object getProxyInstance(){ 23 //1.工具類 24 Enhancer en = new Enhancer(); 25 //2.設置父類 26 en.setSuperclass(target.getClass()); 27 //3.設置回調函數 28 en.setCallback(this); 29 //4.創建子類(代理對象) 30 return en.create(); 32 } 33 34 @Override 35 public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { 36 System.out.println("開始事務..."); 37 //執行目標對象的方法 38 Object returnValue = method.invoke(target, args); 39 System.out.println("提交事務..."); 40 return returnValue; 41 } 42 } 43 /** 44 * 測試類 45 */ 46 public class App { 47 @Test 48 public void test(){ 49 //目標對象 50 UserDao target = new UserDao(); 52 //代理對象 53 UserDao proxy = (UserDao)new ProxyFactory(target).getProxyInstance(); 55 //執行代理對象的方法 56 proxy.save(); 57 } 58 }
在Spring的AOP編程中:如果加入容器的目標對象有實現接口,用JDK代理;如果目標對象沒有實現接口,用Cglib代理;
(2)AOP概念
切面:與業務無關,卻被業務模塊共同調用的邏輯,封裝起來,提高代碼復用,利於維護等;
連接點:被攔截的方法;(攔截哪些方法,增加哪些功能)
切入點:對連接點進行攔截的定義;
通知:攔截到連接點后要執行操作或處理,如前置before、后置after-returning、異常after-throwing、最終after、環繞around通知五類;
引入:在不修改代碼的前提下,為類添加新的屬性或者方法;
織入:將切面應用於目標對象並且導致代理對象的創建;
(3)使用場景
Authentication 權限
Caching 緩存
Context passing 內容傳遞
Error handling 錯誤處理
Lazy loading 懶加載
Debugging 調試
logging, tracing, profiling and monitoring 記錄跟蹤 優化 校准
Performance optimization 性能優化
Persistence 持久化
Resource pooling 資源池
Synchronization 同步
Transactions 事務
(4)實現方式--基礎XML配置AOP、基於注解配置AOP、
AOP編程其實是很簡單的事情,縱觀AOP編程,程序員只需要參與三個部分:
1、定義普通業務組件
2、定義切入點,一個切入點可能橫切多個業務組件
3、定義增強處理,增強處理就是在AOP框架為普通業務組件織入的處理動作
1)、基於xml配置Spring AOP;通過<aop:config>來配置
AOP配置要定義切面aspect、定義切點pointcut、定義連接通知方法等;
配置文件如下:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:aop="http://www.springframework.org/schema/aop" 5 xmlns:tx="http://www.springframework.org/schema/tx" 6 xsi:schemaLocation="http://www.springframework.org/schema/beans 7 http://www.springframework.org/schema/beans/spring-beans-4.2.xsd 8 http://www.springframework.org/schema/aop 9 http://www.springframework.org/schema/aop/spring-aop-4.2.xsd"> 10 11 <bean id="helloWorldImpl1" class="com.xrq.aop.HelloWorldImpl1" /> 12 <bean id="helloWorldImpl2" class="com.xrq.aop.HelloWorldImpl2" /> 13 <bean id="timeHandler" class="com.xrq.aop.TimeHandler" /> 14 15 <aop:config> 16 <aop:aspect id="time" ref="timeHandler"> 17 <aop:pointcut id="addAllMethod" expression="execution(* com.xrq.aop.HelloWorld.*(..))" /> 18 <aop:before method="printTime" pointcut-ref="addAllMethod" /> 19 <aop:after method="printTime" pointcut-ref="addAllMethod" /> 20 </aop:aspect> 21 </aop:config> 22 </beans>
實現代碼如下:
1 //定義接口 2 public interface HelloWorld 3 { 4 void printHelloWorld(); 5 void doPrint(); 6 } 7 //實現類1 8 public class HelloWorldImpl1 implements HelloWorld 9 { 10 public void printHelloWorld() 11 { 12 System.out.println("Enter HelloWorldImpl1.printHelloWorld()"); 13 } 14 public void doPrint() 15 { 16 System.out.println("Enter HelloWorldImpl1.doPrint()"); 17 return ; 18 } 19 } 20 //實現類2 21 public class HelloWorldImpl2 implements HelloWorld 22 { 23 public void printHelloWorld() 24 { 25 System.out.println("Enter HelloWorldImpl2.printHelloWorld()"); 26 } 27 public void doPrint() 28 { 29 System.out.println("Enter HelloWorldImpl2.doPrint()"); 30 return ; 31 } 32 } 33 //橫切關注點--增加打印時間 34 public class TimeHandler 35 { 36 public void printTime() 37 { 38 System.out.println("CurrentTime = " + System.currentTimeMillis()); 39 } 40 } 64 //main方法測試 65 public static void main(String[] args) 66 { 67 ApplicationContext ctx = 68 new ClassPathXmlApplicationContext("aop.xml"); 69 70 HelloWorld hw1 = (HelloWorld)ctx.getBean("helloWorldImpl1"); 71 HelloWorld hw2 = (HelloWorld)ctx.getBean("helloWorldImpl2"); 72 hw1.printHelloWorld(); 73 System.out.println(); 74 hw1.doPrint(); 75 76 System.out.println(); 77 hw2.printHelloWorld(); 78 System.out.println(); 79 hw2.doPrint(); 80 }
打印輸出結果:
CurrentTime = 1446129611993 Enter HelloWorldImpl1.printHelloWorld() CurrentTime = 1446129611993 CurrentTime = 1446129611994 Enter HelloWorldImpl1.doPrint() CurrentTime = 1446129611994 CurrentTime = 1446129611994 Enter HelloWorldImpl2.printHelloWorld() CurrentTime = 1446129611994 CurrentTime = 1446129611994 Enter HelloWorldImpl2.doPrint() CurrentTime = 1446129611994
2)、基於注解配置
目標對象對應的原代碼,即被代理類代碼中,增加注解@Service等;
在通知類代碼中,增加注解@Component會被IOC容器管理、@Aspect聲明是切面;在方法前增加@Before、@After等通知注解;
1 /** 2 * 被代理的目標類 3 */ 4 @Service("math") 5 public class Math{ 6 ...... 7 } 8 /** 9 * 通知類,橫切邏輯 10 * 11 */ 12 @Component 13 @Aspect 14 public class Advices { 15 @Before("execution(* com.zhangguo.Spring052.aop02.Math.*(..))") 16 public void before(JoinPoint jp){ 17 System.out.println("----------前置通知----------"); 18 System.out.println(jp.getSignature().getName()); 19 } 20 21 @After("execution(* com.zhangguo.Spring052.aop02.Math.*(..))") 22 public void after(JoinPoint jp){ 23 System.out.println("----------最終通知----------"); 24 } 25 }
1 <!--XML僅僅聲明需要用到AOP即可--> 2 <aop:aspectj-autoproxy/> 3 <!--或者--> 4 <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
注:1、2中的execution(* com.xrq.aop.HelloWorld.*(..))語句中,execution實際上是切點函數(AspectJ切點函數),除此之外還有within(包名.*)匹配指定的包的所有連接點,this(類名)該類名下所有方法,args(多個參數類型)參數個數以及類型能匹配上的方法,annotation特殊制定注解;
注:除以上知識還有AspectJ注解通知、零配置實現IOC和AOP;
6、Spring的MVC:基於請求驅動的Web框架
(1)、組成部分
模型(model)—視圖(view)—控制器(controller)
MVC執行步驟
- 用戶發起Request請求至控制器(Controller),控制器接收用戶的請求,並將請求委托給模型進行處理。
- 控制器請求模型(Model),模型處理業務數據並得到處理結果,模型通常是指業務邏輯,包括Pojo、Service、Dao等三層
- 模型將處理結果返回給控制器
- 控制器將模型數據在視圖(View)中展示
說明:web中模型無法將數據直接在視圖上顯示,需要通過控制器完成。如果在C/S應用中模型是可以將數據在視圖中展示的。 - 控制器將視圖響應結果,Response響應給用戶,通過視圖展示給用戶數據或者處理結果。
以上MVC僅僅易於入門,實際如下:
Spring MVC主要由DispatcherServlet前端控制器、HandlerMapping處理器映射器、HandlerAdapter處理器(控制器、適配器)、ViewResolver視圖解析器、視圖組成;
前端控制器:Spring MVC的核心,職責分派、職責調度、控制流程,負責控制轉發,中央處理器,接收到用戶的請求,調用處理器映射器找到handler,調用處理器
執行handler,如果遇到異常,統一調用異常處理器;
處理器映射器:根據配置文件的配置或根據注解設置,找到.action對應的Handler(處理器),還找到Handler前邊和后邊執行的攔截器;
處理器:根據handler類的規則去執行Handler,給前端控制器返回一個ModelAndView;
視圖解析器:根據邏輯視圖名解析到真正的view的地址,最終將View返回給前端控制器;
(2)運行流程
- 1Http請求:客戶端請求提交到DispatcherServlet。
- 2尋找處理器:由DispatcherServlet控制器查詢一個或多個HandlerMapping,找到處理請求的Controller。
- 3調用處理器:DispatcherServlet將請求提交到Controller。
- 4-5調用業務處理和返回結果:Controller調用業務邏輯處理后,返回ModelAndView。
- 6-7處理視圖映射並返回模型: DispatcherServlet查詢一個或多個ViewResoler視圖解析器,找到ModelAndView指定的視圖。
- 8 Http響應:視圖負責將結果顯示到客戶端。
流程描述
1、用戶發送請求至前端控制器DispatcherServlet
2、DispatcherServlet收到請求調用HandlerMapping處理器映射器。
3、處理器映射器找到具體的處理器,生成處理器對象及處理器攔截器(如果有則生成)一並返回給DispatcherServlet。
4、DispatcherServlet調用HandlerAdapter處理器適配器
5、HandlerAdapter經過適配調用具體的處理器(Controller,也叫后端控制器)。
6、Controller執行完成返回ModelAndView
7、HandlerAdapter將controller執行結果ModelAndView返回給DispatcherServlet
8、DispatcherServlet將ModelAndView傳給ViewReslover視圖解析器
9、ViewReslover解析后返回具體View
10、DispatcherServlet根據View進行渲染視圖(即將模型數據填充至視圖中)。
11、DispatcherServlet響應用戶
(3)MVC實現 https://www.cnblogs.com/superjt/p/3309255.html
- web.xml文件中進行配置,在配置中設置springmvc-context.xml的路徑
- Spring是一個開源框架,是一個分層架構;
- Spring目標是遵循一系列的接口標准,這樣的好處是只需要簡單的Java對象或者Javabean就能進行Java EE開發,這樣開發的入門、測試、應用部署都得到了簡化;
- Spring有7個核心模塊,Core(核心容器)、AOP(切面)、DAO(事務支持)、ORM(O\R MAPPING封裝)、Web(上下文、服務於Web請求等)、Web MVC(MVC框架);
- Inversion of Control(控制反轉)、Dependency Injection(依賴注入)
二、控制反轉IoC
概念:對組件對象的控制權轉移給外部容器;對象的協作關系由容器來建立。(不再由對象自己來負責)
實現方式:<1>依賴查找(Dependency Lookup)<2>依賴注入(Dependency Injection)
依賴注入:setter注入---- XMl的bean下增加property配置,java增加get/set方法;
三、面向切面的編程AOP
概念:將程序中的交叉業務邏輯提取出來;將業務邏輯的各個部分分離,降低耦合;關注於業務邏輯而不是實體對象;
AOP(Aspect Oriented Programming) OOP(Object Oriented Programming,面向對象的編程)
四、框架優缺點
優點:降低耦合,解耦;AOP,易於實現業務邏輯;支持主流框架;高開放性,有的放矢;
面試常問:
1、最常用的BeanFactory 實現是XmlBeanFactory 類,它根據XML文件中的定義加載beans。該容器從XML 文件讀取配置元數據並用它去創建一個完全配置的系統或應用。
2、bean 裝配是指在Spring 容器中把bean組裝到一起,前提是容器需要知道bean的依賴關系,如何通過依賴注入來把它們裝配到一起。
3、Spring 容器能夠自動裝配相互合作的bean,這意味着容器不需要<constructor-arg>和<property>配置,能通過Bean工廠自動處理bean之間的協作。
4、有五種自動裝配的方式,可以用來指導Spring容器用自動裝配方式來進行依賴注入。
- no:默認的方式是不進行自動裝配,通過顯式設置ref 屬性來進行裝配。
- byName:通過參數名 自動裝配,Spring容器在配置文件中發現bean的autowire屬性被設置成byname,之后容器試圖匹配、裝配和該bean的屬性具有相同名字的bean。
- byType::通過參數類型自動裝配,Spring容器在配置文件中發現bean的autowire屬性被設置成byType,之后容器試圖匹配、裝配和該bean的屬性具有相同類型的bean。如果有多個bean符合條件,則拋出錯誤。
- constructor:這個方式類似於byType, 但是要提供給構造器參數,如果沒有確定的帶參數的構造器參數類型,將會拋出異常。
- autodetect:首先嘗試使用constructor來自動裝配,如果無法工作,則使用byType方式。
5、自動裝配的局限性是:
- 重寫: 你仍需用 <constructor-arg>和 <property> 配置來定義依賴,意味着總要重寫自動裝配。
- 基本數據類型:你不能自動裝配簡單的屬性,如基本數據類型,String字符串,和類。
- 模糊特性:自動裝配不如顯式裝配精確,如果有可能,建議使用顯式裝配。