Spring-1-AOP概念简述-代码演示

目录

0、动态代理简述

1、JDK动态代理

1.1 JDK动态代理的代码演示

1.1.1 创建一个被代理的对象:接口SomeService

1.1.2 创建接口SomeService的实现类SomeServiceImpl

1.1.3 创建接口SomeService的增强类ServiceUtils

1.1.4 创建一个MyInvocationHandler类, 且实现InvocationHandler接口, 来增强被代理类

1.1.5 测试动态代理的效果-测试代码的基类BaseTest

1.1.6 测试动态代理的效果(通过创建动态代理来对被代理类实现方法增强)

1.1.7 测试结果与项目结构

2、AOP:面向切面编程:简述

2.1 AOP简述(掌握)

2.2 AOP面向切面编程对有什么好处?

2.3 AOP编程术语(掌握)

2.3.1 切面(Aspect)

2.3.2 连接点(JoinPoint)

2.3.3 切入点(Pointcut)

2.3.4 目标对象(Target)

2.3.5 通知(Advice)

3、AOP:面向切面编程:代码演示

3.1 AspectJ 对 AOP 的实现(掌握)

3.2 AspectJ 简介

3.3 AspectJ 的通知类型(理解)

3.4 AspectJ 的切入点表达式(掌握)

3.4.1 切入点表达式的原型解释

3.4.2 切入点表达式的原型举例:

3.5 AspectJ 实现AOP的代码演示

3.5.0 使用AspectJ实现AOP的具体步骤如下

3.5.0 创建一个Maven项目, 项目结构图

3.5.1 加入jar包依赖(Spring依赖+AspectJ依赖):pom.xml

3.5.2 创建目标类(是一个接口):SomeService.java

3.5.3 创建目标类的实现类:SomeServiceImpl.java

3.5.4 创建切面类和编写切面方法:MyAspect.java

3.5.5 创建spring.xml配置文件声名对象, 也即把对象交给Spring容器统一管理

3.5.6 创建springmvc.xml(AOP暂时用不到, 只是web项目必要的配置文件)

3.5.7 AOP的测试类基类:BaseTest.java

3.5.8 AOP的测试类和测试结果:MyTest.java

4、AOP-面向切面变成-回顾

5、AOP中的其他类型的通知-代码演示

5.1 前置通知 @Before:方法有JoinPoint参数

5.1.1 测试结果

5.2 后置通知 @AfterReturning:注解有returning属性

5.2.1 测试结果

5.3 环绕通知 @Around:增强方法有 ProceedingJoinPoint 参数

5.3.1 测试结果

5.4 异常通知 @AfterThrowing:注解中有throwing属性

5.5 最终通知 @After:总是会被执行

5.6 定义切入点 @Pointcut

5.7 目前为止的项目结构

【参考B站视频】:https://www.bilibili.com/video/BV1nz4y1d7uy?p=1

0、动态代理简述

Spring-1-AOP概念简述-代码演示_第1张图片

1、JDK动态代理

Spring-1-AOP概念简述-代码演示_第2张图片

1.1 JDK动态代理的代码演示

1.1.1 创建一个被代理的对象:接口SomeService

package com.wind.service;


import org.springframework.stereotype.Service;

@Service
public interface SomeService {

    void doSome();

    void doOther();
}

1.1.2 创建接口SomeService的实现类SomeServiceImpl

package com.wind.serviceImpl;

import com.wind.service.SomeService;
import org.springframework.stereotype.Service;

@Service
public class SomeServiceImpl implements SomeService {

    @Override
    public void doSome() {
        System.out.println("业务方法===SomeServiceImpl.doSome done...");
    }

    @Override
    public void doOther() {
        System.out.println("业务方法===SomeServiceImpl.doOther done...");
    }
}

1.1.3 创建接口SomeService的增强类ServiceUtils

package com.wind.utils;

import org.springframework.stereotype.Component;

import java.util.Date;

@Component
public class ServiceUtils {

    public static void doLog() {
        System.out.println("非业务方法===方法开始执行的时间=" + new Date());
    }

    public static void doTx() {
        System.out.println("非业务方法===方法执行完毕,提交事务的时间=" + new Date());
    }
}

1.1.4 创建一个MyInvocationHandler类, 且实现InvocationHandler接口, 来增强被代理类

package com.wind.handler;


import com.wind.utils.ServiceUtils;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class MyInvocationHandler implements InvocationHandler {

    //定义一个目标对象:即将来的someServiceImpl对象
    private Object targetObject;

    public MyInvocationHandler(Object targetObject) {
        this.targetObject = targetObject;
    }

    /**
     * 通过代理对象执行目标方法时,会执行invoke方法。
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println("动态代理===MyInvocationHandler.invoke() done...+准备执行的方法=" + method.getName());

        //定义一个方法返回值
        Object result = null;

        //动态代理-前置的方法增强
        ServiceUtils.doLog();

        //执行目标类的目标方法,底层通过Method类中的invoke方法完成
        result = method.invoke(targetObject, args);

        //动态代理-后置的方法增强
        ServiceUtils.doTx();

        //返回目标方法的返回值
        return result;
    }
}

1.1.5 测试动态代理的效果-测试代码的基类BaseTest

import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:config/spring.xml"})
public abstract class BaseTest {
}

1.1.6 测试动态代理的效果(通过创建动态代理来对被代理类实现方法增强)

import com.wind.handler.MyInvocationHandler;
import com.wind.service.SomeService;
import com.wind.serviceImpl.SomeServiceImpl;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

@Component
public class MyApp extends BaseTest {

    @Autowired
    private SomeServiceImpl someServiceImpl;

    @Test
    public void someServiceTest() {
        someServiceImpl.doSome();
        someServiceImpl.doOther();
    }

    /***
     * 使用JDK的Proxy创建动态代理
     */
    @Test
    public void someServiceProxyTest() {

        //1.创建目标类对象
        SomeServiceImpl targetService = new SomeServiceImpl();

        //2.创建InvocationHandler对象
        InvocationHandler invocationHandler = new MyInvocationHandler(targetService);

        //3.创建Proxy动态代理对象
        SomeService targetServiceProxy = (SomeService) Proxy.newProxyInstance(
                targetService.getClass().getClassLoader(),
                targetService.getClass().getInterfaces(),
                invocationHandler
        );

        //4.通过动态代理对象执行invocationHandler中的invoke方法
        targetServiceProxy.doSome();
    }
}

1.1.7 测试结果与项目结构

Spring-1-AOP概念简述-代码演示_第3张图片

Spring-1-AOP概念简述-代码演示_第4张图片

2、AOP:面向切面编程:简述

2.1 AOP简述(掌握)

AOP(Aspect Orient Programming),面向切面编程,面向切面编程是从动态角度考虑程序运行过程。AOP,面向切面编程,可通过运行期动态代理实现程序功能的统一维护的一种技术。AOP 是 Spring 框架中的一个重要内容。利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性, 同时提高了开发的效率。

面向切面编程,就是将交叉业务逻辑封装成切面,利用 AOP 容器的功能将切面织入到主业务逻辑中。所谓交叉业务逻辑是指,通用的、与主业务逻辑无 关的代码,如安全检查、事务、日志、缓存等。 若不使用 AOP,则会出现代码纠缠,即交叉业务逻辑与主业务逻辑混合在 一起。这样,会使主业务逻辑变的混杂不清。

例如,转账,在真正转账业务逻辑前后,需要权限控制、日志记录、加载事务、结束事务等交叉业务逻辑,而这些业务逻辑与主业务逻辑间并无直接关系。但是,它们的代码量所占比重能达到总代码量的一半甚至还多。它们的存在,不仅产生了大量的“冗余”代码,还大大干扰了主业务逻辑---转账。

2.2 AOP面向切面编程对有什么好处?

(1)减少重复。(2)专注业务。(3)注意:面向切面编程只是面向对象编程的一种补充。使用 AOP 减少重复代码,专注业务实现:

Spring-1-AOP概念简述-代码演示_第5张图片

2.3 AOP编程术语(掌握)

2.3.1 切面(Aspect)

切面,泛指交叉业务逻辑。上例中的事务处理、日志处理就可以理解为切面。常用的切面是通知(Advice),实际就是对主业务逻辑的一种增强。

2.3.2 连接点(JoinPoint)

连接点,是指可以被切面织入的具体方法,通常业务接口中的方法均为连接点。

2.3.3 切入点(Pointcut)

切入点,是指声明的一个或多个连接点的集合。通过切入点指定一组方法。 被标记为 final 的方法是不能作为连接点与切入点的。因为最终的是不能被修改的,不能被增强的。

2.3.4 目标对象(Target)

目标对象,是指将要被增强的对象。即包含主业务逻辑的类的对象。上例中的 StudentServiceImpl 的对象若被增强,则该类称为目标类,该类对象称为目标 对象。当然,不被增强,也就无所谓目标不目标了。

2.3.5 通知(Advice)

通知,表示切面的执行时间,Advice 也叫增强。上例中的 MyInvocationHandler 就可以理解为是一种通知。换个角度来说,通知定义了增强代码切入到目标代码的时间点,是目标方法执行之前执行,还是之后执行等。通知类型不同,切入时间不同。切入点定义切入的位置,通知定义切入的时间。

3、AOP:面向切面编程:代码演示

Spring-1-AOP概念简述-代码演示_第6张图片

Spring-1-AOP概念简述-代码演示_第7张图片

3.1 AspectJ 对 AOP 的实现(掌握)

对于 AOP 这种编程思想,很多框架都进行了实现。Spring 就是其中之 一,可以完成面向切面编程。然而,AspectJ 也实现了 AOP 的功能,且其实现方式更为简捷,使用更为方便,而且还支持注解式开发。所以,Spring 又将 AspectJ 的对于 AOP 的实现也引入到了自己的框架中。在 Spring 中使用 AOP 开发时,一般使用 AspectJ 的实现方式。

3.2 AspectJ 简介

AspectJ 是一个优秀面向切面的框架,它扩展了 Java 语言,提供了强大的切 面实现。AspetJ官网地址:https://www.eclipse.org/aspectj/

AspetJ是 Eclipse 的开源项目,官网介绍如下:

(1)a seamless aspect-oriented extension to the Javatm programming language:一种基于 Java 平台的面向切面编程的语言。
(2)Java platform compatible:兼容 Java 平台,可以无缝扩展。
(3)easy to learn and use:易学易用。

3.3 AspectJ 的通知类型(理解)

AspectJ 中常用的通知有五种类型:(1)前置通知。(2)后置通知。(3)环绕通知。(4)异常通知。(5)最终通知。

3.4 AspectJ 的切入点表达式(掌握)

AspectJ 定义了专门的表达式用于指定切入点。表达式的原型是:

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)

3.4.1 切入点表达式的原型解释

(1)modifiers-pattern:访问权限类型
(2)ret-type-pattern:返回值类型
(3)declaring-type-pattern:包名类名

(4)name-pattern(param-pattern):方法名(参数类型和参数个数)

(5)throws-pattern:抛出异常类型

(6)?:表示可选的部分以上表达式共 4 个部分。

【总结】execution(访问权限 方法返回值 方法声明(参数) 异常类型)

切入点表达式要匹配的对象就是目标方法的方法名。所以,execution 表达式中明显就是方法的签名。注意,表达式中黑色文字表示可省略部分,各部分间用空格分开。在其中可以使用以下符号:

Spring-1-AOP概念简述-代码演示_第8张图片

3.4.2 切入点表达式的原型举例:

【总结】execution(访问权限 方法返回值 方法声明(参数) 异常类型)

(1)execution(public * *(..)) :指定切入点为:任意公共方法。

(2)execution(* set*(..)) :指定切入点为:任何一个以“set”开始的方法。

(3)execution(* com.xyz.service.*.*(..)) :指定切入点为:定义在 service 包里的任意类的任意方法。

(4)execution(* com.xyz.service..*.*(..)) :指定切入点为:定义在 service 包或者子包里的任意类的任意方法。“..”出现 在类名中时,后面必须跟“*”,表示包、子包下的所有类。

(5)execution(* *..service.*.*(..)) :指定所有包下的 serivce 子包下所有类(接口)中所有方法为切入点。

(6)execution(* *.service.*.*(..)) :指定只有一级包下的 serivce 子包下所有类(接口)中所有方法为切入点。

(7)execution(* *.ISomeService.*(..)) :指定只有一级包下的 ISomeSerivce 接口中所有方法为切入点 。

(8)execution(* *..ISomeService.*(..)) :指定所有包下的 ISomeSerivce 接口中所有方法为切入点。

(9)execution(* com.xyz.service.IAccountService.*(..)):指定切入点为:IAccountService 接口中的任意方法。
(10)execution(* com.xyz.service.IAccountService+.*(..)) :指定切入点为:IAccountService 若为接口,则为接口中的任意方法及其所有 实现类中的任意方法;若为类,则为该类及其子类中的任意方法。

(11)execution(* joke(String,int))) :指定切入点为:所有的 joke(String,int)方法,且 joke()方法的第一个参数是 String,第二个参数是 int。如果方法中的参数类型是 java.lang 包下的类,可 以直接使用类名,否则必须使用全限定类名,如 joke( java.util.List, int)。

(12)execution(* joke(String,*))) :指定切入点为:所有的 joke()方法,该方法第一个参数为 String,第二个参数 可以是任意类型,如 joke(String s1,String s2)和 joke(String s1,double d2) 都是,但 joke(String s1,double d2,String s3)不是。

(13)execution(* joke(String,..))) :指定切入点为:所有的 joke()方法,该方法第一个参数为 String,后面可以有 任意个参数且参数类型不限,如 joke(String s1)、joke(String s1,String s2)和 joke(String s1,double d2,String s3)都是。

(14)execution(* joke(Object)) :指定切入点为:所有的 joke()方法,方法拥有一个参数,且参数是 Object 类 型。joke(Object ob)是,但,joke(String s)与 joke(User u)均不是。

(15)execution(* joke(Object+))) :指定切入点为:所有的 joke()方法,方法拥有一个参数,且参数是 Object 类型或该类的子类。不仅 joke(Object ob)是,joke(String s)和 joke(User u)也 是。

3.5 AspectJ 实现AOP的代码演示

3.5.0 使用AspectJ实现AOP的具体步骤如下

Spring-1-AOP概念简述-代码演示_第9张图片

Spring-1-AOP概念简述-代码演示_第10张图片

3.5.0 创建一个Maven项目, 项目结构图

参考:https://blog.csdn.net/cmm0401/article/details/111773134

Spring-1-AOP概念简述-代码演示_第11张图片

3.5.1 加入jar包依赖(Spring依赖+AspectJ依赖):pom.xml




    4.0.0

    com.wind
    ssm-web8-aop
    1.0-SNAPSHOT
    war

    
        UTF-8
        1.8
        1.8
        5.2.5.RELEASE
        3.4.6
        2.0.3
        8.0.22
        1.2.4
    
    
    

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

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

        
            org.aspectj
            aspectjweaver
            1.9.2
        

        

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

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

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

        
        
            javax.servlet
            javax.servlet-api
            4.0.1
        

        
        
            javax.servlet.jsp
            jsp-api
            2.2.1-b03
        

        
            jstl
            jstl
            1.2
        

        
        
            org.mybatis
            mybatis
            ${mybatis.version}
        

        
        
            org.mybatis
            mybatis-spring
            ${mybatis.spring.version}
        

        
        
            mysql
            mysql-connector-java
            ${mysql.version}
        

        
        
            com.alibaba
            druid
            ${druid.version}
        

        
        
            com.fasterxml.jackson.core
            jackson-core
            2.10.2
        

        
            com.fasterxml.jackson.core
            jackson-databind
            2.10.2
        

        
        
            junit
            junit
            4.13
            test
        

        
            org.springframework
            spring-test
            5.2.5.RELEASE
            test
        

    


    
        ssm-web
        
            
                
                    maven-compiler-plugin
                    3.1
                    
                        ${maven.compiler.source}
                        ${maven.compiler.target}
                    
                
                
                    maven-clean-plugin
                    3.1.0
                
                
                
                    maven-resources-plugin
                    3.0.2
                
                
                    maven-compiler-plugin
                    3.8.0
                
                
                    maven-surefire-plugin
                    2.22.1
                
                
                    maven-war-plugin
                    3.2.2
                
                
                    maven-install-plugin
                    2.5.2
                
                
                    maven-deploy-plugin
                    2.8.2
                
            
        

        
        
        
            
                src/main/java
                
                    **/*.properties
                    **/*.xml
                
            
            
                src/main/resources
                
                    **/*.properties
                    **/*.xml
                
            
        
    


3.5.2 创建目标类(是一个接口):SomeService.java

package com.wind.bao01;


/**
 * 这个目标接口
 */
public interface SomeService {

    void doSome(String name, Integer age);

    String doOther(String name, Integer age);

    String doAround(String name, Integer age);
}

3.5.3 创建目标类的实现类:SomeServiceImpl.java

package com.wind.bao01;


/**
 * 这个是目标接口的实现类,也就是目标类:被代理的对象
 */
public class SomeServiceImpl implements SomeService {

    /**
     * 目标方法:准备给目标方法做增强,在目标方法执行之前打印目标方法执行的开始时间
     */
    @Override
    public void doSome(String name, Integer age) {
        System.out.println("====目标业务方法执行了。SomeServiceImpl.doSome()====");
    }

    @Override
    public String doOther(String name, Integer age) {
        System.out.println("====目标业务方法执行了。SomeServiceImpl.doOther()====");
        return "doOther.result=abcdefg";
    }

    @Override
    public String doAround(String name, Integer age) {
        System.out.println("====目标业务方法执行了。SomeServiceImpl.doAround()====");
        return "doAround.result=abcdefg";
    }
}

3.5.4 创建切面类和编写切面方法:MyAspect.java

package com.wind.bao01;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

import java.util.Date;

/**
 * @Aspect :这是Aspectj框架中的注解,用来声名一个类是作为切面类来使用。
 * 作用:标识当前类是一个切面类。
 * 切面类:用来给业务方法增强功能的类,在这个类中写业务增强功能。
 * 使用位置:该注解定义在一个类的上面。
 */
@Aspect
public class MyAspect {

    /**
     * 在切面类中定义各种各样的切面方法:切面方法是实现业务功能增强的。
     * 切面方法定义的要求:
     * (1)公共的方法,public
     * (2)方法没有返回值
     * (3)方法名称自定义
     * (4)方法可以有参数,也可以没有参数。如果有参数,则参数不是自定义的,有几个参数类型可以使用。
     */

    /**
     * @Before :前置通知的注解。
     * 属性value:切入点表达式execution,用来标识该切面方法在哪些业务方法中被切入。
     * 位置:作用在切面方法的上面。
     * 特点:
     * (1)在目标方法执行之前先执行。
     * (2)不会改变目标方法的执行结果。
     * (3)不会影响目标方法的执行。
     */
    @Before(value = "execution( void com.wind.bao01.SomeServiceImpl.doSome(String,Integer))")
    public void myBefore() {
        //这个方法就是你的切面想要执行的功能
        System.out.println("前置通知,切面功能myBefore()=在目标方法执行之前输出时间=" + new Date());
    }

    @After(value = "execution( void com.wind.bao01.SomeServiceImpl.doSome(String,Integer))")
    public void myAfter() {
        //这个方法就是你的切面想要执行的功能
        System.out.println("后置通知,切面功能myAfter()=在目标方法执行之后输出时间=" + new Date());
    }

}

3.5.5 创建spring.xml配置文件声名对象, 也即把对象交给Spring容器统一管理




    

    
    

    
    

    
    
    

3.5.6 创建springmvc.xml(AOP暂时用不到, 只是web项目必要的配置文件)




    

    
    

    
    
        
        
        
        
    

    
    

    
    

3.5.7 AOP的测试类基类:BaseTest.java

import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:config/*.xml"})
public abstract class BaseTest {
}

3.5.8 AOP的测试类和测试结果:MyTest.java

import com.wind.bao01.SomeService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest extends BaseTest {

    @Test
    public void bao01Test() {
        String config = "config/spring.xml";
        ApplicationContext context = new ClassPathXmlApplicationContext(config);
        SomeService proxy = (SomeService) context.getBean("someServiceImpl");
        System.out.println(proxy.getClass().getName());
        proxy.doSome("yangguo", 20);
    }
}

Spring-1-AOP概念简述-代码演示_第12张图片

4、AOP-面向切面变成-回顾

Spring-1-AOP概念简述-代码演示_第13张图片

5、AOP中的其他类型的通知-代码演示

【不光前置通知的方法可以包含一个 JoinPoint 类型参数,所有的通知方法均可包含该参数,而且,该参数只能放在通知方法中的第一个参数位置上。】

5.1 前置通知 @Before:方法有JoinPoint参数

在目标方法执行之前执行。被注解为前置通知的方法,可以包含一个 JoinPoint 类型参数,该类型的对象本身就是切入点表达式。通过该参数,可获取切入点表达式、方法签名、目标对象等。

下面代码的功能:在通知方法中获取业务方法信息, 如方法签名/参数等-见方法myBefore2()。

package com.wind.bao01;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

import java.util.Arrays;
import java.util.Date;
import java.util.Objects;

/**
 * @Aspect :这是Aspectj框架中的注解,用来声名一个类是作为切面类来使用。
 * 作用:标识当前类是一个切面类。
 * 切面类:用来给业务方法增强功能的类,在这个类中写业务增强功能。
 * 使用位置:该注解定义在一个类的上面。
 */
@Aspect
public class MyAspect {

    /**
     * 在切面类中定义各种各样的切面方法:切面方法是实现业务功能增强的。
     * 切面方法定义的要求:
     * (1)公共的方法,public
     * (2)方法没有返回值
     * (3)方法名称自定义
     * (4)方法可以有参数,也可以没有参数。如果有参数,则参数不是自定义的,有几个参数类型可以使用。
     */

    /**
     * @Before :前置通知的注解。
     * 属性value:切入点表达式execution,用来标识该切面方法在哪些业务方法中被切入。
     * 位置:作用在切面方法的上面。
     * 特点:
     * (1)在目标方法执行之前先执行。
     * (2)不会改变目标方法的执行结果。
     * (3)不会影响目标方法的执行。
     */
    @Before(value = "execution( void com.wind.bao01.SomeServiceImpl.doSome(String,Integer))")
    public void myBefore() {
        //这个方法就是你的切面想要执行的功能
        System.out.println("前置通知,切面功能myBefore()=在目标方法执行之前输出时间=" + new Date());
    }

    /**
     * 指定通知方法中的参数:JoinPoint
     * JoinPoint:业务方法,要加入到切面功能的业务方法。
     * 作用是:可以在通知方法中获取业务方法执行时的信息,例如方法名称、方法参数等。
     * 如果你的通知方法中需要用到业务方法的信息,就可以加入JoinPoint来获取。
     * 这个JoinPoint参数是由框架自动赋值的,必须是在第一个参数位置上。
     */
    @Before(value = "execution( void com.wind.bao01.SomeServiceImpl.doSome(String,Integer))")
    public void myBefore2(JoinPoint joinPoint) {
        //获取方法的完整信息
        System.out.println("方法的签名(定义)=" + joinPoint.getSignature());
        System.out.println("方法的名称=" + joinPoint.getSignature().getName());
        Object[] args = joinPoint.getArgs();
        for (Object arg : args) {
            if (arg != null) {
                System.out.println("方法的名称=" + arg);
            }
        }

        //这个方法就是你的切面想要执行的功能
        System.out.println("前置通知,切面功能myBefore()=在目标方法执行之前输出时间=" + new Date());
    }

    @After(value = "execution( void com.wind.bao01.SomeServiceImpl.doSome(String,Integer))")
    public void myAfter() {
        //这个方法就是你的切面想要执行的功能
        System.out.println("后置通知,切面功能myAfter()=在目标方法执行之后输出时间=" + new Date());
    }

}

5.1.1 测试结果

Spring-1-AOP概念简述-代码演示_第14张图片

5.2 后置通知 @AfterReturning:注解有returning属性

在目标方法执行之后执行。由于是目标方法之后执行,所以可以获取到目标方法的返回值。该注解的 returning 属性就是用于指定接收方法返回值的变量名的。

所以,被注解为后置通知的方法,除了可以包含 JoinPoint 参数外, 还可以包含用于接收返回值的变量。该变量最好为 Object 类型,因为目标方法的返回值可能是任何类型。

package com.wind.bao01;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

import java.util.Arrays;
import java.util.Date;
import java.util.Objects;

/**
 * @Aspect :这是Aspectj框架中的注解,用来声名一个类是作为切面类来使用。
 * 作用:标识当前类是一个切面类。
 * 切面类:用来给业务方法增强功能的类,在这个类中写业务增强功能。
 * 使用位置:该注解定义在一个类的上面。
 */
@Aspect
public class MyAspect {

    /**
     * 在切面类中定义各种各样的切面方法:切面方法是实现业务功能增强的。
     * 切面方法定义的要求:
     * (1)公共的方法,public
     * (2)方法没有返回值
     * (3)方法名称自定义
     * (4)方法可以有参数,也可以没有参数。如果有参数,则参数不是自定义的,有几个参数类型可以使用。
     */


    /**
     * @Before :前置通知的注解。
     * 属性value:切入点表达式execution,用来标识该切面方法在哪些业务方法中被切入。
     * 位置:作用在切面方法的上面。
     * 特点:
     * (1)在目标方法执行之前先执行。
     * (2)不会改变目标方法的执行结果。
     * (3)不会影响目标方法的执行。
     */
    @Before(value = "execution( void com.wind.bao01.SomeServiceImpl.doSome(String,Integer))")
    public void myBefore() {
        //这个方法就是你的切面想要执行的功能
        System.out.println("前置通知,切面功能myBefore()=在目标方法执行之前输出时间=" + new Date());
    }

    /**
     * 注意:@Before直接是可以指定通知方法中的参数的:使用关键字 JoinPoint 来指定。
     * JoinPoint:业务方法,要加入到切面功能的业务方法。
     * 作用是:可以在通知方法中获取业务方法执行时的信息,例如方法名称、方法参数等。
     * 如果你的通知方法中需要用到业务方法的信息,就可以加入JoinPoint来获取。
     * 这个JoinPoint参数是由框架自动赋值的,必须是在第一个参数位置上。
     */
    @Before(value = "execution( void com.wind.bao01.SomeServiceImpl.doSome(String,Integer))")
    public void myBefore2(JoinPoint joinPoint) {
        //获取方法的完整信息
        System.out.println("方法的签名(定义)=" + joinPoint.getSignature());
        System.out.println("方法的名称=" + joinPoint.getSignature().getName());
        Object[] args = joinPoint.getArgs();
        for (Object arg : args) {
            if (arg != null) {
                System.out.println("方法的名称=" + arg);
            }
        }

        //这个方法就是你的切面想要执行的功能
        System.out.println("前置通知,切面功能myBefore()=在目标方法执行之前输出时间=" + new Date());
    }


    /**
     * @AfterReturning :后置通知的注解。
     * 属性:
     * (1)value:切入点表达式execution,用来标识该切面方法在哪些业务方法中被切入。
     * (2)returning 自定义的变量,表示目标方法的返回值的。注:自定义的变量名必须和通知方法饿形参名保持一致。
     * 位置:作用在切面方法的上面。
     * 特点:
     * (1)在目标方法执行之后执行。
     * (2)可以有参数。
     * (3)可以获取到目标方法的返回值,可以根据这个返回值做不同的处理功能。相当于:Object myRes = doOther();
     * (4)可以修改这个返回值。
     */
    @AfterReturning(value = "execution( String com.wind.bao01.SomeServiceImpl.doOther(String,Integer))", returning = "myRes")
    public void myAfter(Object myRes) {
        //myRes:是目标方法执行后返回的值,可以根据这个值来做你的增强功能
        System.out.println("后置通知,切面功能myAfter()=在目标方法执行之后得到的结果=" + myRes);
    }

}
    @Test
    public void bao01Test() {
        String config = "config/spring.xml";
        ApplicationContext context = new ClassPathXmlApplicationContext(config);
        SomeService proxy = (SomeService) context.getBean("someServiceImpl");
        System.out.println(proxy.getClass().getName());
        proxy.doOther("yangguo", 20);
    }

5.2.1 测试结果

Spring-1-AOP概念简述-代码演示_第15张图片

5.3 环绕通知 @Around:增强方法有 ProceedingJoinPoint 参数

在目标方法执行之前与之后执行。被注解为环绕增强的方法要有返回值,Object 类型,并且方法可以包含一个 ProceedingJoinPoint 类型的参数。接口 ProceedingJoinPoint 其有一个 proceed()方法,用于执行目标方法。若目标方法有返回值,则该方法的返回值就是目标方法的返回值。最后,环绕增强方法将其返回值返回,该增强方法实际是拦截了目标方法的执行。

package com.wind.bao01;

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

import java.util.Date;

/**
 * @Aspect :这是Aspectj框架中的注解,用来声名一个类是作为切面类来使用。
 * 作用:标识当前类是一个切面类。
 * 切面类:用来给业务方法增强功能的类,在这个类中写业务增强功能。
 * 使用位置:该注解定义在一个类的上面。
 */
@Aspect
public class MyAspect {

    /**
     * 在切面类中定义各种各样的切面方法:切面方法是实现业务功能增强的。
     * 切面方法定义的要求:
     * (1)公共的方法,public
     * (2)方法没有返回值
     * (3)方法名称自定义
     * (4)方法可以有参数,也可以没有参数。如果有参数,则参数不是自定义的,有几个参数类型可以使用。
     */


    /**
     * @Before :前置通知的注解。
     * 属性value:切入点表达式execution,用来标识该切面方法在哪些业务方法中被切入。
     * 位置:作用在切面方法的上面。
     * 特点:
     * (1)在目标方法执行之前先执行。
     * (2)不会改变目标方法的执行结果。
     * (3)不会影响目标方法的执行。
     */
    @Before(value = "execution( void com.wind.bao01.SomeServiceImpl.doSome(String,Integer))")
    public void myBefore() {
        //这个方法就是你的切面想要执行的功能
        System.out.println("前置通知,切面功能myBefore()=在目标方法执行之前输出时间=" + new Date());
    }

    /**
     * 注意:@Before直接是可以指定通知方法中的参数的:使用关键字 JoinPoint 来指定。
     * JoinPoint:业务方法,要加入到切面功能的业务方法。
     * 作用是:可以在通知方法中获取业务方法执行时的信息,例如方法名称、方法参数等。
     * 如果你的通知方法中需要用到业务方法的信息,就可以加入JoinPoint来获取。
     * 这个JoinPoint参数是由框架自动赋值的,必须是在第一个参数位置上。
     */
    @Before(value = "execution( void com.wind.bao01.SomeServiceImpl.doSome(String,Integer))")
    public void myBefore2(JoinPoint joinPoint) {
        //获取方法的完整信息
        System.out.println("方法的签名(定义)=" + joinPoint.getSignature());
        System.out.println("方法的名称=" + joinPoint.getSignature().getName());
        Object[] args = joinPoint.getArgs();
        for (Object arg : args) {
            if (arg != null) {
                System.out.println("方法的名称=" + arg);
            }
        }

        //这个方法就是你的切面想要执行的功能
        System.out.println("前置通知,切面功能myBefore()=在目标方法执行之前输出时间=" + new Date());
    }


    /**
     * @AfterReturning :后置通知的注解。
     * 属性:
     * (1)value:切入点表达式execution,用来标识该切面方法在哪些业务方法中被切入。
     * (2)returning 自定义的变量,表示目标方法的返回值的。注:自定义的变量名必须和通知方法饿形参名保持一致。
     * 位置:作用在切面方法的上面。
     * 特点:
     * (1)在目标方法执行之后执行。
     * (2)可以有参数。
     * (3)可以获取到目标方法的返回值,可以根据这个返回值做不同的处理功能。相当于:Object myRes = doOther();
     * (4)可以修改这个返回值。
     */
    @AfterReturning(value = "execution( String com.wind.bao01.SomeServiceImpl.doOther(String,Integer))", returning = "myRes")
    public void myAfter(Object myRes) {
        //myRes:是目标方法执行后返回的值,可以根据这个值来做你的增强功能
        System.out.println("后置通知,切面功能myAfter()=在目标方法执行之后得到的结果=" + myRes);
    }


    /**
     * @Around :环绕通知的注解。
     * 属性:
     * (1)value:切入点表达式execution,用来标识该切面方法在哪些业务方法中被切入。
     * 位置:作用在切面方法的上面。
     * 特点:
     * (1)它是功能最强的通知。
     * (2)在目标业务方法的前和后都可以执行增强功能。
     * (3)控制目标方法是否可以被调用执行。
     * (4)修改原来的目标方法的执行结果,影响最后的结果。
     * (5)环绕通知,类似于JDK动态代理中的InvocationHandler接口。
     * 参数:
     * (1)ProceedingJoinPoint:就等同于Method,用于执行目标方法。
     * (2)返回值,就是目标方法的执行结果,可以被修改。
     * 用处:环绕通知通常情况下是用来做事务的:在目标方法执行之前开启事务,然后执行目标方法,最后提交事务。
     */
    @Around(value = "execution( String com.wind.bao01.SomeServiceImpl.doAround(String,Integer))")
    public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {

        Object result = null;

        String name = "";
        Object[] args = proceedingJoinPoint.getArgs();
        if (args != null && args.length >= 1) {
            name = (String) args[0];
        }

        //1.在目标方法之前做增强
        System.out.println("环绕通知===目标方法之前做增强,时间=" + new Date());

        //2.执行目标方法
        if (name.equals("yangguo")) {
            result = proceedingJoinPoint.proceed();
            result = result + ",对目标方法的结果做二次修正";
        } else {
            result = "在AOP.myAround()中,我改变了目标方法执行的结果了";
        }

        //3.在目标方法之后做增强
        System.out.println("环绕通知===目标方法之后做增强,提交事务时间=" + new Date());

        //4.返回最终的结果
        return result;
    }

}
import com.wind.bao01.SomeService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest extends BaseTest {

    @Test
    public void bao01Test() {
        String config = "config/spring.xml";
        ApplicationContext context = new ClassPathXmlApplicationContext(config);
        SomeService proxy = (SomeService) context.getBean("someServiceImpl");
        System.out.println(proxy.getClass().getName());
        //proxy.doSome("yangguo", 20);
        //proxy.doOther("yangguo", 20);
        String around = proxy.doAround("yangguo1", 20);
        System.out.println(around);
    }
}

5.3.1 测试结果

(1)目标方法执行了:

Spring-1-AOP概念简述-代码演示_第16张图片

(2)目标方法没有执行:

Spring-1-AOP概念简述-代码演示_第17张图片

5.4 异常通知 @AfterThrowing:注解中有throwing属性

在目标方法抛出异常后执行。该注解的 throwing 属性用于指定所发生的异常类对象。当然,被注解为异常通知的方法可以包含一个参数 Throwable,参数名称为 throwing 指定的名称,表示发生的异常对象。

Spring-1-AOP概念简述-代码演示_第18张图片

5.5 最终通知 @After:总是会被执行

无论目标方法是否抛出异常,该增强均会被执行。

Spring-1-AOP概念简述-代码演示_第19张图片

5.6 定义切入点 @Pointcut

当较多的通知增强方法使用相同的 execution 切入点表达式时,编写、维 护均较为麻烦。AspectJ 提供了@Pointcut 注解,用于定义 execution 切入点表达式。

其用法是,将@Pointcut 注解在一个方法之上,以后所有的 execution 的 value 属性值均可使用该方法名作为切入点。

代表的就是@Pointcut 定义的切 入点。这个使用@Pointcut 注解的方法一般使用 private 的标识方法,即没有实际作用的方法。

Spring-1-AOP概念简述-代码演示_第20张图片

5.7 目前为止的项目结构

Spring-1-AOP概念简述-代码演示_第21张图片

 

 

 

 

 

你可能感兴趣的:(Spring专题,spring,aop,AspectJ实现AOP演示,JDK动态代理代码演示,SSM)