Spring框架學(xué)習(xí)之AOP詳解
一、概念
1.面向切面編程(方面),利用 AOP 可以對業(yè)務(wù)邏輯的各個部分進(jìn)行隔離,從而使得業(yè)務(wù)邏輯各部分之間的耦合度降低,提高程序的可重用性,同時提高了開發(fā)的效率。
2.通俗描述:不通過修改源代碼方式,在主干功能里面添加新功能
二、底層原理:動態(tài)代理
有兩種情況動態(tài)代理
2.1 有接口, JDK 動態(tài)代理
1.被代理的對象
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;
}
}
2.代理
package cn.zj.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import cn.zj.dao.UserDao;
import cn.zj.dao.impl.UserDaoImpl;
public class JDKProxy {
public static void main(String[] args) {
// 創(chuàng)建接口實現(xiàn)類代理對象
Class[] interfaces = { UserDao.class };
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);
}
}
//創(chuàng)建代理對象代碼
class UserDaoProxy implements InvocationHandler {
private Object target;// 目標(biāo)類
public UserDaoProxy(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 方法執(zhí)行之前
System.out.println("方法執(zhí)行之前.." + method.getName() + ";傳遞的參數(shù):" + Arrays.toString(args));
// 增強(qiáng)方法
Object res = method.invoke(target, args);
// 方法執(zhí)行之后
System.out.println("方法執(zhí)行之后.." + target);
return res;
}
}
3.打印
方法執(zhí)行之前..add;傳遞的參數(shù):[1, 2] add執(zhí)行 方法執(zhí)行之后..cn.zj.dao.impl.UserDaoImpl@6c629d6e result:3
2.2 無接口, CGLIB 動態(tài)代理
1.被代理的對象
public class Cat {
public void eat() {
System.out.println("貓吃魚");
}
}
2.代理
public class CglibProxy {
public static void main(String[] args) {
Cat c = new CglibProxy().createProxyObject(Cat.class);
c.eat();
}
// JDK代理是對對象做代理,cglib代理是對類做代理
public Cat createProxyObject(Class clazz) {
// 1.創(chuàng)建內(nèi)存中的動態(tài)類 Enhance
// 內(nèi)存中造出一個沒有名稱的動態(tài)類
Enhancer enhancer = new Enhancer();
// 2.現(xiàn)在的類最終完成原始類的功能,同時對其進(jìn)行功能的增強(qiáng),必須先具有原始類對應(yīng)的功能————繼承
enhancer.setSuperclass(clazz);
// 3.進(jìn)行功能的增強(qiáng)
// 設(shè)置了方法的調(diào)用攔截
// 設(shè)置具體的回調(diào)操作
Callback callback = new MethodInterceptor() {
// proxy:代理對象
// method:被攔截的方法對象
// args:調(diào)用參數(shù)
// methodProxy:
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy)
throws Throwable {
// 做增強(qiáng)
System.out.println("執(zhí)行前");
// Object obj=method.invoke(proxy, args);//這種執(zhí)行是代理類的方法,而不是目標(biāo)類的,會造成內(nèi)存溢出
// 以下的兩種都是執(zhí)行目標(biāo)類的方法
// Object obj = methodProxy.invokeSuper(proxy, args);//執(zhí)行目標(biāo)類的方法
// Object obj=method.invoke(proxy, args);
Object res = methodProxy.invokeSuper(proxy, args);
System.out.println("執(zhí)行后");
return res;
}
};
enhancer.setCallback(callback);
// 4.創(chuàng)建內(nèi)存中的全新的類的對象
Object proxyObj = enhancer.create();
return (Cat) proxyObj;
}
}
三、術(shù)語
1.連接點(Joinpoint)
類中的任意方法的運行時表示,可以簡單理解為類中的方法,也就是可以被增強(qiáng)的方法
2.切入點(Pointcut)
具有共性功能的方法的運行時表示,可以簡單理解為具有共性功能的方法,也就是實際被增強(qiáng)的方法
注意:切入點對應(yīng)的是被挖去了共性功能后的方法執(zhí)行時匹配斷言(格式)
3.通知/增強(qiáng)(Advice)
共性功能模塊化,可以簡單理解為將共性功能抽取出來制作成獨立的方法,實際增強(qiáng)的邏輯部分
類型:前置,后置,異常,最終,環(huán)繞
4.切面(Aspect)
切入點與通知的對應(yīng)關(guān)系,可以簡單理解為被抽取的共性功能與共性功能被抽取位置對應(yīng)的方法之間的關(guān)系,把通知應(yīng)用到切入點的過程
5.目標(biāo)對象(Target Object)
包含切入點的運行時對象,開發(fā)階段制作的是目標(biāo)對象對應(yīng)的類
6.AOP代理(AOP Proxy)
使用AOP的代理機(jī)制創(chuàng)建目標(biāo)對象運行時代理對象,完成原始的功能
注意:原始目標(biāo)對象已經(jīng)被挖去了共性功能,此時使用目標(biāo)對象執(zhí)行任務(wù)無法完成原始任務(wù),使用AOP代理機(jī)制,創(chuàng)建一個代理對象來完成原始目標(biāo)對象的功能
7.織入(Weaving)
是一個將通知功能加入原始字節(jié)碼的動態(tài)過程,將增強(qiáng)添加對目標(biāo)類具體連接點上的過程
Spring使用的是運行時織入機(jī)制
8.引入(Introduction)
一種特殊的機(jī)制,可以為一個類的字節(jié)碼動態(tài)的加入變量或方法
四、操作
4.1 Spring 框架一般都是基于 AspectJ 實現(xiàn) AOP
AspectJ 不是 Spring 組成部分,獨立 AOP 框架,一般把 AspectJ 和 Spirng 框架一起使
用,進(jìn)行 AOP 操作
4.2 實踐
1.jar包引入
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.3.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.3.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-expression -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>5.3.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.3.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-logging/commons-logging -->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.6</version>
</dependency>
<!-- @Resource注解需要 -->
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/net.sourceforge.cglib/com.springsource.net.sf.cglib -->
<dependency>
<groupId>net.sourceforge.cglib</groupId>
<artifactId>com.springsource.net.sf.cglib</artifactId>
<version>2.2.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aopalliance/com.springsource.org.aopalliance -->
<dependency>
<groupId>org.aopalliance</groupId>
<artifactId>com.springsource.org.aopalliance</artifactId>
<version>1.0.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/com.springsource.org.aspectj.weaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>com.springsource.org.aspectj.weaver</artifactId>
<version>1.6.8.RELEASE</version>
</dependency>
</dependencies>
2.切入點表達(dá)式
作用:知道對哪個類里面的哪個方法進(jìn)行增強(qiáng)
語法結(jié)構(gòu): execution([權(quán)限修飾符] [返回類型] [類全路徑] 方法名稱 )
舉例 1:對 com.zj.dao.BookDao 類里面的 add 進(jìn)行增強(qiáng)
execution(* com.zj.dao.BookDao.add(…))
舉例 2:對 com.zj.dao.BookDao 類里面的所有的方法進(jìn)行增強(qiáng)
execution(* com.zj.dao.BookDao.* (…))
舉例 3:對 com.zj.dao 包里面所有類,類里面所有方法進(jìn)行增強(qiáng)
execution(* com.zj.dao.. (…))
3.xml方式
1.創(chuàng)建類
package cn.zj.aop.xml;
public class Book {
public void buy() {
System.out.println("buy.............");
}
}
2.增強(qiáng)類
package cn.zj.aop.xml;
public class BookProxy {
public void before() {
System.out.println("before.........");
}
}
3.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"
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">
<!--創(chuàng)建對象-->
<bean id="book" class="cn.zj.aop.xml.Book"></bean>
<bean id="bookProxy" class="cn.zj.aop.xml.BookProxy"></bean>
<!--配置aop增強(qiáng)-->
<aop:config>
<!--切入點-->
<aop:pointcut id="p" expression="execution(* cn.zj.aop.xml.Book.buy(..))"/>
<!--配置切面-->
<aop:aspect ref="bookProxy">
<!--增強(qiáng)作用在具體的方法上-->
<aop:before method="before" pointcut-ref="p"/>
</aop:aspect>
</aop:config>
</beans>
4.注解方式
1.創(chuàng)建類
public class User {
public void add() {
System.out.println("add.......");
}
}
2.創(chuàng)建增強(qiáng)類
package cn.zj.aop.an;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
//增強(qiáng)的類
@Component
@Aspect //生成代理對象
public class UserProxy {
//前置通知
@Before(value = "execution(* cn.zj.aop.an.User.add(..))")
public void before() {
System.out.println("before.........");
}
//后置通知(返回通知)
@AfterReturning(value = "execution(* cn.zj.aop.an.User.add(..))")
public void afterReturning() {
System.out.println("afterReturning.........");
}
//最終通知
@After(value = "execution(* cn.zj.aop.an.User.add(..))")
public void after() {
System.out.println("after.........");
}
//異常通知
@AfterThrowing(value = "execution(* cn.zj.aop.an.User.add(..))")
public void afterThrowing() {
System.out.println("afterThrowing.........");
}
//環(huán)繞通知
@Around(value = "execution(* cn.zj.aop.an.User.add(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("環(huán)繞之前.........");
//被增強(qiáng)的方法執(zhí)行
proceedingJoinPoint.proceed();
System.out.println("環(huán)繞之后.........");
}
}
3.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"
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="cn.zj.aop.an"></context:component-scan>
<!-- 開啟Aspect生成代理對象-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
4.測試
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
User user = context.getBean("user", User.class);
user.add();
5.相同點抽取
//相同切入點抽取
@Pointcut(value = "execution(* cn.zj.aop.an.User.add(..))")
public void pointdemo() {
}
//前置通知
//@Before注解表示作為前置通知
@Before(value = "pointdemo()")
public void before() {
System.out.println("before.........");
}
6.有多個增強(qiáng)類多同一個方法進(jìn)行增強(qiáng),設(shè)置增強(qiáng)類優(yōu)先級
在增強(qiáng)類上面添加注解 @Order(數(shù)字類型值),數(shù)字類型值越小優(yōu)先級越高
@Component
@Aspect
@Order(1)
public class PersonProxy {
//后置通知(返回通知)
@Before(value = "execution(* cn.zj.aop.an.User.add(..))")
public void afterReturning() {
System.out.println("Person Before.........");
}
}
//增強(qiáng)的類
@Component
@Aspect //生成代理對象
@Order(2)
public class UserProxy {
到此這篇關(guān)于Spring框架學(xué)習(xí)之AOP詳解的文章就介紹到這了,更多相關(guān)Spring框架AOP內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
深入淺出重構(gòu)Mybatis與Spring集成的SqlSessionFactoryBean(上)
通常來講,重構(gòu)是指不改變功能的情況下優(yōu)化代碼,但本文所說的重構(gòu)也包括了添加功能。這篇文章主要介紹了重構(gòu)Mybatis與Spring集成的SqlSessionFactoryBean(上)的相關(guān)資料,需要的朋友可以參考下2016-11-11
java中進(jìn)制的轉(zhuǎn)換,Byte與16進(jìn)制的轉(zhuǎn)換方法
下面小編就為大家?guī)硪黄猨ava中進(jìn)制的轉(zhuǎn)換,Byte與16進(jìn)制的轉(zhuǎn)換方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-11-11
SpringCloud微服務(wù)多應(yīng)用腳手架的搭建與部署方式
這篇文章主要介紹了SpringCloud微服務(wù)多應(yīng)用腳手架的搭建與部署方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-07-07
Java wait和notifyAll實現(xiàn)簡單的阻塞隊列
這篇文章主要介紹了Java wait和notifyAll實現(xiàn)簡單的阻塞隊列,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2019-10-10

