(三)Spring的AOP思想

spring中的AOP 一,概念 1.AOP AOP(Aspect Oriented Programming),面向切面思想,是Spring的三大核心思想之一,另外两个:IOC-控制反转、DI-依赖注入 在我们的程序中,经常存在一些系统性的需求,比如权限校验、日志记录、统计等,这些代码会散落穿插在各个业务逻辑中,非常冗余且不利于维护,有多少业务操作,就要写多少重复的校验和日志记录代码,这显然是无法接受的。当然,用面向对象的思想,我们可以把这些重复的代码抽离出来,写成公共方法,这样,代码冗余和可维护性的问题得到了解决,但每个业务方法 阅读详情
title: (三)Spring的AOP思想
comments: false
date: 2018-05-25 21:01:45
top: 8
tags: Spring

AOP —> 面向切面编程

想要学习AOP思想,我们必须要理解几个名词:

横切关注点: 分布于应用中的众多功能被称为横切关注点。将横切关注点与业务逻辑相分离正是面向切面编程(AOP),横切关注点可以被模块化为特殊的类,这些类被称为切面。

通知(Advice): 定义了切面是什么以及何时使用切面。

连接点(Joinpoint): 定义了应用被通知的时机。

切点(Poincut): 定义了切面在何处使用。

切面(Aspect): 通知和切点共同定义了切面的全部内容(知道完成工作的一切事宜)。

引入(Introduction): 以上整个过程我们可以称其为引入;是实现将现有类中添加新方法和属性。

织入(Wearving): 织入是当切面应用到目标对象后创建新的代理对象的过程。

<!--more-->

那么我们用一张图来概括上面的内容吧:

解释几个点:

  1. Spring切面可以应用五种类型的通知:

    • Before —>在方法被调用之前调用通知

    • After —>在方法完成之后调用通知,无论方法执行是否成功。

    • After-returning —>在方法成功执行之后调用通知。

    • After-throwing —>在方法是抛出异常后调用通知。

    • Around —>通知包裹了呗通知的方法,在呗通知的方法调用之前后调用之后执行自定义的行为。

  2. 切面在指定的连接点被织入到目标对象中。在目标对象的声明周期里有多个点可以进行织入:

    • 编译期 —>切面在目标类编译时被织入。这种方式需要特殊的编译器。AspectJ的织入编译器就是以这种方式织入切面的。

    • 类加载期 —>切面在目标类加载到JVM时被织入。这种方式需要特殊的类加载器(ClassLoader),它可以在目标类被引入应用之前增强该目标类的字节码。AspectJ5就支持以这种方式的织入切面。

    • 运行期 —>切面在应用运行的某个时刻被织入。一般情况下,在织入切面时AOP容器会为目标对象动态的创建一个代理对象。Spring AOP就是以这种方式织入切面的。

Spring对AOP的支持

AOP领域主要以下列三种框架为主:

  • AspectJ

  • JBoss AOP

  • Spring AOP

不是所有的AOP框架都是一样的,它们在连接点的模型上可能有强弱之分,它们织入切面的方式和时机也会有所不同。但是无论哪种,创建切点来定义切面织入的连接点是AOP框架的基本功能。

Spring提供了4种各具特色的AOP支持:

基于代理的经典AOP

@AspectJ注解驱动的切面

纯POJO切面

注入式AspectJ切面(适合Spring各版本)

注意: 前三种都是Spring基于代理的AOP的变体,Spring对AOP的支持局限于方法拦截

关于Spring AOP框架的一些关键点:

  • Spring通知是Java编写的(AspectJ则是用以Java语言扩展的方式实现的)

  • Spring在运行期通知对象

  • Spring只支持方法连接点

解释:

  1. 我们必须了解一下Spring是如何实现在运行期通知对象的:

  2. 因为Spring基于动态代理,所以Spring只支持方法连接点。其他框架如AspectJ和Jboss,除了方法切点,它们还提供了字段和构造器接入点。若需要方法拦截之外的连接点可利用AspectJ来协助。

使用切点选择连接点

在Spring AOP中,需要使用AspectJ的切点表达式语言来定义切点。但是:Spring仅支持AspectJ切点指示器的一个子集(Spring是基于代理的,而某些切点表达式是与基于代理的AOP无关的)。

Spring AOP所支持的切点表达式:

arg() —>限制连接点匹配参数为指定类型的执行方法

@args() —>限制连接点匹配参数由指定注解标注的执行方法

execution() —>用于匹配是连接点的执行方法

this() —>限制连接点匹配AOP代理的Bean引用为指定类型的类

target() —>限制连接点匹配目标对象为指定类型的类

@target() —>限制连接点匹配特定的执行对象,这些对象对应的类要具备指定类型的注解

within() —>限制连接点匹配指定的类型

@within() —>限制连接点匹配指定注解所标注的类型(使用SpringAOP,方法定义在由指定的注解所标注的类里)

@annotation —>限制匹配带有指定注解连接点

编写切点

以上可以看到只有execution()是唯一的执行匹配,其他都是限制匹配的,那么我们以一张图来分析一下如何编写切点。

如上图,整个execution方法表达式以*开头,标识我们不关系方法的返回值的类型。然后我们指定了全限定类名和方法名。对于参数列表,使用(..)表示可以匹配任意的play()方法。另外:

  • 使用within()指示器限制匹配,比如within(* cn.tycoding.demo1.*)

  • 可以使用一些表达式来连接多个限定。比如&& ||

  • 使用Spring的bean()指示器,允许在切点表达式中使用Bean的ID表示Bean。bean()使用BeanID或Bean名称作为参数来限制切点只匹配特定的Bean。例如:execution(* xxx.price()) and bean(beanID)

在XML中声明切面

<aop:advisor> 定义AOP通知器

<aop:after> 定义AOP后置通知(不管被通知的方法是否执行成功)

<aop:after-returning> 定义AOP after-retturning通知

<aop:after-throwing> 定义after-throwing通知

<aop:around> 定义AOP环绕通知

<aop:aspect> 定义切面

<aop:aspectj-autoproxy> 启用@AspectJ注解驱动的切面

<aop:before> 定义AOP的前置通知

<aop:config> 顶层的AOP配置元素。大多数的<aop:*>元素必须包含在<aop:config>元素内

<aop:declare-parents> 为被通知的对象引入额外的接口,并透明的实现

<aop:pointcut> 定义切点

综上我们已经解释了Spring AOP的一下概念性问题,那么下面我们写一个小案例来体会一下Spring AOP

在写案例之前必须要提示一下,之前我们已经给出了Spring的全部jar包,其中也包含针对AOP功能的jar:

要注意这里需要Aspectjweaver的jar文件

案例

  1. 定义一个Advice.java通知类

package demo3;
public class Advice {
    public void changeBefore(){
        System.out.println("price change before...");
    }
    public void changeAfter(){
        System.out.println("price change after...");
    }
    public void errorAfter(){
        System.out.println("price change error...");
    }
}
  1. 定义一个Fruits.java接口

package demo3;
public interface Fruits {
    void price();
}
  1. 定义一个Fruits的实现类Apple.java

package demo3;
public class Apple implements Fruits {
    public void price(){
        System.out.println("apple wang change price...");
    }
}
  1. 编写配置文件spring.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-3.0.xsd
          http://www.springframework.org/schema/aop
          http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
​
    <bean id="apple" class="demo3.Apple"/>
    <bean id="advice" class="demo3.Advice"/>
    <aop:config>
        <aop:aspect ref="advice">
            <aop:before pointcut="execution(* demo3.Apple.price(..))" method="changeBefore"/>
            <aop:after pointcut="execution(* demo3.Apple.price(..))" method="changeAfter"/>
            <aop:after-throwing pointcut="execution(* demo3.Apple.price(..))" method="errorAfter"/>
        </aop:aspect>
    </aop:config>
</beans>

解释:

  1. 大多数的AOP配置元素必须在<aop:config>元素的上下文内使用。

  2. <aop:config>元素内,可以声明多个通知器、切面、或者切点。

  3. 使用<aop:aspect>元素声明一个简单的切面。

  1. Test测试类

@Test
public void run(){
    ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
    //Apple apple = (Apple) ac.getBean("apple");
    //apple.price();
    Fruits fruits = (Fruits) ac.getBean("apple");
    fruits.price();
}

打印结果:

拓展:

我们使用Test测试类中被注释的部分去获取Bean会怎样呢?

@Test
public void run(){
    ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
    Apple apple = (Apple) ac.getBean("apple");
    apple.price();
    //Fruits fruits = (Fruits) ac.getBean("apple");
    //fruits.price();
}

就会出现如上报错,为什么呢?

原因

对于Spring AOP采用两种代理方法,一种是JDK的动态代理,一种是CGLIB。当代理对象实现了至少一个接口时,默认使用JDK动态创建代理对象。当代理对象没有实现任何接口时就是使用CGLIB

综上: JDK的动态代理是基于接口的; CJLIB是基于类的

首先我们要明白Spring AOP是通过什么方式创建代理对象的(可以查看这篇博文:代理对象生成)。上面的案例中,Apple实现了接口Fruits,这一点就决定了Spring会采用JDK的动态代理方式创建代理对象。而我们使用getBean即获取到一个返回的代理对象,这个代理对象其实是和Apple一样实现了Fruits接口。那么getBean()返回的代理对象让Apple这个实现类的引用来用,只能由他们的共同接口来引用。

改进

观察spring.xml我们发现,下面的通知元素中pointcut属性值都是一样的,这是因为所有的通知都是应用到同一个切点上,那么我们就可以通过<aop:pointcut>改进上述代码:

spring.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-3.0.xsd
          http://www.springframework.org/schema/aop
          http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
​
    <bean id="apple" class="demo3.Apple"/>
    <bean id="advice" class="demo3.Advice"/>
    <aop:config>
        <aop:aspect ref="advice">
            <aop:pointcut id="price" expression="execution(* demo3.Apple.price(..))"/>
            <aop:before pointcut-ref="price" method="changeBefore"/>
            <aop:after pointcut-ref="price" method="changeAfter"/>
            <aop:after-throwing pointcut-ref="price" method="errorAfter"/>
        </aop:aspect>
    </aop:config>
</beans>

声明环绕通知

使用环绕通知可以完成之前前置通知和后置通知所实现相同的功能。首先环绕通知需要使用ProceedingJoinPoint作为方法的入参。这个对象可以让我们在通知里调用被通知方法proceed()

  1. 创建环绕通知类:AroundAdvice.java

package demo3;
import org.aspectj.lang.ProceedingJoinPoint;
public class AroundAdvice {
    public void watchPrice(ProceedingJoinPoint joinPoint) {
        try{
            //前置通知
            System.out.println("around: price change before...");
            //执行被通知的方法
            joinPoint.proceed();
            //后置通知
            System.out.println("around: price change after...");
        } catch(Throwable t){
            System.out.println("around: change error...");
        }
    }
}
  1. 在spring.xml中需要使用<aop:pointcut>即可

<?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-3.0.xsd
          http://www.springframework.org/schema/aop
          http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
​
    <bean id="apple" class="demo3.Apple"/>
    <bean id="aroundAdvice" class="demo3.AroundAdvice"/>
    <aop:config>
        <aop:aspect ref="aroundAdvice">
            <aop:pointcut id="price" expression="execution(* demo3.Apple.price(..))"/>
            <!-- 声明环绕通知 -->
            <aop:around method="watchPrice" pointcut-ref="price"/>
        </aop:aspect>
    </aop:config>
</beans>

通知方法可以完成任何他需要做的事情,如果我们想讲控制权转给被通知的方法时,就可以调用proceed()实现。

为通知传递参数

  1. Fruits.java

package demo3;
public interface Fruits {
    void changePrice(int price);
    int getPrice();
}

在接口定义声明一个方法changePrice(),并在其参数列表中写入一个参数。为了方便观察通知前后参数的变化,在这里又声明一个方法getPrice()。

  1. Apple.java

package demo3;
public class Apple implements Fruits {
    private int price;
    {
        System.out.println("at first,apple price is: " + price);
    }
    public void changePrice(int price) {
        this.price = price;
    }
    public int getPrice() {
        return price;
    }
}

为了观察参数price的初始值,我们写了一个代码块打印price的初始值。因为Spring AOP是采用动态代理方式创建代理对象的,所以都要定义接口和实现类,这里Apple实现了Fruits.

  1. 创建实现传递参数功能的通知类:ParamAdvice.java

package demo3;
public class ParamAdvice  {
    private int price;
    public void changePrice(int price) {
        System.out.println("boss decision change price...");
        this.price = price;
    }
}

这是一个可以传递参数的通知类,可以看到,这与普通的类并没有什么区别,只是多了一个参数而已。

  1. spring.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-3.0.xsd
          http://www.springframework.org/schema/aop
          http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
    
    <!-- 通知类 -->
    <bean id="paramAdvice" class="demo3.ParamAdvice"/>
    <!-- 目标类 -->
    <bean id="apple" class="demo3.Apple"/>
    <aop:config>
        <aop:aspect ref="paramAdvice">
            <aop:pointcut id="fruits" expression="execution(* demo3.Fruits.changePrice(int)) and args(price)"/>
            <aop:after method="changePrice" pointcut-ref="fruits" arg-names="price"/>
        </aop:aspect>
    </aop:config>
</beans>

织入一个可以实现传递参数的通知,我们首先要在<aop:config>下的<aop:aspect>中引入通知类的id;然后可以配置一个通用的切入点<aop:pointcut>,在使用execution()编写切点有所不同,首先在切入的方法changePrice()中指明切入的参数数据类型,再通过and args()来指明切入点的参数名称。

  1. Test测试类

@Test
public void run() {
    ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
    Fruits fruits = (Fruits) ac.getBean("apple");
    fruits.changePrice(12);
    System.out.println("now,this apple price is :" + fruits.getPrice());
}

这里还是要注意:因为Spring AOP采用JDK的动态代理和CGLIB的方式创建代理对象,所以对于这里有接口的实现类就必须用实现类和代理对象够共同具有的接口来接收getBean()获取的代理对象,然后我们就可以通过Fruits接口中的changePrice()设置参数的值。

通过切面引入新功能

之前我们一直探讨的是Spring AOP如何编写一个简单的通知类,即使上面我们实现了为通知传递参数,也仅仅是在目标类原有方法上进行的操作。其实Spring AOP可以借助<aop:declare-parent>元素来为目标对象添加信息的功能。我们用一张图来看一下实现流程:

实现代码

  1. 创建一个新的接口Orange.java

package demo3;
public interface Orange {
    void wantChangePrice();
}
  1. 创建Orange接口的实现类

package demo3;
public class OrangeAdvice implements Orange {
    public void wantChangePrice() {
        System.out.println("It's new method.Orange want  change price...");
    }
}
  1. spring.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-3.0.xsd
          http://www.springframework.org/schema/aop
          http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
​
    <bean id="orangeAdvice" class="demo3.OrangeAdvice"/>
    <aop:config>
        <aop:aspect>
            <aop:declare-parents types-matching="demo3.Fruits+" implement-interface="demo3.Orange" delegate-ref="orangeAdvice"/>
        </aop:aspect>
    </aop:config>
</beans>

<aop:declare-parents>声明了此切面所通知的Bean在它的对象层次结构中拥有新的父类型;types-matching指明创建的新类型匹配此接口;delegate-ref或default-impl都是指明匹配接口的实现类,区别是前者指明一个引用Bean,后置是直接写Bean的全限定名。

  1. Test测试类

@Test
public void run() {
    ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
    Orange orange = (Orange) ac.getBean("orangeAdvice");
    orange.wantChangePrice();
}

注解切面

Spring AOP提供了一个@AspectJ注解,实现了使其不需要额外的类或Bean声明就能将它转换成一个切面。不同的通知类型对应不同的注解,如:@Before() @After() @AfterReturning() @AfterThrowing()。下面我们通过一个案例来观察:

  1. 首先新创建一个Melon类

package demo3;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class Melon {
    //使用注解声明Bean对象
    @Bean
    public Melon melon(){
        return new Melon();
    }
    public void changePrice(){
        System.out.println("melon also need change price...");
    }
}

如果使用注解注入一个Bean对象,要使用@Configuration标记Java类,并通过@Bean标记一个return 对象的方法,告诉Spring这个方法将返回一个对象,且该对象应该被注册为Spring上下文中的一个Bean。

  1. 创建一个基于注解的通知类AnnoAdvice

package demo3;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Aspect
@Configuration
public class AnnoAdvice {
    @Pointcut("execution(* demo3.Melon.changePrice(..))")
    public void price(){
    }
    //后置通知
    @After("price()")
    public void changeAfter(){
        System.out.println("after melon price change...");
    }
    //使用注解注入Bean对象
    @Bean
    public AnnoAdvice annoAdvice (){
        return new AnnoAdvice();
    }
}

使用@Aspect标记的类会被Spring转换为一个切面,那么我们可以用@Pointcut定义一个切点,但是,注意被@Pointcut标记的方法内容并无意义,但是此方法名称则是切点名称,该方法文本只是一个标识,供@pointcut注解依附。

  1. spring.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-3.0.xsd
          http://www.springframework.org/schema/aop
          http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
    <!-- 开启注解扫描 -->
    <context:component-scan base-package="demo3"/>
    <!-- 声明自动代理的Bean,该Bean知道如何把@AspectJ注解所标注的Bean转变为代理通知 -->
    <aop:aspectj-autoproxy/>
</beans>

通过<context:component-scan>可以实现注解扫描,这样我们在类中写的注解就会被扫描到。<aop:aspectj-autoproxy>是在Spring上下文中声明一个自动代理的Bean,这样Spring就会将被@Aspect注解标记的Bean定义为一个切面,而Spring自动代理的Bean就会与用@Pointcut注解定义的切点相匹配。

  1. Test测试类

@Test
public void run2(){
    ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
    Melon melon = (Melon) ac.getBean("melon");
    melon.changePrice();
}

注意

  1. <aop:aspectj-autoproxy>仅仅使用@AspectJ注解作为指引来创建基于代理的切面,但本质上它仍然是一个Spring风格的切面。这就意味着我们仍然限于代理方法的调用,想使用AspectJ的全部功能就必须在运行时使用AspectJ并不依赖Spring来创建基于代理的切面。

    1. <aop:aspect>元素和@Aspect注解都是把一个POJO转变成一个切面的有效方式。但是<aop:aspect>相比@Aspect优势就是不需要实现切面的代码,而@Aspect必须标注类和方法,需要由源码。

注解环绕通知

使用Spring的@Around注解就可以实现环绕通知,Demo如下:

我们改进上面的AnnoAdvice.java

package demo3;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.context.annotation.Configuration;
@Aspect
@Configuration
public class AnnoAdvice {
    @Pointcut("execution(* demo3.Melon.changePrice(..))")
    public void price(){
    }
    //创建环绕通知
    @Around("price()")
    public void aroundAdvice(ProceedingJoinPoint joinPoint){
        try {
            System.out.println("before, it's around advice...");
            joinPoint.proceed();
            System.out.println("after, it's around advice...");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }
}

同样,@Around标注的方法也需要一个ProceedingJoinPoint对象作为入参,以便可以通过proceed()调用被代理的方法。

传递参数给所标注的通知

通过注解传递参数与XML传递参数相似,只需要改变切点的表达式就能实现。

@Pointcut("execution(* demo3.Xxx(int)) && args(xxx)")
​
@Before("Xxx(xx)")

标注引入

前面我们使用了XML实现在原有类中引入新的接口,其实使用注解也可以实现。XML的<aop:declare-parents> 对应 @AspectJ的@DeclareParents。观察一下代码:

Melon“瓜”类

package demo3;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class Melon implements WaterMelon{
    //使用注解声明Bean对象
    @Bean
    public Melon melon(){
        return new Melon();
    }
    public void changePrice(){
        System.out.println("melon also need change price...");
    }
}

创建WaterMelon“西瓜”接口

package demo3;
public interface WaterMelon {
    void changePrice();
}

通知类AnnoAdvice

package demo3;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.DeclareParents;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Aspect
@Configuration
public class AnnoAdvice {
    //使用注解注入Bean对象
    @Bean
    public AnnoAdvice annoAdvice (){
        return new AnnoAdvice();
    }
    @DeclareParents(value = "demo3.Fruits", defaultImpl = Melon.class)
    private static WaterMelon waterMelon;
}

如上所示,AnnoAdvice其实就是一个切面,通过@DeclareParents注解,为Fruits引入了一个新的接口WaterMelon,该接口的实现类是Melon,对于@DeclareParents注解由3个部分组成:

  • value属性 等同于<aop:declare-parents>的types-matching属性。它表示应该被引入指定接口的Bean类型。(目标对象)

  • defaultImpl属性 等同于<aop:declare-parents>的default-impl属性。它标识该类提供了所引入接口的实现。(引入的新接口的实现)

  • @DeclareParents 注解所标注的static指定了被引入的接口。(引入的新的接口)

<br/>

交流

如果大家有兴趣,欢迎大家加入我的Java交流群:671017003 ,一起交流学习Java技术。博主目前一直在自学JAVA中,技术有限,如果可以,会尽力给大家提供一些帮助,或是一些学习方法,当然群里的大佬都会积极给新手答疑的。所以,别犹豫,快来加入我们吧!


<br/>

联系

If you have some questions after you see this article, you can contact me or you can find some info by clicking these links.


[RK3588从入门到精通] 专栏目录及介绍 阅读详情

相关推荐

Gephi新手必看:如何用Excel整理CSV数据快速生成网络图(附模板)

本文为Gephi新手提供了一份从Excel数据整理到生成专业网络图的完整指南。详细讲解了如何构建规范的节点表和边表CSV文件,并导入Gephi进行可视化分析。通过实战案例,帮助读者快速掌握利用Excel整理数据、规避常见错误,并运用布局算法与视觉编码,将复杂关系数据转化为直观的网络洞察。

weixin_28487433的博客 389

AOP(四)之After增强处理

After增强处理 使用@After可以修饰After增强处理,它与AfterReturning增强处理有点相似,但也有区别: AfterReturing增强处理只有在目标方法成功完成后才会被织入。 After增强处理不管目标方法如何结束(包括成功完成和遇到异常中止两种情况),它都会被织入。 测试 实现类方法 @Override public void testAfter1()...

qq_40179479的博客 1347

eDP接口信号组成认识

eDP接口信号主要由Main Link、AUXCH与HPD三部分组成,如下图所示。 Main Link:表示主通道,用来传输各类型视频数据和音频数据; AUX CH:表示辅助通道,用于传输低带宽需求的数据,以及链路管理和设备控制信号; HPD:表示热插拔检测通道。 Main Link南1~4对数据线组成,每对数据线都是一对差分线。对于一款液晶屏而言,Main Link具体需几对数据线,这取决于屏幕的分辨率和彩色位数。 在该通道中传送的信号有视频像素信号、视频定时信号、视频格式信号、比特/像素及

精诚所至 2万+

AOP中After和AfterReturning的区别

样例代码: 原始类: @Repository public class UserDaoImp implements UserDao { @Override public int addUser() { System.out.println("add user ......"); return 6666; } @Override ...

Tom098的博客 2万+

2023-11-20 MySQL ARM64 银河麒麟

背景 MySQL是常用数据库,Windows安装比较容易,笔者也有记录。现在国产化系统银河麒麟中安装,不同的是软件安装包不容易下载。一是安装的方式,再一个是MySQL本身的版本,网上很多文档说明其实都是对的,但是为什么那么多都不一样呢?就是MySQL本身的版本差异比较大,这些差异笔者也不是很清楚,但是不要用8和5.7的差异非常大,8即使安装的很顺利,在使用过程也会很多坑,导致我宁愿卸载8,也要重新...

jxmh1991@163.com 808

为什么大语言模型推理要分成 Prefill 和 Decode?深入理解这两个阶段的真正意义

Prefill 阶段是语言模型推理中的第一个步骤,它负责处理你输入的所有上下文内容(prompt),为后续生成打下基础。“请解释一下 Transformer 的原理。这句话会被 tokenizer 编码为一串 token,比如["请", "解释", "一下", "Trans", "##former", "的", "原理", "。"]。然后这些 token 会进入 Transformer 模型进行前向传播。重点来了 👇。

Harry的博客 1万+

Java练习(十):Spring应用之AOP的@Before, @After操作(简单工程示例)

目录 1. 背景概念 1.1 定义 1.2 AOP的本质 1.3 AOP的实现 1.4各个名称的概念详解 2. 示例工程 2.1 maven的配置文件: pom.xml 2.2 bean配置文件: bean_aop.xml 2.3 接口和类的代码 2.3.1 接口:UserService 2.3.2实现接口的类:UserServiceImpl 2.3.3Before类:BeforeLog 2.3.4After类:AfterLog 2.4 测试类 1. 背景概念 1....

sulia1234567890的博客 9090

2026年紫光同创数字IC笔试试卷带答案

FPGA基础知识与设计要点摘要 本文围绕FPGA与数字IC设计核心概念,通过单选题、波形分析题、多选题和简答题形式,系统梳理了以下关键知识点: FPGA基础架构 查找表(LUT)是实现组合逻辑的基本单元(题1) CLB包含LUT、触发器和进位链,后者加速算术运算(题6,10) 布局布线优化是减少关键路径延迟的核心方法(题7) 时序分析要点 建立时间(时钟沿前数据需稳定)与保持时间(时钟沿后需保持)的定义(题2,简答1) 亚稳态成因及两级同步器处理跨时钟域信号的方法(多选题1,波形题5) 特殊设计技术 异步复

Open 114

Spring @Aspect、@Before、@After 注解实现 AOP 切面功能

Spring AOP 注解概述 1、Spring 的 AOP 功能除了在配置文件中配置一大堆的配置,比如切入点、表达式、通知等等以外,使用注解的方式更为方便快捷,特别是 Spring boot 出现以后,基本不再使用原先的 beans.xml 等配置文件了,而都推荐注解编程。 @Aspect 切面声明,标注在类、接口(包括注解类型)或枚举上。 @Pointcut 切入点声明,即切入到哪些目标类的目标方法。 value 属性指定切入点表达式,默认为 "",用于被通知注解引用

蚩尤后裔-汪茂雄 7万+

windows exe爬虫:exe抓包

在不知情所拦截应用使用的流量时,所以建议用Wireshark。本文利用python代码,实现自动拦截,写入pcap包,并用代码分析流量的协议。这样会在本地目录下生成capture_inbound.pcap,可以用wireshark分析,也可以利用python代码进行分析,如果使用python代码进行分析,需要借助pyshark或者scapy。使用的python WinDivert的包,当然前提也是需要安装Wireshark软件,设置环境变量,并安装pydirvert包。

youbingchen的博客 616

AI Agent Harness自动化漏洞扫描

本文要介绍的AI Agent Harness自动化漏洞扫描方案,正是为了解决传统漏扫的上述痛点而生。漏洞检出率提升35%以上,逻辑漏洞检出率从不足30%提升到92%误报率从27%降到3%以下,人工复核成本降低90%支持自定义业务系统、IoT、工控等多场景资产扫描,适配成本降低80%内置反馈学习机制,扫描效果越用越好AI Agent是指具备自主感知、决策、行动能力的大模型驱动程序,它可以根据给定的目标,自主调用工具、规划步骤、完成任务。

AI 原生应用开发的博客 473

AOP之后置通知@After、返回通知@AfterReturning、异常通知@AfterThrowing

以上两种写法都表明监听com.example.demo.service.impl.AccountServiceImpl类下的aopBeforeMsg函数。需要注意的是,该注解只能抓捕未被自己代码处理的异常,加入在业务逻辑中对代码进行了try…以上写法表示监听AccountServiceImpl类与OrderService类中get开头的函数。以上写法表明监听AccountServiceImpl类下所有get开头函数。以上表明监听AccountServiceImpl类下的所有函数。

忽胖忽瘦的博客 1518

2021-09-29 每天几个LCEDA小知识——如何交叉选择并阵列放置元器件

立创EDA专业版之交叉选择并阵列放置元器件????交叉选择????阵列放置 ????交叉选择 在原理图界面,先框选需要交叉选择的元器件对象,右键,选择交叉选择(如果熟练之后,直接选择元器件,快捷键Shift+X)。 完成上述操作之后,可以看到自动跳转到PCB绘图界面,并自动选定PCB中相关的元器件,接下来不要取消选择的状态,在相关元器件被选中的情况下,即可开展阵列放置,将这些元器件都放在一个区域,方便PCB画线前的布局工作。 ????阵列放置 在布局菜单栏中,选择分布,选择阵列分布, 在弹出的对话框

l64481000的博客 1463

Spring的AOP思想(三)

AOP —&gt; 面向切面编程 想要学习AOP思想,我们必须要理解几个名词: 横切关注点: 分布于应用中的众多功能被称为横切关注点。将横切关注点与业务逻辑相分离正是面向切面编程(AOP),横切关注点可以被模块化为特殊的类,这些类被称为切面。 通知(Advice): 定义了切面是什么以及何时使用切面。 连接点(Joinpoint): 定义了应用被通知的时机。 切点(Poincut): ...

史天航的博客 283

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

Spring三大核心思想之AOP(面向切面编程)

Spring三大核心思想之AOP(面向切面编程) 学习Spring三大核心思想之AOP之前建议先学习: ????1、Srping之IOC思想及使用 ????2、Spring核心思想AOP的底层设计模式之代理模式 ???? 程序员小王的博客:程序员小王的博客 ???? 欢迎点赞 ???? 收藏 ⭐留言 ???? ???? 如有编辑错误联系作者,如果有比较好的文章欢迎分享给我,我会取其精华去其糟粕 ????java自学的学习路线:java自学的学习路线 一、什么是AOP(面向切面编程)? AOP 为 Aspec

程序员小王的博客 3896

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

Spring三大核心思想之三:AOP

一.前言     在以前的项目中,很少去关注spring aop的具体实现与理论,只是简单了解了一下什么是aop具体怎么用,看到了一篇博文写得还不错,就转载来学习一下,博文地址:http://www.cnblogs.com/xrq730/p/4919025.html AOP AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Obje

zhengcs的博客 3948

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

【spring学习笔记三】aop思想介绍及实现原理

aop思想:一句话介绍介绍就是横向重复,纵向抽取。 最早接触就是学习servlet时使用filter的解决乱码设置,实际上就是一种aop编程。 使用动态代理技术,

public_TIM的博客 580

Spring三大核心思想之面向切面编程(AOP)

AOPAOP是Spring提供的关键特性之一。AOP即面向切面编程,是OOP编程的有效补充。使用AOP技术,可以将一些系统性相关的编程工作,独立提取出来,独立实现,然后通过切面切入进系统。从而避免了在业务逻辑的代码中混入很多的系统相关的逻辑——比如权限管理,事物管理,日志记录等等。可以理解为:在运行时,动态的 将代码 切入到 类的 指定方法。这种编程思想就称为面向切面编程。来举个例子吧,你写了A方法之后,老板想让你看看这段代码的执行时间。ok,那你就在A方法的前后加上代码,查看时间。。。

2301_78523177的博客 273

Spring三大核心思想之AOP(面向切面编程),springmvc 面试

关于面试刷题也是有方法可言的,建议最好是按照专题来进行,然后由基础到高级,由浅入深来,效果会更好。Java基础部分算法与编程数据库部分流行的框架与新技术(Spring+SpringCloud+SpringCloudAlibaba)这份面试文档当然不止这些内容,实际上像JVM、设计模式、ZK、MQ、数据结构等其他部分的面试内容均有涉及,因为文章篇幅,就不全部在这里阐述了。

2401_84024576的博客 895

Spring三大核心思想之AOP(面向切面编程),大厂面试必备知识点

当多个增强的切点表达式相同时,可以将切点表达式进行抽取,在增强中使用 pointcut-ref 属性代替 pointcut 属性来引用抽取后的切点表达式。引用myAspect的Bean为切面对象(…))”/>

2401_84104883的博客 1313

AOP(Aspect Oriented Programming)面向切面思想

AOP是一种动态的技术思想, 目的是实现业务功能和非业务功能的解耦合。业务功能是独立的模块, 其他功能也是独立的模块。例如事务功能、日志等等。让事务、日志功能是可以被复用的。当目标方法需要一些功能时,可以在不修改,不能修改源代码的情况下, 使用AOP技术在程序执行期间,生成代理对象, 通过代理执行业务方法,同时增加功能。

m0_65224643的博客 1170

aop执行跳过某个方法_spring第二章AOP

AOP:面向切面思想,是一种思想,而不是一种技术。作为Spring三大核心思想(Sping三大核心思想:AOP、IOC、DI)之一,确实具有一定的难度,特别是其中的专业术语并不是只看看文章或者看看视频就可以完全理解的,需要不断的实践与练习才能更好的掌握。说实话,写这篇文章我自己对于AOP也确实不是完全理解,但是也有一些理解作为记录,便于以后如有新理解或者其他想法时便于在此进行修改与对比。...

weixin_39788382的博客 1070

spring简单介绍和IOC思想

spring简单介绍和使用 spring概念 spring是一个轻量级的,开源的ioc/aop容器,目的是提高开发效率,实现层与层之间的分离。 名词解释: 轻量级:消耗资源少 重量级:消耗资源多 开源:公开的源码 spring三大思想 IOC思想:控制反转: Inversion of control 控制反转 创建对象权限的反向转移 正常情况下:由使用者创建类的对象 谁用谁创建 new类() 反转:将创建对象的权限交给spring容器,由spring容器统一创建对象,我们使用时,

qq_39095899的博客 258

Spring 底层核心全集:从 IOC 思想到 AOP 代理,一篇通透

本文深入解析Spring框架三大核心概念:IOC(控制反转)、DI(依赖注入)和AOP(面向切面编程)。IOC将对象创建和依赖管理的控制权从程序员转移给容器,通过DI实现依赖的自动注入,推荐使用构造器注入。AOP则用于处理横切关注点,将日志、事务等公共逻辑从业务代码中解耦。文章对比了@Autowired和@Resource的区别,阐明了IOC与DI的关系(IOC是思想,DI是实现手段),并介绍了AOP如何通过切面编程补充OOP的不足。内容涵盖核心原理、实现方式和最佳实践,适合面试准备和日常学习参考。

qq_45475255的博客 335

详解Spring AOP

Aspect Oriented Programming(面向切面编程),OOP是面向对象编程(开发的时候把一个个都抽象成对象来开发),两者的维度不同,处理的事情不同切⾯就是指某⼀类特定问题, 所以AOP也可以理解为⾯向特定⽅法编程。面向切面编程就是针对某一类特定问题统一进行编程比如people类有自己的属性和行为,但是有小一部分人生病要去医院看病,看病这个业务逻辑就不属于哪一个类,因为people泛指所有人,所有人不会都看病。

N_0050的博客 2030
上一篇: (二)Spring自动装配
下一篇: AngularJS入门案例
TyCoding
博客等级 码龄9年 316粉丝 49原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值