SpringAop的一些理解和实现

问题引入(重点)

1 什么是目标对象?

2 怎么找到目标对象需要增强(修改)的方法?

3 如何对要增强(修改)的方法进行处理?

Spring AOP相关概念

Spring的AOP实现底层就是对动态代理的代码进行了封装,封装后我们只需要对需要关注的部分进行代码编写,并通过配置的方式完成指定目标的方法增强。

AOP中的常见术语

Target(目标对象)
要被增强的对象,一般是业务逻辑类的对象。

Proxy(代理)
一个类被 AOP 织入增强后,就产生一个结果代理类。

Aspect(切面)
表示增强的功能,就是一些代码完成的某个功能,非业务功能。
是切入点和通知的结合。

Joinpoint(连接点)
所谓连接点是指那些被拦截到的点。在Spring中,这些点指的是
方法(一般是类中的业务方法),因为Spring只支持方法类型的连接点。

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

Advice(通知/增强)
所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知。通知定义了
增强代码切入到目标代码的时间点,是目标方法执行之前执行,还是之后执
行等。通知类型不同,切入时间不同。
通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。

Weaving(织入).
是指把增强应用到目标对象来创建新的代理对象的过程。 spring 采用动态
代理织入,而 AspectJ 采用编译期织入和类装载期织入。

AspectJ 对 AOP 的实现

AspectJ的通知类型

  • 1.前置通知
  • 2 后置通知
  • 3.环绕通知
  • 4.异常通知
  • 5.最终通知

AspectJ的切入点表达式

AspectJ 定义了专门的表达式用于指定切入点。
表达式的原型如下:
execution(modifiers-pattern? ret-type-pattern
declaring-type-pattern?name-pattern(param-pattern)
throws-pattern?)
说明:
modifiers-pattern] 访问权限类型
ret-type-pattern 返回值类型
declaring-type-pattern 包名类名
name-pattern(param-pattern) 方法名(参数类型和参数个数)
throws-pattern 抛出异常类型
?表示可选的部分

以上表达式共 4 个部分。
execution(访问权限 方法返回值 方法声明(参数) 异常类型)
切入点表达式要匹配的对象就是目标方法的方法名。所以,execution 表达式中就是方法的签名。
PS:表达式中黑色文字表示可省略部分,各部分间用空格分开。在其中可以使用以下符号:

符号意义
*0-多个任意字符
. .用在方法参数中,表示任意个参数;用在包名后,表示当前及其子包路径
+用在类名后,表示当前及其子类;用在接口后,表示当前接口及其实现类

其中+不常用

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

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

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

注解方式实现AOP

1.创建项目依赖

<dependencies>
        <!--spring 核心依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.13.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.2.13.RELEASE</version>
        </dependency>
        <!--测试依赖-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
</dependencies>
<build>
    <plugins>
        <!--编译插件-->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.0</version>
            <configuration>
            <source>1.8</source>
            <target>1.8</target>
            </configuration>
        </plugin>
    </plugins>
</build>

2.创建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:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/springbeans.xsd
                           http://www.springframework.org/schema/aop
                           http://www.springframework.org/schema/aop/spring-aop.xsd
                           http://www.springframework.org/schema/context
                           http://www.springframework.org/schema/context/spring-context.xsd">
<!--在beans标签中 引入AOP和context约束-->
</beans>

3.创建核心业务类
接口

package com.cave.spring.service;

public interface UserService {
    void insertUser(String name);
    void updateUser(String name);
    String findUserById(int id);
    void deleteUserById(int id);
}

实现

package com.cave.spring.service;

import org.springframework.stereotype.Component;

@Component
public class UserServiceImpl implements UserService{
    @Override
    public void insertUser(String name) {
        System.out.println("插入操作");
    }

    @Override
    public void updateUser(String name) {
        System.out.println("修改操作");
    }

    @Override
    public String findUserById(int id) {
        System.out.println("查询操作");
//        String str = null;
//        str.indexOf(100);
        return "返回值:查询完成";
    }

    @Override
    public void deleteUserById(int id) {
        System.out.println("删除操作");
    }
}

4.定义切面类

package com.cave.spring.aop;

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

@Component
@Aspect
public class MyAspect {
    /**
     * Pointcut注解表示切入点表达式,方法一般声明为私有
     * 其他通知可以直接在value中通过方法名称调用
     */
    @Pointcut("execution(* com.cave.spring.service.*.*(..))")
    private void pointCut(){

    }
    //前置通知:目标对象方法执行介绍之前
    @Before("pointCut()")
    public void before(JoinPoint jp){//jp指代要处理的目标对象的方法
        System.out.println("前置通知:在目标对象方法执行之前执行:");
        System.out.println("拦截到要处理的方法的名字:");
        String name = jp.getSignature().getName();//要处理方法的名字
        System.out.println("方法名:"+name);
        System.out.println("拦截要处理的方法的参数:");
        Object[] args = jp.getArgs();
        for (Object arg : args){
            System.out.println("参数:"+arg);
        }
    }

    //后置通知:目标对象方法执行介绍之后,执行后置通知(可以获得当前方法的返回值)
    @AfterReturning(value = "pointCut()",returning = "result")
    public void afterReturning(Object result){
        System.out.println("后置通知:获得目标对象方法的返回值");
        System.out.println(result);
    }

    //异常通知:只有当目标对象的方法,出现异常,才执行异常通知,反之没有异常,不执行该通知
    @AfterThrowing(value = "pointCut()",throwing = "ex")
    public void exception(JoinPoint joinPoint,Throwable ex){
        System.out.println(joinPoint.getSignature()+"方法抛出异常");
        System.out.println(ex.getMessage());
    }

    //环绕通知:方法执行前和执行后都执行该通知
    // 环绕通知携带ProceedingJoinPoint类型的参数,环绕通知类似于动态代理的全过程
    // ProceedingJoinPoint类型的参数可以决定是否执行目标方法
    //环绕通知必须有返回值,返回值即为目标方法的返回值
    @Around(value = "pointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        System.out.println("环绕通知:目标方法执行之前执行");
        Object proceed = point.proceed();
        System.out.println("环绕通知:目标方法执行之后执行");
        return proceed;
    }

    //最终通知:在目标方法执行后(无论是否发生异常),执行的通知
    @After(value = "pointCut()")
    public void after(){
        System.out.println("最终通知:在目标方法执行后(无论是否发生异常)执行");
    }
}

5.在spring.xml配置文件中开启包扫描和注册aspectj的自动代理

<!--包扫描-->
<context:component-scan base-package="com.cave.spring.service,com.cave.spring.aop"></context:component-scan>
<!--开启注解AOP的使用-->
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
<!--aop:aspectj-autoproxy的底层是由 AnnotationAwareAspectJAutoProxyCreator 实现的,
是基于 AspectJ 的注解适配自动代理生成器。
其工作原理是,aop:aspectj-autoproxy通过扫描找到@Aspect 定义的切面类,再由切面类根据切入点找
到目标类的目标方法,再由通知类型找到切入的时间点。-->

6.测试类

public class test01 {
    @Test
    public void test01(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        UserService userService = (UserService) applicationContext.getBean("userServiceImpl");
        userService.findUserById(100);
    }
}

7.结果
在这里插入图片描述

问题导入(答案)

1.什么是目标对象?
Target(目标对象):要被增强(修改)的方法的对象。企业中,一般以业务类的对象作为目标对象。

2.怎么找到目标对象中需要增强(修改)的方法?
切入点
Joinpoint(连接点)和PointCut(切入点)
Joinpoint(连接点):指代需要增强(修改)的目标对象中的特定方法。
PointCut(切入点):切入点就是表达式,用来找到对应的要增强(处理)的目标对象中的特定的方法。(即怎么找到连接点)

3.如何对要增强(修改)的方法进行处理?
通过Advice(通知)和Aspect(切面类)
Advice(通知):指定处理方法位置(PointCut),具体处理方法
Aspect(切面类):Advice所在的类就是切面类

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值