热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

SpringAop详解二

这是SpringAop的第二篇,案例代码很详解,可以查看前文。切入点表达式executionexecution表达式是到方法级别,具体构成查看上一篇文章中SpringAop详解一完

这是Spring Aop的第二篇,案例代码很详解,可以查看前文。

切入点表达式

execution

execution表达式是到方法级别,具体构成查看上一篇文章中 Spring Aop 详解一

完全不限制(不可用,报错)

正常情况下,我们可以写出这样不会编译报错的表达式,但是执行效果就呵呵了。不知道是不是规避这种不限制的规则。也不知道是不是我写错了,有搞明白的师兄可以指点一下。

@Before("execution(* *..*(..))")

限制到包的所有方法

@Before("execution(* demo.aop.service.*(..))") //demo.aop.service包
@Before("execution(* demo.aop.service..*(..))") //demo.aop.service包及子包
@Before("execution(* demo.aop.service..save*(..))")//demo.aop.service 包及子包中sava开头的方法

参数限制

@Before("execution(* demo.aop.service..*()) ")//demo.aop.service 包及子包中 不需要参数的方法  如save()方法

//demo.aop.service 包及子包中参数列表为 (int,String) 的方法,如save(int age, String name)方法
@Before("execution(* demo.aop.service..*(int,String)) ")

//限制方法参数时,如果参数是复杂类型参数,需要将这个类型写完整:java.util.List
@Before("execution(* demo.aop.service..*(java.util.List)) ")

且或非

//表达式可以用 '&', '||' 和 '!'  做合并
//包含  demo.aop.controller 下所有类的所有方法  且 不包含 demo.aop.controller.PassportController的所有方法
@Before("execution(* demo.aop.controller..*(..)) " +
            " && !execution(* demo.aop.controller.PassportController.*(..))"
            + " || execution(* demo.aop.service..*(..))"
    )

within

within表示确切到类的所有方法,表达式只表达到类级别

//所有controller包里所有的类的方法
@Before("within(demo.aop.controller.*)")

//所有controller包,及子包 里所有的类的方法
@Before("within(demo.aop.controller..*)")

//within 确切到具体的类WithinTestController
@Before("within(demo.aop.controller.WithinTestController)")

bean

//通过bean制定 spring容器中的名为beanTestController的bean
//spring中的bean名字会默认小写首字母,或者显示的命名 @RestController("beanTestController")
@Before("bean(beanTestController)")

参数引用 args

如果我们在通知中需要获取到被切入方法(连接点)的参数,那么我们可以通过args表达式来引用。

引用

//参数引用,讲切入点的参数引入到通知里面去
args这里除了引用连接点中的参数之外,还有限制的作用,也就是它只会匹配拥有(String name, Integer age)参数的连接点
@Before("execution(* demo.aop.controller..*.*(..)) && args( name, age)")
public void cut2(String name, Integer age) {
    log.info("参数引用,讲切入点的参数引入到通知里面去");
    log.info(name);
    log.info(age.toString());
}

限制

//限制参数列表的类型为 ( ),切入没有参数的方法
@Before("execution(* demo.aop.controller..*.*(..)) && args( )")
public void cut() {
    log.info("限制参数列表的类型为 ()");
}
    
//限制参数列表的类型为 ( String, Integer)
@Before("execution(* demo.aop.controller..*.*(..)) && args( String, Integer)")
public void cut1() {
log.info("限制参数列表的类型为 ( String, Integer)");
}

引入(Introduction)

上一篇中概念中提到了引入,具体的实现我们来看一个案例

  • 切面类DeclareParentsAspect.java
package demo.aop.aspect;

import demo.aop.introduction.CommonParent;
import demo.aop.introduction.CommonParentImpl;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.DeclareParents;
import org.springframework.stereotype.Component;

@Aspect
@Component
@Slf4j
public class DeclareParentsAspect {

    //demo.spring.aop.service..* 代表demo.spring.aop.service包和子包下的所有类
    //这样声明了service包中的类都会引入一个CommonParent父接口,并用CommonParentImpl实现,形成一个代理对象commonParent
    @DeclareParents(value="demo.aop.service..*", defaultImpl= CommonParentImpl.class)
    private CommonParent parent;
    
    
    @Before("execution (* demo.aop.service.UserService.*(..)) && this(commonParent)")
    public void beforeUserService(CommonParent commonParent) {

            log.info(commonParent.getClass().toString());
            commonParent.doSomething();
    }

}

  • 具体引入的接口的实现CommonParent.java CommonParentImpl.java
package demo.aop.introduction;

public interface CommonParent {
    public void doSomething();
}

package demo.aop.introduction;

public class CommonParentImpl implements CommonParent {
    @Override
    public void doSomething() {
       log.info("doSomething");
    }
}
  • 测试接口 DeclareParentsTestController.java
package demo.aop.controller;

import demo.aop.service.RoleService;
import demo.aop.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@Slf4j
public class DeclareParentsTestController {

    @Autowired
    UserService userService;

    @Autowired
    RoleService roleService;

    @GetMapping("/declare")
    public String test(){
        userService.save();
        return "ok";
    }

}

访问http://localhost:8080/declare 运行结果如下

2020-10-21 10:22:53.645   demo.aop.aspect.DeclareParentsAspect     : class demo.aop.service.impl.UserServiceImpl$$EnhancerBySpringCGLIB$$58f5d27e
2020-10-21 10:22:53.645   demo.aop.introduction.CommonParentImpl   : 引入(Introduction)测试 doSomething

this

表达式中的this在前文 Spring Aop 详解一。这里和args类似,但这里是直接把service包中的代理类引入进来,我们输出了 commonParent的类型是demo.aop.service.impl.UserServiceImpl$$EnhancerBySpringCGLIB$$58f5d27e代理对象,并且这个代理对象通过代理得到了doSomething()方法。

@DeclareParents

该注解传入一个类似切入点表达式的表达式,让所有的demo.aop.service中的接口的spring bean都成为了代理类了。

接下来我们证明所有的的service都已经成了代理类了。我们修改一下测试接口如下

package demo.aop.controller;


import demo.aop.service.RoleService;
import demo.aop.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@Slf4j
public class DeclareParentsTestController {

    @Autowired
    UserService userService;

    @Autowired
    RoleService roleService;

    @GetMapping("/declare")
    public String test(){
        userService.save();
        return "ok";
    }


     @GetMapping("/declare2")
    public String test2(){
        log.info(roleService.getClass().toString());
        CommonParent commOnParent=(CommonParent)roleService;
        commonParent.doSomething();
        roleService.save();
        return "ok";
    }
}

访问http://localhost:8080/declare2 运行结果如下

2020-10-21 11:04:04.976   d.a.c.DeclareParentsTestController       : class demo.aop.service.impl.RoleServiceImpl$$EnhancerBySpringCGLIB$$e316d514
2020-10-21 11:04:04.977   demo.aop.introduction.CommonParentImpl   : 引入(Introduction)测试 doSomething

解读如下

  • execution (* demo.aop.service.UserService.*(..))使得beforeUserService通知不会再roleService的save方法执行。
  • @DeclareParents(value="demo.aop.service..*", defaultImpl= CommonParentImpl.class)会使得 roleService 成为一个引入了CommonParent接口实现的 代理对象
  • 因为roleService是代理对象(划重点),并且是实现了CommonParent接口,所以能够类型转换再调用doSomething()方法

目标对象引用 target

接着在DeclareParentsAspect.java中添加如下方法

@Before("execution (* demo.aop.service..*.*(..)) && target(o)")
    public void beforeUserService(Object o) {
        log.info(o.getClass().toString());

    }

访问http://localhost:8080/declare2 运行结果如下

2020-10-21 11:20:45.329   demo.aop.aspect.DeclareParentsAspect     : class demo.aop.service.impl.RoleServiceImpl

通过target表达式,我们可以引用得到目标对象目标对象就是被代理的对象,也就是未被切入,也没被代理的对象(官方文档:这个对象永远是一个被代理(proxied)对象)。

连接点对象(JoinPoint)

在前文中我们使用了环绕通知,而环绕通知中用到了连接点ProceedingJoinPoint,ProceedingJoinPoint是JoinPoint的子类。其他的通知我们可以使用JoinPoint来引入。

  • 切面 JoinPointAspect.java
package demo.aop.aspect;

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

import java.util.Arrays;


@Aspect
@Component
public class JoinPointAspect {

    //JoinPoint 接口提供了一系列有用的方法,
    // 比如 getArgs()(返回方法参数)、
    // getThis()(返回代理对象)、
    // getTarget()(返回目标对象)、
    // getSignature()(返回正在被通知的方法相关信息)和 toString() (打印出正在被通知的方法的有用信息)
    @Before("execution (* demo.aop.controller.JoinPointTestController.before(..))")
    public void jp3(JoinPoint point) {
        System.out.println(Arrays.toString(point.getArgs()));
        System.out.println(point.getThis().getClass());
        System.out.println(point.getTarget().getClass());
    }

}
  • 测试接口 JoinPointTestController.java
package demo.aop.controller;


import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class JoinPointTestController {

    @GetMapping("/join/point/Before")
    public String before(String name){
        return "ok";
    }
}

下文预告

  • 通知优先级
  • @ControllerAdvice 实现统一错误处理

完整代码 https://gitee.com/haimama/java-study/tree/master/spring-aop-demo


推荐阅读
  • SpringBoot uri统一权限管理的实现方法及步骤详解
    本文详细介绍了SpringBoot中实现uri统一权限管理的方法,包括表结构定义、自动统计URI并自动删除脏数据、程序启动加载等步骤。通过该方法可以提高系统的安全性,实现对系统任意接口的权限拦截验证。 ... [详细]
  • Java实战之电影在线观看系统的实现
    本文介绍了Java实战之电影在线观看系统的实现过程。首先对项目进行了简述,然后展示了系统的效果图。接着介绍了系统的核心代码,包括后台用户管理控制器、电影管理控制器和前台电影控制器。最后对项目的环境配置和使用的技术进行了说明,包括JSP、Spring、SpringMVC、MyBatis、html、css、JavaScript、JQuery、Ajax、layui和maven等。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 标题: ... [详细]
  • 在springmvc框架中,前台ajax调用方法,对图片批量下载,如何弹出提示保存位置选框?Controller方法 ... [详细]
  • Spring常用注解(绝对经典),全靠这份Java知识点PDF大全
    本文介绍了Spring常用注解和注入bean的注解,包括@Bean、@Autowired、@Inject等,同时提供了一个Java知识点PDF大全的资源链接。其中详细介绍了ColorFactoryBean的使用,以及@Autowired和@Inject的区别和用法。此外,还提到了@Required属性的配置和使用。 ... [详细]
  • SpringBoot整合SpringSecurity+JWT实现单点登录
    SpringBoot整合SpringSecurity+JWT实现单点登录,Go语言社区,Golang程序员人脉社 ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • 展开全部下面的代码是创建一个立方体Thisexamplescreatesanddisplaysasimplebox.#Thefirstlineloadstheinit_disp ... [详细]
  • Tomcat/Jetty为何选择扩展线程池而不是使用JDK原生线程池?
    本文探讨了Tomcat和Jetty选择扩展线程池而不是使用JDK原生线程池的原因。通过比较IO密集型任务和CPU密集型任务的特点,解释了为何Tomcat和Jetty需要扩展线程池来提高并发度和任务处理速度。同时,介绍了JDK原生线程池的工作流程。 ... [详细]
  • ALTERTABLE通过更改、添加、除去列和约束,或者通过启用或禁用约束和触发器来更改表的定义。语法ALTERTABLEtable{[ALTERCOLUMNcolu ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • 本文介绍了MVP架构模式及其在国庆技术博客中的应用。MVP架构模式是一种演变自MVC架构的新模式,其中View和Model之间的通信通过Presenter进行。相比MVC架构,MVP架构将交互逻辑放在Presenter内部,而View直接从Model中读取数据而不是通过Controller。本文还探讨了MVP架构在国庆技术博客中的具体应用。 ... [详细]
  • Asp.net Mvc Framework 七 (Filter及其执行顺序) 的应用示例
    本文介绍了在Asp.net Mvc中应用Filter功能进行登录判断、用户权限控制、输出缓存、防盗链、防蜘蛛、本地化设置等操作的示例,并解释了Filter的执行顺序。通过示例代码,详细说明了如何使用Filter来实现这些功能。 ... [详细]
  • 本文介绍了ASP.NET Core MVC的入门及基础使用教程,根据微软的文档学习,建议阅读英文文档以便更好理解,微软的工具化使用方便且开发速度快。通过vs2017新建项目,可以创建一个基础的ASP.NET网站,也可以实现动态网站开发。ASP.NET MVC框架及其工具简化了开发过程,包括建立业务的数据模型和控制器等步骤。 ... [详细]
author-avatar
ecrbw_9870105634
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有