天天看點

Spring架構(二)AOP基礎概述1、AOP簡介# 2、AOP的實作3、AOP的注解配置4、日志跟蹤案例結束

超詳細的Java知識點路線圖

概述

本文介紹Spring架構的另一個重要特性:AOP

1、AOP簡介

1.1 AOP基本概念

AOP(Aspect Oriented Programming)面向切面程式設計,是OOP的一種重要補充,也是Spring的另一個核心。

OOP是基于封裝、繼承、多态的程式設計思想,關注類之間縱向關系;AOP關注橫向關系,能夠為多個互相沒有關系,又都需要某些共同功能的類,提供一些通用服務(如:日志、權限、緩存、事務等)。

1.2 AOP的作用

代碼解耦,可以把與類的核心業務無關,又都需要的功能封裝起來,讓類隻關注自己的核心業務,分離了系統的核心業務和非核心業務。

Spring架構(二)AOP基礎概述1、AOP簡介# 2、AOP的實作3、AOP的注解配置4、日志跟蹤案例結束

1.3 AOP的術語

1、切面(Aspect)

對哪些方法進行攔截,攔截後怎麼處理,這些關注點稱之為切面

2、連接配接點(joinpoint)

被攔截到的點,因為Spring隻支援方法類型的連接配接點,是以在Spring中連接配接點指的就是被攔截到的方法,實際上連接配接點還可以是字段或者構造器

3、切入點(pointcut)

對連接配接點進行攔截的定義

4、通知(advice)

所謂通知指的就是指攔截到連接配接點之後要執行的代碼,通知分為前置、後置、異常、最終、環繞通知五類

5、目标對象(target)

代理的目标對象,将切面應用到目标對象并導緻代理對象建立的過程

6、引入\織入(introduction、weave)

在不修改代碼的前提下,引入可以在運作期為類動态地添加一些方法或字段

# 2、AOP的實作

2.1 AOP配置步驟

2.1.1 引入依賴

<dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>5.2.8.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.9.5</version>
        </dependency>

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.5</version>
        </dependency>
           

2.1.2 編寫通知類

/**
 * 日志輸出通知類
 */
public class LogAdvise {

    public void beforeLog(){
        System.out.println("方法開始執行!");
    }

    public void afterLog(){
        System.out.println("方法後置執行!");
    }

    public void afterReturning(){
        System.out.println("方法傳回了資料");
    }

    public void afterThrowing(){
        System.out.println("方法抛出了異常");
    }

    public void around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("around方法名:" + joinPoint.getSignature().getName());
        System.out.println("around --前置");
        //原來方法
        joinPoint.proceed();
        System.out.println("around --後置");
    }
}
           

2.1.3 AOP的配置

<?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 https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--配置包的掃描-->
    <context:component-scan base-package="com.blb.aop_demo"></context:component-scan>
    <!--配置通知類-->
    <bean id="logAdvise" class="com.blb.aop_demo.util.LogAdvise"></bean>
    <!--配置切面-->
    <aop:config>
        <!--配置切入點-->
        <aop:pointcut id="pc" expression="execution(* com.blb.aop_demo.service.*Service.*(..))"/>
        <!--配置切面 ref是通知類的bean-->
        <aop:aspect id="aspect1" ref="logAdvise">
            <!--前置通知 method是對應的通知方法 pointcut-ref是切入點-->
            <aop:before method="beforeLog" pointcut-ref="pc"></aop:before>
            <!--後置-->
            <aop:after method="afterLog" pointcut-ref="pc"></aop:after>
            <!--後置傳回-->
            <aop:after-returning method="afterReturning" pointcut-ref="pc"></aop:after-returning>
            <!--後置抛異常-->
            <aop:after-throwing method="afterThrowing" pointcut-ref="pc"></aop:after-throwing>
            <!--環繞-->
            <aop:around method="around" pointcut-ref="pc"></aop:around>
        </aop:aspect>
    </aop:config>
</beans>
           

2.1.4 測試

在com.blb.aop_demo.service包下添加幾個Service類做測試,可以看到執行的方法加入了前置、後置、環繞等功能

ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-aop.xml");
GoodsService goodsService = context.getBean(GoodsService.class);
goodsService.queryGoods();
goodsService.queryGoodsById(1);
goodsService.createGoods();
goodsService.updateGoods();
goodsService.deleteGoodsById(1);
           
Spring架構(二)AOP基礎概述1、AOP簡介# 2、AOP的實作3、AOP的注解配置4、日志跟蹤案例結束

2.1.5 配置詳解

aop:pointcut 是切入點配置

其中的核心是expression,通過表達式控制切面應用的範圍

文法:

execution(通路修飾符 傳回值 包名.類名.方法名(參數類型,參數類型....))
           

通配符:

*代表任意長度的字元

.. 代替子包或任意參數
           

3、AOP的注解配置

3.1 常用AOP相關注解

@Aspect 切面,配置到切面類上

@PointCut(“表達式”) 配置切入點,加在方法上

@Before 配置前置通知方法

@After 配置後置通知方法

@Around 配置環繞通知方法

@AfterReturning 配置後置傳回值通知方法

@AfterThrowing 配置後置抛出異常通知方法

3.2 AOP配置

1)配置類

@ComponentScan(basePackages = "com.blb.aop_demo")
@Configuration
//啟動AspectJ的注解配置
@EnableAspectJAutoProxy
public class AopConfig {
}
           

2) 日志切面

/**
 * 日志切面
 */
@Aspect
@Component
public class LogAspect {

    //配置切入點
    @Pointcut("execution(* com.blb.aop_demo.service.*Service.*(..))")
    public void pointcut(){
    }

    //配置通知方法
    @Before("pointcut()")
    public void beforeLog(){
        System.out.println("這是前置的通知方法!!");
    }
}


           

3)測試

AnnotationConfigApplicationContext context2 = new   
                              AnnotationConfigApplicationContext(AopConfig.class);
GoodsService goodsService = context2.getBean(GoodsService.class);
goodsService.queryGoods();
goodsService.queryGoodsById(1);
goodsService.createGoods();
goodsService.updateGoods();
goodsService.deleteGoodsById(1);

           

4、日志跟蹤案例

4.1 案例概述

在實際項目部署上線後,都需要通過日志的搜集來定位出現的bug,日志跟蹤代碼如果在所有方法都寫,就會很繁瑣,代碼也不利于維護,如果使用AOP就能很好解決這個問題。

4.2 案例實作

  1. 導入log4j依賴
  2. 添加log4j.properties
  3. 編寫日志切面
/**
 * Log4j日志輸出切面
 */
@Aspect
@Component
public class Log4jAspect {

    //建立日志對象
    private Logger logger = Logger.getLogger(Log4jAspect.class);

    //給所有的service類的所有方法加日志跟蹤
    @Pointcut("execution(* com.blb.aop_demo.service.*Service.*(..))")
    public void logPointcut(){
    }

    //配置環繞通知
    @Around("logPointcut()")
    public Object aroundLog(ProceedingJoinPoint point) throws Throwable {
        //記錄方法執行前時間
        long start = System.currentTimeMillis();
        //列印方法名稱
        if(logger.isDebugEnabled()){
            logger.debug("目前執行方法:" + point.getSignature().getName());
        }
        //列印參數
        Object[] args = point.getArgs();
        for(Object arg : args){
            if(logger.isDebugEnabled()){
                logger.debug("參數:"+arg);
            }
        }
        //列印傳回值
        Object result = point.proceed();
        if(logger.isDebugEnabled()){
            logger.debug("方法傳回值:" +result);
        }
        //列印執行時間
        long end = System.currentTimeMillis();
        if(logger.isDebugEnabled()){
            logger.debug("方法執行時間:" +(end - start));
        }
        return result;
    }
}

           

結束

大家如果需要學習其他Java知識點,戳這裡 超詳細的Java知識點彙總