莫道君行早,更有早行人
本篇主要是簡單的嘗試一下Spring的兩大功能,來感覺一下Spring的強大,後面將進行更加詳細的介紹。java
咱們都知道,Spring兩大核心功能就是控制反轉/依賴注入、面向切面編程。下面介紹一下兩大功能。git
Don't call me , I will call you!
控制反轉(Inversion of Control)/依賴注入(Dependency Injection),簡稱IoC/DI.正則表達式
控制反轉不是一種技術,而是一種設計思想:將原來程序須要什麼對象本身建立 轉變爲 須要什麼對象向IoC容器獲取,建立對象的工做由原來程序自身控制,反轉到了由IoC容器進行建立。把相關的控制權進行了反轉,反轉給了Spring IoC容器。spring
DI:Dependency Injection。即依賴注入。對象(組件)之間的依賴關係由IoC容器來進行決定。express
好比:編程
在UserController中須要調用UserService(暫不考慮接口設計)。則在UserController中須要其自身經過new來建立UserService對象。網絡
以下:學習
UserService:測試
public class UserService{ private PrintStream printStream; public UserService(PrintStream printStream){ this.printStream = printStream; } public void sayHello(){ printStream.println("sayHello!") } }
UserController:this
public class UserController{ private UserService userService; public UserController(){ this.userService = new UserService(System.out); } public void sayHi(){ userService.sayHello(); } }
在Spring中,程序的對象控制權由其自身反轉到了Spring容器,也就是不須要應用程序來new對象。既然不須要應用程序自身來建立Bean了,那麼程序在運行的過程當中,Bean從何而來呢?此時就是DI的show time了。
Spring中的DI正是來實現IoC的一種方式:Spring容器負責維護對象(Bean)之間的依賴關係,並經過DI來向對象中注入其所依賴的對象。
下面使用Spring的方式來設計:
public class UserService{ private PrintStream printStream; public UserService(PrintStream printStream){ this.printStream = printStream; } public void sayHello(){ printStream.println("sayHello!") } } public class UserController{ private UserService userService; public UserController(UserService userService){ this.userService = userService; } public void sayHi(){ userService.sayHello(); } }
spring.xml文件:
<?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.xsd"> <bean id="userService" class="com.ooyhao.spring.bean.UserService"> <constructor-arg value="#{T(System).out}"/> </bean> <bean id="UserController" class="com.ooyhao.spring.bean.UserController"> <constructor-arg ref="userService"/> </bean> </beans>
測試類:
public void testXml(){ ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml"); UserController userController = ctx.getBean(UserController.class); userController.sayHi(); }
若是須要面向接口設計的話,接口因爲沒法實例化,因此在編碼的時候必須指定具體的實現類,如此一來,致使沒法自由動態的切換實現類,耦合度過高。而Spring xml方式的話,實現類鬆耦合,簡化了開發,後期若是須要修改的話,直接修改xml文件,不用修改代碼。
SpringConfig配置類:
public class SpringConfig{ @Bean public UserService userService(){ UserService userService = new UserService(System.out); return userService; } @Bean public UserController userController(){ UserController userController = new UserController(userService()); return userController; } }
測試類:
public void testJavaConfig(){ AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class); UserController userController = ctx.getBean(UserController.class); userController.sayHi(); }
::: tip
確實Spring xml文件實現了鬆耦合,可是實際項目中能夠發現,每每xml不多修改。因此,SpringBoot又主張Java配置類的方式,可是Java配置類的絕大部分都是由Spring xml轉化過來的。因此,不論是Java配置類方式仍是Spring xml文件方式都有必要了解,固然我以爲,因爲爲了後期更好的學習和使用SpringBoot,能夠以Java配置類方式爲主。
:::
參考自:阿古拉斯啦啦 http://www.javashuo.com/article/p-hfyrskjs-mu.html
Aop:是Aspect oriented Programming的縮寫,即面向切面編程。經過預編譯方式和運行期動態代理實現程序功能的統一維護的一種技術。AOP(面向切面編程)是OOP(面向對象編程)的一種補充,而不是一種替代品。利用AOP能夠對業務邏輯的各個部分進行隔離,從而下降各個模塊之間的耦合度,簡化維護。常見使用AOP的場景:事務控制,日誌管理,權限控制等等。
下面先用幾張圖熟悉一下AOP是怎麼回事。(圖片來源於網絡)
看完了上面的理論部分知識, 我相信仍是會有很多朋友感受到 AOP 的概念仍是很模糊, 對 AOP 中的各類概念理解的還不是很透徹. 其實這很正常, 由於 AOP 中的概念是在是太多了, 我當時也是花了老大勁才梳理清楚的.
下面我以一個簡單的例子來比喻一下 AOP 中 Aspect, Joint point, Pointcut 與 Advice之間的關係.
讓咱們來假設一下, 從前有一個叫爪哇的小縣城, 在一個月黑風高的晚上, 這個縣城中發生了命案. 做案的兇手十分狡猾, 現場沒有留下什麼有價值的線索. 不過萬幸的是, 剛從隔壁回來的老王剛好在這時候無心中發現了兇手行兇的過程, 可是因爲天色已晚, 加上兇手蒙着面, 老王並無看清兇手的面目, 只知道兇手是個男性, 身高約七尺五寸. 爪哇縣的縣令根據老王的描述, 對守門的士兵下命令說: 凡是發現有身高七尺五寸的男性, 都要抓過來審問. 士兵固然不敢違背縣令的命令, 只好把進出城的全部符合條件的人都抓了起來.
來讓咱們看一下上面的一個小故事和 AOP 到底有什麼對應關係.
首先咱們知道, 在 Spring AOP 中 Joint point 指代的是全部方法的執行點, 而 point cut 是一個描述信息, 它修飾的是 Joint point, 經過 point cut, 咱們就能夠肯定哪些 Joint point 能夠被織入 Advice. 對應到咱們在上面舉的例子, 咱們能夠作一個簡單的類比, Joint point 就至關於 爪哇的小縣城裏的百姓,pointcut 就至關於 老王所作的指控, 即兇手是個男性, 身高約七尺五寸, 而 Advice 則是施加在符合老王所描述的嫌疑人的動做: 抓過來審問.
爲何能夠這樣類比呢?
AOP中的Joinpoint
能夠有多種類型:構造方法調用,字段的設置和獲取,方法的調用,方法的執行,異常的處理執行,類的初始化。也就是說在AOP的概念中咱們能夠在上面的這些Joinpoint
上織入咱們自定義的Advice
,可是在Spring中卻沒有實現上面全部的joinpoint
,確切的說,Spring只支持方法執行類型的Joinpoint
。
Advice 的類型
before advice
, 在 join point
前被執行的 advice
. 雖然 before advice
是在 join point
前被執行, 可是它並不可以阻止 join point
的執行, 除非發生了異常(即咱們在 before advice
代碼中, 不能人爲地決定是否繼續執行 join point
中的代碼)after return advice
, 在一個 join point
正常返回後執行的 advice
after throwing advice
, 當一個 join point
拋出異常後執行的 advice
after(final) advice
, 不管一個 join point
是正常退出仍是發生了異常, 都會被執行的 advice
.around advice
, 在 join point
前和 joint point
退出後都執行的 advice
. 這個是最經常使用的 advice
.introduction
,introduction
能夠爲原有的對象增長新的屬性和方法。在Spring中,經過動態代理和動態字節碼技術實現了AOP
,這些內容,咱們將在之後進行講解。
下面分別使用XML和Java配置類實現AOP
目標類:
public class Person { public String sayHello(){ System.out.println("Person say Hello!"); return "sayHelloMethod"; } public String sayBye(){ System.out.println("Person say ByeBye!"); return "sayByeMethod"; } }
切面類:
public class XmlLoggerAspect { public void before(){ System.out.println("--->before"); } public void after(){ System.out.println("--->after"); } public void afterReturning(Object returnVal){ System.out.println("--->afterReturning : " + returnVal); } public void afterThrowing(Exception exception){ System.out.println("--->afterTrowing:"+exception.getMessage()); } public Object around(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("--->around before"); Object proceed = joinPoint.proceed(); System.out.println("around result : "+proceed); System.out.println("--->around after"); return proceed; } }
xml配置文件:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--業務類Target--> <bean id="person" class="com.ooyhao.spring.aop.Person"/> <!--切面類--> <bean id="loggerAspect" class="com.ooyhao.spring.aspect.XmlLoggerAspect"/> <aop:config> <aop:aspect ref="loggerAspect"> <!--public String com.ooyhao.spring.aop.Person.sayHello() * String com.ooyhao.spring.aop.Person.sayHello() * com.ooyhao.spring.aop.Person.sayHello() * *.sayHello() * *.say*() * *.say*(..) --> <aop:pointcut id="pointCut" expression="execution(* *.say*(..))"/> <aop:before method="before" pointcut-ref="pointCut"/> <aop:after method="after" pointcut-ref="pointCut"/> <aop:after-returning method="afterReturning" pointcut-ref="pointCut" returning="returnVal"/> <aop:after-throwing method="afterThrowing" pointcut-ref="pointCut" throwing="exception"/> <aop:around method="around" pointcut-ref="pointCut"/> </aop:aspect> </aop:config> </beans>
測試:
@Test public void testXmlAop(){ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-aop.xml"); Person bean = context.getBean(Person.class); bean.sayHello(); System.out.println("================="); bean.sayBye(); /** * * --->before * --->around before * Person say Hello! * around result : sayHelloMethod * --->around after * --->afterReturning : sayHelloMethod * --->after * ================= * --->before * --->around before * Person say ByeBye! * around result : sayByeMethod * --->around after * --->afterReturning : sayByeMethod * --->after */ //sayHello出現 int i = 1/0;時 /** * * --->before * --->around before * Person say Hello! * --->afterTrowing:/ by zero * --->after * * java.lang.ArithmeticException: / by zero * at com.ooyhao.spring.aop.Person.sayHello(Person.java:7) * * */ }
由上測試結果能夠看出:
before
-- > around before
--> target method
--> around after
--> afterReturning
--> after
before
--> around before
--> target method
--> afterTrowing
--> after
java配置方式的切面:
@Aspect @Component @EnableAspectJAutoProxy public class ConfigLoggerAspect { @Pointcut("execution(**.say*()))") public void pointCut(){} @Before("pointCut()") public void before(){ System.out.println("--->before"); } @After("pointCut()") public void after(){ System.out.println("--->after"); } @AfterReturning(value = "pointCut()", returning = "returnVal") public void afterReturning(Object returnVal){ System.out.println("--->afterReturning : " + returnVal); } @AfterThrowing(value = "pointCut()", throwing = "exception") public void afterThrowing(Exception exception){ System.out.println("--->afterTrowing:"+exception.getMessage()); } @Around(value = "pointCut()") public Object around(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("--->around before"); Object proceed = joinPoint.proceed(); System.out.println("around result : "+proceed); System.out.println("--->around after"); return proceed; } }
@Aspect
: 定義爲一個切面@Component
:定義爲一個Spring
組件@EnableAspectJAutoProxy
:開啓Aop
自動代理模式Java配置類:
@ComponentScan(basePackages = "com.ooyhao.spring") public class AopConfig { @Bean public Person person(){ return new Person(); } }
@ComponentScan(basePackages = "com.ooyhao.spring")
: 將前面的切面進行掃描成組件。測試類:
@Test public void testJavaConfigAop(){ AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AopConfig.class); Person bean = context.getBean(Person.class); bean.sayHello(); bean.sayBye(); }
:::tip
提示:
前面均使用的是AspectJ表達式,這樣能夠定位到有必定規律的目標方法,下降程序耦合,可是操做不是特別靈活,我的比較使用註解方式,能夠指定到某一個目標方法。
@pointcut("@annotation(com.sample.security.AdminOnly)")
// 匹配註解有AdminOnly註解的方法
:::
源碼地址: https://gitee.com/ooyhao/JavaRepo_Public/tree/master/Spring-in-Action
若是以爲不錯的話,那就關注一下小編哦!一塊兒交流,一塊兒學習