接上篇《JAVA WEB快速入門之從編寫一個JSP WEB網站了解JSP WEB網站的基本結構、調試、部署》,經過一個簡單的JSP WEB網站了解了JAVA WEB相關的知識,好比:Servlet、Fitler、Listner等,這爲後面搭建基於SSM的框架奠基了基礎知識,固然光了解JSP相關的知識還不行,咱們還得了解掌據Spring相關的知識,由於SSM,是基於Spring框架(SpringMVC)搭建的,好了廢話很少說,直接進入主題。php
什麼是Spring?html
Spring是一個開放源代碼的設計層面框架,他解決的是業務邏輯層和其餘各層的鬆耦合問題,所以它將面向接口的編程思想貫穿整個系統應用...詳見百度百科:https://baike.baidu.com/item/spring/85061java
核心模塊以下圖示:(來源網絡)程序員
依賴關係:(來源網絡)spring
1、創建一個Spring項目:express
1.1打開eclipse,依次操做:File->New->Java Project,而後設置一些必要的項目屬性(相似操做在上一篇),最後finish建立完成一個空的JAVA Project,注意目前並無Spring環境。以下圖示:apache
1.2下載Spring相關的JAR包(下載地址:http://repo.spring.io/release/org/springframework/spring/ 或使用MAVAN的下載地址:http://maven.springframework.org/release/org/springframework/spring/)編程
打開下載頁面後,從列表中找到最新的一個地址,如目前的最新版本:(5.1.2.RELEASE)網絡
經過Spring官網也能看到當前顯示的最新版本(官網地址:https://spring.io/projects/spring-framework#learn)app
1.3點擊進入選擇的下載版本連接,而後點擊以下圖示的地址下載Spring JAR包:
1.4下載後解壓,而後在JAVA項目中引入剛纔下載的Spring JAR包(在解壓後的libs目錄下),引入方式與上篇介紹基本相同,經過項目右擊:Buid path->Configure Buid Path->切換到Libraries頁籤->Add External JARs(即:添加外部JAR包),以下圖示:
導入到項目後的效果以下圖示:
固然除了引入Spring相關JAR包外,應該還須要導入一個Commons Logging JAR包,由於Spring-core 包有依賴此包,故咱們也應該下載並引入(地址:http://commons.apache.org/proper/commons-logging/download_logging.cgi),下載頁面以下圖示:
導入方法同上面導入Spring JAR包操做相同,故再也不重述。
到目前爲止一個Spring的項目環境已經搭建好了,有點感受像在VS中建立一個空的WEB項目,而後引入相關的DLL最後造成一個MVC或WEB API框架。
2、 使用Spring的依賴注入功能
2.1 瞭解依賴注入必要知識:
Srping IOC容器:是 Spring 框架的核心。容器將建立對象並把它們鏈接在一塊兒,配置它們,並管理他們的整個生命週期從建立到銷燬。Spring 容器使用依賴注入(DI)來管理組成一個應用程序的組件(這些對象被稱爲 Spring Beans)。經過閱讀配置元數據提供的指令,容器知道對哪些對象進行實例化,配置和組裝。配置元數據能夠經過 XML,Java 註解或 Java 代碼來表示。IOC 容器負責實例化、定位、配置應用程序中的對象及創建這些對象間的依賴。一般new一個實例,控制權由程序員控制,而"控制反轉"是指new實例工做不禁程序員來作而是交給Spring容器來作。簡容容器對象接口:BeanFactory(經常使用實現類:XmlBeanFactory)、高級容器對象接口:ApplicationContext(經常使用實現類:FileSystemXmlApplicationContext、ClassPathXmlApplicationContext、WebXmlApplicationContext)
Spring Bean:全部能夠被spring容器實例化並管理的java類均可以稱爲SpringBean
POJO、Java Bean、Spring Bean區別:
POJO是一個簡單的、普通Java對象,特色是有private的屬性(在C#中稱爲字段)和public的getter、setter方法,除此以外不具備任何特殊角色,不繼承或不實現任何其它Java框架的類或接口。通常用於數據的傳輸,好比做爲DTO對象;
JavaBean 是一種JAVA語言寫成的可重用組件。JavaBean符合必定規範編寫的Java類,不是一種技術,而是一種規範。它的方法命名,構造及行爲必須符合特定的約定:
A.全部屬性爲private。B.類必須具備一個公共的(public)無參構造函數,C.private屬性必須提供public的getter和setter來給外部訪問,而且方法的命名也必須遵循必定的命名規範。 D.這個類應是可序列化的,要實現serializable接口。
當一個POJO可序列化,有一個無參的構造函數,使用getter和setter方法來訪問屬性時,他就是一個JavaBean,而Spring Bean,不須要像JavaBean 同樣遵循一些規範(不過對於經過設值方法注入的Bean,必定要提供setter 方法。)
2.2在項目根目錄下建立Beans.xml文件(Spring Bean配置文件),操做步驟:src右鍵->New->Other->搜索xml->選擇xml file->按默認步驟操做直至完成便可,建立完的XML文件可能只有以下內容:
<?xml version="1.0" encoding="UTF-8"?>
這時咱們須要手動添加必要的Spring Bean的命名空間(xmlns),添加後的完整的Spring Bean空配置文件以下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd"> </beans>
其中http://www.springframework.org/schema/beans/spring-beans-4.3.xsd這個4.3是目前最新的版本,能夠根據http://www.springframework.org/schema/beans得到最新的XSD文件
2.3定義一個Bean,並配置到Bean配置文件中(beans.xml),同時使用ClassPathXmlApplicationContext IOC容器來得到實例,代碼以下:
package cn.zuowenjun.java; public class FirstBean { private String uuidStr = java.util.UUID.randomUUID().toString(); private String message; public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public void showMessage(String name) { System.out.printf("hello,%1$s,Message:%2$s UUID:%3$s %n", name, message, uuidStr); } public void throwEx() throws Exception { throw new Exception("throw a new Exception!"); } public void init() { System.out.println(uuidStr + ":init..."); } public void destroy() { System.out.println("destroy..."); } } package cn.zuowenjun.java; import org.springframework.context.support.ClassPathXmlApplicationContext; public class SpringDemoApp { public static void main(String[] args) { // TODO Auto-generated method stub ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext("Beans.xml"); context.start(); FirstBean firstBean= (FirstBean)context.getBean("firstBean"); firstBean.showMessage("夢在旅途"); context.close(); context.registerShutdownHook(); } }
在Beans.xml中註冊FirstBean(省略XML聲明定義的固定部份,僅貼出Bean的定義)
<bean id="firstBean" class="cn.zuowenjun.java.FirstBean" init-method="init" destroy-method="destroy" scope="singleton"> <property name="message" value="i love Spring!"></property> </bean>
運行SpringDemoApp便可看到輸出的結果:
配置說明:
bean元素:表示註冊一個bean;
id:bean的惟一識別名稱(IOC容器getBean就是取這個名稱);
class:bean的完整類名(包含包名);
init-method:定義bean初始化完成後回調的方法;(也能夠經過將Bean實現InitializingBean接口,重寫afterPropertiesSet方法)
destroy-method:定義當包含該 bean 的容器被銷燬時回調方法;(也能夠經過將Bean實現DisposableBean接口,重寫destroy方法)
注:若是你有太多具備相同名稱的初始化或者銷燬回調方法的 Bean,那麼你不須要在每個 bean 上聲明初始化方法和銷燬方法,直接在Bean元素中配置 default-init-method 和 default-destroy-method 屬性便可
scope:定義bean的做用域,生命週期範圍,具體值以下圖示:(來源網絡)
property元素:表示Bean的屬性
固然還有其它屬性,咱們稍後示例中有用到的時候再補充說明。
注:能夠經過定義一個類並實現BeanPostProcessor接口(稱爲:Bean 後置處理器),實如今調用初始化方法先後對 Bean 進行額外的處理
2.4分別再定義SecondBean、ThirdBean類,演示經過構造函數注入、屬性注入,實現代碼以下:
package cn.zuowenjun.java; import java.util.List; public class SecondBean { private int intProp; private String strProp; private ThirdBean thirdBean; private FirstBean firstBean=null; public SecondBean(int ipro,String sPro,FirstBean frtBean) { this.intProp=ipro; this.strProp=sPro; this.firstBean=frtBean; } public int getIntProp() { return intProp; } public void setIntProp(int intProp) { this.intProp = intProp; } public String getStrProp() { return strProp; } public void setStrProp(String strProp) { this.strProp = strProp; } public ThirdBean getThirdBean() { return thirdBean; } public void setThirdBean(ThirdBean thirdBean) { this.thirdBean = thirdBean; } public void outPutAll() { System.out.println("output start>>>>"); System.out.printf("intProp:%d,strProp:%s %n",intProp,strProp); firstBean.showMessage(strProp); List<Integer> list=thirdBean.getListProp(); StringBuffer strBuffer=new StringBuffer(); for(Integer i:list) { strBuffer.append(i.toString()+","); } System.out.println(strBuffer.toString()); System.out.println("output end<<<<"); } } package cn.zuowenjun.java; import java.util.*; public class ThirdBean { private List<Integer> listProp; public List<Integer> getListProp() { return listProp; } public void setListProp(List<Integer> listProp) { this.listProp = listProp; } }
配置註冊相關Bean:
<bean id="firstBean" class="cn.zuowenjun.java.FirstBean" init-method="init" destroy-method="destroy" scope="singleton"> <property name="message" value="i love Spring!"></property> </bean> <bean id="secondBean" class="cn.zuowenjun.java.SecondBean"> <constructor-arg type="int" value="520"></constructor-arg> <constructor-arg type="java.lang.String" value="JAVAER"></constructor-arg> <constructor-arg name="frtBean" ref="firstBean"></constructor-arg> <property name="thirdBean" ref="thirdBean"></property> </bean> <bean id="thirdBean" class="cn.zuowenjun.java.ThirdBean"> <property name="listProp"> <list> <value>1</value> <value>2</value> <value>3</value> <value>4</value> <value>5</value> <value>6</value> <value>7</value> <value>8</value> <value>9</value> </list> </property> </bean>
配置補充說明:
constructor-arg元素:表示構造函數參數,type:表示參數類型,name:表示參數名,value:表示參數注入的值(通常經常使用於基礎類型及字符串),ref:表示參數注入的依賴Bean的ID;
property下的list元素表示的是注入這個屬性的值爲list集合,按照list集合方式配置集合中的每一個值,固然除了list還有其它的集合注入方式,可參見:https://www.w3cschool.cn/wkspring/kp5i1ico.html
在constructor-arg元素使用ref能夠實現構造函數注入指定依賴的Bean,property元素使用ref能夠實現屬性setter方法注入指定依賴的Bean
從上面的配置來看,構造函數注入能夠根據name、type來實現自動匹配完成依賴注入,還支持根據參數個數索引來實現自動匹配完成依賴注入,以下所示:
<bean id="secondBean" class="cn.zuowenjun.java.SecondBean"> <constructor-arg index="0" value="520"></constructor-arg> <constructor-arg index="1" value="JAVAER"></constructor-arg> <constructor-arg index="2" ref="firstBean"></constructor-arg> <property name="thirdBean" ref="thirdBean"></property> </bean>
除了在XML中顯式的配置 constructor-arg、property元素來指定注入,咱們還能夠經過Bean的自動裝配功能實現自動根據構造函數或屬性的參數名(byName)、類型(byType)自動匹配注入依賴項,具體作法是:
在定義Bean元素時,autowire屬性設置爲:byName或byType或constructor或autodetect(優先嚐試經過構造函數參數類型匹配,若不行則按照byType),同時對應的constructor-arg、property元素可省略不匹配;(注意要符合自動裝配要求)
最後在main方法加入以下代碼而後運行:
SecondBean secondBean=(SecondBean)context.getBean("secondBean"); secondBean.outPutAll();
輸出結果以下:
2.5經過JAVA註解來配置依賴注入,這樣Bean配置文件能夠少些配置,要實現註解配置依賴注入首先須要在Bean配置文件的根元素(beans)添加context命名空間,而後去掉相關的依賴注入的元素節點,最後改造完成的bean配置以下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"> <context:annotation-config /> <bean id="firstBean" class="cn.zuowenjun.java.FirstBean" init-method="init" destroy-method="destroy" scope="singleton"> <property name="message" value="i love Spring!"></property> </bean> <bean id="firstBean2" class="cn.zuowenjun.java.FirstBean" init-method="init" destroy-method="destroy" scope="singleton"> <property name="message" value="i love Spring -2!"></property> </bean> <bean id="secondBean" class="cn.zuowenjun.java.SecondBean"> <constructor-arg type="int" value="520"></constructor-arg> <constructor-arg type="java.lang.String" value="JAVAER"></constructor-arg> </bean> <bean id="thirdBean" class="cn.zuowenjun.java.ThirdBean"> <property name="listProp"> <list> <value>1</value> <value>2</value> <value>3</value> <value>4</value> <value>5</value> <value>6</value> <value>7</value> <value>8</value> <value>9</value> </list> </property> </bean> </beans>
配置變化點:增長了context命名空間及對應的schemaLocation信息,而後添加了<context:annotation-config />元素,該元素告訴Spring IOC容器採要註解配置,最後移除了有關以前使用ref的依賴注入的元素,改成在代碼中經過以下一系列註解來實現:
注意:@Required已被廢棄,使用@Autowired(required=true)替代,有些網上教程仍是舊的。
代碼改變部份(主要是給FirstBean增長@PostConstruct、@PreDestroy註解,用於指示初始回調、銷燬回調方法,給SecondBean增長@Autowired(required=true)、@Qualifier註解):
package cn.zuowenjun.java; import javax.annotation.*; public class FirstBean { private String uuidStr = java.util.UUID.randomUUID().toString(); private String message; public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public void showMessage(String name) { System.out.printf("hello,%1$s,Message:%2$s UUID:%3$s %n", name, message, uuidStr); } public void throwEx() throws Exception { throw new Exception("throw a new Exception!"); } @PostConstruct public void init() { System.out.println(uuidStr + ":init..."); } @PreDestroy public void destroy() { System.out.println("destroy..."); } } package cn.zuowenjun.java; import java.util.List; import org.springframework.beans.factory.annotation.*; public class SecondBean { private int intProp; private String strProp; private ThirdBean thirdBean; private FirstBean firstBean=null; @Autowired(required=true) public SecondBean(int ipro,String sPro,@Qualifier("firstBean2") FirstBean frtBean) { this.intProp=ipro; this.strProp=sPro; this.firstBean=frtBean; } public int getIntProp() { return intProp; } public void setIntProp(int intProp) { this.intProp = intProp; } public String getStrProp() { return strProp; } public void setStrProp(String strProp) { this.strProp = strProp; } public ThirdBean getThirdBean() { return thirdBean; } @Autowired(required=true) public void setThirdBean(ThirdBean thirdBean) { this.thirdBean = thirdBean; } public void outPutAll() { System.out.println("output start>>>>"); System.out.printf("intProp:%d,strProp:%s %n",intProp,strProp); firstBean.showMessage(strProp); List<Integer> list=thirdBean.getListProp(); StringBuffer strBuffer=new StringBuffer(); for(Integer i:list) { strBuffer.append(i.toString()+","); } System.out.println(strBuffer.toString()); System.out.println("output end<<<<"); } }
注意@Autowired默認是按byType模式進行自動裝配(即:bean的類型與被標註的成員:字段、屬性、構造函數參數類型相同即爲匹配注入),若是存在註冊多個相同的bean類型,這時可能會報錯,由於Spring容器不知道使用哪一個類型進行注入實例,如上面示例的Bean配置文件中的FirstBean, 定義了有兩個,只是id不一樣,那麼這種狀況咱們就須要使用@Qualifier("firstBean2")來顯式指定注入哪一個bean;
另外@Autowired(required=true)中的required表示是否必需依賴注入,若是爲true,且在bean配置文件中沒有找到注入的bean則會報錯,若是爲false則在注入失敗時忽略,即爲默認值。
最後咱們運行SpringDemoApp,最終結果以下:(你們可能注意到初始回調方法、銷燬回調方法分別調用了2次,都打印了2次,這是由於在bean配置文件中,FirstBean註冊了兩次,雖然名字不一樣,雖然只用了其中的一個,但這兩個方法是與同一個bean類型有關,同時scope配置爲了singleton,因爲是單例,故須要爲每個註冊的bean都初始化一下,通過認證,若是把scope改成prototype將只會出現一次)
2.6經過定義Spring Bean配置類+註解來實現Bean的註冊及依賴注入的設置 ,具體實現代碼以下
SpringBeansConfig bean配置類:(用以取代beans.xml配置文件)
package cn.zuowenjun.java; import java.util.Arrays; import org.springframework.context.annotation.*; @Configuration public class SpringBeansConfig { @Bean(initMethod="init",destroyMethod="destroy") @Scope(value="prototype") public FirstBean firstBean() { FirstBean firstBeanObj= new FirstBean(); firstBeanObj.setMessage("i love java!"); return firstBeanObj; } @Bean public SecondBean secondBean() { return new SecondBean(666,"夢在旅途",firstBean()); } @Bean public ThirdBean thirdBean() { ThirdBean thirdBeanObj= new ThirdBean(); thirdBeanObj.setListProp(Arrays.asList(1,2,3,4,5,6,7,8,9)); return thirdBeanObj; } }
而後在main方法添加以下代碼:(此次使用的是AnnotationConfigApplicationContext 的IOC容器,無需bean xml配置文件)
AnnotationConfigApplicationContext annoCfgAppContext=new AnnotationConfigApplicationContext(SpringBeansConfig.class); annoCfgAppContext.start(); FirstBean firstBean= (FirstBean)annoCfgAppContext.getBean("firstBean"); firstBean.showMessage("firstBean單獨"); SecondBean secondBean= annoCfgAppContext.getBean(SecondBean.class); secondBean.outPutAll(); annoCfgAppContext.close(); annoCfgAppContext.registerShutdownHook();
運行效果以下圖示:(發現沒有,destroy方法沒有輸出,知道爲何?由於FirstBean的scope使用了prototype模式,若是不指定或指定爲singleton,則能正常輸出destroy方法打印的內容,但具體緣由你們自行查找資料)
2.7 定義相關Spring事件處理器類(繼承自ApplicationListener泛型接口,泛型參數爲具體的事件ApplicationEvent的子類),從而訂閱相關的Spring事件觸發的事件方法,Spring 提供瞭如下的標準事件接口:(圖片來源網絡)
示例代碼以下:(訂閱開始及中止事件)
package cn.zuowenjun.java; import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextStartedEvent; public class CStartEventHandler implements ApplicationListener<ContextStartedEvent> { @Override public void onApplicationEvent(ContextStartedEvent event) { String appName= event.getApplicationContext().getDisplayName(); System.out.println(appName + " was Started!"); } } package cn.zuowenjun.java; import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextStoppedEvent; public class CStopEventHandler implements ApplicationListener<ContextStoppedEvent> { @Override public void onApplicationEvent(ContextStoppedEvent event) { String appName= event.getApplicationContext().getDisplayName(); System.out.println(appName + " was Stopped!"); } } //SpringBeansConfig 類中增長註冊上述兩個Bean(若是是用XML配置文件則換成在XML中定義相關的bean元素) @Bean public CStartEventHandler cStartEventHandler() { return new CStartEventHandler(); } @Bean public CStopEventHandler cStopEventHandler() { return new CStopEventHandler(); } //最後main方法運行以下邏輯: AnnotationConfigApplicationContext annoCfgAppContext=new AnnotationConfigApplicationContext(SpringBeansConfig.class); annoCfgAppContext.setDisplayName("SpringDemoAppContext"); annoCfgAppContext.start(); //start,以觸發start事件 SecondBean secondBean= annoCfgAppContext.getBean(SecondBean.class); secondBean.outPutAll(); annoCfgAppContext.stop();//stop,以觸發stop事件 annoCfgAppContext.close(); annoCfgAppContext.registerShutdownHook();
運行結果以下圖示:
固然除了標準的事件外,咱們還能夠自定義事件,主要是分別定義:繼承ApplicationEvent (即:自定義事件消息類)、實現ApplicationEventPublisherAware(即:事件發佈類或稱觸發事件類)、實現ApplicationListener(即:實現訂閱事件類),因爲篇幅有限,具體的實現代碼在此再也不貼出,可參見:https://www.w3cschool.cn/wkspring/7jho1ict.html
特別說明:以上經過各類示例代碼分別演示了:經過IOC容器得到bean的實例對象、基於bean配置文件實現構造函數及屬性依賴注入、基於註解及Bean配置類實現構造函數及屬性依賴注入等,但其實示例代碼中並無充分發揮依賴注入的功能或者說是核心思想(解除直接依賴,依賴抽象,而不能依賴具體實現),由於都是基於普通類來實現注入的,只是把實例化的過程(new)交給了Spring容器而矣,依賴仍然存在,沒法替換,那要如何作呢?其實很簡單,咱們只須要給對應的bean定義接口,而後bean去實現這個接口,咱們在再bean配置中註冊實現類便可,這樣就真正的發揮了IOC的做用了,代碼改造以下:
//定義一個IFirstBean 接口 package cn.zuowenjun.java; public interface IFirstBean { String getMessage(); void setMessage(String message); void showMessage(String name); void init(); void destroy(); } //FirstBean 實現IFirstBean 接口 package cn.zuowenjun.java; import javax.annotation.*; public class FirstBean implements IFirstBean { private String uuidStr = java.util.UUID.randomUUID().toString(); private String message; public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public void showMessage(String name) { System.out.printf("hello,%1$s,Message:%2$s UUID:%3$s %n", name, message, uuidStr); } public void throwEx() throws Exception { throw new Exception("throw a new Exception!"); } @PostConstruct public void init() { System.out.println(uuidStr + ":init..."); } @PreDestroy public void destroy() { System.out.println("destroy..."); } } package cn.zuowenjun.java; import java.util.List; import org.springframework.beans.factory.annotation.*; public class SecondBean { private int intProp; private String strProp; private ThirdBean thirdBean; private IFirstBean firstBean=null;//這裏依賴關係由實現類改成接口IFirstBean @Autowired(required=true) public SecondBean(int ipro,String sPro,@Qualifier("firstBean2") IFirstBean frtBean) { this.intProp=ipro; this.strProp=sPro; this.firstBean=frtBean; } ....省略其它代碼 } //main方法運行以下代碼: AnnotationConfigApplicationContext annoCfgAppContext=new AnnotationConfigApplicationContext(SpringBeansConfig.class); annoCfgAppContext.setDisplayName("SpringDemoAppContext"); annoCfgAppContext.start(); IFirstBean firstBean= (IFirstBean)annoCfgAppContext.getBean(IFirstBean.class); firstBean.showMessage("firstBean單獨"); SecondBean secondBean= annoCfgAppContext.getBean(SecondBean.class); secondBean.outPutAll(); annoCfgAppContext.stop(); annoCfgAppContext.close(); annoCfgAppContext.registerShutdownHook();
最終運行的結果與以前是同樣的,固然這樣歸功於Spring的byType(找同類型或父類或實現類) 、byName(找註冊bean的id)依賴注入方式。
三、 使用Spring的面向切面功能(AOP)
以前我曾寫過一篇專門介紹AOP的文章《C# 實現AOP 的幾種常見方式》,雖然那是用C#實現的,但思路及實現原理是同樣的,今天咱們就藉助於Spring AOP框架來實現面向切面編程的能力,因爲AOP的一些原理及基本實現已經講過了不在重述,這裏就直接演示實現過程及效果。
3.1下載並安裝AspectJ框架環境
下載頁面:https://www.eclipse.org/aspectj/downloads.php,找到合適的下載版本連接(最新穩定版本)點擊下載,以下圖示:
Latest Stable Release翻譯成中文就是:最新穩定版本,每一個版本都有兩個下載連接,一個是編譯後的字節碼包,一個是src源碼包,我這裏下載的是可執行的字節碼包,下載後須要經過java命令運行該jar包(其實就是一個安裝程序,安裝後會釋放出相關的jar包)
java -jar aspectj-xxxx.jar
項目中引用AspectJ安裝lib目錄下的相關jar包,引用外部JAR包以前都有說明在此不重述,引入後效果:
至此一個Spring的AOP項目開發環境就準備OK了,固然AOP環境OK了,還須要瞭解必要的AOP知識要點,由於網上有不少相關的介紹,我就不在一 一說明了,可參考:
http://www.javashuo.com/article/p-rdagsfro-db.html 、https://blog.csdn.net/csdn_terence/article/details/55804421
如下是個人簡單理解:
aspect:切面,實現各類通知的API方法集合類;
pointcut:切入點,指從哪一個位置進行攔截並通知;
joinpoint:鏈接點,被攔截到的對象信息(通常指:方法,屬性、構造器);
advice:通知,指被攔截後在相應的時機(有:前置、後置、異常、最終、環繞通知)處理的方法
Target object:目標對象,被代理的對象
Weaving:織入,將切面應用到目標對象並致使代理對象建立的過程
Introduction:引用,在運行時期,可動態的添加新方法或屬性到現有的類中。
3.2 經過XML配置AOP,實現步驟以下
A.定義一個Aspect的類(MyAspect),裏面主要包含各類通知處理方法:
package cn.zuowenjun.java; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; public class MyAspect { public void before(JoinPoint point) { System.out.println("call MyAspect.before!" + point.getSignature().getName()); } public void after(JoinPoint point) { System.out.println("call MyAspect.after!" + point.getSignature().getName()); } public void afterReturning(Object retVal){ System.out.println("call MyAspect.afterReturning! return Value:" + retVal); } public void afterThrowing(Exception ex) { System.out.println("call MyAspect.afterThrowing! Exception:" + ex.getMessage()); } public Object around(ProceedingJoinPoint pjp) throws Throwable { System.out.println("call around start-" + pjp.getSignature().getName()); Object obj=pjp.proceed(); System.out.println("call around end-" + pjp.getSignature().getName()); return obj; } }
改造一下IFirstBean及FirstBean,以便後面能夠進行AOP的演示(增長一個throwEx方法),改動後以下:
package cn.zuowenjun.java; public interface IFirstBean { String getMessage(); void setMessage(String message); void showMessage(String name); void init(); void destroy(); void throwEx(); } package cn.zuowenjun.java; import javax.annotation.*; public class FirstBean implements IFirstBean { private String uuidStr = java.util.UUID.randomUUID().toString(); private String message; public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public void showMessage(String name) { System.out.printf("hello,%1$s,Message:%2$s UUID:%3$s %n", name, message, uuidStr); } public void throwEx() { System.out.println("need throw a Exception"); throw new IllegalArgumentException("a test Exception msg!"); } @PostConstruct public void init() { System.out.println(uuidStr + ":init..."); } @PreDestroy public void destroy() { System.out.println("destroy..."); } }
B.配置AOP,將Aspect類(MyAspect)註冊到Spring 容器中,完整AOP相關的XML配置以下:(爲了演示效果,僅保留FirstBean註冊及AOP的配置)
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd"> <context:annotation-config /> <bean id="firstBean" class="cn.zuowenjun.java.FirstBean" init-method="init" destroy-method="destroy" scope="singleton"> <property name="message" value="i love Spring!"></property> </bean> <bean id="myaspectHandler" class="cn.zuowenjun.java.MyAspect"> </bean> <aop:config> <aop:aspect id="myaspect" ref="myaspectHandler"> <aop:pointcut id="showCut" expression="execution(public * cn.zuowenjun.java.*.show*(..))" /> <aop:pointcut id="exCut" expression="execution(public * cn.zuowenjun.java.*.*(..))" /> <aop:pointcut id="getterCut" expression="execution(public Object+ cn.zuowenjun.java.*.get*())" /> <aop:before method="before" pointcut-ref="showCut"/> <aop:after method="after" pointcut-ref="showCut"/> <aop:around method="around" pointcut-ref="getterCut"/> <aop:after-returning method="afterReturning" pointcut-ref="getterCut" returning="retVal"/> <aop:after-throwing method="afterThrowing" pointcut-ref="exCut" throwing="ex"/> </aop:aspect> </aop:config> </beans>
能夠看到,配置文件根節點中增長了aop的命名空間,同時增長了AOP的配置節點aop:config,aop:config中分別配置了:aspect(同時ref指向註冊的bean的id)、pointcut、以及相關的通知方法(都關聯到pointcut),其中關於pointcut的表達式的用法比較複雜,深刻了解能夠參見:https://blog.csdn.net/zhengchao1991/article/details/53391244
最後在main方法中運行以下代碼:
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml"); context.start(); IFirstBean firstBean = (IFirstBean)context.getBean("firstBean"); firstBean.showMessage("夢在旅途"); String msg= firstBean.getMessage(); System.out.println("print getMessage:" + msg); try { firstBean.throwEx(); }catch(Exception ex) { System.out.println("catch Exception:" + ex.getMessage()); } context.stop(); context.close(); context.registerShutdownHook();
運行效果以下:
3.3經過註解配置AOP,咱們只需改造以前的MyAspect類,而後加上相關的AOP註解便可實現無需XML配置,完善後的MyAspect類代碼以下:
package cn.zuowenjun.java; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; @Aspect @Component public class MyAspect { @Pointcut(value="execution(public * cn.zuowenjun.java.*.show*(..))") private void showCut() { } @Pointcut(value="execution(public * cn.zuowenjun.java.*.*(..))") private void exCut() { } @Pointcut(value="execution(public Object+ cn.zuowenjun.java.*.get*())") private void getterCut() { } @Before("showCut()") public void before(JoinPoint point) { System.out.println("call MyAspect.before!" + point.getSignature().getName()); } @After("showCut()") public void after(JoinPoint point) { System.out.println("call MyAspect.after!" + point.getSignature().getName()); } @AfterReturning(pointcut="getterCut()",returning="retVal") public void afterReturning(Object retVal){ System.out.println("call MyAspect.afterReturning! return Value:" + retVal); } @AfterThrowing(pointcut="exCut()",throwing="ex") public void afterThrowing(Exception ex) { System.out.println("call MyAspect.afterThrowing! Exception:" + ex.getMessage()); } @Around("getterCut()") public Object around(ProceedingJoinPoint pjp) throws Throwable { System.out.println("call around start-" + pjp.getSignature().getName()); Object obj=pjp.proceed(); System.out.println("call around end-" + pjp.getSignature().getName()); return obj; } }
從上面代碼能夠看出差別的部份,僅僅在類上增長了:@Aspect、@Component ,在對應的方法上增長了@Pointcut、@Before、@After、@AfterReturning、@AfterThrowing、@Around,每一個被特定註解標記的方法都表明着相應的做用,從註解含義就能夠看得出來。
固然還有一個很重要的點的就是XML配置中還須要增長一點配置,以下:
<aop:aspectj-autoproxy /> <bean id="myaspect" class="cn.zuowenjun.java.MyAspect"> </bean>
最後直接運行以前的main方法(邏輯所有都不用變),獲得的輸出結果與上面3.2節(用XML配置AOP)顯示的同樣。
話說雖然省掉了AOP的XML配置但仍然還有一點配置,能不能徹底不配置呢?答案是能夠的,咱們在上面2.6節介紹過可使用定義Spring Bean配置類+註解來實現Bean的註冊及依賴注入的設置,那這裏一樣咱們將MyAspect加入到Spring Bean配置類中,並在Spring Bean配置類(示例類:SpringBeansConfig)上添加@EnableAspectJAutoProxy註解便可,以下所示:
package cn.zuowenjun.java; import java.util.Arrays; import org.springframework.context.annotation.*; @Configuration @EnableAspectJAutoProxy public class SpringBeansConfig { @Bean(initMethod="init",destroyMethod="destroy") @Scope(value="prototype") public FirstBean firstBean() { FirstBean firstBeanObj= new FirstBean(); firstBeanObj.setMessage("i love java!"); return firstBeanObj; } @Bean public MyAspect myAspect() { return new MyAspect(); } } //main方法代碼: AnnotationConfigApplicationContext annoCfgAppContext=new AnnotationConfigApplicationContext(SpringBeansConfig.class); annoCfgAppContext.setDisplayName("SpringDemoAppContext"); annoCfgAppContext.start(); IFirstBean firstBean = (IFirstBean)annoCfgAppContext.getBean(IFirstBean.class); firstBean.showMessage("夢在旅途"); String msg= firstBean.getMessage(); System.out.println("print getMessage:" + msg); try { firstBean.throwEx(); }catch(Exception ex) { System.out.println("catch Exception:" + ex.getMessage()); } annoCfgAppContext.stop(); annoCfgAppContext.close(); annoCfgAppContext.registerShutdownHook();
最終運行的結果仍然與3.2節(XML配置AOP)、註解+XML配置AOP 相同,說明徹底能夠不用XML了。
好了,本文的主題(IOC、AOP)就分享到這裏,咱們總結一下吧!
最後小結:
1.Spring的IOC主要解決抽象與實現分離,代碼中如有依賴儘量的使用抽象類或接口,好比示例中的:(SecondBean依賴IFristBean),實現部份能夠單獨一個項目(JAR包),而後經過 XML配置或註解配置注入便可。咱們在配置中通常配置實現類。
2.Spring的AOP(準確的說是:AspectJ AOP)主要解決橫切面問題,消除功能相同的代碼邏輯,好比:日誌、監控、異常捕獲、驗證、攔截、過濾等,能夠把相同的業務邏輯代碼集中到某一類Aspec類中,便於統一管理與擴展。一樣支持XML配置或註解配置
3.雖然IOC、AOP功能強大,但配置有些繁瑣,依賴的JAR包要本身手動添加不夠智能,須要能有像VS的NUGET包同樣的依賴管理工具,JAVA這邊MAVEN就出場了,解決依賴問題,後續將會分享基於MAVEN來快速構建Spring項目。
PS:本篇文章涉及的知識點雖然只有IOC、AOP但裏面包括的細節很是多,我也是在工做之餘反覆學習與嘗試,但願對JAVA新手或對Spring不瞭解的人有幫助,謝謝!若以爲幫助到你,支持一下吧。^ v ^
補充一點小知識:
1.在Eclipse調試或查看JDK源代碼方法:
方法一:選擇項目右鍵->build path->Configure Buid Path->切換到Libraries頁籤->展開JRE System Libararys->找到rt.jar,並展開->選中soure attachment->點擊右側的edit按鈕,而後選擇外部路徑(external location)->瀏覽文件(external file)->選擇JRE的安裝目錄下的src,zip(這裏麪包含源碼),最後apply應用便可,以下圖示:
方法二:直接參見這篇博文:http://www.cnblogs.com/Keith-Fong/p/9570375.html
2.在Eclipse調試或查看Spring源代碼方法:
當咱們經過F3查看定義時,若是隻是顯示元數據定義而沒法顯示源代碼,則能夠點擊「Attach Source」,而後選擇對應的組件的源碼包,如:spring-context-5.1.2.RELEASE-sources.jar ,Spring各源碼包均以:sources.jar結尾
3.除了本身手動從Spring官網下載相關的JAR包並引入外,還能夠經過在Eclipse上安裝相關的Spring插件(Spring Tool Suite),安裝後就能夠直接在建立的時候選擇Spring的模板項目便可,相關介紹文章可參考: