Spring——AOP

1. 相关概念

1.1 AOP 相关术语

  • 连接点(Joinpoint):所谓连接点是指那些被拦截到的点,在spring中,这些点指方法,因为spring只支持方法类型的连接,实际上,jionpoint还可以是field或类构造器。
  • 切点(Pointcut):被增强的连接点。例如:add()
  • 通知或增强(Advice):所谓通知是指拦截到 joinpoint 之后所要做的事情,分为前通知、后置通知、异常通知、最终通知、环绕通知。
  • 目标对象(Target):代理的目标对象.。如果没有 AOP,目标业务类需要自己实现所有逻辑,而在 AOP的帮助下,目标业务类只实现那些非横切逻辑的程序逻辑,而性能监视和事务管理等这些横切逻辑则可以使用 AOP 动态织入到特定的连接点上。
  • 切面(Aspect):切面由切点和增强(引介)组成,它既包括了横切逻辑的定义,也包括了连接点的定义,Spring AOP 就是负责实施切面的框架,它将切面所定义的横切逻辑织入到切面所指定的连接点中。
  • 织入(Weaving)织入是将aspects对象应用到target对象并导致proxy对象创建的过程。

1.2 通知类型

  • 前置通知(Before):在目标方法被调用之前调用通知功能;
  • 后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么;
  • 返回通知(After-returning):在目标方法成功执行之后调用通知;
  • 异常通知(After-throwing):在目标方法抛出异常后调用通知;
  • 环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为;

 1.3 spring 提供了 2 种 AOP 实现方式

  1.3.1  Schema-based

  • 每个通知都需要实现接口或类
  • 配置 spring 配置文件时在<aop:config>配置

1.3.2 AspectJ

  • 每个通知不需要实现接口或类
  • 配置 spring 配置文件是在<aop:config>的子标签
  • <aop:aspect>中配置

2. Schema-based 方式实现

2.1 前置、后置通知

2.1.1 包依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.zth.spring</groupId>
    <artifactId>spring09</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <spring.version>5.1.4.RELEASE</spring.version>
    </properties>
    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-beans -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${spring.version}</version>
        </dependency>


        <!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework/spring-expression -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-expression</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.1.4.RELEASE</version>
            <scope>test</scope>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.3</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/junit/junit -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>



    </dependencies>

    <build>
        <finalName>spring09</finalName>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>utf-8</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

2.1.2 原始类

@Component
public class Email {
    public void send(){
        System.out.println("发送中。。。");
    }

}

2.1.3 通知类

前置通知类

package com.zth.asp;

import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * @author zth
 * @Date 2019-07-19 10:26
 */
@Component
public class BeforeAdvice implements MethodBeforeAdvice {
    /**
     *
     * @param method 切点方法对象 Method 对象
     * @param args 切点方法参数
     * @param target 切点在哪个对象中
     * @throws Throwable
     */
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("发送前邮件。。。");
    }
}

后置通知类:

package com.zth.asp;

import org.springframework.aop.AfterReturningAdvice;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * @author zth
 * @Date 2019-07-19 10:26
 */
@Component
public class AfterAdvice implements AfterReturningAdvice {

    /**
     *
     * @param returnValue 切点方法返回值
     * @param method 切点方法对象
     * @param args 切点方法参数
     * @param target 切点方法所在的类对象
     * @throws Throwable
     */
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("发送邮件后。。。");
    }
}

2.1.4  spring 配置文件

<?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:annotation-config/>
    <context:component-scan base-package="com"/>

    <!--配置切面-->
    <aop:config>
        <!--配置切点-->
        <aop:pointcut id="point01" expression="execution(* com.zth.service.*.*(..))"/>
        <!--通知-->
        <aop:advisor advice-ref="beforeAdvice" pointcut-ref="point01"/>
        <aop:advisor advice-ref="afterAdvice" pointcut-ref="point01"/>
    </aop:config>

</beans>

2.1.5 测试类

package com.zth;

import com.zth.service.Email;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;


/**
 * @author zth
 * @Date 2019-07-19 10:52
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value={"/ApplicationContext.xml"})
public class MyTest {

    @Autowired
    Email email;
    @Test
    public void test(){
        email.send();
    }

}

2.1.6 执行结果

 

2.2 异常通知

2.2.1 创建异常通知类

package com.zth.asp;

import org.springframework.aop.ThrowsAdvice;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * @author zth
 * @Date 2019-07-19 11:41
 */
@Component
public class ExceptionAdvice implements ThrowsAdvice {
    public void afterThrowing(Method m, Object[] args, Object target, Exception ex) {
        System.out.println("执行异常通知");
    }
   /* public void afterThrowing(Exception ex) throws Throwable {
        System.out.println("执行异常通过-schema-base 方式");
    }*/
}
  • 新建一个类实现 throwsAdvice 接口
  • 必须自己写方法,且必须叫 afterThrowing
  • 有两种参数方式 , 必须是 1 个或 4 个
  • 异常类型要与切点报的异常类型一致

2.2.2 spring 配置文件

<!--配置切面-->
<aop:config>
<!--配置切点-->
    <aop:pointcut id="point01" expression="execution(* com.zth.service.*.*(..))"/>
    <!--通知-->
    <aop:advisor advice-ref="exceptionAdvice" pointcut-ref="point01"/>
</aop:config>

【注】其他步骤同上

 

2.2.3 执行结果

没有异常:

添加异常:

@Component
public class Email {
    public void send() throws Exception {

        System.out.println("发送中。。。");
        throw new Exception();
    }

}

执行结果:

 

2.2 环绕通知

2.2.1 新建环绕通知类

package com.zth.asp;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.stereotype.Component;

@Component
public class MyArround implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("环绕前置。。。");
        Object result = invocation.proceed();
        System.out.println("环绕后置。。。");
        return result;
    }
}

2.2.2 spring 配置文件

 <!--配置切面-->
    <aop:config>
        <!--配置切点-->
        <aop:pointcut id="point01" expression="execution(* com.zth.service.*.*(..))"/>
        <!--通知-->
        <aop:advisor advice-ref="myArround" pointcut-ref="point01"/>
    </aop:config>

【注】其他同上

2.2.3 测试结果:

 

3. AspectJ 方式实现

3.1 前置、后置通知

3.1.1 原始类

@Component
public class Email {
    public void send(){
        System.out.println("发送中。。。");
    }

}

3.1.2 通知类

package com.zth.asp;

import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class Logger {

    public void before() {
        System.out.println("before...");
    }
    public void after() {
        System.out.println("after....");
    }
    public void exception(){
        System.out.println("出错了。。。");
    }

}

 

3.1.3  spring 配置文件

<!--配置切面-->
<aop:config>
   <aop:aspect ref="logger">
	   <aop:pointcut id="point01" expression="execution(* com.zth.service.*.*(..))"/>
	   <aop:before method="before" pointcut-ref="point01"/>
	   <aop:after method="after" pointcut-ref="point01"/>
	   <aop:after-throwing method="exception" pointcut-ref="point01"/>
   </aop:aspect>
</aop:config>

3.1.4  执行结果

有异常:

没有异常:

3.2 环绕通知

3.2.1 创建通知类

package com.zth.asp;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class Logger {

    public Object myArround(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("环绕前置。。。");
        Object result = joinPoint.proceed();
        System.out.println("环绕后置。。。");
        return result;
    }



}

3.2.2 spring 配置文件

<aop:config>
   <aop:aspect ref="logger">
	   <aop:pointcut id="point01" expression="execution(* com.zth.service.*.*(..))"/>
	   <aop:around method="myArround" pointcut-ref="point01"/>
   </aop:aspect>
</aop:config>

【注】其他步骤同上

3.2.3 执行结果

  • <aop:after/> 后置通知,是否出现异常都执行
  • <aop:afte-returing />后置通知,只有当切点正确执行时 执行
  • <aop:after/>和 <aop:afte-returing />和<aop:afte-throwing/> 执行顺序和配置顺序有关

带参数的切点:

  • execution() 括号不能扩上 args
  • 中间使用 and 不能使用&& 由 spring 把 and 解析成&&
  • args() 有几个参数,arg-names 里面必须有几个参数
  • arg-names=”” 里面名称必须和通知方法参数名对应 

 

4. 使用注解(基于 Aspect)

4.1 applicationContent.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:annotation-config/>
    <context:component-scan base-package="com"/>
    //启用Aspectj自动代理
    <aop:aspectj-autoproxy />

</beans>

4.2代理类

package com.zth.asp;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class Logger {

    @Pointcut("execution(* com.zth.service.*.*(..))")
    private void anyPublicOperation() {}

    @Before("anyPublicOperation()")
    public void before() {
        System.out.println("before...");
    }
    @After("anyPublicOperation()")
    public void after() {
        System.out.println("after....");
    }
    @AfterThrowing("anyPublicOperation()")
    public void exception(){
        System.out.println("出错了。。。");
    }

    @Around("anyPublicOperation()" )
    public Object myArround(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("环绕前置。。。");
        Object result = joinPoint.proceed();
        System.out.println("环绕后置。。。");
        return result;
    }
    
}

4.3 测试结果

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值