【SpringAop】【統一日誌處理】註解方式理解以及使用

【注意:本次代碼的demo會存在百度網盤,因爲公司的保密,禁止上傳,因此僅本人可見】java

目前公司在作數據資產項目,數據質量部分使用到了springaop作統一日誌處理,之前對這塊有了解,有點模糊不清,今天從新複習了一次,發現以前的概念理解有誤,這裏再作一次記憶。程序員

1、概念【手敲增長記憶】web

=====================================轉喬志勇【csdn】=============================spring

  1,aop:是對OOP編程方式的一種補充,翻譯爲「面向切面編程」express

 能夠理解爲一個攔截器框架,可是這個攔截器會很是武斷,若是它攔截一個類,那麼它就會攔截這個類的全部方法。就像對一個目標類的代理,加強了目標類的全部方法。apache

兩個解決辦法:編程

  • 不優雅的作法:在添加加強時,根據方法名去判斷,是否添加加強,可是這樣就得一直去維護這個加強類;
  • 面向切面:將加強類和攔截條件組合在一塊兒,而後將這個切面配置到ProxyFactory中,從而生成代理。

  2,aop和切面的關係json

  • 類與對象相比,aop和切面就是這樣的一種關係【實例關係】;
  • 也能夠將切面當作是aop的一個工具【實現關係】;

  3,幾個概念後端

切面(Advidsor):是aop中的一個術語,表示從業務邏輯中分離出來的橫切邏輯,好比性能監控,日誌記錄,權限控制等。api

這些功能均可以從核心的業務邏輯中抽離出去,能夠解決代碼耦合問題,職責更加單一。封裝了加強和切點。

本身理解:切面就是一個抽象的概念,自己就是一個類,包含了加強通知與切點的定義;

加強(Advice):加強代碼的功能的類,橫切到代碼中。

本身理解:加強通知就是要給你的目標類或者方法進行動態加強處理

目標:接口目標方法(JDK代理)或目標類(CGLIB代理)

代理:JDK代理,CGLIB代理,或者經過ProxyFactory類生產。

切點:經過一個條件來匹配要攔截的類,這個條件稱爲切點。如攔截全部帶Controller註解的類,加強的條件。

本身理解:就是代理的一個對象或者方法,經過這個切點能夠找到對應的目標;

鏈接點:做爲加強方法的入參,能夠獲取到目標方法的信息;

本身理解:其實就是找到具體須要加強的地方,經過切點鏈接到目標類或者方法

  4,歸納一張圖

  5,加強

    • Weaving(織入):對方法進行加強
      • 前置加強(BeforeAdvice):在目標方法前調用。
      • 後置加強(AfterAdvice):在目標方法後調用。
      • 環繞加強(AroundAdvice):將Before和After,甚至拋出加強和返回加強合到一塊兒。
      • 返回加強(AfterReturningAdvice):在方法返回結果後執行,該加強能夠接收到目標方法返回結果。
      • 拋出加強(AfterThrowingAdvice):在目標方法拋出對應的類型後執行,能夠接收到對應的異常信息。
    • INtroduction(引入) :對類進行加強
      • 引入加強(DeclareParentsAdvice):想讓程序在運行的時候動態去實現 某個接口,須要引入加強。

  6,SpringAop實現方式

    • 編程式【實現接口方式】
      • 前置加強,須要實現:MethodBeforeAdvice接口

        加強類:

public class CarBeforeAdvice implements MethodBeforeAdvice{
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable { System.out.println("before"); } }

        測試方法:

@Test
public void test03() {   ProxyFactory proxyFactory = new ProxyFactory();   proxyFactory.setTarget(new Car());   proxyFactory.addAdvice(new CarBeforeAdvice());   Wheel carProxy = (Wheel)proxyFactory.getProxy();   carProxy.run(); }
      • 後置加強:實現AfterReturningAdvice接口
      • 環繞加強:實現org.aopalliance.intercept.MethodInterceptor 接口,使用Object result = methodInvocation.proceed();調用目標方法,在目標方法先後添加加強。
    • 聲明式【xml形式】:spring+Aspect

      開發步驟:

      1,定義切面類,將該切面類放入到IOC容器中

@Component
public class XMLLoggingAspect { public void beforeAdvice(JoinPoint point) { System.out.println("xml aspects logging before"); } public void afterAdvice(JoinPoint point) { System.out.println("xml aspects logging after"); } public void afterReturningAdvice(JoinPoint point, Object result) { System.out.println("xml aspects logging afterReturningAdvice"); } public void afterThrowingAdvice(JoinPoint point, Exception e) { System.out.println("xml aspects logging afterThrowingAdvice"); } public Object aroundAdvice(ProceedingJoinPoint point) throws Throwable { Object result = point.proceed(); System.out.println("xml aspects logging aroundAdvice"); return result; } }

      2,SpringAop配置都必須定義在<aop:config>元素內部

      3,在<aop:config>中,每一個切面都須要建立一個<aop:aspect>元素

      4,爲具體的切面實現引用後端的bean實例。

下面展現  前置加強,後置加強,環繞加強,返回加強,拋出加強的【織入加強】例子:

<aop:config>
  <aop:aspect ref="XMLLoggingAspect">
    <aop:pointcut id="carPointcut" expression="execution(void run())"/>
    <aop:before method="beforeAdvice" pointcut-ref="carPointcut"/>
    <aop:after method="afterAdvice" pointcut-ref="carPointcut"/>
    <aop:after-returning method="afterReturningAdvice" pointcut-ref="carPointcut" returning="result"/>
    <aop:after-throwing method="afterThrowingAdvice" pointcut-ref="carPointcut" throwing="e"/>
    <aop:around method="aroundAdvice" pointcut-ref="carPointcut"/>
  </aop:aspect>
</aop:config>

控制檯輸出:

xml aspects logging before
i am a car, i can run.
xml aspects logging aroundAdvice
xml aspects logging afterReturningAdvice
xml aspects logging after

基於聲明式的spring aspect織入加強配置說明:

  支持配置兩個級別的公共切點表達式,一個是針對某個切面的全部方法(定義在<aop:aspect>節點內),另外一個是針對全部切面(定義在<aop:config>節點內),使用ponitcut-ref來引入切點。

下面展現【引入加強】的一個例子:

對於引入加強,只須要配置<aop:aspect>節點下就能夠,不須要去切面類中添加任何屬性

//types-matching這是目標類
//implement-interface 這是代理的接口,default-impl這是代理接口的實現類
<aop:config>
  <aop:aspect ref="XMLLoggingAspect">
    <aop:declare-parents
          types-matching="com.nucsoft.spring.target.impl.Student"         implement-interface="com.nucsoft.spring.target.Fly"          default-impl="com.nucsoft.spring.target.impl.SuperMan"/>   </aop:aspect> </aop:config>

測試:

@Test
public void test04() {   Person student = context.getBean(Student.class);   System.out.println(student.say("james"));   Fly fly = (Fly) student;   fly.fly(); }

控制檯輸出

hello,james
i am super man, i can fly.

基於聲明式的spring aspect 引入加強配置說明:

  1,引入加強是類級別,因此不存在切點表達式

  2,利用<aop:declare-parents>節點在<aop:aspect>內部聲明

  3,types-matching屬性,要加強的目標類,這裏須要全類名

  4,implement-interface屬性:動態的加強類接口

  5,default-impl屬性:動態加強類接口的實現

    • 註解方式【註解方式】:spring+aspect

對切面類添加@Aspect註解,將切面類和目標類放入到IOC容器中,能夠經過<context :component-scan base-package=""/>進行掃描

添加加強方法(包括加強類型和切點表達式,以及鏈接點)

在Spring Config文件中添加(<aop:aspectj-autoproxy proxytarget-class="true">,proxy-target-class屬性,false只能代理接口(JDK動態代理),true代理類(CGLIB動態代理))

---------------------------------------------------------------------------------------------------------------------------

  1,經過切點表達式(AspectJ execution)進行攔截

  spring-congfig.xml

<context:component-scan base-package="com.nucsoft.spring"/>
<aop:aspectj-autoproxy proxy-target-class="true"/>

  Person接口:

public interface Person {
    String say(String name);
}

Person實現類Student:

@Component
public class Student implements Person{ @Override public String say(String name) { return "hello," + name; } }

測試類:

public class SpringTest {
    @Test
    public void test01() { ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml"); Student student = context.getBean(Student.class); String james = student.say("james"); System.out.println(james); } }

    (1)前置加強:關鍵字:@Before,JoinPoint,execution切點表達式,表達式內容支持通配符;

@Aspect
@Component
public class LoggingAspect { @Before("execution(String say(String))") public void before(JoinPoint point) { System.out.println("before"); } }

控制檯輸出:

before
hello,james

    (2)後置加強:關鍵字:@After

@Aspect
@Component
public class LoggingAspect { @After("execution(String say(String))") public void afterAdvice(JoinPoint point) { System.out.println("after..."); } }

控制檯輸出:

hello,james

after...

    (3)環繞加強,關鍵字:@around,Proceeding JoinPoint

將Student類還原,切面類:

@Aspect
@Component
public class LoggingAspect { @Around("execution(String say(String))") public Object aroundAdvice(ProceedingJoinPoint point) throws Throwable { before();  Object result = point.proceed(); after(); return result; } private void before(){ System.out.println("before"); } private void after(){ System.out.println("after"); } }

控制檯輸出:

before

hello james

after

注意:

  <1>加強方法的返回值爲Object類型,該返回值與目標方法返回值一致

  <2>Object rusult = point.proceed();該result即爲目標方法執行後的返回值

  <3>在環繞通知中須要明確調用ProceedingJoinPoint的proceed()方法來執行被代理的方法,若是忘記這樣作會致使通知被執行了,可是目標方法沒有被執行;

  <4>環繞通知的方法須要返回目標方法執行以後的結果,即調用joinPoint.proceed()的返回值,不然會出現空指針異常

    (4)返回加強:關鍵字:@AfterReturning,returning,JoinPoint

@Aspect
@Component
public class LoggingAspect { @AfterReturning(value = "execution(String say(String))", returning = "str") public void aferRetruningAdvice(JoinPoint point, String str) { System.out.println("str:" + str); System.out.println("aferRetruningAdvice"); } }

控制檯輸出:

str:hello,james
aferRetruningAdvice
hello,james

    (4)拋出加強:關鍵字:@AfterThrowing,throwing。注意:拋出的異常類型必須和切面拋出加強接收的異常類型相同或是其子類。

更改Student類,手動拋出一個異常:

@Component
public class Student implements Person{
    @Override
    public String say(String name) {
        throw new RuntimeException("exception");
    }
}

切面類:

@Aspect
@Component
public class LoggingAspect { @AfterThrowing(value = "execution(String say(String))", throwing = "e") public void AfterThrowingAdvice(JoinPoint point, Exception e) { String message = e.getMessage(); System.out.println(message); System.out.println("AfterThrowingAdvice"); } }

控制檯輸出:

exception
AfterThrowingAdvice

---------------------------------------------------------------------------------------------------------------------------

    2,經過切點註解表達式(AspectJ @annotation)進行攔截

開發步驟:

  (1)自定義定義註解類

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AuthorityTag {}

  (2)爲切面類中加強指定註解表達式

@Aspect
@Component
public class AuthorityAspect { @Before("@annotation(com.nucsoft.spring.annotation.AuthorityTag)") public void before(JoinPoint point) { System.out.println("authority before"); } }

  (3)在目標類目標方法上標註註解

@Component
public class Car implements Wheel{ @AuthorityTag @Override public void run() { System.out.println("i am a car, i can run."); } }

------------------------------------------------------------------------------------------------------------------------------

各類加強的使用:

1,前置加強
上面的步驟就是一個前置加強

控制檯輸出:

authority before
i am a car, i can run.

2,後置加強

@Aspect
@Component
public class AuthorityAspect { @Before("@annotation(com.nucsoft.spring.annotation.AuthorityTag)") public void before(JoinPoint point) { System.out.println("authority before"); } @After("@annotation(com.nucsoft.spring.annotation.AuthorityTag)") public void afterAdvice(JoinPoint point) { System.out.println("authority after"); } }

控制檯輸出:

authority before
i am a car, i can run.
authority after

3,爲每一個加強使用不一樣的註解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface BeforeAuthorityTag {} @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface AfterAuthorityTag {}

切面:

@Aspect
@Component
public class AuthorityAspect { @Before("@annotation(com.nucsoft.spring.annotation.BeforeAuthorityTag)") public void before(JoinPoint point) { System.out.println("authority before"); } @After("@annotation(com.nucsoft.spring.annotation.AfterAuthorityTag)") public void afterAdvice(JoinPoint point) { System.out.println("authority after"); } }

使用:

@Component
public class Car implements Wheel{ @BeforeAuthorityTag @AfterAuthorityTag @Override public void run() { System.out.println("i am a car, i can run."); } }

控制檯輸出:

authority before
i am a car, i can run.
authority after

4,環繞加強

註解類:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AroundAuthorityTag {}

切面加強:

@Around(value = "@annotation(com.nucsoft.spring.annotation.AroundAuthorityTag)")
public Object aroundAdvice(ProceedingJoinPoint point) throws Throwable {   Object result = point.proceed();   after();   System.out.println("authority aroundAdvice");   return result; } private void after() {   System.out.println("after"); }

 

目標類:

@Component
public class Car implements Wheel{ @AroundAuthorityTag @Override public void run() { System.out.println("i am a car, i can run."); } }

控制檯輸出:

i am a car, i can run.
after
authority aroundAdvice

(5)返回加強

註解類:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AfterReturningAuthorityTag {}

切面加強:

@AfterReturning(value = "@annotation(com.nucsoft.spring.annotation.AfterReturningAuthorityTag)", returning = "result")
public void afterReturningAdvice(JoinPoint point, Object result) {   System.out.println("authority afterReturning"); }

目標類:

@Component
public class Car implements Wheel{ @AfterReturningAuthorityTag @Override public void run() { System.out.println("i am a car, i can run."); } }

控制檯輸出:

i am a car, i can run.
authority afterReturning

(6)拋出加強

註解類:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AfterThrowingAuthorityTag {}

切面加強:

@AfterThrowing(value = "@annotation(com.nucsoft.spring.annotation.AfterThrowingAuthorityTag)", throwing = "e")
public void afterThrowAdvice(JoinPoint point, Exception e) {   System.out.println(e.getMessage());   System.out.println("authority afterThrowing"); }

目標類:

@Component
public class Car implements Wheel{ @AfterThrowingAuthorityTag @Override public void run() { System.out.println("i am a car, i can run."); throw new RuntimeException("throw a new runtimeException"); } }

控制檯輸出:

i am a car, i can run.
throw a new runtimeException

java.lang.RuntimeException: throw a new runtimeException
authority afterThrowing

(7)引入加強:關鍵字:@DeclareParents

被代理的類與接口實現類實現了同一個接口,也就是說,A類被加強(被接口代理,實現接口),接口實現類B類與A類共同實現了接口,在A類多態轉向接口時,能夠直接調用接口實現類B類中的方法

將要引入的接口:

public interface Fly {
    void fly(); }

將要引入的接口的實現:

public class SuperMan implements Fly{
    @Override
    public void fly() { System.out.println("i am super man, i can fly."); } }

切面類:

@Aspect
@Component
public class LoggingAspect {   @DeclareParents(value = "com.nucsoft.spring.target.impl.Student", defaultImpl = SuperMan.class) private Fly fly; }

測試類:

public class SpringTest {
    @Test
    public void test01() { ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml"); Person student = context.getBean(Student.class); String james = student.say("james"); System.out.println(james); Fly fly = (Fly) student; fly.fly(); } }

控制檯輸出:

hello,james
i am super man, i can fly.

說明:

1,在Aspect類中定義一個須要引入加強的接口,它也就是運行時須要動態實現的接口。

2,@DeclareParents註解:

  value屬性:目標類,能夠是一個Aspect類型的表達式,能夠引入到多個類中;

  defaultImpl屬性:引入接口的默認實現類。

3,雖然切面類中標註有@DeclareParents註解的屬性能夠是任意的,可是通常仍是將其設置爲引入加強類型。

4,從ApplicationContext中獲取到的student對象實際上是一個代理對象,能夠轉型爲本身靜態實現的接口Person,也能夠轉型爲動態實現接口fly,切換起來很是方便。【我的感受有點像包裝類型與基本數據類型的裝箱與拆箱】

 

注意:

  1,能夠爲多個加強使用同一個註解;

  2,也能夠爲每一個加強使用不一樣的註解【例以下面的例子,能夠爲Controller層和Service層分別編寫各自的自定義註解,或者在同一個方法上使用不一樣的註解】;

 

    3.3對比基於xml聲明的切點表達式和註解表達式

註解表達式:更加靈活,可是在開發過程當中,須要程序員手動的去爲每一個須要添加加強的方法添加對應的註解,更加容易擴散。

切點表達式:能夠寫出通用的加強,也不須要程序員手動的去爲每一個方法添加加強,可是須要切點表達式適配。

------------------------------------------------------------------------------------------------------------------------------

 小結:

1,利用方法簽名編寫AspectJ切點表達式:execution * com.alan.spring.Calcultor.*(..);匹配Calcultor中聲明的全部方法;

第一個*表明任意修飾符以及任意返回值,第二個*表明任意方法..匹配任意數量的參數,若目標類與接口與該切面在同一個包中,能夠省略包名。

execution public * com.alan.spring.Calcultor.*(..); 匹配ArthmeticCalculator接口的全部公共方法;

execution public double com.alan.spring.Calcultor.*(..);匹配Calculator中返回double類型的全部公共方法

execution * com.alan.spring.Calcultor.*(double,..);匹配Calculator中第一個參數爲double類型的方法

 ecution * com.alan.spring.Calcultor.*(double,double);匹配Calculator中參數爲double,double類型的方法

2,能夠合併切點表達式 使用&& || ! 來合併,如:

execution(void run()) || execution(void say())

3,切面優先級

能夠經過實現Order接口或者利用@Order註解指定

  (1)實現Orderd接口,getOrder()方法返回的值越小,優先級就越高

  (2)使用@Order註解,須要出如今註解中,一樣是值越小優先級越高。

4,重用切點定義

在Aspect切面中,能夠經過@pointcut註解講一個切入點聲明成簡單的方法,切入點的方法體一般是空的,由於將切入點定義與應用程序邏輯混在一塊兒是不合理的。

切入點方法的訪問控制符同時也控制着這個切入點的可見性【也就是與目標鏈接的條件】。若是切入點要在多個切面中共用,最好將它們集中在一個公共的類中【抽取成父類】。

在這種狀況下,它們必須被聲明爲public。在引入這個切入點時,必須將類名也包括在內,若是類沒有與這個切面放在同一個包中,還必須包含包名。

其餘加強通知能夠經過方法名稱引入該切入點。

例如:

@Pointcut("execution(void run())")
public void LoggingPointcut(){}; @Before("LoggingPointcut()") public void before() {   System.out.println("before"); }

 

總結:

  1,在學習springAOP以前,最好先理解了靜態代理(普通對象),JDK動態代理(接口),CGLIB動態代理(類)。其springaop就是一個攔截,能夠定義權限認證,統一日誌,統一異常,事務,減小代碼複用性等等,很強大。

  2,明白切面和加強以及切點之間的關係:切面包含加強和切點,切點能夠經過註解條件/通則表達式找到目標類/方法,加強(通知)就是對目標的加強(前置,後置,環繞)。

  3,幾個加強之間的區別

  除了環繞加強外,全部的鏈接點使用的都是JoinPoint類型的入參,而環繞加強使用的是ProceedingJoinPoint

  返回加強能夠接收到返回值

  拋出加強能夠接收到拋出的異常

  環繞加強的返回值類型爲目標方法返回值類型

  4,springaop支持xml聲明式和註解方式,註解優先。

  5,步驟:

    1,定義切面,以及加強,編寫切點表達式

    2,若是切點表達式是基於註解的,還須要對目標方法添加對應的註解。

=====================================轉喬志勇【csdn】=============================

 

2、小demo【經過切點註解表達式、織入方式對其加強】

==============================================小demo開始==============================================

  1.建立一個maven_web工程【具體參考maven-web工程的建立】,而後導入依賴

 <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <version.spring>4.3.3.RELEASE</version.spring>
  </properties>

  <dependencies>
      <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.11</version>
        <scope>test</scope>
      </dependency>

      <!-- javax.servlet -->
      <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>servlet-api</artifactId>
        <version>2.5</version>
      </dependency>
      <!-- spring framework -->
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        <version>${version.spring}</version>
      </dependency>
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>${version.spring}</version>
      </dependency>
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>${version.spring}</version>
      </dependency>
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>${version.spring}</version>
      </dependency>
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-orm</artifactId>
        <version>${version.spring}</version>
      </dependency>
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${version.spring}</version>
      </dependency>
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-support</artifactId>
        <version>${version.spring}</version>
      </dependency>
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>${version.spring}</version>
      </dependency>
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>${version.spring}</version>
      </dependency>
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>${version.spring}</version>
      </dependency>
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>${version.spring}</version>
        <scope>test</scope>
      </dependency>
      <dependency>
        <groupId>javax.inject</groupId>
        <artifactId>javax.inject</artifactId>
        <version>1</version>
      </dependency>
      <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>2.2.2</version>
      </dependency>
      <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.10</version>
      </dependency>
      <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-1.2-api</artifactId>
        <version>2.6.2</version>
      </dependency>
      <!-- 切面aspectj -->
      <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.8.8</version>
      </dependency>
      <!-- springmvc依賴jar包jackson -->
      <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-core</artifactId>
        <version>2.6.2</version>
      </dependency>
      <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.6.2</version>
      </dependency>
      <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-annotations</artifactId>
        <version>2.6.2</version>
      </dependency>
      <!-- fastjson -->
      <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.1.39</version>
      </dependency>
  </dependencies>

  2.配置springmvc 與spring配置文件

關於spring-servlet.xml與applicationContext.xml的區別:
1,用途不一樣: applicationContext-*.xml文件一般用於加載spring系統級別的組件,好比bean的初始化; spring-servlet.xml文件一般用於加載controller層須要的類,好比攔截器,mvc標籤加載的類 2,加載位置不一樣: applicationContext-*.xml加載在標籤中,做位FrameworkServlet的參數屬性 spring-servlet.xml文件當作DispatcherServlet的參數屬性進行加載 3,補充: classpath*與classpath的區別: classpath:指classes路徑下文件 classpath*:除了classes路徑下文件,還包含jar包中文件

 

 

 

 

 

 

 

 

  3,而後咱們建立一個接口,並配置好web.xml

  4,能夠訪問成功,使用postman便可【接口不要忘記添加responseBody註解】

  5,編寫自定義註解,由於此springaop代碼是經過註解方式實現,其實也能夠經過xml形式實現,網上有不少相關資料,由於公司項目用的註解,我理解的也快,也方便記憶,好維護。

  springaop是經過此註解定義切點,而後定位到對應的鏈接點;

  

/**
 * 此註解做用到方法和參數上,運行時加載有效,這個切點對應的註解做用於Controller層上
 */
@Target({ElementType.METHOD,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ControllerLogAnnotation { /** * 描述 */ String description() default ""; /** * 是否記錄返回內容 */ boolean isContent() default false; /** * 返回內容是否須要加密 */ boolean isContentEncrypted() default false; /** * 參數是否須要加密 */ boolean isParametersEncrypted() default false;

 

/**
 * 這個切點對應的註解做用於service層
 */
@Target({ElementType.PARAMETER,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ServiceLogAnnotation { /** * 描述 */ String description() default ""; /** * 是否記錄返回內容 */ boolean isContent() default false; /** * 返回內容是否須要加密 */ boolean isContentEncrypted() default false; /** * 參數是否須要加密 */ boolean isParametersEncrypted() default false; }

  6,編寫切面類,idea很智能,在新建時直接能夠新建一個切面類,新建Aspect,選擇下拉@Aspect

  7,關於切面類:

    • 首先配置切點,根據這個切點找到對應的自定義註解上,也就鏈接上實際註解對應的方法

  

     //service aspect 服務層切點
    @Pointcut("@annotation(ServiceLogAnnotation)")
    public void serviceAspect() { } //controller aspect 控制層切點 @Pointcut("@annotation(ControllerLogAnnotation)") public void controllerAspect() { }
    • 而後開始配置通知,根據鏈接點能夠獲取相應的信息,以及session等,其實就是對這個切點進行動態加強,原本這個代理切點方法中沒有任何內容,鏈接點會從新對這個切點對應的方法進行加強,前置、後置、環繞等;
    • 這裏只以before前置通知爲例
/**
     * 前置通知 用於攔截Controller層記錄用戶的操做
     *
     * @param joinPoint 切點
     */
    @Before("controllerAspect()") public void doBefore4control(JoinPoint joinPoint) { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); HttpSession session = request.getSession(); //讀取session中的用戶 User user = (User) session.getAttribute("USER"); if( user == null ) { user = new User(); user.setId("1"); user.setName("測試帳號"); } //請求的IP String ip = request.getRemoteAddr(); try { //*========控制檯輸出=========*// System.out.println("=====control 前置通知開始====="); System.out.println("請求方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()")); System.out.println("方法描述:" + getControllerMethodDescription(joinPoint)); System.out.println("請求人ID:" + user.getId()); System.out.println("請求人NAME:" + user.getName()); System.out.println("請求IP:" + ip); System.out.println("=====前置通知結束====="); Map<String, Object> map = new HashMap<>(); map.put("controlPath", (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()")); map.put("menthodDescr", getControllerMethodDescription(joinPoint)); map.put("id", user.getId()); map.put("name", user.getName()); map.put("ip", ip); map.put("date", new Date()); } catch (Exception e) { //記錄本地異常日誌 logger.error("==前置通知異常=="); logger.error("異常信息:{}", e.getMessage()); } }

  7,而後在你想要加強的方法上標記你的自定義註解便可。這樣,在執行這個方法前,就會執行前置通知方法中對應的操做;

@ControllerLogAnnotation(description = "測試test")

控制檯輸出結果:

==============================================小demo結束==============================================

相關文章
相關標籤/搜索