前言
Spring的核心之一:AOP
一、AOP概述和原理
用的依賴(包括上篇文章講訴的IOC依賴):
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.0.9</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>6.0.9</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>6.0.9</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>6.0.9</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>6.0.9</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.19</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>net.sourceforge.cglib</groupId>
<artifactId>com.springsource.net.sf.cglib</artifactId>
<version>2.1.3</version>
</dependency>
1.概述和原理
- AOP:面向切面編程。利用 AOP 可以對業(yè)務(wù)邏輯的各個部分進行隔離,從而使得業(yè)務(wù)邏輯各部分之間的耦合度降低,提高程序的可重用性,同時提高了開發(fā)的效率。通俗來說就是在不修改代碼的情況下添加新的功能。
- 底層通過動態(tài)代理實現(xiàn):
(1)有接口情況:使用JDK動態(tài)代理,即創(chuàng)建接口實現(xiàn)類的代理對象。
(2)無接口情況:使用CGLIB動態(tài)代理,即創(chuàng)建當(dāng)前類子類的代理對象。
2.JDK動態(tài)代理實例
核心:
- 通過 java.lang.reflect.Proxy類 的 newProxyInstance方法 創(chuàng)建代理類。
- newProxyInstance方法
參數(shù)一:類加載器
參數(shù)二:所增強方法所在的類,這個類實現(xiàn)的接口,支持多個接口
參數(shù)三:實現(xiàn)InvocationHandle接口,重寫invoke方法來添加新的功能
模擬代理:
UserDao、UserDaoImpl類:
package com.dragon.springaop;
public interface UserDao {
public int add(int a,int b);
public String update(String id);
}
================================================
package com.dragon.springaop;
public class UserDaoImpl implements UserDao{
@Override
public int add(int a,int b) {
System.out.println("add方法執(zhí)行了...");
return a+b;
}
@Override
public String update(String id) {
System.out.println("update方法執(zhí)行了...");
return id;
}
}
JDKProxy類(通過 java.lang.reflect.Proxy類 的 newProxyInstance方法創(chuàng)建代理類):
package com.dragon.springaop;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
public class JDKProxy {
public static void main(String[] args) {
Class[] interfaces={UserDao.class};//創(chuàng)建接口實現(xiàn)類代理對象
UserDaoImpl userDao=new UserDaoImpl();
UserDao dao=(UserDao)Proxy.newProxyInstance(JDKProxy.class.getClassLoader(),interfaces,new UserDaoProxy(userDao)) ;
int result=dao.add(1,2);
System.out.println("result:"+result);
}
}
class UserDaoProxy implements InvocationHandler{
//創(chuàng)建的是誰的代理對象,把誰傳遞過來
//有參構(gòu)造傳遞
private Object obj;
public UserDaoProxy (Object obj){
this.obj=obj;
}
//參數(shù)含義:代理對象、方法、參數(shù)
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法之前執(zhí)行...."+ method.getName()+":傳遞參數(shù)...."+ Arrays.toString(args));
Object res=method.invoke(obj,args);
System.out.println("方法之后執(zhí)行...."+obj);
return res;
}
}
其實JDK動態(tài)代理本質(zhì)就是在原來要增強的方法前面或后面增加一些邏輯處理等,而不修改源代碼。從上面運行結(jié)果看以看出,add方法執(zhí)行結(jié)果是3,但是在執(zhí)行add方法執(zhí)行多一些語句輸出,這些就是我們增加的邏輯處理部分。其中method.invoke()就是模擬的原add方法執(zhí)行,而前后的輸出語句是模擬的增強的代碼
二、基于AspectJ實現(xiàn)的AOP操作
1.什么是AspectJ
AspectJ不是Spring組成部分,獨立AOP框架,一般把AspectJ和Spring框架一起使用進行AOP操作。
2.AOP相關(guān)術(shù)語
- 連接點:類中可以被增強的方法,稱為連接點。
- 切入點:實際被增強的方法,稱為切入點。
-
通知:增強的那一部分邏輯代碼。通知有多種類型:
(1)前置通知:增強部分代碼在原代碼前面。
(2)后置通知:增強部分代碼在原代碼后面。
(3)環(huán)繞通知:增強部分代碼既有在原代碼前面,也有在原代碼后面。
(4)異常通知:原代碼發(fā)生異常后才會執(zhí)行。
(5)最終通知:類似與finally那一部分 - 切面:指把通知應(yīng)用到切入點這一個動作。
3.切點表達(dá)式
- 語法:execution([權(quán)限修飾符] [返回類型] [類全路徑] [方法名稱] [參數(shù)列表])
- 例1:對 com.atguigu.dao.BookDao 類里面的 add 進行增強
execution(* com.auguigu.dao.BookDao.add(..))
- 例2:對 com.atguigu.dao.BookDao 類里面的所有的方法進行增強
execution(* com.atguigu.dao.BookDao.*(..))
- 例3:對 com.atguigu.dao 包里面所有類,類里面所有方法進行增強
execution(* com.atguigu.dao.*.* (..))
3.基于注解方式實現(xiàn)
(1)實例:
User:
注解方式創(chuàng)建User對象user
package com.dragon.springaop.anno;
import org.springframework.stereotype.Component;
@Component
public class User {
public void add(){
System.out.println("add....");
}
}
UserProxy類(先忽略@Order注解):
注解方式創(chuàng)建UserProxy對象userProxy
@Aspect定義UserProxy為切面類(即指把通知應(yīng)用到切入點的類)
package com.dragon.springaop.anno;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.junit.jupiter.api.Order;
import org.springframework.stereotype.Component;
@Component
@Aspect//定義切面類
@Order(3)
public class UserProxy {
@Before(value = "execution(* com.dragon.springaop.anno.User.add(..))")
public void before(){
System.out.println("before.....");
}
@AfterReturning(value = "execution(* com.dragon.springaop.anno.User.add(..))")
public void afterReturning(){
System.out.println("afterReturning....");
}
@After(value = "execution(* com.dragon.springaop.anno.User.add(..))")
public void after(){
System.out.println("after....");
}
@AfterThrowing(value = "execution(* com.dragon.springaop.anno.User.add(..))")
public void afterThrowing(){
System.out.println("afterThrowing....");
}
@Around(value = "execution(* com.dragon.springaop.anno.User.add(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint)throws Throwable{
System.out.println("環(huán)繞之前....");
proceedingJoinPoint.proceed();
System.out.println("環(huán)繞之后....");
}
}
spring配置文件:
<?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.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--開啟組件掃描-->
<context:component-scan base-package="com.dragon.springaop.anno"/>
<!--開啟AspectJ生成代理對象-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
測試:
ApplicationContext context=new ClassPathXmlApplicationContext("beanaop1.xml");
User user=context.getBean("user", User.class);
user.add();
可以看出Befoe(前置通知,在add前運行)、AfterRetruning(后置通知或返回通知,在add返回值后運行)、AfterThrowing(異常通知,在add出現(xiàn)異常后,這個沒有運行,因為沒異常,大家在add內(nèi)添加 int i=1/0 測試一下,不在演示)、After(最終通知,在add執(zhí)行完后)、Around(環(huán)繞通知,在add前和后)的運行時期。
(2)切入點提取
上面的UserProxy 類中有很多重復(fù)的切入點表達(dá)式,可以進行公共提取
@Pointcut(value = "execution(* com.dragon.springaop.anno.User.add(..))")
public void pointDemo(){
}
//前置通知
@Before(value="pointDemo()")
public void before(){
System.out.println("before....");
}
(3)設(shè)置增強類優(yōu)先級
上面的實例只有UserProxy一個增強類,當(dāng)有多個增強類時,可以使用@Order(數(shù)值)設(shè)置優(yōu)先級執(zhí)行。(數(shù)字越小優(yōu)先級越高)
在上訴例子中多創(chuàng)建一個PeopleProxy類,設(shè)置@Order:
package com.dragon.springaop.anno;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.junit.jupiter.api.Order;
import org.springframework.stereotype.Component;
@Component
@Aspect
@Order(1)
public class PersionProxy {
@Before(value = "execution(* com.dragon.springaop.anno.User.add(..))")
public void persionBefore(){
System.out.println("persionBefore.....");
}
}
上面的UserProxy類中我設(shè)置的是Order(3),所以PeopleProxylei類先運行。
(4)完全注解開發(fā)
AOPConfig類:
package com.dragon.springaop.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@ComponentScan(basePackages = {"com.dragon.springaop"})
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AopConfig {
}
Book、BookProxy:
package com.dragon.springaop.aopxml;
import org.springframework.stereotype.Component;
@Component
public class Book {
public void buy(){
System.out.println("buy......");
}
}
========================================
package com.dragon.springaop.aopxml;
public class BookProxy {
public void before(){
System.out.println("before....");
}
}
測試:
ApplicationContext context=new AnnotationConfigApplicationContext(AopConfig.class);
Book book=context.getBean("book", Book.class);
book.buy();
4.基于xml配置文件實現(xiàn)(了解)
Book、BookProxy:
package com.dragon.springaop.aopxml;
import org.springframework.stereotype.Component;
public class Book {
public void buy(){
System.out.println("buy......");
}
}
========================================
package com.dragon.springaop.aopxml;
public class BookProxy {
public void before(){
System.out.println("before....");
}
}
spring配置文件:
<bean id="book" class="com.dragon.springaop.aopxml.Book"></bean>
<bean id="bookProxy" class="com.dragon.springaop.aopxml.BookProxy"></bean>
<!-- 配置aop增強-->
<aop:config>
<!-- 切入點-->
<aop:pointcut id="p" expression="execution(* com.dragon.springaop.aopxml.Book.buy(..))"/>
<!-- 配置切面-->
<aop:aspect ref="bookProxy">
<!-- 增強作用在具體的方法上-->
<aop:before method="before" pointcut-ref="p"></aop:before>
</aop:aspect>
</aop:config>
測試:文章來源:http://www.zghlxwxcb.cn/news/detail-604100.html
ApplicationContext context=new ClassPathXmlApplicationContext("beanaop2.xml");
Book book=context.getBean("book", Book.class);
book.buy();
總結(jié)
以上就是AOP的講解。文章來源地址http://www.zghlxwxcb.cn/news/detail-604100.html
到了這里,關(guān)于Spring之AOP(帶你一篇文章搞定AOP)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!