最新最全面的Spring详解(四)——面向切面编程(下)

简介: 最新最全面的Spring详解(四)——面向切面编程(下)

🍀切入点表达式运算


可以使用’ &&’ || ‘和’ ! '组合切入点表达式。 您还可以通过名称引用切入点表达式。 下面的例子展示了三个切入点表达式:

@Pointcut("execution(public * *(..))")
private void anyPublicOperation() {} 
@Pointcut("within(com.xyz.myapp.trading..*)")
private void inTrading() {} 
@Pointcut("anyPublicOperation() && inTrading()")
private void tradingOperation() {} 

🍀共享公共切入点定义


在使用企业应用程序时,开发人员经常希望从几个切面引用应用程序的模块和特定的操作集。 我们建议定义一个【CommonPointcut】切面来捕获通用的切入点表达式。 这样一个方面典型地类似于以下示例:

package com.xyz.myapp;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class CommonPointcuts {
    /**
     * A join point is in the web layer if the method is defined
     * in a type in the com.xyz.myapp.web package or any sub-package
     * under that.
     */
    @Pointcut("within(com.xyz.myapp.web..*)")
    public void inWebLayer() {}
    /**
     * A join point is in the service layer if the method is defined
     * in a type in the com.xyz.myapp.service package or any sub-package
     * under that.
     */
    @Pointcut("within(com.xyz.myapp.service..*)")
    public void inServiceLayer() {}
    /**
     * A join point is in the data access layer if the method is defined
     * in a type in the com.xyz.myapp.dao package or any sub-package
     * under that.
     */
    @Pointcut("within(com.xyz.myapp.dao..*)")
    public void inDataAccessLayer() {}
    /**
     * A business service is the execution of any method defined on a service
     * interface. This definition assumes that interfaces are placed in the
     * "service" package, and that implementation types are in sub-packages.
     *
     * If you group service interfaces by functional area (for example,
     * in packages com.xyz.myapp.abc.service and com.xyz.myapp.def.service) then
     * the pointcut expression "execution(* com.xyz.myapp..service.*.*(..))"
     * could be used instead.
     *
     * Alternatively, you can write the expression using the 'bean'
     * PCD, like so "bean(*Service)". (This assumes that you have
     * named your Spring service beans in a consistent fashion.)
     */
    @Pointcut("execution(* com.xyz.myapp..service.*.*(..))")
    public void businessService() {}
    /**
     * A data access operation is the execution of any method defined on a
     * dao interface. This definition assumes that interfaces are placed in the
     * "dao" package, and that implementation types are in sub-packages.
     */
    @Pointcut("execution(* com.xyz.myapp.dao.*.*(..))")
    public void dataAccessOperation() {}
}

您可以在任何需要切入点表达式的地方引用在这样一个切面中定义的切入点。 例如,要使服务层成为事务性的,可以这样写:

<aop:config>
    <aop:advisor
        pointcut="com.xyz.myapp.CommonPointcuts.businessService()"
        advice-ref="tx-advice"/>
</aop:config>
<tx:advice id="tx-advice">
    <tx:attributes>
        <tx:method name="*" propagation="REQUIRED"/>
    </tx:attributes>
</tx:advice>

4️⃣声明通知


通知与切入点表达式相关联,并在切入点匹配的方法执行之前、之后或前后运行。 切入点表达式可以是对指定切入点的【简单引用】,也可以是适当声明的切入点表达式。


🍀(1)(Before advice)前置通知


你可以使用【@Before】注解在方面中声明before通知:

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class BeforeExample {
    @Before("com.xyz.myapp.CommonPointcuts.dataAccessOperation()")
    public void doAccessCheck() {
        // ...
    }
}

如果使用切入点表达式,可以将前面的示例重写为以下示例:

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class BeforeExample {
    @Before("execution(* com.xyz.myapp.dao.*.*(..))")
    public void doAccessCheck() {
        // ...
    }
}

🍀(2)(After returning advice)返回通知


当匹配的方法执行正常返回时,返回通知运行。 你可以通过使用【@AfterReturning】注解声明它:

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterReturning;
@Aspect
public class AfterReturningExample {
    @AfterReturning("com.xyz.myapp.CommonPointcuts.dataAccessOperation()")
    public void doAccessCheck() {
        // ...
    }
}

有时,您需要在通知主体中访问返回的实际值。 你可以使用’ @afterreturn '的形式绑定返回值以获得访问,如下例所示:

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterReturning;
@Aspect
public class AfterReturningExample {
    @AfterReturning(
        pointcut="com.xyz.myapp.CommonPointcuts.dataAccessOperation()",
        returning="retVal")
    public void doAccessCheck(Object retVal) {
        // ...
    }
}

🍀(3)(After throwing advice)抛出异常后通知


抛出通知后,当匹配的方法执行通过抛出异常退出时运行。 你可以通过使用【 @AfterThrowing】注解来声明它,如下面的例子所示:

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterThrowing;
@Aspect
public class AfterThrowingExample {
    @AfterThrowing("com.xyz.myapp.CommonPointcuts.dataAccessOperation()")
    public void doRecoveryActions() {
        // ...
    }
}

通常,您如果希望通知仅在【抛出给定类型】的异常时运行,而且您还经常需要访问通知主体中抛出的异常。 你可以使用’ thrown ‘属性来限制匹配(如果需要,则使用’ Throwable '作为异常类型),并将抛出的异常绑定到一个advice参数。 下面的例子展示了如何做到这一点:

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterThrowing;
@Aspect
public class AfterThrowingExample {
    @AfterThrowing(
        pointcut="com.xyz.myapp.CommonPointcuts.dataAccessOperation()",
        throwing="ex")
    public void doRecoveryActions(DataAccessException ex) {
        // ...
    }
}

【throwing】属性中使用的【名称必须与通知方法中的参数名称】相对应。 当一个方法执行通过抛出异常而退出时,异常将作为相应的参数值传递给advice方法。


🍀(4)After (Finally) 最终通知


After (finally)通知在匹配的方法执行退出时运行。 它是通过使用【@After 】注解声明的。 After advice必须准备好处理正常和异常返回条件,它通常用于释放资源以及类似的目的。 下面的例子展示了如何使用after finally通知:

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.After;
@Aspect
public class AfterFinallyExample {
    @After("com.xyz.myapp.CommonPointcuts.dataAccessOperation()")
    public void doReleaseLock() {
        // ...
    }
}


更多值得注意的地方,AspectJ中的’ @After '通知被定义为【after finally】,类似于try-catch语句中的finally块。 它将对任何结果,其中包括【正常返回】或【从连接点抛出异常】都会进行调用,而【 @ afterreturn】只适用于成功的正常返回。


🍀(5)Around通知


【Around advice】环绕匹配的方法执行。 它有机会在方法运行之前和之后进行工作,并确定方法何时、如何运行,甚至是否真正运行。 如果您需要在方法执行之前和之后以线程安全的方式共享状态(例如,启动和停止计时器),经常使用Around通知。 我们推荐,总是使用最弱的通知形式,以满足你的要求(也就是说,不要使用环绕通知,如果前置通知也可以完成需求)。


Around通知是通过使用【@Around】注解声明的。 advice方法的第一个参数必须是【ProceedingJoinPoint】类型。 在通知体中,在【ProceedingJoinPoint】上调用【proceed()】会导致底层方法运行。 【proceed】方法也可以传入【Object[] 】。 当方法执行时,数组中的值被用作方法执行的参数。


下面的例子展示了如何使用around advice:

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.ProceedingJoinPoint;
@Aspect
public class AroundExample {
    @Around("com.xyz.myapp.CommonPointcuts.businessService()")
    public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
        // 我们可以在前边做一些工作,比如启动计时器
        // 这里是真正的方法调用的地方
        Object retVal = pjp.proceed();
        // 我们可以在后边做一些工作,比如停止计时器,搜集方法的执行时间
        return retVal;
    }
}

注意: around通知返回的值是【方法调用者看到的返回值】。 例如,一个简单的缓存切面可以从缓存返回一个值(如果它有一个值),如果没有,则调用’ proceed() '。 注意,【proceed方法】你可以只调用一次,也可以调用多次,也可以根本不去调用, 这都是可以的。


🍀(6)通知的参数


Spring提供了完整类型的通知,这意味着您可以在【通知签名】中声明【所需的参数】(就像我们前面在返回和抛出示例中看到的那样)。


访问当前 JoinPoint


任何通知方法都可以声明一个类型为【org.aspectj.lang.JoinPoint】的参数作为它的【第一个参数】(注意,around通知需要声明类型为’ ProceedingJoinPoint )的第一个参数,它是【oinPoint】的一个子类。 【JoinPoint】接口提供了许多有用的方法:


getArgs(): 返回方法参数。

getThis(): 返回代理对象。

getTarget(): 返回目标对象。

getSignature(): 返回被通知的方法的签名。

toString(): 打印被建议的方法的有用描述。

@Before("beforePointcut()")
private void beforeAdvice(JoinPoint jp) throws InvocationTargetException, IllegalAccessException {
    MethodSignature signature = (MethodSignature)jp.getSignature();
    // 能拿到方法,能不能拿到方法的注解
    Method method = signature.getMethod();
    // 调用方法的过程
    method.invoke(jp.getTarget(), jp.getArgs());
    System.out.println("this is before advice");
}

bb4a2f9156ef44ad95f99eff86b8581d.png


将参数传递给Advice


我们已经看到了如何绑定【返回值或异常值】。 要使参数值对通知主体可用,可以使用【args 】的绑定形式。 如果在args表达式中使用【参数名】代替类型名,则在【调用通知】时将传递相应值作为参数值。


举个例子应该能更清楚地说明这一点:

@Override
public String order(Integer money) {
    try {
        logger.info("这是order的方法");
        return "inner try";
    } finally {
        logger.info("finally");
        //return "finally";
    }
}
@Before("execution(* com.ydlclass.service.impl.OrderService.*(..)) && args(money,..)")
public void validateAccount(Integer money) {
    System.out.println("before--------" + money);
}

切入点表达式的’ args(account,…) '部分有两个目的


首先,它限制只匹配哪些方法执行,其中方法接受至少一个参数,并且传递给该参数的参数是’ Account '的一个实例。


其次,它通过’ Account ‘参数使通知可以使用实际的’ Account '对象。

另一种方式是【编写方法】声明一个切入点,该切入点在匹配连接点时“提供”‘Account’对象值,然后从通知中引用指定的切入点。 这看起来如下:

@Pointcut("com.xyz.myapp.CommonPointcuts.dataAccessOperation() && args(account,..)")
private void accountDataAccessOperation(Account account) {}
@Before("accountDataAccessOperation(account)")
public void validateAccount(Account account) {
    // ...
}

5️⃣引入Introduction


引入使切面能够声明被通知的对象【实现给定的接口】,也就是让代理对象实现新的接口。

@DeclareParents(value="com.ydlclass.service.impl.OrderService",defaultImpl= ActivityService.class)
public static IActivityService activityService;

要实现的接口由注解字段的类型决定。 @DeclareParents注解的【value】属性是一个AspectJ类型类。 任何与之匹配的类型的bean都将实现【UsageTracked】接口。 注意,在前面示例的before通知中,服务bean可以直接用作【UsageTracked】接口的实现。 如果以编程方式访问bean,您将编写以下代码:

IActivityService bean = ctx.getBean(IActivityService.class);
bean.sendGif();

搞过debug看到了,生成的代理实现了两个接口:


1c359fcddcd848b7971d7a0202077ded.png


6️⃣Advice Ordering


当多个通知都想在同一个连接点上运行时,Spring AOP遵循与AspectJ相同的优先规则来确定通知执行的顺序。优先级最高的通知在【进入时】首先运行【因此,给定两个before通知,优先级最高的将首先运行】。从连接点【退出】时,优先级最高的通知最后运行【因此,给定两个after通知,优先级最高的通知将第二运行】。

当在不同切面定义的两个通知都需要在同一个连接点上运行时,除非另行指定,否则执行顺序是未定义的。 您可以通过指定优先级来控制执行顺序。在切面类中使用【Ordered】接口,或者用【@Order】注释它。 对于两个切面,从’Ordered.getOrder() '返回较低值的切面(或注释值)具有较高的优先级。


7️⃣AOP 的例子


业务代码的执行有时会由于【并发性问题】而失败(例如,死锁而导致的失败)。 如果再次尝试该操作,很可能在下一次尝试时成功。 对于适合在这种条件下重试的业务服务,我们希望进行透明地重试操作。 这是一个明显跨越service层中的多个服务的需求,因此是通过切面实现的理想需求。


因为我们想要重试操作,所以我们需要使用around通知,以便我们可以多次调用’ proceed '。 下面的例子显示了基本方面的实现:

@Aspect
public class ConcurrentOperationExecutor implements Ordered {
    private static final int DEFAULT_MAX_RETRIES = 2;
    private int maxRetries = DEFAULT_MAX_RETRIES;
    private int order = 1;
    public void setMaxRetries(int maxRetries) {
        this.maxRetries = maxRetries;
    }
    public int getOrder() {
        return this.order;
    }
    public void setOrder(int order) {
        this.order = order;
    }
    @Around("com.xyz.myapp.CommonPointcuts.businessService()")
    public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable {
        int numAttempts = 0;
        PessimisticLockingFailureException lockFailureException;
        do {
            numAttempts++;
            try {
                return pjp.proceed();
            }
            catch(PessimisticLockingFailureException ex) {
                lockFailureException = ex;
            }
        } while(numAttempts <= this.maxRetries);
        throw lockFailureException;
    }
}

注意,切面实现了’ Ordered '接口,因此我们可以将【该切面的优先级】设置得高于【事务通知】,我们希望每次重试时都有一个新的事务。 ’ maxRetries ‘和’ order '属性都是可以由Spring配置注入的。

对应的Spring配置如下:

<aop:aspectj-autoproxy/>
<bean id="concurrentOperationExecutor" class="com.xyz.myapp.service.impl.ConcurrentOperationExecutor">
    <property name="maxRetries" value="3"/>
    <property name="order" value="100"/>
</bean>

五、基于schema的AOP支持


如果您喜欢基于xml的格式,Spring还提供了使用【aop命名空间】标记定义切面的支持。 它支持与使用@AspectJ样式时完全相同的切入点表达式和通知类型。


要使用本节中描述的aop命名空间标记,您需要导入’ spring-aop '模块。


在Spring配置中,所有【切面和通知】元素都必须放在一个<aop:config> 元素中(在应用程序上下文配置中可以有多个<aop:config> 元素)。 一个<aop:config> 元素可以包含切入点、通知和切面元素(注意这些元素必须按照这个顺序声明)。


🍀配置切面,切点表达式,通知的方法如下

<?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"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>
<?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"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--    <aop:aspectj-autoproxy/>-->
    <aop:config>
        <aop:aspect ref="aop">
            <aop:pointcut id="point" expression="execution(* com.ydlclass..*(..))"/>
            <aop:before method="beforeAdvice" pointcut="execution(* com.ydlclass..*(..)) and args(money,..)"/>
            <aop:after method="afterAdvice" pointcut-ref="point"/>
            <aop:after-returning method="afterReturningAdvice" pointcut-ref="point"/>
            <aop:after-throwing throwing="ex" method="afterThrowing" pointcut-ref="point"/>
        </aop:aspect>
    </aop:config>
    <bean id="aop" class="com.ydlclass.aspecj.MyAop"/>
    <bean id="orderService" class="com.ydlclass.service.impl.OrderService"/>
    <bean id="userService" class="com.ydlclass.service.impl.UserService"/>
</beans>

🍀Introduction

<aop:aspect id="usageTrackerAspect" ref="usageTracking">
    <aop:declare-parents
        types-matching="com.xzy.myapp.service.*+"
        implement-interface="com.xyz.myapp.service.tracking.UsageTracked"
        default-impl="com.xyz.myapp.service.tracking.DefaultUsageTracked"/>
</aop:aspect>

🍀AOP示例

public class ConcurrentOperationExecutor implements Ordered {
    private static final int DEFAULT_MAX_RETRIES = 2;
    private int maxRetries = DEFAULT_MAX_RETRIES;
    private int order = 1;
    public void setMaxRetries(int maxRetries) {
        this.maxRetries = maxRetries;
    }
    public int getOrder() {
        return this.order;
    }
    public void setOrder(int order) {
        this.order = order;
    }
    public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable {
        int numAttempts = 0;
        PessimisticLockingFailureException lockFailureException;
        do {
            numAttempts++;
            try {
                return pjp.proceed();
            }
            catch(PessimisticLockingFailureException ex) {
                lockFailureException = ex;
            }
        } while(numAttempts <= this.maxRetries);
        throw lockFailureException;
    }
}

对应的Spring配置如下:

<aop:config>
    <aop:aspect id="concurrentOperationRetry" ref="concurrentOperationExecutor">
        <aop:pointcut id="idempotentOperation"
            expression="execution(* com.xyz.myapp.service.*.*(..))"/>
        <aop:around
            pointcut-ref="idempotentOperation"
            method="doConcurrentOperation"/>
    </aop:aspect>
</aop:config>
<bean id="concurrentOperationExecutor"
    class="com.xyz.myapp.service.impl.ConcurrentOperationExecutor">
        <property name="maxRetries" value="3"/>
        <property name="order" value="100"/>
</bean>

六、AOP声明风格


一旦您确定使用aop是实现给定需求的最佳方法,您如何决定是使用Spring AOP还是Aspect?是使用@AspectJ注解风格还是Spring XML风格?


如果您选择使用Spring AOP,那么您可以选择【@AspectJ或XML】样式。


XML样式可能是现有Spring用户最熟悉的,并且它是由真正的【pojo支持】(侵入性很低)的。 当使用AOP作为配置企业服务的工具时,XML可能是一个很好的选择(一个很好的理由是您【是否认为切入点表达式】 是需要【独立更改】的一部分配置)。使用XML样式,可以从配置中更清楚地看出系统中存在哪些切面。


XML样式有两个缺点。 首先,它没有将它所处理的需求的实现完全封装在一个地方。 其次,与@AspectJ风格相比,XML风格在它能表达的内容上稍微受到一些限制,不可能在XML中声明的命名切入点进行组合。 例如,在@AspectJ风格中,你可以写如下内容:

@Pointcut("execution(* get*())")
public void propertyAccess() {}
@Pointcut("execution(org.xyz.Account+ *(..))")
public void operationReturningAnAccount() {}
@Pointcut("propertyAccess() && operationReturningAnAccount()")
public void accountPropertyAccess() {}

在XML样式中,可以声明前两个切入点:

<aop:pointcut id="propertyAccess"
        expression="execution(* get*())"/>
<aop:pointcut id="operationReturningAnAccount"
        expression="execution(org.xyz.Account+ *(..))"/>

XML方法的缺点是不能通过组合这些定义来定义“accountPropertyAccess”切入点。


@AspectJ还有一个优点,即@AspectJ切面可以被Spring AOP和AspectJ理解(从而被使用)。 因此,如果您以后决定需要AspectJ的功能来实现额外的需求,您可以轻松地迁移到经典的AspectJ当中。


总的来说,Spring团队更喜欢自定义切面的@AspectJ风格,而不是简单的企业服务配置。


七、以编程方式创建@AspectJ代理


除了通过使用<aop:config> 或<aop:aspectj-autoproxy>在配置中声明方面之外,还可以通过编程方式创建通知目标对象的代理。


代码如下,这只是一个小例子,用来看一下spring是怎么封装代理的:


public static void main(String[] args) {
    AspectJProxyFactory aspectJProxyFactory = new AspectJProxyFactory(new OrderService());
    aspectJProxyFactory.addAspect(MyAspect.class);
    IOrderService proxy = (IOrderService)aspectJProxyFactory.getProxy();
    proxy.order(111);
}


相关文章
|
3天前
|
Java Spring
一键注入 Spring 成员变量,顺序编程
介绍了一款针对Spring框架开发的插件,旨在解决开发中频繁滚动查找成员变量注入位置的问题。通过一键操作(如Ctrl+1),该插件可自动在类顶部添加`@Autowired`注解及其成员变量声明,同时保持光标位置不变,有效提升开发效率和代码编写流畅度。适用于IntelliJ IDEA 2023及以上版本。
一键注入 Spring 成员变量,顺序编程
|
2月前
|
Java 数据库连接 Spring
【2021Spring编程实战笔记】Spring开发分享~(下)
【2021Spring编程实战笔记】Spring开发分享~(下)
31 1
|
2月前
|
XML Java 数据库连接
【2020Spring编程实战笔记】Spring开发分享~(上)
【2020Spring编程实战笔记】Spring开发分享~
55 0
|
4月前
|
XML Java 数据格式
Spring5入门到实战------11、使用XML方式实现AOP切面编程。具体代码+讲解
这篇文章是Spring5框架的AOP切面编程教程,通过XML配置方式,详细讲解了如何创建被增强类和增强类,如何在Spring配置文件中定义切入点和切面,以及如何将增强逻辑应用到具体方法上。文章通过具体的代码示例和测试结果,展示了使用XML配置实现AOP的过程,并强调了虽然注解开发更为便捷,但掌握XML配置也是非常重要的。
Spring5入门到实战------11、使用XML方式实现AOP切面编程。具体代码+讲解
|
4月前
|
Java Spring XML
掌握面向切面编程的秘密武器:Spring AOP 让你的代码优雅转身,横切关注点再也不是难题!
【8月更文挑战第31天】面向切面编程(AOP)通过切面封装横切关注点,如日志记录、事务管理等,使业务逻辑更清晰。Spring AOP提供强大工具,无需在业务代码中硬编码这些功能。本文将深入探讨Spring AOP的概念、工作原理及实际应用,展示如何通过基于注解的配置创建切面,优化代码结构并提高可维护性。通过示例说明如何定义切面类、通知方法及其应用时机,实现方法调用前后的日志记录,展示AOP在分离关注点和添加新功能方面的优势。
61 0
|
4月前
|
Java Spring 容器
彻底改变你的编程人生!揭秘 Spring 框架依赖注入的神奇魔力,让你的代码瞬间焕然一新!
【8月更文挑战第31天】本文介绍 Spring 框架中的依赖注入(DI),一种降低代码耦合度的设计模式。通过 Spring 的 DI 容器,开发者可专注业务逻辑而非依赖管理。文中详细解释了 DI 的基本概念及其实现方式,如构造器注入、字段注入与 setter 方法注入,并提供示例说明如何在实际项目中应用这些技术。通过 Spring 的 @Configuration 和 @Bean 注解,可轻松定义与管理应用中的组件及其依赖关系,实现更简洁、易维护的代码结构。
56 0
|
4月前
|
Java Spring 供应链
Spring 框架事件发布与监听机制,如魔法风暴席卷软件世界,开启奇幻编程之旅!
【8月更文挑战第31天】《Spring框架中的事件发布与监听机制》介绍了Spring中如何利用事件发布与监听机制实现组件间的高效协作。这一机制像城市中的广播系统,事件发布者发送消息,监听器接收并响应。通过简单的示例代码,文章详细讲解了如何定义事件类、创建事件发布者与监听器,并确保组件间松散耦合,提升系统的可维护性和扩展性。掌握这一机制,如同拥有一把开启高效软件开发大门的钥匙。
50 0
|
5月前
|
Java Spring 容器
Spring问题之Spring AOP是如何实现面向切面编程的
Spring问题之Spring AOP是如何实现面向切面编程的
|
4月前
|
XML Java 数据库
Spring5入门到实战------10、操作术语解释--Aspectj注解开发实例。AOP切面编程的实际应用
这篇文章是Spring5框架的实战教程,详细解释了AOP的关键术语,包括连接点、切入点、通知、切面,并展示了如何使用AspectJ注解来开发AOP实例,包括切入点表达式的编写、增强方法的配置、代理对象的创建和优先级设置,以及如何通过注解方式实现完全的AOP配置。
|
5月前
|
设计模式 缓存 程序员
Spring6(三):面向切面AOP(1)
Spring6(三):面向切面AOP(1)
37 1