<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Spring5.0源碼學習系列之Spring AOP簡述

          共 28364字,需瀏覽 57分鐘

           ·

          2020-12-05 23:47

          走過路過不要錯過

          點擊藍字關(guān)注我們


          在學習Spring AOP源碼之前,您是否對AOP有足夠熟悉的理解?在對應(yīng)用都不熟悉之前就去學習源碼,肯定是很難理解的,所以本文先不描述源碼的實現(xiàn),先通過本篇博客了解熟悉Spring AOP,然后再學習源碼

          1、什么是AOP技術(shù)?

          引用Spring官網(wǎng)對AOP技術(shù)的概述:

          Aspect-Oriented Programming (AOP) complements Object-Oriented Programming (OOP) by providing another way of thinking about program structure. The key unit of modularity in OOP is the class, whereas in AOP the unit of modularity is the aspect. Aspects enable the modularization of concerns such as transaction management that cut across multiple types and objects. (Such concerns are often termed crosscutting concerns in AOP literature.)

          挑重點來說,所謂AOP(Aspect-Oriented Programming)也即面向方面的編程,是指通過跨領(lǐng)域關(guān)注點的分離來實現(xiàn)模塊化的一種面向?qū)ο蠹夹g(shù)。

          • 跨領(lǐng)域也即跨多種類型和對象的事務(wù)管理等等;

          • 關(guān)注點通常被稱為橫切關(guān)注點,OOP中模塊化的關(guān)鍵單元是類,而在AOP中模塊化是方面關(guān)注點通常被稱為橫切關(guān)注點

          2、AOP的本質(zhì)目的

          AOP本質(zhì):在不改變原有業(yè)務(wù)邏輯的情況下增強橫切邏輯,這個橫切邏輯可以是權(quán)限校驗邏輯、日志監(jiān)控、事務(wù)控制等等

          AOP相關(guān)知識詳情可以參考:Spring AOP官方文檔

          3、AOP的相關(guān)術(shù)語

          名詞描述
          連接點(Joinpoint)連接點是一個程序的執(zhí)行,如方法的執(zhí)行或異常的處理過程中的一個點
          切入點(Pointcut)指的是將增強代碼織入到業(yè)務(wù)主線進來之后的連接點
          通知/增強(Advice)Advice可以翻譯為通知或者增強,指的是切面類中用于提供增強功能的方法
          目標對象(Target)指代理的目標對象,即被代理對象
          代理(Proxy)指一個類被AOP織入增強之后,產(chǎn)生的代理類,即代理對象
          織入(Weaving)指的是將增強(Advice)應(yīng)用到目標對象(Target)產(chǎn)生代理對象(Proxy)的過程。ps:AspectJ采用的是編譯期織入和類裝載期織入,而Spring AOP采用的是動態(tài)代理織入
          切面(Aspect)切面也就是AOP的關(guān)注點,也就是說是Advice代碼的關(guān)注點,切面是對上述概念的一個綜合。將這些Advice代碼放在一個類中,這個類就是切面類,切面類是跨多個類的關(guān)注點的模塊化類

          看了前面這些理論,您可能不是很理解,所以引用國外網(wǎng)站的圖例進行說明AOP概念,圖來自鏈接

          綜上所述,其實所謂的目的其實只是要鎖定在某個切入點(Pointcut)織入(Weaving)特定的增強邏輯(Advice)

          4、Spring AOP和AspectJ

          有了前面對AOP概念的概述之后,我們能夠大致理解AOP了,不過本文還是要理理Spring AOP和AspectJ的關(guān)系

          Spring官網(wǎng)給指出了Spring AOP和AspectJ的關(guān)系,官網(wǎng)明確指出其立場,表明Spring AOP不會和AspectJ項目競爭哪個項目能提供更成熟的AOP解決方案,Spring AOP和AspectJ是一種互補的關(guān)系,Spring AOP無縫結(jié)合了IOC和AspectJ,從而使Spring AOP能夠符合大部分的AOP需求,這是一種能提供代理對象的模式

          從官網(wǎng)也可以知道了Spring AOP和AspectJ的大致關(guān)系,其實這是兩種AOP的實現(xiàn)技術(shù),Spring AOP是集成了AspectJ部分功能,同時結(jié)合IOC實現(xiàn)的,AspectJ則是一種比較完善成熟的AOP解決方案,接著本文做下簡單對比

          • Spring AOP

            • Spring AOP是SpringFramework的組件,屬于Springframework的一個比較核心的功能,Spring AOP是結(jié)合了AspectJ和IOC實現(xiàn)的,提供了AspectJ的功能

            • Spring AOP 致力于解決的是企業(yè)級開發(fā)中最普遍的 AOP 需求(方法織入),Spring AOP不會和AspectJ競爭

            • Spring AOP 只能作用于 Spring 容器中的 Bean

            • 性能方面,Spring AOP是在運行時進行動態(tài)織入的,所以性能比不上AspectJ

            • Spring AOP使用動態(tài)代理的方式進行方法織入,有兩種動態(tài)代理方法,一種是CGLIB,另外一種是JDK提供的動態(tài)代理
              引用https://www.baeldung.com/spring-aop-vs-aspectj的圖進行說明

          • AspectJ

            • AspectJ來自Eclipse的開源項目,鏈接:https://www.eclipse.org/aspectj

            • AspectJ是一種比較成熟的AOP解決方案,能夠提供比Spring AOP更多的AOP功能

            • AspectJ的方法織入屬于靜態(tài)織入,它的織入時機可以是:compile-time(編譯期)、post-compile(編譯后)、load-time(JVM類加載器加載時候)

            • AspectJ在編譯時進行方法織入,所以性能比Spring AOP好

          ok,前面已經(jīng)簡單列舉了Spring AOP和AspectJ的主要不同,現(xiàn)在可以用表格列舉出不同點對比,表格參考自國外網(wǎng)站:

          對比Spring AOPAspectJ
          實現(xiàn)語言使用存Java語言使用Java編程語言的擴展實現(xiàn)
          編譯過程無需單獨的編譯過程除非設(shè)置了LTW,否則需要AspectJ編譯器(ajc)
          織入時機動態(tài)代理,在運行時織入靜態(tài)織入,在編譯過程織入,它的織入時機可以是:compile-time(編譯期)、post-compile(編譯后)、load-time(JVM類加載器加載時候)
          功能基本的方法織入可以編織字段,方法,構(gòu)造函數(shù),靜態(tài)初始值設(shè)定項,最終類/方法等…
          范圍只能作用于Spring容器管理的bean上可以作用于所有領(lǐng)域?qū)ο笊蠈嵤?/td>
          性能比AspectJ慢得多更好的性能(編譯時編織比運行時編織要快得多)
          學習易于學習和應(yīng)用比Spring AOP復(fù)雜

          補充,AspectJ靜態(tài)織入時機:

          • compile-time weaving:編譯期織入,在編譯時候就直接進行方法織入,直接編譯出包含織入代碼的 .class 文件

          • post-compile weaving:編譯后織入,也可以稱之為二進制織入,它將Advice織入于編織后現(xiàn)有的類文件和JAR文件

          • Load-time weaving(LTW):指的是在加載類的時候進行織入,與以前的二進制編織完全一樣,不同之處在于編織被推遲到類加載器將類文件加載到JVM的過程

          5、Spring中AOP代理選擇

          在前面知識,我們知道Spring AOP是使用動態(tài)代理技術(shù)實現(xiàn)Spring AOP中的代理選擇,方法織入實現(xiàn)有兩種方法,一種是JDK動態(tài)代理,一種是CGLIB

          • 默認情況下,Spring框架會根據(jù)被代理對象(Target Object)是否實現(xiàn)接口來選擇JDK還是CGLIB。如果被代理對象沒有實現(xiàn)任何接口,Spring會選擇CGLIB,如果被代理對象有實現(xiàn)接口,Spring會選擇JDK提供的動態(tài)代理。

          • ps:雖然默認情況是這樣的,不過我們可以通過配置的方式來自定義選擇動態(tài)代理方式

          6、實驗環(huán)境準備參考

          學習了前面的理論知識之后,現(xiàn)在可以通過例子進行實踐,實踐之前,您需要如下的環(huán)境準備,實驗環(huán)境參考:

          • SpringFramework版本

            • Springframework5.0.x

          • 開發(fā)環(huán)境

            • JAR管理:gradle 4.9/ Maven3.+

            • 開發(fā)IDE:IntelliJ IDEA 2018.2.5

            • JDK:jdk1.8.0_31

            • Git Server:Git fro window 2.8.3

            • Git Client:SmartGit18.1.5(可選)

          7、Spring AOP實現(xiàn)方式

          在Spring AOP中,主要提供了三種配置方式:

          • Spring1.2 基于接口的配置:Spring最早的AOP實現(xiàn)是基于Spring提供的AOP接口實現(xiàn)的,通過實現(xiàn)接口,進行Advice邏輯代碼編寫等等

          • Spring2.0+ schema-based 配置 :Spring2.0之后,提供了 schema-based 配置,也就是xml類型的配置,使用命名空間

          • Spring2.0+ @Aspect配置:Spring2.0之后,也提供了@Aspect這種方法,@Aspect是用AspectJ的jar,但是實現(xiàn)是Spring AOP自己實現(xiàn)的

          8、Spring AOP例子參考

          前面介紹了Spring AOP實現(xiàn)的三種方式,接著本文通過代碼例子進行驗證:

          maven配置


          5.0.19.RELEASE
          1.9.4





          org.springframework
          spring-aop
          ${springframework.version}



          org.springframework
          spring-context
          ${springframework.version}



          org.aspectj
          aspectjweaver
          ${aspectj.version}




          8.1、Spring1.2 基于接口的配置

          8.1.1、基礎(chǔ)類編寫

          User.java

          package com.example.spring.aop.bean;

          public class User {
          private String username;
          private String password;

          public String getUsername() {
          return username;
          }

          public void setUsername(String username) {
          this.username = username;
          }

          public String getPassword() {
          return password;
          }

          public void setPassword(String password) {
          this.password = password;
          }

          @Override
          public String toString() {
          return "User{" +
          "username='" + username + '\'' +
          ", password='" + password + '\'' +
          '}';
          }
          }

          UserService .java:

          package com.example.spring.aop.service;

          import com.example.spring.aop.bean.User;

          /**
          *

          * UserService
          *

          *
          *

          * @author mazq
          * 修改記錄
          * 修改后版本: 修改人:修改日期: 2020/11/20 18:02 修改內(nèi)容:
          *

          */

          public interface UserService {

          User addUser(User user);

          User getUser();
          }

          UserServiceImpl .java

          package com.example.spring.aop.service.impl;

          import com.example.spring.aop.bean.User;
          import com.example.spring.aop.service.UserService;
          import org.springframework.beans.BeanUtils;
          import org.springframework.stereotype.Service;

          /**
          *

          * UserServiceImpl
          *

          *
          *

          * @author mazq
          * 修改記錄
          * 修改后版本: 修改人:修改日期: 2020/11/20 17:57 修改內(nèi)容:
          *

          */

          @Service
          public class UserServiceImpl implements UserService {

          private static User user = null;

          @Override
          public User addUser(User userDto) {
          user = new User();
          BeanUtils.copyProperties(userDto,user);
          return user;
          }

          @Override
          public User getUser() {
          return user;
          }
          }

          8.1.2、使用Advice接口

          LogMethodBeforeAdvice .java

          package com.example.spring.aop.core.advice;

          import org.springframework.aop.MethodBeforeAdvice;

          import java.lang.reflect.Method;
          import java.util.Arrays;

          /**
          *

          * LogMethodBeforeAdvice
          *

          *
          *

          * @author mazq
          * 修改記錄
          * 修改后版本: 修改人:修改日期: 2020/11/20 17:38 修改內(nèi)容:
          *

          */

          public class LogMethodBeforeAdvice implements MethodBeforeAdvice {
          @Override
          public void before(Method method, Object[] args, Object target) throws Throwable {
          System.out.println(String.format("執(zhí)行方法:%s,參數(shù)列表:%s", method.getName(), Arrays.toString(args) ));
          }
          }

          LogAfterReturningAdvice .java

          package com.example.spring.aop.core.advice;

          import org.springframework.aop.AfterReturningAdvice;

          import java.lang.reflect.Method;

          /**
          *

          * LogAfterReturningAdvice
          *

          *
          *

          * @author mazq
          * 修改記錄
          * 修改后版本: 修改人:修改日期: 2020/11/20 17:41 修改內(nèi)容:
          *

          */

          public class LogAfterReturningAdvice implements AfterReturningAdvice {
          @Override
          public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
          System.out.println(String.format("方法返回:%s", returnValue ));
          }
          }

          spring_interfaces_config.xml:


          xmlns="http://www.springframework.org/schema/beans"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">


          id="userServiceTarget" class="com.example.spring.aop.service.impl.UserServiceImpl">


          id="logMethodBeforeAdvice" class="com.example.spring.aop.core.advice.LogMethodBeforeAdvice">

          id = "logAfterReturningAdvice" class="com.example.spring.aop.core.advice.LogAfterReturningAdvice">

          id="userServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">

          name="proxyInterfaces">

          com.example.spring.aop.service.UserService



          name="target" ref="userServiceTarget">

          name="interceptorNames">

          logMethodBeforeAdvice
          logAfterReturningAdvice





          TestApplication.java:

          package com.example.spring.aop;


          import com.example.spring.aop.bean.User;
          import com.example.spring.aop.service.UserService;
          import org.springframework.context.ApplicationContext;
          import org.springframework.context.support.ClassPathXmlApplicationContext;

          public class TestApplication {

          public static void testAopProxy() {
          ApplicationContext ioc = new ClassPathXmlApplicationContext("classpath:spring_interfaces_config.xml");
          UserService userService = (UserService) ioc.getBean("userServiceProxy");
          User userDto = new User();
          userDto.setUsername("tom");
          userDto.setPassword("11");
          userService.addUser(userDto);
          System.out.println(String.format("用戶數(shù)據(jù)打印:%s",userService.getUser().toString()));
          }

          public static void main(String[] args) {
          testAopProxy();
          }
          }

          執(zhí)行方法:addUser,參數(shù)列表:[User{username='tom', password='11'}]
          方法返回:User{username='tom', password='11'}
          執(zhí)行方法:getUser,參數(shù)列表:[]
          方法返回:User{username='tom', password='11'}
          用戶數(shù)據(jù)打印:User{username='tom', password='11'}

          8.1.3、使用Advisor接口

          • NameMatchMethodPointcutAdvisor使用
            定義一個只會攔截查詢方法的Advisor,修改配置:


          id="logOnlyObtainQueryAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">

          name="advice" ref="logMethodBeforeAdvice">

          name="mappedNames" value="getUser">


          id="userServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">

          name="proxyInterfaces">

          com.example.spring.aop.service.UserService



          name="target" ref="userServiceTarget">

          name="interceptorNames">

          logOnlyObtainQueryAdvisor



          執(zhí)行方法:getUser,參數(shù)列表:[]
          用戶數(shù)據(jù)打印:User{username='tom', password='11'}

          • RegexpMethodPointcutAdvisor使用
            前面介紹了NameMatchMethodPointcutAdvisor,不過不夠通用,所以Spring aop還提供了RegexpMethodPointcutAdvisor,可以支持正則表達式

           
          id="regexpMethodAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
          name="advice" ref="logMethodBeforeAdvice">
          name="pattern" value="com.example.spring.aop.*.service.*.get.*">


          id="userServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">

          name="proxyInterfaces">

          com.example.spring.aop.service.UserService



          name="target" ref="userServiceTarget">

          name="interceptorNames">

          regexpMethodAdvisor



          執(zhí)行方法:getUser,參數(shù)列表:[]
          用戶數(shù)據(jù)打印:User{username='tom', password='11'}

          8.1.4、Interceptor接口使用

          TestMethodInterceptor .java:

          package com.example.spring.aop.core.interceptor;


          import org.aopalliance.intercept.MethodInterceptor;
          import org.aopalliance.intercept.MethodInvocation;

          /**
          *

          * TestMethodInterceptor
          *

          *
          *

          * @author mazq
          * 修改記錄
          * 修改后版本: 修改人:修改日期: 2020/11/23 10:28 修改內(nèi)容:
          *

          */

          public class TestMethodInterceptor implements MethodInterceptor {

          @Override
          public Object invoke(MethodInvocation methodInvocation) throws Throwable {
          System.out.println(String.format("方法調(diào)用前(before method invoke) :%s",methodInvocation));
          Object implObj = methodInvocation.proceed();
          System.out.println(String.format("方法調(diào)用后(after method invoke) :%s",implObj));
          return implObj;
          }
          }


          修改配置文件:


          id="methodInterceptor" class="com.example.spring.aop.core.interceptor.TestMethodInterceptor">

          id="userServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">

          name="proxyInterfaces">

          com.example.spring.aop.service.UserService



          name="target" ref="userServiceTarget">

          name="interceptorNames">

          logMethodBeforeAdvice
          logAfterReturningAdvice
          methodInterceptor



          挑addUser方法的日志信息:

          方法調(diào)用前(before method invoke) :ReflectiveMethodInvocation: public abstract com.example.spring.aop.bean.User com.example.spring.aop.service.UserService.addUser(com.example.spring.aop.bean.User); target is of class [com.example.spring.aop.service.impl.UserServiceImpl]


          方法調(diào)用后(after method invoke) :User{username='tom', password='11'}

          8.1.5、beanNameAutoProxy使用

          前面介紹了ProxyFactoryBean配置對應(yīng)的業(yè)務(wù)代理進行調(diào)用,不過不夠靈活,所以Spring中還提供了beanNameAutoProxy,這種方式是自動匹配beanName id,不需要每個業(yè)務(wù)都配對應(yīng)的proxy進行代理

          新建一個新的配置文件,spring_beanNameAutoProxy_config.xml:


          xmlns="http://www.springframework.org/schema/beans"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">


          id="userServiceTarget" class="com.example.spring.aop.service.impl.UserServiceImpl">


          id="logMethodBeforeAdvice" class="com.example.spring.aop.core.advice.LogMethodBeforeAdvice">

          id = "logAfterReturningAdvice" class="com.example.spring.aop.core.advice.LogAfterReturningAdvice">


          id="methodInterceptor" class="com.example.spring.aop.core.interceptor.TestMethodInterceptor">


          id="autoProxyCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">

          name="interceptorNames">

          logMethodBeforeAdvice
          logAfterReturningAdvice
          methodInterceptor



          name="beanNames" value="*ServiceTarget">




          TestApplication.java:

          package com.example.spring.aop;


          import com.example.spring.aop.bean.User;
          import com.example.spring.aop.service.UserService;
          import org.springframework.context.ApplicationContext;
          import org.springframework.context.support.ClassPathXmlApplicationContext;

          public class TestApplication {

          public static void testBeanNameAutoProxy() {
          ApplicationContext ioc = new ClassPathXmlApplicationContext("classpath:spring_beanNameAutoProxy_config.xml");
          UserService userService = ioc.getBean(UserService.class);
          User userDto = new User();
          userDto.setUsername("tom");
          userDto.setPassword("11");
          userService.addUser(userDto);
          System.out.println(String.format("用戶數(shù)據(jù)打印:%s",userService.getUser().toString()));
          }

          public static void main(String[] args) {
          // BeanNameAutoProxyCreator
          testBeanNameAutoProxy();
          }
          }

          8.2、Spring2.0+ @Aspect配置

          @Aspect這種方式是比較常用的,pom需要加上aspectjweaver配置,spring aop引用了aspectJ的api,但是實現(xiàn)是spring自己進行實現(xiàn)拓展的

          8.2.1、啟用@AspectJ支持

          注解方式,使用@EnableAspectJAutoProxy開啟

          @Configuration
          @EnableAspectJAutoProxy
          public class AppConfig {

          }

          xml方式,可以使用開啟


          xmlns="http://www.springframework.org/schema/beans"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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 https://www.springframework.org/schema/aop/spring-aop.xsd">


          8.2.2、聲明方面

          xml方式:

          id="myAspect" class="org.xyz.NotVeryUsefulAspect">


          java方式,使用注解@Aspect

          package org.xyz;
          import org.aspectj.lang.annotation.Aspect;

          @Aspect
          public class NotVeryUsefulAspect {

          }

          8.2.3、聲明切入點

          切入點(Pointcut)的類型,Spring官網(wǎng)給出了比較詳情的介紹:

          在官網(wǎng)的建議是聲明一個通用的SystemArchitecture

          package com.xyz.someapp;

          import org.aspectj.lang.annotation.Aspect;
          import org.aspectj.lang.annotation.Pointcut;

          @Aspect
          public class SystemArchitecture {

          /**
          * A join point is in the web layer if the method is defined
          * in a type in the com.xyz.someapp.web package or any sub-package
          * under that.
          */

          @Pointcut("within(com.xyz.someapp.web..*)")
          public void inWebLayer() {}

          /**
          * A join point is in the service layer if the method is defined
          * in a type in the com.xyz.someapp.service package or any sub-package
          * under that.
          */

          @Pointcut("within(com.xyz.someapp.service..*)")
          public void inServiceLayer() {}

          /**
          * A join point is in the data access layer if the method is defined
          * in a type in the com.xyz.someapp.dao package or any sub-package
          * under that.
          */

          @Pointcut("within(com.xyz.someapp.dao..*)")
          public void inDataAccessLayer() {}

          /**
          * A business service is the execution of any method defined on a service
          * interface. This definition assumes that interfaces are placed in the
          * "service" package, and that implementation types are in sub-packages.
          *
          * If you group service interfaces by functional area (for example,
          * in packages com.xyz.someapp.abc.service and com.xyz.someapp.def.service) then
          * the pointcut expression "execution(* com.xyz.someapp..service.*.*(..))"
          * could be used instead.
          *
          * Alternatively, you can write the expression using the 'bean'
          * PCD, like so "bean(*Service)". (This assumes that you have
          * named your Spring service beans in a consistent fashion.)
          */

          @Pointcut("execution(* com.xyz.someapp..service.*.*(..))")
          public void businessService() {}

          /**
          * A data access operation is the execution of any method defined on a
          * dao interface. This definition assumes that interfaces are placed in the
          * "dao" package, and that implementation types are in sub-packages.
          */

          @Pointcut("execution(* com.xyz.someapp.dao.*.*(..))")
          public void dataAccessOperation() {}

          }

          因為官網(wǎng)的介紹比較詳細,所以本博客只挑部分比較重要的進行介紹:

          • execution
            execution:執(zhí)行,這是最基本的切入點類型:

          // 匹配UserService里的任何方法
          @Pointcut("execution(* com.example.spring.aop.service.UserService.*(..))")
          public void regexpExecution(){}

          ps:第一個*表示匹配任何返回值,第二個*表示匹配任何方法,(..)表示匹配任何數(shù)量的方法參數(shù)

          eg:execution(* *..find*(Long,..))用于匹配方法名為find...,而第一個參數(shù)是long類型的

           @Pointcut("within(com.example.spring.aop..*) && execution(* *..find*(Long,..))")
          public void regexpExecutionByMethodName(){}
          • within
            within:表示服務(wù)包中的任何連接點(僅在Spring AOP中執(zhí)行方法)

          @Pointcut("within(com.example.spring.aop..*)")
          • this和target
            前者在Spring AOP創(chuàng)建基于CGLIB的代理時起作用,而后者在創(chuàng)建基于JDK的代理時使用

          如下實例代碼:

          public class UserServiceImpl implements UserService {
          //...
          }

          對于UserService,使用target,這種情況是基于CGLIB的代理

          @Pointcut("target(com.example.spring.aop.service.UserService)")

          對于UserService,使用this,這種情況是基于JDK的代理

          @Pointcut("this(com.example.spring.aop.service.impl.UserServiceImpl)")
          • args
            限制匹配點(參數(shù)是給定類型的實例)的連接點(使用Spring AOP時方法的執(zhí)行)

          • @target
            @target不要和target混淆了,其中類的執(zhí)行的對象的具有給定類型的注釋

          @Pointcut("@target(org.springframework.stereotype.Repository)")
          • @args
            @args其中傳遞的實際參數(shù)的運行時類型具有給定類型的注釋

          package com.example.spring.aop.annotation;

          import java.lang.annotation.ElementType;
          import java.lang.annotation.Retention;
          import java.lang.annotation.RetentionPolicy;
          import java.lang.annotation.Target;

          @Retention(RetentionPolicy.RUNTIME)
          // 作用于類
          @Target(ElementType.TYPE)
          public @interface Entity {
          }

          @Pointcut("within(com.example.spring.aop..*) && @args(com.example.spring.aop.annotation.Entity)")
          public void argsMethod(){}
          • @within
            將匹配限制為具有給定注釋的類型內(nèi)的連接點

          Pointcut("@within(org.springframework.stereotype.Repository)")

          等效于:

          @Pointcut("within(@org.springframework.stereotype.Repository *)")
          • @annotation
            這個@annotation是用于匹配注解的,比較常用,我們可以自己寫個注解類:

          package com.example.spring.aop.annotation;

          import java.lang.annotation.ElementType;
          import java.lang.annotation.Retention;
          import java.lang.annotation.RetentionPolicy;
          import java.lang.annotation.Target;

          /**
          *

          * EnableLog
          *

          *
          *

          * @author mazq
          * 修改記錄
          * 修改后版本: 修改人:修改日期: 2020/11/23 18:27 修改內(nèi)容:
          *

          */

          @Retention(RetentionPolicy.RUNTIME)
          // 作用于方法
          @Target(ElementType.METHOD)
          public @interface EnableLog {
          }

          然后,在對應(yīng)方法加上@EnableLog的方法都能被攔截到:

          @Pointcut("within(com.example.spring.aop..*) && @annotation(com.example.spring.aop.annotation.EnableLog)")
          public void annotationMethod(){}
          • 組合表達式
            組合表達式可以使用&&,||和!進行組合

           @Pointcut("within(com.example.spring.aop..*) && execution(public String com.example.spring.aop.service.UserService.*(..))")

          8.2.4、聲明Advice類型

          Advice類型,Spring官網(wǎng)也有比較詳細的介紹:

          歸納一下通知類型:

          • 前置通知(@Before):在方法執(zhí)行之前,使用@Before注釋聲明

          • 后置通知(@AfterReturning):當匹配的方法執(zhí)行正常返回時運行建議。它使用@AfterReturning注釋聲明

          • 異常通知(@AfterThrowing):程序拋出異常后執(zhí)行,不拋異常不會調(diào)用,使用@AfterThrowing注釋聲明

          • 最后通知(@After):匹配的方法執(zhí)行退出,如果是有try...catch,一般是在finally執(zhí)行完成后,使用@After注釋聲明

          • 環(huán)繞通知(@Around):圍繞建議在匹配的方法執(zhí)行過程中“圍繞”運行。它有機會在該方法執(zhí)行之前和之后進行工作,并確定該方法何時,如何以及什至完全可以執(zhí)行,使用@Around注釋聲明

          package com.example.spring.aop.config;

          import com.example.spring.aop.service.UserService;
          import com.example.spring.aop.service.impl.UserServiceImpl;
          import org.aspectj.lang.JoinPoint;
          import org.aspectj.lang.ProceedingJoinPoint;
          import org.aspectj.lang.annotation.*;
          import org.springframework.context.annotation.Bean;
          import org.springframework.context.annotation.Configuration;
          import org.springframework.context.annotation.EnableAspectJAutoProxy;

          import java.text.SimpleDateFormat;
          import java.util.Arrays;
          import java.util.Date;

          /**
          *

          * SpringAspectJConfiguration
          *

          *
          *

          * @author mazq
          * 修改記錄
          * 修改后版本: 修改人:修改日期: 2020/11/24 10:52 修改內(nèi)容:
          *

          */

          @Configuration
          @EnableAspectJAutoProxy(proxyTargetClass = false)
          @Aspect
          public class SpringAspectJConfiguration {

          @Bean
          public UserService userService(){
          return new UserServiceImpl();
          }

          private ThreadLocal simpleDateFormat =new ThreadLocal(){
          @Override
          protected SimpleDateFormat initialValue() {
          //return super.initialValue();
          return new SimpleDateFormat("[yyyy-mm-dd hh:mm:ss:SSS]");
          }
          };

          //---------------------------------------------------------------------
          // Types of pointcut
          //---------------------------------------------------------------------

          @Pointcut("within(com.example.spring.aop..*) && execution(public String com.example.spring.aop.service.UserService.*(..))")
          public void regexpExecution(){}

          @Pointcut("within(com.example.spring.aop..*) && execution(* *..find*(Long,..))")
          public void regexpExecutionByMethodName(){}

          @Pointcut("within(com.example.spring.aop..*) && target(com.example.spring.aop.service.UserService)")
          public void targetInterface(){}

          @Pointcut("within(com.example.spring.aop..*) && @args(com.example.spring.aop.annotation.Entity)")
          public void argsMethod(){}

          @Pointcut("within(com.example.spring.aop..*) && @annotation(com.example.spring.aop.annotation.EnableLog)")
          public void annotationMethod(){}

          //---------------------------------------------------------------------
          // Types of advice
          //---------------------------------------------------------------------

          @Before(value = "regexpExecution()")
          public void beforeAdvice(JoinPoint joinPoint){
          Object[] args = joinPoint.getArgs();
          String methodName = joinPoint.getSignature().getName();
          System.out.println(String.format("前置通知:beforeAdvice,參數(shù)是:%s", Arrays.toString(args)));
          System.out.println(simpleDateFormat.get().format(new Date()) + methodName);
          }

          @AfterReturning(value = "regexpExecution()", returning = "returnVal")
          public void afterReturningAdvice(Object returnVal){
          System.out.println(String.format("后置通知:afterReturningAdvice,返回參數(shù)是:%s", returnVal));
          }

          @AfterThrowing(value = "regexpExecution()", throwing = "e")
          public void afterThrowingAdvice(Throwable e) {
          System.out.println(String.format("異常通知:afterThrowingAdvice,異常信息:%s", e));
          }

          @After(value = "regexpExecution()")
          public void afterAdvice() {
          System.out.println(String.format("最后通知:afterAdvice"));
          }

          @Around(value = "regexpExecution()")
          public Object aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) {
          Object rtValue = null;
          try {
          System.out.println("aroundAdvice前置通知!");
          // 獲取參數(shù)
          Object[] args = proceedingJoinPoint.getArgs();
          // 執(zhí)行切入點方法
          rtValue = proceedingJoinPoint.proceed(args);

          System.out.println("aroundAdvice后置通知!");
          } catch (Throwable e) {
          System.out.println("aroundAdvice異常通知!");
          e.printStackTrace();
          } finally {
          System.out.println("aroundAdvice最后通知!");
          }
          return rtValue;
          }

          }

          aroundAdvice前置通知!
          前置通知:beforeAdvice,參數(shù)是:[1]
          [2020-13-24 02:13:48:509]findUserNameById
          aroundAdvice后置通知!
          aroundAdvice最后通知!
          最后通知:afterAdvice
          后置通知:afterReturningAdvice,返回參數(shù)是:tom

          8.2.5、例子:實現(xiàn)日志監(jiān)控

          Service加個方法:


          AopConfiguration.java:

          package com.example.spring.aop.config;

          import com.example.spring.aop.core.interceptor.TestMonitoringInterceptor;
          import com.example.spring.aop.service.UserService;
          import com.example.spring.aop.service.impl.UserServiceImpl;
          import org.aspectj.lang.annotation.Aspect;
          import org.aspectj.lang.annotation.Pointcut;
          import org.springframework.aop.Advisor;
          import org.springframework.aop.aspectj.AspectJExpressionPointcut;
          import org.springframework.aop.support.DefaultPointcutAdvisor;
          import org.springframework.context.annotation.Bean;
          import org.springframework.context.annotation.Configuration;
          import org.springframework.context.annotation.EnableAspectJAutoProxy;

          /**
          *

          * AOP日志監(jiān)控配置類
          *

          *
          *

          * @author mazq
          * 修改記錄
          * 修改后版本: V1.0.0 修改人:mazq 修改日期: 2020/11/23 14:30 修改內(nèi)容: 新增配置類
          *

          */

          @Configuration
          @Aspect
          @EnableAspectJAutoProxy
          public class AopLogMonitorConfiguration {

          @Pointcut("within(com.example.spring.aop..*) && execution(public String com.example.spring.aop.service.UserService.findUserNameById(Long, ..))")
          public void monitor(){ }

          @Bean
          public TestMonitoringInterceptor monitoringInterceptor() {
          return new TestMonitoringInterceptor(true);
          }

          @Bean
          public Advisor monitoringAdvisor() {
          AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
          pointcut.setExpression("com.example.spring.aop.config.AopConfiguration.monitor()");
          return new DefaultPointcutAdvisor(pointcut, monitoringInterceptor());
          }

          @Bean
          public UserService userService(){
          return new UserServiceImpl();
          }


          }


          TestMonitoringInterceptor.java

          package com.example.spring.aop.core.interceptor;

          import org.aopalliance.intercept.MethodInvocation;
          import org.apache.commons.logging.Log;
          import org.springframework.aop.interceptor.AbstractMonitoringInterceptor;

          /**
          *

          * TestMonitoringInterceptor
          *

          *
          *

          * 修改記錄
          * 修改后版本: 修改人:修改日期: 2020/11/23 16:39 修改內(nèi)容:
          *

          */

          public class TestMonitoringInterceptor extends AbstractMonitoringInterceptor {

          public TestMonitoringInterceptor(){}

          public TestMonitoringInterceptor (boolean useDynamicLogger) {
          setUseDynamicLogger(useDynamicLogger);
          }

          @Override
          protected Object invokeUnderTrace(MethodInvocation methodInvocation, Log log) throws Throwable {
          String name = createInvocationTraceName(methodInvocation);
          long start = System.currentTimeMillis();
          try {
          return methodInvocation.proceed();
          } finally {
          long end = System.currentTimeMillis();
          long time = end - start;
          log.info(String.format("方法名:%s,執(zhí)行時間:%s ms",name,time));
          if (time > 10) {
          log.warn(String.format("方法名:%s,執(zhí)行時間超過10 ms! ",name));
          }
          }
          }
          }

          pom.xml加上logback配置:


          1.7.25
          1.2.3




          org.slf4j
          slf4j-api
          ${slf4j.version}


          ch.qos.logback
          logback-core
          ${logback.version}


          ch.qos.logback
          logback-access
          ${logback.version}


          ch.qos.logback
          logback-classic
          ${logback.version}


          logback.xml,copy @https://github.com/eugenp/tutorials/blob/master/spring-aop/src/main/resources/logback.xml,進行一點改寫:





          name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">

          %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n




          name="org.springframework" level="WARN" />
          name="org.springframework.transaction" level="WARN" />


          name="org.springframework.web.servlet.mvc" level="WARN" />

          name="com.example.spring.aop.core.interceptor.TestMonitoringInterceptor" level="INFO" />

          name="org.springframework.aop.interceptor.PerformanceMonitorInterceptor" level="TRACE" />

          level="TRACE">
          ref="STDOUT" />


          package com.example.spring.aop;


          import com.example.spring.aop.bean.User;
          import com.example.spring.aop.config.AopConfiguration;
          import com.example.spring.aop.service.UserService;
          import org.springframework.context.ApplicationContext;
          import org.springframework.context.annotation.AnnotationConfigApplicationContext;

          public class TestApplication {

          public static void testLogMonitoring() {
          AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext();
          // 注冊配置類
          ioc.register(AopConfiguration.class);
          // 啟動IOC容器
          ioc.refresh();
          UserService userService = (UserService) ioc.getBean("userService");
          System.out.println(userService.findUserNameById(1L));
          }

          public static void main(String[] args) {
          // logging monitoring
          testLogMonitoring();
          }
          }

          17:54:05.553 [main] INFO c.e.s.a.service.impl.UserServiceImpl - 方法名:com.example.spring.aop.service.UserService.findUserNameById,執(zhí)行時間:2531 ms
          17:54:05.559 [main] WARN c.e.s.a.service.impl.UserServiceImpl - 方法名:com.example.spring.aop.service.UserService.findUserNameById,執(zhí)行時間超過10 ms!

          8.3、Spring2.0+ schema-based 配置

          Spring2.0之后提供了基于??命名空間的 XML 配置,這也就是本文介紹的schema-based 配置

          SchemaBasedAspect .java:

          package com.example.spring.aop.aspect;

          import org.aspectj.lang.JoinPoint;
          import org.aspectj.lang.ProceedingJoinPoint;

          import java.text.SimpleDateFormat;
          import java.util.Arrays;
          import java.util.Date;

          public class SchemaBasedAspect {

          private ThreadLocal simpleDateFormat =new ThreadLocal(){
          @Override
          protected SimpleDateFormat initialValue() {
          //return super.initialValue();
          return new SimpleDateFormat("[yyyy-mm-dd hh:mm:ss:SSS]");
          }
          };

          public void beforeAdvice(JoinPoint joinPoint){
          Object[] args = joinPoint.getArgs();
          String methodName = joinPoint.getSignature().getName();
          System.out.println(String.format("前置通知:beforeAdvice,參數(shù)是:%s", Arrays.toString(args)));
          System.out.println(simpleDateFormat.get().format(new Date()) + methodName);
          }

          public void afterReturningAdvice(Object returnVal){
          System.out.println(String.format("后置通知:afterReturningAdvice,返回參數(shù)是:%s", returnVal));
          }

          public void afterThrowingAdvice(Throwable e) {
          System.out.println(String.format("異常通知:afterThrowingAdvice,異常信息:%s", e));
          }

          public void afterAdvice() {
          System.out.println(String.format("最后通知:afterAdvice"));
          }

          public Object aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) {
          Object rtValue = null;
          try {
          System.out.println("aroundAdvice前置通知!");
          // 獲取參數(shù)
          Object[] args = proceedingJoinPoint.getArgs();
          // 執(zhí)行切入點方法
          rtValue = proceedingJoinPoint.proceed(args);

          System.out.println("aroundAdvice后置通知!");
          } catch (Throwable e) {
          System.out.println("aroundAdvice異常通知!");
          e.printStackTrace();
          } finally {
          System.out.println("aroundAdvice最后通知!");
          }
          return rtValue;
          }
          }

          spring_schemaBased_config.xml:


          xmlns="http://www.springframework.org/schema/beans"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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 https://www.springframework.org/schema/aop/spring-aop.xsd">


          id="userService" class="com.example.spring.aop.service.impl.UserServiceImpl">

          id="LoggingAspect" class="com.example.spring.aop.aspect.SchemaBasedAspect">





          id="executionPointcut" expression="execution(* com.example.spring.aop.service.UserService.*(..))" />

          id="logAspect" ref="LoggingAspect">

          method="beforeAdvice"
          pointcut-ref="executionPointcut">

          method="afterReturningAdvice"
          pointcut-ref="executionPointcut" returning="returnVal">

          method="afterThrowingAdvice" pointcut-ref="executionPointcut" throwing="e"
          >

          method="afterAdvice" pointcut-ref="executionPointcut">

          method="aroundAdvice" pointcut-ref="executionPointcut">




          TestApplication.java:

          package com.example.spring.aop;

          import com.example.spring.aop.bean.User;
          import com.example.spring.aop.service.UserService;
          import org.springframework.context.ApplicationContext;
          import org.springframework.context.support.ClassPathXmlApplicationContext;

          public class TestApplication {

          public static void testSchemaBasedAop(){
          ApplicationContext ioc = new ClassPathXmlApplicationContext("classpath:spring_schemaBased_config.xml");
          UserService userService = (UserService) ioc.getBean("userService");
          User userDto = new User();
          userDto.setUsername("tom");
          userDto.setPassword("11");
          userService.addUser(userDto);
          System.out.println(String.format("用戶數(shù)據(jù)打印:%s",userService.getUser().toString()));
          }

          public static void main(String[] args) {
          // schema Based config
          testSchemaBasedAop();
          }
          }




          往期精彩推薦



          騰訊、阿里、滴滴后臺面試題匯總總結(jié) — (含答案)

          面試:史上最全多線程面試題 !

          最新阿里內(nèi)推Java后端面試題

          JVM難學?那是因為你沒認真看完這篇文章


          END


          關(guān)注作者微信公眾號 —《JAVA爛豬皮》


          了解更多java后端架構(gòu)知識以及最新面試寶典


          你點的每個好看,我都認真當成了


          看完本文記得給作者點贊+在看哦~~~大家的支持,是作者源源不斷出文的動力


          作者:smileNicky

          出處:https://www.cnblogs.com/mzq123/p/14041943.html

          瀏覽 67
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美性爱综合在线 | 性爱网站在线 | 大香蕉在线视频欧美 | 熟女 人妻 人蜜桃视频 | 中文字幕超碰在线 |