尚矽谷AOP
AOP(概念)
1.什麼是AOP
(1)面向切面程式設計(方面),利用 AOP 可以對業務邏輯的各個部分進行隔離,進而使得 業務邏輯各部分之間的耦合度降低,提高程式的可重用性,同時提高了開發的效率。
(2)通俗描述:不通過修改源代碼方式,在主幹功能裡面添加新功能
(3)使用登入例子說明 AOP
AOP(底層原理)
-
AOP底層使用動态代理
(1)有兩種情況動态代理
第一種 有接口情況,使用JDK動态代理
- 建立接口實作類的代理對象,增強類的方法
- 建立目前類子類代理對象,增強類的方法
-
AOP(JDK動态代理)
第一步 使用JDK動态代理,使用Proxy類方法建立對象
(1)調用newProxyInstance方法
-
class類 Proxy() 中 (查api)
static Object
傳回指定接口的代理類的執行個體,該接口将方法調用分派給指定的調用處理程式。newProxyInstance(ClassLoader loader, 類<?>[] interfaces, InvocationHandler h)
- 第一個參數,類加載器
- 第一個參數,增強方法所在的類,這個類實作的接口,支援多個接口
- 第一個參數,實作這個接口InvocationHandler,建立代理對象,寫增強的方法;
第二步:編寫JDK動态代碼
(1)建立接口,定義方法
(2)建立接口實作類,實作方法package com.atguigu.spring5.Dao; public interface UserDao { public int add(int a, int b); public String update(String id); }
(3)使用Proxy類建立接口的代理對象package com.atguigu.spring5.Dao.impl; import com.atguigu.spring5.Dao.UserDao; public class UserDaoImpl implements UserDao { @Override public int add(int a, int b) { return a+b; } @Override public String update(String id) { return id; } }
- 建立代理對象的兩種方式
//匿名内部類 Class[] interfaces={UserDao.class}; Proxy.newProxyInstance(JDKProxy.class.getClassLoader() , interfaces, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return null; } });
//建立對象實作接口 package com.atguigu.spring5.test; import com.atguigu.spring5.Dao.UserDao; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class JDKProxy { public static void main(String[] args) { //建立是實作類類的代理對象 Class[] interfaces={UserDao.class}; Proxy.newProxyInstance(JDKProxy.class.getClassLoader() , interfaces, new UserDaoProxy() ); } } //建立代理對象代碼 class UserDaoProxy implements InvocationHandler{ //寫增強的邏輯 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return null; } }
- 代碼
package com.atguigu.spring5.test; import com.atguigu.spring5.Dao.UserDao; import com.atguigu.spring5.Dao.impl.UserDaoImpl; 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}; 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{ //1 把建立的是誰的代理對象,把誰傳遞過來 private Object obj; //有參數構造傳遞 public UserDaoProxy(Object obj){ this.obj=obj; } //寫增強的邏輯 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //方法之前做一個處理 System.out.println("方法之前執行..."+method.getName()+":傳遞的參數.."+ Arrays.toString(args)); //被增強的方法執行 Object res=method.invoke(obj,args); //方法之後做一個處理 System.out.println("方法之後行..."+obj); return res; } }
-
-
AOP術語
(1)連接配接點
- 類的那些方法可以被增強,這些方法為連接配接點
- 實際被真正增強的方法,稱為切入點
- 實際增強的邏輯部分稱為通知(增強)
-
通知有多種類型
(1)前置通知
(2)後置通知
(3)環繞通知
(4)異常通知
(5)最終通知
- 把通知應用到切入點過程
AOP操作(準備)
-
spring架構一般都是基于AspectJ實作AOP操作
(1)什麼是AspectJ
*AspectJ不是Spring組成部分,獨立AOP架構,一般吧AspectJ和Spring架構一起使用,進行AOP操作
-
基于AspectJ實作AOP操作
(1)基于xml配置檔案實作
(2)基于注解方式實作(使用)
- 在項目工程裡面引入AOP相關依賴
-
切入點表達式
(1)切入點表達式的作用:知道對哪個類裡面的哪個方法進行增強
(2)文法結構:
- execution([權限修飾符] [ 傳回類型] [ 類的全路徑] [方法名稱] ([參數清單]) )
-
舉例1:對com.atguigu.dao.BookDao類裡面的add進行增強
execution(*com.atguigu.dao.BookDao.add(…) )
-
舉例2:對com.atguigu.dao.BookDao類裡面的所有方法進行增強
execution(com.atguigu.dao.BookDao.(…) )
-
舉例三:對 com.atguigu.dao 包裡面所有類,類裡面所有方法進行增強
execution(* com.atguigu.dao.* .* (…))
AOP操作(AspectJ注解)
- 建立類,在類裡面定義方法
package com.atguigu.spring5.aopanno; //被增強類 public class User { public void add(){ System.out.println("add........."); } }
-
建立增強類(編寫你增強的邏輯)
(1)在增強類裡面,建立方法,讓不同的方法代表不同的通知類型
//增強類 public class UserProxy { //前置通知 public void before(){ System.out.println("before......."); } }
- 進行通知的配置
- (1)在spring 配置檔案中,開啟注解掃描
(2)使用注解建立User和UserProxy對象<?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:p="http://www.springframework.org/schema/p" 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/aop http://www.springframework.org/schema/aop/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-aop.xsd"> <!--開啟注解的掃描--> <context:component-scan base-package="com.atguigu.spring5.aopanno"></context:component-scan> </beans>
package com.atguigu.spring5.aopanno; import org.springframework.stereotype.Component; //被增強類 @Component public class User { public void add(){ System.out.println("add........."); } }
(3)在增強類上面添加注解@Aspectpackage com.atguigu.spring5.aopanno; import org.springframework.stereotype.Component; //增強類 @Component public class UserProxy { //前置通知 public void before(){ System.out.println("before......."); } }
(4)在spring配置檔案開啟生成代理對象package com.atguigu.spring5.aopanno; import org.aspectj.lang.annotation.Aspect; import org.springframework.stereotype.Component; //增強類 @Component @Aspect //生成代理對象 public class UserProxy { //前置通知 public void before(){ System.out.println("before......."); } }
<?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:p="http://www.springframework.org/schema/p" 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/aop http://www.springframework.org/schema/aop/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-aop.xsd"> <!--開啟注解的掃描--> <context:component-scan base-package="com.atguigu.spring5.aopanno"></context:component-scan> <!--開啟Aspectj代理對象--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>
- (1)在spring 配置檔案中,開啟注解掃描
-
配置不同類型的通知
(1)在增強類的裡面,在作為通知方法上添加通知類型注解,使用切入點表達式
package com.atguigu.spring5.aopanno; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; //增強類 @Component @Aspect //生成代理對象 public class UserProxy { //前置通知 //before注解作為表示為前置通知 @Before(value="execution(* com.atguigu.spring5.aopanno.User.add(..))") public void before(){ System.out.println("before......."); } @After(value="execution(* com.atguigu.spring5.aopanno.User.add(..))") public void after(){ System.out.println("after......."); } @AfterReturning(value="execution(* com.atguigu.spring5.aopanno.User.add(..))") public void afterReturning(){ System.out.println("afterReturning......."); } //異常通知 @AfterThrowing(value="execution(* com.atguigu.spring5.aopanno.User.add(..))") public void afterThrowing(){ System.out.println("AfterThrowing......."); } //環繞通知. @Around(value="execution(* com.atguigu.spring5.aopanno.User.add(..))") public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{ System.out.println("環繞之前......."); //被增強的方法執行 proceedingJoinPoint.proceed(); System.out.println("環繞之後......."); } }
- 相同的切入點抽取
package com.atguigu.spring5.aopanno; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; //增強類 @Component @Aspect //生成代理對象 public class UserProxy { //相同的切入點進行抽取 @Pointcut(value="execution(* com.atguigu.spring5.aopanno.User.add(..))" ) public void pointdemo(){ } //前置通知 //before注解作為表示為前置通知 @Before(value="pointdemo()") public void before(){ System.out.println("before......."); } @After(value="pointdemo()") public void after(){ System.out.println("after......."); } @AfterReturning(value="pointdemo()") public void afterReturning(){ System.out.println("afterReturning......."); } //異常通知 @AfterThrowing(value="pointdemo()") public void afterThrowing(){ System.out.println("AfterThrowing......."); } //環繞通知. @Around(value="pointdemo()") public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{ System.out.println("環繞之前......."); //被增強的方法執行 proceedingJoinPoint.proceed(); System.out.println("環繞之後......."); } }
-
有多個增強類對同一個方法進行增強,可以設定增強類的優先級
(1)在增強類上面添加注解@Order(數字類型值),數字類型越小,優先級越高
@Component @Aspect @Order(1) public class PersonProxy {
-
完全注解開發
(1)建立配置類,不需要建立xml配置檔案
@Configuration @ComponentScan(basePackages = {"com.atguigu"}) @EnableAspectJAutoProxy(proxyTargetClass = true) public class configAop {
AOP操作(AspectJ配置檔案)
- 建立兩個類,增強類和被增強類,建立方法
- 在spring配置檔案中建立兩個類對象
<!--建立兩個類的對象--> <bean id="book" class="com.atguigu.spring5.aopxml.Book"></bean> <bean id="bookProxy" class="com.atguigu.spring5.aopxml.BookProxy"></bean>
- 在spring配置檔案中配置切入點
<!--配置 aop 增強--> <aop:config> <!--切入點--> <aop:pointcut id="p" expression="execution(* com.atguigu.spring5.aopxml.Book.buy(..))"/> <!--配置切面--> <aop:aspect ref="bookProxy"> <!--增強作用在具體的方法上--> <aop:before method="before" pointcut-ref="p"/> </aop:aspect>