Spring AOP三种实现方式

本文详细介绍了面向切面编程(AOP)的基本原理及其在实际项目中的应用案例,包括权限认证、缓存管理、事务处理等多个典型场景。文章还探讨了AOP的实现方式,包括XML配置、注解和实现特定接口等不同方法。

AOP应用场景及原理

AOP适合于那些具有横切逻辑的应用:如性能监测,访问控制,事务管理、缓存、对象池管理以及日志记录。AOP将这些分散在各个业务逻辑中的代码通过横向切割的方式抽取到一个独立的模块中。AOP 实现的关键就在于 AOP 框架自动创建的 AOP 代理,AOP 代理则可分为静态代理和动态代理两大类,其中静态代理是指使用 AOP 框架提供的命令进行编译,从而在编译阶段就可生成 AOP 代理类,因此也称为编译时增强;而动态代理则在运行时借助于 JDK 动态代理、CGLIB 等在内存中“临时”生成 AOP 动态代理类,因此也被称为运行时增强。
AOP用来封装横切关注点,具体可以在下面的场景中使用
Authentication 权限
Caching 缓存
Context passing 内容传递
Error handling 错误处理
Lazy loading 懒加载
Debugging 调试
logging, tracing, profiling and monitoring 记录跟踪 优化 校准
Performance optimization 性能优化
Persistence 持久化
Resource pooling 资源池
Synchronization 同步
Transactions 事务

AOP相关概念

Aop又叫面向切面编程,其中“通知”是切面的具体实现,分为before(前置通知)、after(后置通知)、around(环绕通知)
三个注解的区别:@Before是在所拦截方法执行之前执行一段逻辑。@After 是在所拦截方法执行之后执行一段逻辑。@Around是可以同时在所拦截方法的前后执行一段逻辑。

pointcut“切入点”表达式

例如定义切入点表达式 execution (* com.sample.service.impl...(..))
execution()是最常用的切点函数,其语法如下所示:
整个表达式可以分为五个部分:
1、execution(): 表达式主体。
2、第一个号:表示返回类型,号表示所有的类型。
3、包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包,com.sample.service.impl包、子孙包下所有类的方法。
4、第二个号:表示类名,号表示所有的类。
5、(..):最后这个星号表示方法名,号表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数。

实现方式

实现方式一(在XML文档中声明)

    <aop:config>
        <!-- 切入的内容 -->
        <aop:aspect ref="MyLog">
            <!-- 切入的位置,切入点 -->
            <aop:pointcut id="point1" expression="execution(* SpringAOPTest.MyBean.*(..)) "/> 
            <aop:after method="logEnd" pointcut-ref="point1"/>
            <aop:before method="logHead" pointcut-ref="point1"/>       
        </aop:aspect>
    </aop:config>

实现方式二(注解方式)

注意:需要在XML文档中加入,这样系统就会自动寻找进行

@Aspect
public class AOPLog {
    @Before("execution(* SpringAOPTest.MyBean.*(..))")
    public void logHead(){
        System.out.println("....AOPLog I'm head....");
    }
    @After("execution(* SpringAOPTest.MyBean.*(..))")
    public void logEnd(){
        System.out.println("....AOPLog I'm End....");
    }
}

等同于

@Aspect
public class AOPLog {
    @Pointcut("execution(* SpringAOPTest.MyBean.*(..))")
    private void show(){}
    @Before("show()")
    public void logHead(){
        System.out.println("....AOPLog I'm head....");
    }
    @After("show()")
    public void logEnd(){
        System.out.println("....AOPLog I'm End....");
    }
}

通过实现的方式三

public class ImplementsLog implements MethodBeforeAdvice{
    @Override
    public void before(Method method, Object[] os, Object o) throws Throwable {
        System.out.println("hello implements MethodBeforeAdvice ");        
    }
}
<bean id="ImLog" class="SpringAOPTest.ImplementsLog"/>

    <aop:config>
      <aop:pointcut id="point1" expression="execution(* SpringAOPTest.MyBean.*(..)) "/> 
      <aop:advisor advice-ref="ImLog" pointcut-ref="point1"/>  
    </aop:config>
<think>好的,我现在需要回答用户关于Spring AOP实现方式的问题。首先,我得回忆一下Spring AOP的基本概念和实现方式。根据用户提供的引用内容,Spring AOP主要是通过动态代理来实现的,具体有两种方式:基于JDK的动态代理和基于CGLIB的动态代理。此外,可能还涉及到AspectJ的集成,但需要区分Spring AOP和AspectJ的不同。 接下来,我需要确认用户的问题核心是“Spring AOP实现方式”,特别是切面编程的具体方法。用户可能想知道不同的实现手段以及它们的区别。根据引用[1]和[2],Spring AOP主要使用动态代理,而引用[2]提到Spring Boot中的AOP实现可能与AspectJ结合使用,但需要明确Spring AOP本身是否支持其他方式。 首先,要明确两种代理方式的适用场景:JDK动态代理适用于接口实现的类,而CGLIB可以代理没有接口的类。这点很重要,需要详细说明。然后,可能还需要提到如何通过配置选择使用哪种代理方式,比如在Spring配置中设置proxy-target-class属性。 另外,用户可能对AspectJ的注解支持感兴趣,比如@Aspect、@Pointcut、@Around等,这些是Spring AOP中定义切面的常用方式。需要解释如何通过注解声明切面,并将其纳入Spring管理。 还需要考虑XML配置的方式,虽然现在可能更常用注解,但传统项目可能仍在使用XML配置AOP,这也是实现方式之一。因此,应该涵盖注解和XML两种配置方法。 然后,需要比较Spring AOP和AspectJ的区别,引用[2]提到了两者的对比,可能需要说明Spring AOP是基于代理的,而AspectJ是在编译时或加载时织入,更强大但更复杂。这部分有助于用户理解不同实现方式的优缺点。 最后,根据用户提供的示例,回答需要结构清晰,分点列出,可能包括实现方式、配置方法、注解使用、代理类型比较等。同时,确保所有LaTeX格式正确,如类名或方法名使用行内格式,独立公式如果有的话使用$$,但可能这里用不上太多数学公式,更多的是代码示例。 检查是否有遗漏的部分,比如是否需要提到AOP的应用场景或优势,但用户问题集中在实现方式,所以应集中在技术细节上。确保引用的正确性,引用[1]、[2]、[3]中的相关内容需要适当标注。 总结步骤: 1. 介绍Spring AOP的两种动态代理实现方式:JDK和CGLIB,解释它们的区别和适用场景。 2. 说明基于注解的配置方式,如@Aspect、@Pointcut等,以及如何启用注解支持。 3. 提及XML配置方式,作为传统方法。 4. 比较Spring AOP与AspectJ的不同,帮助用户理解选择依据。 5. 提供代码示例,展示如何定义切面和使用注解。 6. 确保回答符合格式要求,正确使用引用标注,并生成相关问题。 现在需要组织这些点,确保逻辑连贯,语言准确,符合中文表达习惯,并正确应用引用标注。</think>### Spring AOP实现方式 Spring AOP主要通过**动态代理技术**实现面向切面编程,其核心实现方式可分为以下两类: --- #### 1. **基于JDK动态代理** - **适用场景**:当目标类实现了接口时,Spring AOP默认使用JDK动态代理。代理对象会实现相同的接口,并在调用目标方法前后插入切面逻辑。 - **特点**: - 仅支持接口代理,无法代理未实现接口的类。 - 运行时生成代理类,性能开销较小。 - **示例**: ```java public interface UserService { void saveUser(); } public class UserServiceImpl implements UserService { public void saveUser() { System.out.println("保存用户"); } } ``` JDK动态代理会为`UserService`接口生成代理对象,拦截方法调用[^1]。 --- #### 2. **基于CGLIB动态代理** - **适用场景**:当目标类未实现接口时,Spring AOP会使用CGLIB库生成目标类的子类作为代理。 - **特点**: - 可以代理普通类(无需接口)。 - 通过继承实现代理,因此`final`类或方法无法被代理。 - 性能略低于JDK动态代理,但功能更灵活。 - **配置方式**: 在Spring配置中设置`proxy-target-class=true`,强制使用CGLIB代理: ```xml <aop:aspectj-autoproxy proxy-target-class="true"/> ``` 或通过注解启用: ```java @EnableAspectJAutoProxy(proxyTargetClass = true) ``` --- #### 3. **基于注解的切面定义** Spring AOP支持通过`@Aspect`注解声明切面,结合以下注解定义通知(Advice): - **`@Pointcut`**:定义切入点表达式,指定哪些方法需要被拦截。 - **`@Before`/`@After`/`@Around`**:定义前置通知、后置通知和环绕通知。 - **示例**: ```java @Aspect @Component public class LoggingAspect { @Pointcut("execution(* com.example.service.*.*(..))") public void serviceMethods() {} @Around("serviceMethods()") public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable { long start = System.currentTimeMillis(); Object result = joinPoint.proceed(); long end = System.currentTimeMillis(); System.out.println("方法执行时间:" + (end - start) + "ms"); return result; } } ``` 需在配置类中添加`@EnableAspectJAutoProxy`以启用注解支持[^2][^3]。 --- #### 4. **基于XML的切面配置(传统方式)** 通过XML文件定义切面和通知,适用于早期Spring版本或需要解耦代码的场景: ```xml <aop:config> <aop:aspect ref="loggingAspect"> <aop:pointcut id="serviceMethods" expression="execution(* com.example.service.*.*(..))"/> <aop:around pointcut-ref="serviceMethods" method="logExecutionTime"/> </aop:aspect> </aop:config> ``` 需在Bean定义中注入切面类`loggingAspect`。 --- #### 对比:Spring AOP与AspectJ | 特性 | Spring AOP | AspectJ | |---------------------|--------------------------------|----------------------------------| | **实现方式** | 动态代理(运行时织入) | 编译时/加载时织入 | | **性能** | 适用于轻量级场景 | 更高性能,适合复杂切面 | | **功能范围** | 仅支持方法级别拦截 | 支持字段、构造方法等更细粒度控制 | | **依赖** | 无需额外编译步骤 | 需编译器或类加载器支持 | ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值