AOP的理解以及实现
AOP
一、AOP概念
? AOP即面向切面編程。我們先來理解下為什么會需要AOP?
? 如果在編程時多個類中有重復(fù)出現(xiàn)的代碼,那么我們就應(yīng)該考慮將這些重復(fù)代碼抽取出來定義成一個抽象類,這種情況我們稱為縱向抽取。但是如果這些重復(fù)的代碼是分散在各個業(yè)務(wù)邏輯中的,比如事務(wù)控制中有大量的try-catch-finally代碼,這些重復(fù)代碼嵌套在主要的業(yè)務(wù)邏輯代碼中,我們用上面的方法就抽取不了,這時我們就可以通過橫向切割的方式,把這些重復(fù)代碼抽取到一個獨立的模塊中,在以后調(diào)用對應(yīng)的業(yè)務(wù)邏輯方法時,又將這些重復(fù)代碼橫向切入進去。從而達到讓業(yè)務(wù)邏輯類保持最初的單純。
? 我們用一個例子來說明。如下圖:是一個對數(shù)據(jù)庫進行操作的步驟:
? 1、連接數(shù)據(jù)庫
? 2、執(zhí)行SQL語句
? 3、是否有異常,有就回滾事務(wù),沒有就提交事務(wù)
? 4、關(guān)閉資源
? 我們可以看到每次對數(shù)據(jù)庫進行一次操作后,都會有如下的步驟,其中的1-3-4步驟都是重復(fù)的,而作為開發(fā)人員我們每次真正需要關(guān)心的只是步驟2的SQL語句執(zhí)行(因為這在每個操作中是不一樣的),而對于其他的我們則可以交由AOP去實現(xiàn)這些步驟,我們只需通過注解或者XML的配置來告訴AOP我們需要在那些類下的哪些方法中切入這些重復(fù)的代碼,這樣在編寫代碼時我們就只需關(guān)注核心邏輯代碼,而在運行時AOP會對我們需要加入重復(fù)代碼的方法進行攔截,并將這些重復(fù)代碼切入進去。
? 值得一提的是AOP并不是Spring框架特有的,Spring只是支持AOP編程的框架之一,且SpringAOP是一種基于方法攔截的AOP,也就是說Spring只能支持方法攔截的AOP。
二、AOP術(shù)語
1、切面(Aspect):切面由切點和增加(需要切入的代碼、方法也叫通知)組成,它既包括了橫切邏輯的定義,也包括了連接點的定義,SpringAOP就是將切面所定義的橫切邏輯織入到切面所制定的連接點中。
2、增強、通知(Advice):是切面的方法,也就是一段需要切入到目標(biāo)對象的代碼。根據(jù)他在代理對象真實方法調(diào)用前、后和邏輯分為以下幾種:
? 前置通知(before):在真實對象方法調(diào)用前執(zhí)行
? 后置通知(after):在真實對象方法調(diào)用后執(zhí)行
? 返回通知(afterReturning):在執(zhí)行業(yè)務(wù)代碼后無異常執(zhí)行
? 異常通知(afterThrowing):在執(zhí)行業(yè)務(wù)代碼發(fā)送異常后執(zhí)行
? 環(huán)繞通知(around):最強的通知,可以取代原對象方法。但使用也較復(fù)雜
3、切點(Pointcut):告訴SpringAOP在什么時候啟動攔截并織入對應(yīng)的流程中。
4、連接點(join point):連接點對應(yīng)的是一個具體的方法,比如通過切點的正則表達式去判斷那些方法是連接點。
5、織入(Weaving):織入就是將增強添加到目標(biāo)具體連接點上的過程。
三、基于純注解的AOP實現(xiàn):
主要步驟:
? 1、創(chuàng)建業(yè)務(wù)邏輯接口,實現(xiàn)接口
? 2、創(chuàng)建切面,切面中包含切點和需要切入的方法
? 3、創(chuàng)建AOP配置類,啟動Aspectj自動代理、掃描bean、生成切面類
? 4、測試AOP
1、創(chuàng)建一個接口 里面的方法就是我們的連接點,也就是我們需要攔截這個方法并將通知織入
package com.test.aop;public interface Student {void getName(); }2、創(chuàng)建這個接口的實現(xiàn)
package com.test.aop;import org.springframework.stereotype.Component;@Component public class StudentImpl implements Student {@Overridepublic void getName() {System.out.println("我叫唐鵬,我自豪");} }3、創(chuàng)建切面,并定義切點和邏輯方法
package com.test.aop;import org.aspectj.lang.annotation.*;@Aspect public class TestAspect {@Pointcut("execution(* com.test.aop.*.getName(..))")public void pointCut(){}@Before("pointCut()")public void before(){System.out.println("=====前置:小么小么小二郎喲,背著書包上學(xué)堂咯");}@After("pointCut()")public void after(){System.out.println("=====后置:終于放學(xué)了!");}@AfterReturning("pointCut()")public void afterReturning(){System.out.println("=====返回:今天真呀真高興");}@AfterThrowing("pointCut()")public void afterThrowing(){System.out.println("=====異常:oh 我考試考了0分,可不高興");} }4、創(chuàng)建AOP配置類
package com.test.aop;import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.EnableAspectJAutoProxy;//啟用Aspectj自動代理 @EnableAspectJAutoProxy //配置掃描的包 這是掃描bean的配置 @ComponentScan("com.test") public class AopConfig {//生成切面類,自定義的類里面設(shè)置了切點@Beanpublic TestAspect getTestAspect(){return new TestAspect();} }5、測試AOP
package com.test.aop;import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class TestMain {public static void main(String[] args) {//因為是采用注解所以使用AnnotationConfigAopApplicationContext來實現(xiàn)ApplicationContext ac = new AnnotationConfigApplicationContext(AopConfig.class);Student tp = (Student) ac.getBean("studentImpl");tp.getName();} }6、結(jié)果打印
=====前置:小么小么小二郎喲,背著書包上學(xué)堂咯 我叫唐鵬,我自豪 =====后置:終于放學(xué)了! =====返回:今天真呀真高興四、基于純XML開發(fā)AOP的方式:
基本步驟:
? 1、同樣先創(chuàng)建接口和接口實現(xiàn)類
? 2、創(chuàng)建切面類和切入方法,因為是采用XML的方式,所以這里不需要指明他是個切面類,也不需要指定切入方法對應(yīng)的切入點。,只需要生成對應(yīng)的方法和方法代碼即可。
? 3、創(chuàng)建spirng配置文件
? 4、測試
1、接口和實現(xiàn)同上,這里就不在給出。(注意如果是在不同包下使用相同類名,注意包的引用)
2、編寫切面類
package com.test.aopXml;public class TestAspect {public void before(){System.out.println("=====XML前置:小么小么小二郎喲,背著書包上學(xué)堂咯");}public void after(){System.out.println("=====XML后置:終于放學(xué)了!");}public void afterReturning(){System.out.println("=====XML返回:今天真呀真高興");}public void afterThrowing(){System.out.println("=====XML異常:oh 我考試考了0分,可不高興");}}3、spring的配置文件applicationContext.xml 注意放在resources源碼文件下面
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:context="http://www.springframework.org/schema/context"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd"><bean id ="student" class="com.test.aopXml.StudentImpl"/><bean id ="testAspect" class="com.test.aopXml.TestAspect"/><!--aop配置--><aop:config ><!--定義切面,和切點配置--><aop:aspect ref="testAspect"><!--定義需要重復(fù)使用的切點--><aop:pointcut id="pointCut" expression="execution(* com.test.aopXml.StudentImpl.*(..))"/><!--定義切點,method是指切面類中對應(yīng)的方法名--><aop:before method="before" pointcut-ref="pointCut"/><aop:after method="after" pointcut-ref="pointCut"/><aop:after-returning method="afterReturning" pointcut-ref="pointCut"/><aop:after-throwing method="afterThrowing" pointcut-ref="pointCut"/></aop:aspect></aop:config></beans>4、測試
package com.test.aopXml;import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;public class TestMain {public static void main(String[] args) {ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");Student student = (Student) ac.getBean("student");student.getName();} }5、測試結(jié)果
=====XML前置:小么小么小二郎喲,背著書包上學(xué)堂咯 我叫唐鵬,我自豪 =====XML后置:終于放學(xué)了! =====XML返回:今天真呀真高興五、注解和XML混合開發(fā)的方式
注解和XML混合使用:切點信息定義在切面類,xml中只需要開啟對aspectj自動代理和配置掃描就好
步驟如下:
? 1、同樣給出接口和實現(xiàn)類
? 2、編寫切面類,切面類需要加入容器,讓ioc管理
? 3、配置applicationContext1.xml文件
? 4、測試
1、接口和實現(xiàn)同上
2、編寫切面類,這里切面類加上了@Component注解 而在使用純注解時是沒有加上的
package com.test.aop;import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component;@Component @Aspect public class TestAspect {@Pointcut("execution(* com.test.aop.*.getName(..))")public void pointCut(){}@Before("pointCut()")public void before(){System.out.println("=====混合前置:小么小么小二郎喲,背著書包上學(xué)堂咯");}@After("pointCut()")public void after(){System.out.println("=====混合后置:終于放學(xué)了!");}@AfterReturning("pointCut()")public void afterReturning(){System.out.println("=====混合返回:今天真呀真高興");}@AfterThrowing("pointCut()")public void afterThrowing(){System.out.println("=====混合異常:oh 我考試考了0分,可不高興");} }3、applicationContext1.xml文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:context="http://www.springframework.org/schema/context"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd"><!--此配置是為了讓spring知道我們配置了切面,讓他掃描bean的時候注意到切面類--><aop:aspectj-autoproxy/><!--掃描bean--><context:component-scan base-package="com.test.aop"/></beans>4、測試
package com.test.aop;import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;public class TestMain {public static void main(String[] args) {ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext1.xml");Student student = (Student) ac.getBean("studentImpl");student.getName();} }5、測試結(jié)果
=====混合前置:小么小么小二郎喲,背著書包上學(xué)堂咯 我叫唐鵬,我自豪 =====混合后置:終于放學(xué)了! =====混合返回:今天真呀真高興總結(jié)
以上是生活随笔為你收集整理的AOP的理解以及实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 读书笔记软件调试之道 :问题的核心-如何
- 下一篇: 一个算法笨蛋的12月leetCode刷题