AOP面向切面編程
aop:面向切面編程,采取的是橫向抽取機(jī)制仇哆,取代了傳統(tǒng)的縱向繼承體系重復(fù)性的代碼酪我,簡單的來說就是拓展功能的時(shí)候可以不通過修改源碼實(shí)現(xiàn)。
舉個(gè)例子,在最原始的機(jī)制中把沼,當(dāng)我們要給一個(gè)類中的方法添加某個(gè)功能的時(shí)候啊易,比如為了后期維護(hù)系統(tǒng)方便,要給方法添加日志打印的功能饮睬,這個(gè)時(shí)候只能通過修改源代碼的方式實(shí)現(xiàn)租谈,當(dāng)系統(tǒng)中有很多類的時(shí)候,修改源代碼就顯得費(fèi)時(shí)費(fèi)力了捆愁。
這個(gè)時(shí)候可以采用縱向的抽取機(jī)制割去,比如將所有的需要更改功能的類都繼承一個(gè)父類,在子類中調(diào)用父類的方法昼丑,這樣就少寫很多代碼呻逆。然而,這樣的機(jī)制仍然不是最方便的菩帝,如果父類中的方法發(fā)生改變咖城,那么調(diào)用父類方法的所有子類都要修改。
1.AOP的原理
aop采用的是橫向抽取機(jī)制呼奢,其底層使用動(dòng)態(tài)代理方式實(shí)現(xiàn)宜雀。在采用動(dòng)態(tài)代理方式時(shí),針對有接口和沒有接口的情況握础,分別采用不同的方式實(shí)現(xiàn)辐董。
1.針對有接口的情況,使用的是jdk的動(dòng)態(tài)代理禀综。實(shí)現(xiàn)類在實(shí)現(xiàn)接口的中間简烘,創(chuàng)建接口實(shí)現(xiàn)類的代理對象,這個(gè)代理對象和實(shí)現(xiàn)類是平級的定枷,并不是一個(gè)真正的對象夸研,只是實(shí)現(xiàn)和接口實(shí)現(xiàn)類相同的功能。
2.針對沒有接口的情況依鸥,使用的是cglib的動(dòng)態(tài)代理,創(chuàng)建該類的子類代理對象悼沈,在子類中調(diào)用父類的方法完成增強(qiáng)贱迟。
2.AOP操作術(shù)語
class UserService{
void add(User user){}
void update(User user){}
void delete(Integer userId){}
User getUserById(Integer userId){}
}
Joinpoint(連接點(diǎn)):在一個(gè)類中那些可以被增強(qiáng)的方法,這些方法稱之為連接點(diǎn)絮供,比如UserService中的四個(gè)方法衣吠,都可以稱之為連接點(diǎn),
Pointcut(切入點(diǎn)):在一個(gè)類中可以有多個(gè)連接點(diǎn)壤靶,但是只有實(shí)際被增強(qiáng)的方法才稱之為切入點(diǎn)缚俏。如果只對UserService中的add()方法進(jìn)行增強(qiáng),那么這個(gè)類中只有一個(gè)切入點(diǎn)。
Advice(通知/增強(qiáng)):增強(qiáng)的邏輯忧换,比如說要給add()方法增加日志打印的功能恬惯,那么這個(gè)功能就稱之為通知/增強(qiáng)。有前置通知亚茬、后置通知酪耳、異常通知、最終通知刹缝、環(huán)繞通知碗暗。
Aspect(切面):把增強(qiáng)應(yīng)用到具體方法上面的過程。比如說給add()方法增加日志打印的這個(gè)過程梢夯,就是切面言疗。
3.Spring中的aop操作@Aspectj(基于xml)
//被增強(qiáng)的類
public class BookService {
public void add() {
System.out.println("BookService.add()");
}
}
public class BookServiceAdvice {
//前置通知
public void before() {
System.out.println("before advice");
}
//后置通知
public void after() {
System.out.println("after advice");
}
//環(huán)繞通知
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
//在方法之前執(zhí)行的方法
System.out.println("before method advice");
proceedingJoinPoint.proceed();
//在方法之后執(zhí)行的方法
System.out.println("after method advice");
}
}
1.導(dǎo)入jar包
- aopallinace-1.0.jar
- aspectjweaver-1.8.7.jar
- spring-aop-4.2.4.RELEASE.jar
- spring-aspects-4.2.4.RELEASE.jar
或者添加pom依賴:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
2.在Spring配置文件中導(dǎo)入約束
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
3.幾種常見的常用的表達(dá)式
execution(<訪問修飾符>?<返回類型><方法名>(<參數(shù)>)<異常>)
execution(* com.aop.BookService.add(..)) ?*表示匹配所有的方法修飾符,..表示參數(shù)
execution(* com.aop.BookService.*(..)) ?第一個(gè)*表示匹配所有的方法修飾符,第二個(gè)*表示匹配該類下的所有方法
execution(* *.*(..))?表示對所有的方法進(jìn)行增強(qiáng)
execution(* add*(..))?表示對所有的以add開頭的方法進(jìn)行增強(qiáng)
4.配置對象(基于xml)
<bean id="bookService" class="com.aop.BookService"></bean>
<bean id="bookServiceAdvice" class="com.aop.BookServiceAdvice"></bean>
5.配置aop操作
<aop:config>
<!--配置切入點(diǎn)-->
<aop:pointcut expression="execution(* com.aop.BookService.add(..))" id="bookService_add_pointcut"/>
<!--配置切面-->
<aop:aspect ref="bookServiceAdvice">
<!--配置增強(qiáng)類型-->
<!--前置通知-->
<aop:before method="before" pointcut-ref="bookService_add_pointcut"/>
<!--后置通知-->
<aop:after-returning method="after" pointcut-ref="bookService_add_pointcut"/>
<!--環(huán)繞通知-->
<aop:around method="around" pointcut-ref="bookService_add_pointcut"/>
</aop:aspect>
</aop:config>
6.測試
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
BookService bookService = (BookService) context.getBean("bookService");
bookService.add();
運(yùn)行結(jié)果:
before advice
before method advice
BookService.add()
after method advice
after advice
4.Spring中的aop操作@Aspectj(基于注解)
在類上面加注解,導(dǎo)入jar包或者添加依賴颂砸,在配置文件中導(dǎo)入約束
@Service("bookService")
public class BookService {...}
@Service("bookServiceAdvice")
public class BookServiceAdvice {...}
1.在配置文件中開啟aop操作
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
2.在增強(qiáng)類上面添加注解
@Service("bookServiceAdvice")
@Aspect
public class BookServiceAdvice {
//前置通知
@Before(value = "execution(* com.aop.BookService.add(..))")
public void before() {
System.out.println("before advice");
}
//后置通知
@After(value = "execution(* com.aop.BookService.add(..))")
public void after() {
System.out.println("after advice");
}
//環(huán)繞通知
@Around(value = "execution(* com.aop.BookService.add(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
//在方法之前執(zhí)行的方法
System.out.println("before method advice");
proceedingJoinPoint.proceed();
//在方法之后執(zhí)行的方法
System.out.println("after method advice");
}
}