- 浏览: 1570424 次
文章分类
- 全部博客 (557)
- Spring 3 系列 (26)
- Spring 3 (4)
- oracle (7)
- java (6)
- css3 (1)
- andorid (11)
- IE中页面不居中 (1)
- crm (1)
- ibatis (1)
- jdbc (1)
- javacore (1)
- IT 生活 (3)
- 创业的简单感受 (1)
- web前端 (1)
- Java静态代理 (1)
- pdf (6)
- 模拟 (1)
- 数论 (1)
- ACM_POJ (2)
- C/C++求职面试必备考点 (1)
- 学习Android遇到的错误 (1)
- 嵌入式学习 (1)
- magento付费模板! (1)
- PHP (1)
- Oracle 开发 (1)
- MSSQL (1)
- javascript (6)
- 随感随想 (1)
- RobotFramework (1)
- Ajax (2)
- 数据库复习 (1)
- Java Web (1)
- Way (1)
- eclipse (1)
- 分布式 (1)
- 【ASP.NET开发】 (1)
- 搜索 (1)
- UML建模 (1)
- ANDROID (2)
- 编程技巧 (1)
- 程序员 (2)
- C语言相关 (1)
- Struts2 (1)
- 精品下载资源推荐 (1)
- CUDA (1)
- MFC (1)
- 游戏编程 (1)
- oracle数据库 (1)
- 暴力求解--哈希表 (1)
- 个人文章 (1)
- 最小生成树 (1)
- linux 基础 (1)
- Flex (1)
- Linux (1)
- UML (1)
- 云计算 (1)
- android ListView (1)
- java数据库连接池 (1)
- cxf (1)
- javas (0)
- jquery (2)
最新评论
-
lj杰:
您好,最近项目涉及这这方面的技术,能分享下源码不,小弟非常感谢 ...
Java实现视频网站的视频上传、视频转码、视频关键帧抽图, 及视频播放功能 -
成大大的:
Android自动化测试从入门到精通网盘地址:https:// ...
4种手机自动化测试框架介绍 -
u012944589:
[size=xx-large][size=xx-small][ ...
Java实现视频网站的视频上传、视频转码、视频关键帧抽图, 及视频播放功能 -
stone520520:
同求源码,这几天正想研究一下视频的相关功能mail: 1862 ...
Java实现视频网站的视频上传、视频转码、视频关键帧抽图, 及视频播放功能 -
zhen8023wan:
源代码可以发给我一份吗?谢谢!qq邮箱:1796482787@ ...
Java实现视频网站的视频上传、视频转码、视频关键帧抽图, 及视频播放功能
Spring中的AOP
Written by Tony Jiang @ 2012-1-18
(转)何为AOP
AOP,面向切面编程。
在不改动代码的前提下,灵活的在现有代码的执行顺序前后,添加进新规机能。
来一个简单的Sample:
目标类:
- package com.hyron.tony;
- public class CustomerService {
- private String name;
- private String url;
- public void setName(String name) {
- this.name = name;
- }
- public void setUrl(String url) {
- this.url = url;
- }
- public void printName() {
- System.out.println("Customer name : " + this.name);
- }
- public void printURL() {
- System.out.println("Customer website : " + this.url);
- }
- public void printThrowException() {
- throw new IllegalArgumentException();
- }
- }
advice:只以Around advice为例
- import java.util.Arrays;
- import org.aopalliance.intercept.MethodInterceptor;
- import org.aopalliance.intercept.MethodInvocation;
- public class HijackAroundMethod implements MethodInterceptor {
- @Override
- public Object invoke(MethodInvocation methodInvocation) throws Throwable {
- System.out.println("Method name : "
- + methodInvocation.getMethod().getName());
- System.out.println("Method arguments : "
- + Arrays.toString(methodInvocation.getArguments()));
- // same with MethodBeforeAdvice
- System.out.println("HijackAroundMethod : Before method hijacked!");
- try {
- // proceed to original method call
- Object result = methodInvocation.proceed();
- // same with AfterReturningAdvice
- System.out.println("HijackAroundMethod : Before after hijacked!");
- return result;
- } catch (IllegalArgumentException e) {
- // same with ThrowsAdvice
- System.out
- .println("HijackAroundMethod : Throw exception hijacked!");
- throw e;
- }
- }
- }
编织切入关系的配置文件:
- <bean id="customerService" class="com.mkyong.customer.services.CustomerService">
- <property name="name" value="Yong Mook Kim" />
- <property name="url" value="http://www.mkyong.com" />
- </bean>
- <bean id="hijackAroundMethodBean" class="com.mkyong.aop.HijackAroundMethod" />
- <bean id="customerServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
- <property name="target" ref="customerService" />
- <property name="interceptorNames">
- <list>
- <value>hijackAroundMethodBean</value>
- </list>
- </property>
- </bean>
Sample的启动:
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
- import com.mkyong.customer.services.CustomerService;
- public class App {
- public static void main(String[] args) {
- ApplicationContext appContext = new ClassPathXmlApplicationContext(
- new String[] { "Spring-Customer.xml" });
- CustomerService cust = (CustomerService) appContext
- .getBean("customerServiceProxy");
- System.out.println("*************************");
- cust.printName();
- System.out.println("*************************");
- cust.printURL();
- System.out.println("*************************");
- try {
- cust.printThrowException();
- } catch (Exception e) {
- }
- }
- }
以上代码,用customerServiceProxy代理CustomerService的执行
在customerServiceProxy的配置中,定义了用hijackAroundMethodBean作为方法拦截器,在hijackAroundMethodBean中利用invoke方法,拦截住所有的方法调用,塞入自己的逻辑业务。
AOP的两种实现
上面看到的是Spring的Sample。
其实,Spring的AOP也是调用了其他开源技术实现。
比较常用的是JDK自己的Proxy,和开源的CGLIB
两者的区别,Proxy需要Advice必须从接口继承过来。如果切入的目标物是实体类,则无法使用。
CGLIB则可以用于直接覆盖实体类的方法。
Spring对以上两种都有支持。
Spring的底层实现
Spring在配置文件中,通过ProxyFactoryBean编织和实现了切面的构成。
我们在执行以下这行话的时候
CustomerService cust = (CustomerService) appContext
.getBean("customerServiceProxy");
其实是将动态对象的生成委托给了ProxyFactoryBean
当配置文件中 <bean>的class属性配置的实现类是FactoryBean时,通过getBean方法返回的不是FactoryBean本身,而是 FactoryBean#getObject()方法所返回的对象,相当于FactoryBean#getObject()代理了getBean()方 法。
执行顺序如下:
1. ProxyFactoryBean中的getObject
- /**
- * Return a proxy. Invoked when clients obtain beans from this factory bean.
- * Create an instance of the AOP proxy to be returned by this factory.
- * The instance will be cached for a singleton, and create on each call to
- * <code>getObject()</code> for a proxy.
- * @return a fresh AOP proxy reflecting the current state of this factory
- */
- public Object getObject() throws BeansException {
- initializeAdvisorChain();
- if (isSingleton()) {
- return getSingletonInstance();
- }
- else {
- if (this.targetName == null) {
- logger.warn("Using non-singleton proxies with singleton targets is often undesirable. " +
- "Enable prototype proxies by setting the 'targetName' property.");
- }
- return newPrototypeInstance();
- }
- }
2. ProxyFactoryBean中的initializeAdvisorChain
从配置文件中的advice list中取得interceptorNames,并将其加入advisorChain
- for (String name : this.interceptorNames) {
- if (logger.isTraceEnabled()) {
- logger.trace("Configuring advisor or advice '" + name + "'");
- }
- if (name.endsWith(GLOBAL_SUFFIX)) {
- if (!(this.beanFactory instanceof ListableBeanFactory)) {
- throw new AopConfigException(
- "Can only use global advisors or interceptors with a ListableBeanFactory");
- }
- addGlobalAdvisor((ListableBeanFactory) this.beanFactory,
- name.substring(0, name.length() - GLOBAL_SUFFIX.length()));
- }
3. 被切面改造过的instance的生成
我们以单例举例:
- /**
- * Return the singleton instance of this class's proxy object,
- * lazily creating it if it hasn't been created already.
- * @return the shared singleton proxy
- */
- private synchronized Object getSingletonInstance() {
- if (this.singletonInstance == null) {
- this.targetSource = freshTargetSource();
- if (this.autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) {
- // Rely on AOP infrastructure to tell us what interfaces to proxy.
- Class targetClass = getTargetClass();
- if (targetClass == null) {
- throw new FactoryBeanNotInitializedException("Cannot determine target class for proxy");
- }
- setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.proxyClassLoader));
- }
- // Initialize the shared singleton instance.
- super.setFrozen(this.freezeProxy);
- this.singletonInstance = getProxy(createAopProxy());
- }
- return this.singletonInstance;
- }
AopProxy是最终生成instance的地方,但是它是接口,和框架分离开来了
接口的生成在父类的ProxyCreatorSupport中
- /**
- * Subclasses should call this to get a new AOP proxy. They should <b>not</b>
- * create an AOP proxy with <code>this</code> as an argument.
- */
- protected final synchronized AopProxy createAopProxy() {
- if (!this.active) {
- activate();
- }
- return getAopProxyFactory().createAopProxy(this);
- }
- createAopProxy的实际运行代码:
- public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
- if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
- Class targetClass = config.getTargetClass();
- if (targetClass == null) {
- throw new AopConfigException("TargetSource cannot determine target class: " +
- "Either an interface or a target is required for proxy creation.");
- }
- if (targetClass.isInterface()) {
- return new JdkDynamicAopProxy(config);
- }
- if (!cglibAvailable) {
- throw new AopConfigException(
- "Cannot proxy target class because CGLIB2 is not available. " +
- "Add CGLIB to the class path or specify proxy interfaces.");
- }
- return CglibProxyFactory.createCglibProxy(config);
- }
- else {
- return new JdkDynamicAopProxy(config);
- }
- }
大家注意一下,这里根据targetClass的类型判断,采用JDK Proxy还是CGLIB模式生成动态对象
4. JDK Proxy的生成
如果大家使用过原生的JDK PROXY,下面的代码是在熟悉不过了
JdkDynamicAopProxy中实例化instance的
- public Object getProxy(ClassLoader classLoader) {
- if (logger.isDebugEnabled()) {
- logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
- }
- Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
- findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
- return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
- }
JdkDynamicAopProxy中的invoke方法
- // Get the interception chain for this method.
- List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
- // Check whether we have any advice. If we don't, we can fallback on direct
- // reflective invocation of the target, and avoid creating a MethodInvocation.
- if (chain.isEmpty()) {
- // We can skip creating a MethodInvocation: just invoke the target directly
- // Note that the final invoker must be an InvokerInterceptor so we know it does
- // nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
- retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
- }
- else {
- // We need to create a method invocation...
- invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
- // Proceed to the joinpoint through the interceptor chain.
- retVal = invocation.proceed();
- }
Proceed的真实代码,把对象本身的invoke和拦截器的invoke交织在一起
- public Object proceed() throws Throwable {
- // We start with an index of -1 and increment early.
- if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
- return invokeJoinpoint();
- }
- Object interceptorOrInterceptionAdvice =
- this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
- if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
- // Evaluate dynamic method matcher here: static part will already have
- // been evaluated and found to match.
- InterceptorAndDynamicMethodMatcher dm =
- (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
- if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
- return dm.interceptor.invoke(this);
- }
- else {
- // Dynamic matching failed.
- // Skip this interceptor and invoke the next in the chain.
- return proceed();
- }
- }
- else {
- // It's an interceptor, so we just invoke it: The pointcut will have
- // been evaluated statically before this object was constructed.
- return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
- }
- }
拦截器是如何编织的
我们在JdkDynamicAopProxy中的invoke方法中看到如下拦截器链条的生成
- // Get the interception chain for this method.
- List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
它对应的代码是DefaultAdvisorChainFactory中的getInterceptorsAndDynamicInterceptionAdvice方法
- AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
- for (Advisor advisor : config.getAdvisors()) {
- if (advisor instanceof PointcutAdvisor) {
- // Add it conditionally.
- PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
- if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(targetClass)) {
- MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
最终advisor的登记由DefaultAdvisorAdapterRegistry完成
我们可以看到DefaultAdvisorAdapterRegistry中首先登记了所有的AdviceAdapter
- /**
- * Create a new DefaultAdvisorAdapterRegistry, registering well-known adapters.
- */
- public DefaultAdvisorAdapterRegistry() {
- registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
- registerAdvisorAdapter(new AfterReturningAdviceAdapter());
- registerAdvisorAdapter(new ThrowsAdviceAdapter());
- }
在如下代码中按照AdviceAdapter的类型塞入责任练中
- public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
- List<MethodInterceptor> interceptors = new ArrayList<MethodInterceptor>(3);
- Advice advice = advisor.getAdvice();
- if (advice instanceof MethodInterceptor) {
- interceptors.add((MethodInterceptor) advice);
- }
- for (AdvisorAdapter adapter : this.adapters) {
- if (adapter.supportsAdvice(advice)) {
- interceptors.add(adapter.getInterceptor(advisor));
- }
- }
- if (interceptors.isEmpty()) {
- throw new UnknownAdviceTypeException(advisor.getAdvice());
- }
- return interceptors.toArray(new MethodInterceptor[interceptors.size()]);
- }
这种做法类似于:
1. 首先为切入Adivsor建立责任链模式
2. 其次将每个Adivsor委托给DefaultAdvisorAdapterRegistry登录
3. 在DefaultAdvisorAdapterRegistry中封装Adivsor到各个专门的Advisor适配器中
比如,AfterReturningAdviceAdapter的代码如下:
- /**
- * Adapter to enable {@link org.springframework.aop.AfterReturningAdvice}
- * to be used in the Spring AOP framework.
- *
- * @author Rod Johnson
- * @author Juergen Hoeller
- */
- class AfterReturningAdviceAdapter implements AdvisorAdapter, Serializable {
- public boolean supportsAdvice(Advice advice) {
- return (advice instanceof AfterReturningAdvice);
- }
- public MethodInterceptor getInterceptor(Advisor advisor) {
- AfterReturningAdvice advice = (AfterReturningAdvice) advisor.getAdvice();
- return new AfterReturningAdviceInterceptor(advice);
- }
- }
4. 在适配器中,将Advisor的方法和目标类方法交织在一起
- **
- * Interceptor to wrap am {@link org.springframework.aop.AfterReturningAdvice}.
- * Used internally by the AOP framework; application developers should not need
- * to use this class directly.
- *
- * @author Rod Johnson
- */
- public class AfterReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice, Serializable {
- private final AfterReturningAdvice advice;
- /**
- * Create a new AfterReturningAdviceInterceptor for the given advice.
- * @param advice the AfterReturningAdvice to wrap
- */
- public AfterReturningAdviceInterceptor(AfterReturningAdvice advice) {
- Assert.notNull(advice, "Advice must not be null");
- this.advice = advice;
- }
- public Object invoke(MethodInvocation mi) throws Throwable {
- Object retVal = mi.proceed();
- this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
- return retVal;
- }
- }
发表评论
-
Spring Batch学习(一)介绍
2015-01-08 17:17 8459为什么我们需要批处理? 我们不会总是想要立即得到需要的信 ... -
spring包详解
2012-04-22 14:58 1726下载的spring包中文件及各种包众多,在项目中往往只有 ... -
我的spring学习笔记2-IoC(反向控制 依赖注入)
2012-04-10 07:50 2783IoC(反向控制 依赖注入)这是Spring提出来了,这也是S ... -
我的spring学习笔记1-spring 简介
2012-04-10 07:49 21881.1. 概览 ... -
我的spring学习笔记15-容器扩展点之PropertyOverrideConfigurer
2012-04-22 14:55 1905PropertyOverrideConfigurer类似于Pr ... -
我的spring学习笔记14-容器扩展点之PropertyPlaceholderConfigurer
2012-04-22 14:55 1876PropertyPlaceholderConfigurer是个 ... -
我的spring学习笔记10-轻量级_Spring框架
2012-04-14 21:59 2006一、问题提问: ... -
我的spring学习笔记9-Spring使用工厂方法实例化Bean的注意点
2012-04-14 21:59 1590方法一: <bean id="m ... -
我的spring学习笔记8-Spring中Bean的实例化
2012-04-14 21:59 1503在Spring中要实例化一个Bean有几种方法: 1、最常用 ... -
我的spring学习笔记7-Spring的Bean配置文件给Bean定义别名
2012-04-14 21:59 3048本文介绍如何给Spring的Bean配置文件的Bean定义别名 ... -
我的spring学习笔记6-ApplicationContext实例化的参数兼容思想
2012-04-14 21:59 1520ApplicationContext能读取多个Bean定义文件 ... -
我的spring学习笔记5-如何使用ApplicationContext替换BeanFactory
2012-04-12 22:03 1921如何使用ApplicationContext替换BeanFac ... -
我的spring学习笔记4-ApplicationContext详解
2012-04-12 22:03 4368ontext的核心作用是ApplicationConte ... -
我的spring学习笔记3-BeanFactory 详解
2012-04-12 22:03 19921、BeanFactory是 ... -
我的spring学习笔记2-IoC(反向控制 依赖注入)
2012-04-12 22:03 1238IoC(反向控制 依赖注入)这是Spring提出来了,这也是S ... -
我的spring学习笔记-spring 简介
2012-04-12 22:03 16001.1. 概览 ... -
Spring中事件处理de小技巧
2012-04-08 12:01 1625Spring 中提供一些Aware相关de接口,Be ... -
关于使用Spring导致c3p0数据库死锁问题
2012-04-08 11:59 6527这个问题我实在是为整个 springsource 的员工蒙羞 ... -
SPRING多数据源切换的问题和解决方法
2012-04-08 11:56 4523在应用中,需要热切换数据源。但发现如下问题: J ... -
再析在spring框架中解决多数据源的问题
2012-04-08 11:52 1765在前面我写了《如何在 spring 框架中解决 ...
相关推荐
spring-aop-1.1.1.jar spring-aop-1.2.6.jar spring-aop-1.2.9.jar spring-aop-2.0.2.jar spring-aop-2.0.6.jar spring-aop-2.0.7.jar spring-aop-2.0.8.jar spring-aop-2.0.jar spring-aop-2.5.1.jar spring-aop-...
在Spring框架中,AOP(面向切面编程)是一种强大的设计模式,它允许开发者将关注点从业务逻辑中分离出来,比如日志记录、事务管理、权限检查等。AOP的核心概念包括切面(Aspect)、连接点(Join Point)、通知...
开发工具 spring-aop-4.3.6.RELEASE开发工具 spring-aop-4.3.6.RELEASE开发工具 spring-aop-4.3.6.RELEASE开发工具 spring-aop-4.3.6.RELEASE开发工具 spring-aop-4.3.6.RELEASE开发工具 spring-aop-4.3.6.RELEASE...
在Spring框架中,AOP(面向切面编程)是一种强大的工具,它允许程序员定义横切关注点,如日志、事务管理、权限控制等,并将它们模块化为可重用的切面。为了在Spring中进行AOP编程,我们需要一些特定的JAR包。以下是...
赠送jar包:spring-aop-5.2.0.RELEASE.jar; 赠送原API文档:spring-aop-5.2.0.RELEASE-javadoc.jar; 赠送源代码:spring-aop-5.2.0.RELEASE-sources.jar; 赠送Maven依赖信息文件:spring-aop-5.2.0.RELEASE.pom;...
在Spring框架中,AOP(面向切面编程)是一种强大的设计模式,它允许开发者将关注点分离,将横切关注点(如日志、事务管理、安全检查等)与核心业务逻辑解耦。本篇文章将深入探讨Spring AOP的实现原理,并通过一个名...
在AOP中,bean是被织入切面的对象,因此这个库是必不可少的。 3. **spring-context.jar**:提供了上下文相关的功能,如依赖注入(DI),事件传播,资源加载等。AOP的实现离不开Spring上下文的支持,因为它负责管理...
在Spring AOP中,切面可以通过注解或XML配置来定义。 - 连接点(Join Point):连接点是程序执行过程中的一个特定点,例如方法的调用或字段的访问。 - 切入点(Pointcut):切入点是连接点的集合,定义了切面将在...
spring-aop-3.2.0.RELEASE.jar,一个Spring中AOP的jar包
在Spring AOP中,切点通常用正则表达式或预定义的注解来指定,例如`@Within("com.example.service.*")`表示拦截com.example.service包下的所有类的所有方法。 4. **通知(Advice)**:通知是在切点匹配的方法执行前...
在本文中,我们将深入探讨如何在Spring框架中集成并使用AOP(面向切面编程)来实现对EHCache的高效管理。Spring是一个广泛使用的Java应用框架,它提供了强大的依赖注入和面向切面编程功能。而EHCache是一款流行、高...
赠送jar包:spring-aop-5.0.10.RELEASE.jar; 赠送原API文档:spring-aop-5.0.10.RELEASE-javadoc.jar; 赠送源代码:spring-aop-5.0.10.RELEASE-sources.jar; 赠送Maven依赖信息文件:spring-aop-5.0.10.RELEASE....
spring-aop-5.3.22.jar Spring AOP provides an Alliance-compliant aspect-oriented programming implementation allowing you to define method interceptors and pointcuts to cleanly decouple code that ...
赠送jar包:spring-aop-5.0.8.RELEASE.jar; 赠送原API文档:spring-aop-5.0.8.RELEASE-javadoc.jar; 赠送源代码:spring-aop-5.0.8.RELEASE-sources.jar; 赠送Maven依赖信息文件:spring-aop-5.0.8.RELEASE.pom;...
在Spring框架中,AOP主要用于日志记录、事务管理、性能统计等场景。本示例是关于如何在Spring Boot项目中实现AOP功能的一个简单演示。 首先,我们需要了解AOP的基本概念。AOP的核心是切面(Aspect),它封装了跨越...
在Spring AOP中,连接点总是表示方法执行。 5. **引入(Introduction)**:允许在现有的类中添加新的接口及其实现,从而让其他对象能够像处理普通对象一样处理引入的对象。 6. **织入(Weaving)**:把切面加入到程序...
赠送jar包:spring-aop-5.3.12.jar; 赠送原API文档:spring-aop-5.3.12-javadoc.jar; 赠送源代码:spring-aop-5.3.12-sources.jar; 赠送Maven依赖信息文件:spring-aop-5.3.12.pom; 包含翻译后的API文档:spring...
spring-aop-6.0.2.jar
在Spring框架中,AOP(面向切面编程)是一种强大的工具,它允许程序员将横切关注点(如日志、事务管理、权限检查等)与业务逻辑分离,从而实现代码的模块化和可维护性。这里我们将深入探讨两种在Spring中实现AOP的...