Spring框架深度解析:从IOC容器到AOP

Spring框架深度解析:从IOC容器到AOP

目录

  • Spring框架深度解析:从IOC容器到AOP
    • 1. Spring的核心:IOC容器
      • 1.1 什么是Spring IOC?
      • 1.2 Spring IOC的好处
      • 1.3 Spring中的DI是什么?
    • 2. Spring Bean
      • 2.1 什么是Spring Bean?
      • 2.2 Spring Bean的作用域
      • 2.3 Spring中的BeanFactory是什么?
      • 2.4 Spring中的FactoryBean是什么?
      • 2.5 Spring中的ObjectFactory是什么?
      • 2.6 Spring中的ApplicationContext是什么?
    • 3. 循环依赖问题
      • 3.1 什么是循环依赖?
      • 3.2 Spring如何解决循环依赖?
      • 3.3 为什么Spring循环依赖需要三级缓存,二级不够吗?
    • 4. 面向切面编程(AOP)
      • 4.1 什么是AOP?
      • 4.2 Spring AOP默认用的是什么动态代理,两者的区别?
      • 4.3 Spring AOP和AspectJ有什么区别?

1. Spring的核心:IOC容器

1.1 什么是Spring IOC?

IOC(Inversion of Control,控制反转)是Spring框架的核心。它是一种设计思想,将传统上由程序代码直接操控的对象的调用权交给容器,通过容器来实现对象组件的装配和管理。

相较于传统的程序设计中,我们通常是在某个对象中主动创建另一个对象,控制权在自己手上。而在IOC中,创建对象的控制权被反转了,转到了Spring框架,当我们需要某个对象时从容器池中进行注入即可。

举例说明:

假设我们有一个简单的应用,包含一个UserService和一个DatabaseConnection:

public class DatabaseConnection {
    // 数据库连接逻辑
}

public class UserService {
    private DatabaseConnection dbConnection;

    public UserService() {
        this.dbConnection = new DatabaseConnection(); // 主动创建依赖对象
    }
}

在这个例子中,UserService主动创建了DatabaseConnection的实例。这种方式会导致UserService和DatabaseConnection之间的强耦合。

使用Spring IOC后,我们可以这样改写:

public class UserService {
    private DatabaseConnection dbConnection;

    public UserService(DatabaseConnection dbConnection) {
        this.dbConnection = dbConnection; // 依赖由外部注入
    }
}

// 在Spring配置文件中
<bean id="dbConnection" class="com.example.DatabaseConnection" />
<bean id="userService" class="com.example.UserService">
    <constructor-arg ref="dbConnection" />
</bean>

在这个IOC的例子中,UserService不再主动创建DatabaseConnection,而是通过构造函数参数接收一个DatabaseConnection实例。Spring容器负责创建这些对象,并将它们装配在一起。

1.2 Spring IOC的好处

  1. 降低代码耦合度
    对象之间的依赖关系由容器负责,不再由对象自身管理。这意味着修改一个组件不会影响其他组件。

例如,如果我们需要更换数据库连接方式,只需要修改Spring配置,而不需要修改UserService的代码。

  1. 更易于测试
    可以轻松地替换依赖对象,便于单元测试和集成测试。

例如,在测试UserService时,我们可以轻松地注入一个模拟的DatabaseConnection:

@Test
public void testUserService() {
    DatabaseConnection mockConnection = mock(DatabaseConnection.class);
    UserService userService = new UserService(mockConnection);
    // 进行测试...
}
  1. 更好的可维护性
    集中管理对象的创建和生命周期,便于统一修改和维护。

例如,如果我们需要为所有的Service类添加日志功能,我们可以通过修改Spring配置来统一处理,而不需要修改每个Service类。

  1. 支持面向切面编程(AOP)
    IOC容器为AOP提供了基础,使得面向切面编程更容易实现。

例如,我们可以轻松地为所有的Service方法添加事务支持:

<tx:advice id="txAdvice" transaction-manager="txManager">
    <tx:attributes>
        <tx:method name="*" propagation="REQUIRED"/>
    tx:attributes>
tx:advice>

<aop:config>
    <aop:pointcut id="serviceOperation" expression="execution(* com.example.service.*.*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="serviceOperation"/>
aop:config>
  1. 提高开发效率
    减少了手动编写工厂类和配置代码的工作量。开发者可以专注于业务逻辑的实现,而不是对象的创建和管理。

1.3 Spring中的DI是什么?

DI(Dependency Injection,依赖注入)是实现IOC的一种方式。在DI中,对象的依赖关系由容器在运行期决定,即由容器动态地将依赖对象注入到组件之中。

Spring提供了多种依赖注入的方式,主要包括:

  1. 构造器注入
    通过构造函数注入依赖。这种方式可以确保依赖不可变,并且确保需要的依赖在对象创建时就已经设置好了。
public class UserService {
    private final UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}
<bean id="userService" class="com.example.UserService">
    <constructor-arg ref="userRepository" />
bean>
  1. Setter方法注入
    通过setter方法注入依赖。这种方式的优点是可以在运行时动态地改变依赖关系。
public class UserService {
    private UserRepository userRepository;

    public void setUserRepository(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}
<bean id="userService" class="com.example.UserService">
    <property name="userRepository" ref="userRepository" />
bean>
  1. 字段注入
    直接在字段上使用注解来注入依赖。这种方式虽然代码最简洁,但不推荐使用,因为它使得测试和重构变得困难。
public class UserService {
    @Autowired
    private UserRepository userRepository;
}
  1. 方法注入
    在任意方法上使用注解来注入依赖。这种方式较少使用,主要用于某些特殊场景。
public class UserService {
    private UserRepository userRepository;

    @Autowired
    public void init(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}

每种注入方式都有其适用场景,选择哪种方式取决于具体的需求和设计考虑。通常,构造器注入是最推荐的方式,因为它可以保证依赖的完整性,并支持不可变对象的创建。

2. Spring Bean

2.1 什么是Spring Bean?

在Spring中,构成应用程序主干并由Spring IOC容器管理的对象称为Bean。Bean是一个被实例化、组装和管理的对象。

Bean的定义包含了配置元数据,这些配置元数据告诉容器如何创建Bean,它的生命周期详情,及它的依赖关系。

以下是定义一个Bean的简单例子:

<bean id="userService" class="com.example.UserService">
    <property name="userRepository" ref="userRepository"/>
bean>

在这个例子中,我们定义了一个id为"userService"的Bean,它的类是com.example.UserService,并且它依赖于另一个id为"userRepository"的Bean。

使用注解方式,我们可以这样定义一个Bean:

@Component
public class UserService {
    @Autowired
    private UserRepository userRepository;
}

在这个例子中,@Component注解告诉Spring这个类应该被当作一个Bean来管理,@Autowired注解表示userRepository应该被自动注入。

2.2 Spring Bean的作用域

Spring框架支持以下几种bean的作用域:

  1. singleton(单例模式):
    在Spring IOC容器中只会存在一个共享的Bean实例。这是Spring中的默认作用域。
@Bean
@Scope("singleton")
public UserService userService() {
    return new UserService();
}

这意味着无论你从容器中获取多少次这个Bean,都是同一个实例。

  1. prototype(原型模式):
    每次请求都会创建一个新的Bean实例。
@Bean
@Scope("prototype")
public UserService userService() {
    return new UserService();
}

这意味着每次你从容器中获取这个Bean,都会得到一个新的实例。

  1. request
    每一次HTTP请求都会产生一个新的Bean,该Bean仅在当前HTTP request内有效。
@Bean
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public UserService userService() {
    return new UserService();
}

这种作用域主要用于Web应用程序中,每个HTTP请求都会有自己的Bean实例。

  1. session
    每一次HTTP Session都会产生一个新的Bean,该Bean仅在当前HTTP session内有效。
@Bean
@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
public UserPreferences userPreferences() {
    return new UserPreferences();
}

这种作用域允许你为每个用户会话创建一个Bean实例,例如存储用户的偏好设置。

  1. global-session
    全局session作用域,仅在使用portlet context时有效。这个作用域类似于标准HTTP Session作用域,不过它仅仅在基于portlet的web应用中才有意义。
@Bean
@Scope(value = "globalSession", proxyMode = ScopedProxyMode.TARGET_CLASS)
public UserPreferences userPreferences() {
    return new UserPreferences();
}

Portlet规范定义了全局Session的概念,它被所有构成某个portlet web应用的各种不同的portlet所共享。

2.3 Spring中的BeanFactory是什么?

BeanFactory是Spring IOC容器的核心接口,它负责管理所有的Bean,包括Bean的创建、配置和管理。BeanFactory使用懒加载策略,只有当客户端请求某个Bean时,才会创建该Bean的实例。

BeanFactory提供了以下主要功能:

  1. Bean的实例化:根据Bean的定义创建Bean的实例。
  2. Bean的配置:设置Bean的属性,注入依赖。
  3. Bean的生命周期管理:包括Bean的初始化和销毁。
  4. Bean的作用域管理:管理不同作用域的Bean。
  5. Bean的类型转换:在获取Bean时进行必要的类型转换。

以下是一个使用BeanFactory的简单例子:

public class Main {
    public static void main(String[] args) {
        BeanFactory factory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
        UserService userService = (UserService) factory.getBean("userService");
        userService.doSomething();
    }
}

在这个例子中,我们创建了一个XmlBeanFactory(BeanFactory的一个实现),它从classpath中读取一个XML配置文件。然后我们使用这个factory来获取一个名为"userService"的Bean。

2.4 Spring中的FactoryBean是什么?

FactoryBean是一种特殊的Bean,它可以生产或修饰对象实例。当你需要对一个Bean进行复杂的初始化时,可以实现FactoryBean接口。Spring容器会识别该Bean为FactoryBean,并使用它的getObject()方法返回最终想要使用的Bean。

FactoryBean接口定义如下:

public interface FactoryBean<T> {
    T getObject() throws Exception;
    Class<?> getObjectType();
    boolean isSingleton();
}

以下是一个FactoryBean的例子,它用于创建一个复杂的数据源对象:

public class DataSourceFactoryBean implements FactoryBean<DataSource> {
    private String driverClassName;
    private String url;
    private String username;
    private String password;

    @Override
    public DataSource getObject() throws Exception {
        BasicDataSource dataSource = new BasicDataSource();
        dataSource.setDriverClassName(driverClassName);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        return dataSource;
    }

    @Override
    public Class<?> getObjectType() {
        return DataSource.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }

    // setters for properties
}

在Spring配置中,我们可以这样使用这个FactoryBean:

<bean id="dataSource" class="com.example.DataSourceFactoryBean">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/mydb"/>
    <property name="username" value="user"/>
    <property name="password" value="password"/>
bean>

当其他Bean需要注入DataSource时,Spring会调用DataSourceFactoryBean的getObject()方法来获取实际的DataSource对象。

2.5 Spring中的ObjectFactory是什么?

ObjectFactory是一个函数式接口,用于延迟查找或创建对象。它主要用于解决循环依赖问题,我们稍后会详细讨论这个问题。

ObjectFactory接口定义如下:

public interface ObjectFactory<T> {
    T getObject() throws BeansException;
}

ObjectFactory的主要用途是延迟对象的创建或获取。例如,在处理prototype作用域的Bean时,我们可能需要在每次使用时都获取一个新的实例,这时就可以使用ObjectFactory:

public class UserManager {
    private ObjectFactory<User> userFactory;

    public void setUserFactory(ObjectFactory<User> userFactory) {
        this.userFactory = userFactory;
    }

    public void doSomethingWithNewUser() {
        User user = userFactory.getObject();  // 每次调用都获取一个新的User实例
        // 使用新的User实例进行操作
    }
}

在这个例子中,每次调用doSomethingWithNewUser方法时,都会通过userFactory获取一个新的User实例。这种方式特别适用于需要频繁创建新实例的场景。

2.6 Spring中的ApplicationContext是什么?

ApplicationContext是BeanFactory的子接口,它扩展了BeanFactory的功能。除了提供IOC容器的基本功能外,ApplicationContext还提供了以下的功能:

  1. 支持国际化
    ApplicationContext提供了MessageSource接口的实现,用于解析消息,支持国际化。
@Autowired
private MessageSource messageSource;

public void displayMessage() {
    String message = messageSource.getMessage("greeting", null, Locale.US);
    System.out.println(message);
}
  1. 支持事件发布
    ApplicationContext实现了ApplicationEventPublisher接口,允许通过事件机制在应用程序组件之间进行通信。
@Component
public class EmailService {
    @Autowired
    private ApplicationEventPublisher eventPublisher;

    public void sendEmail(String to, String content) {
        // 发送邮件的逻辑
        eventPublisher.publishEvent(new EmailSentEvent(this, to, content));
    }
}

@Component
public class EmailMonitor {
    @EventListener
    public void onEmailSent(EmailSentEvent event) {
        System.out.println("Email sent to: " + event.getTo());
    }
}
  1. 支持不同的资源加载方式
    ApplicationContext可以从不同的源(如文件系统、类路径、URL等)加载配置文件。
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// 或者
ApplicationContext context = new FileSystemXmlApplicationContext("C:/config/applicationContext.xml");
  1. 支持应用层特定的上下文
    例如,WebApplicationContext用于Web应用程序。
WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(servletContext);

ApplicationContext在启动时就实例化所有单例Bean(即使加载),相比BeanFactory(懒加载),它的启动时间更长,但运行时的性能更好。

以下是使用ApplicationContext的一个简单例子:

public class Main {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService = context.getBean("userService", UserService.class);
        userService.doSomething();
    }
}

在这个例子中,我们创建了一个ClassPathXmlApplicationContext,它会从类路径中加载applicationContext.xml文件。然后我们使用这个context来获取一个UserService的Bean实例。

3. 循环依赖问题

3.1 什么是循环依赖?

循环依赖是指两个或多个Bean互相依赖对方,形成一个闭环。这种情况下,Spring IOC容器在尝试创建这些Bean时会陷入死循环。

例如,考虑以下场景:

@Component
public class A {
    @Autowired
    private B b;
}

@Component
public class B {
    @Autowired
    private A a;
}

在这个例子中,A依赖B,B又依赖A,形成了一个循环依赖。

3.2 Spring如何解决循环依赖?

为了便于后续理解,这里先简单说一下bean的生命周期:

Spring中bean的生命周期包括以下步骤:

  1. 通过BeanDefinition获取bean的定义信息(每一个bean都会被封装成一个BeanDefinition)。
  2. 通过反射调用构造函数实例化bean。
  3. 进行bean的依赖注入,例如通过setter方法或@Autowired注解。
  4. 处理实现了Aware接口的bean。
  5. 执行BeanPostProcessor的前置处理器。
  6. 调用初始化方法,如实现了InitializingBean接口或自定义的init-method
  7. 执行BeanPostProcessor的后置处理器,可能在这里产生代理对象。
  8. 最后是销毁bean。

总结

主要把握创建过程和销毁过程这两个大的方面; 创建过程:首先实例化Bean,并设置Bean的属性,根据其实现的Aware接口(主要是BeanFactoryAware接口,BeanFactoryAware,ApplicationContextAware)设置依赖信息, 接下来调用BeanPostProcess的postProcessBeforeInitialization方法,完成initial前的自定义逻辑;afterPropertiesSet方法做一些属性被设定后的自定义的事情;调用Bean自身定义的init方法,去做一些初始化相关的工作;然后再调用postProcessAfterInitialization去做一些bean初始化之后的自定义工作。这四个方法的调用有点类似AOP。 此时,Bean初始化完成,可以使用这个Bean了。 销毁过程:如果实现了DisposableBean的destroy方法,则调用它,如果实现了自定义的销毁方法,则调用之。

接着再来说解决循环依赖的问题:

spring对循环依赖的处理有三种情况:

①构造器的循环依赖:这种依赖spring是处理不了的,直 接抛出BeanCurrentlylnCreationException异常。

  • 由于构造函数是bean生命周期中最先执行的,Spring框架无法解决构造方法的循环依赖问题。可以使用@Lazy懒加载注解,延迟bean的创建直到实际需要时。

②单例模式下的setter循环依赖:通过“三级缓存”处理循环依赖。

③非单例循环依赖:无法处理。

下面分析单例模式下的setter使用三级缓存来解决循环依赖问题:

  1. 一级缓存(singletonObjects)
    用于存放完全初始化好的Bean。这些Bean已经经历了完整的创建过程,可以直接使用。
  2. 二级缓存(earlySingletonObjects)
    用于存放原始的Bean对象(尚未填充属性)。这些Bean实例已经被创建,但是还没有被初始化。
  3. 三级缓存(singletonFactories)
    用于存放Bean的工厂对象,可以动态获取对象的引用。主要用于解决AOP代理问题。这个缓存存储的是一个Lambda表达式,可以在需要的时候创建代理对象。

让我们通过一个例子来详细说明这个过程:

@Component
public class A {
    @Autowired
    private B b;

    public A() {
        System.out.println("Creating A");
    }
}

@Component
public class B {
    @Autowired
    private A a;

    public B() {
        System.out.println("Creating B");
    }
}

A的某个field或者setter依赖了B的实例对象,同时B的某个field或者setter依赖了A。

A首先完成了初始化的第一步(实例化),并且将自己提前曝光到singletonFactories(三级缓存)中。

此时进行初始化的第二步,A发现自己依赖对象B,此时就尝试去获取B对象,但发现B还没有被创建,所以开始去创建B。

B在初始化的时候发现自己依赖了对象A,于是尝试获取A对象,先尝试从singletonObjects(一级缓存,此时一级缓存中没有A对象,因为A还没初始化完全),接着尝试从earlySingletonObjects(二级缓存,二级缓存也没有A),最后尝试从singletonFactories(三级缓存)获取,由于A通过三级缓存将自己提前曝光了,所以B能够通过ObjectFactory.getObject拿到A对象(尽管此时的A对象还未完全初始化),同时将A对象放入到二级缓存中去,B拿到A对象后顺利完成了初始化阶段,完全初始化之后将自己放入到一级缓存singletonObjects中。

此时再返回A的初始化过程中,A此时就能从一级缓存中拿到B的对象并顺利完成自己的初始化阶段,最终A也完成了初始化,进入了一级缓存singletonObjects中,至此A、B对象都能创建成功。

简而言之:

  1. 实例化A对象,并创建一个工厂ObjectFactory存入三级缓存。
  2. A在初始化时需要B对象,开始B的创建逻辑。
  3. B实例化完成,也创建一个ObjectFactory存入三级缓存。
  4. B需要注入A,通过三级缓存到A对象(原始对象或者代理对象),并将A对象存入二级缓存。
  5. B获得A对象后,B创建成功,存入一级缓存。
  6. A对象初始化时,由于B已创建完成,可以直接从一级缓存中注入B对象,A创建成功并存入一级缓存同时清除二级缓存中的对象A。
    1. **注意:**A被放入二级缓存时,三级缓存中的ObjectFactory并不会被清除。三级缓存仍然可以用于动态创建对象或返回代理对象。后置处理器在对象初始化完成后执行,可以根据需要返回代理对象,而不是直接返回原始对象。

3.3 为什么Spring循环依赖需要三级缓存,二级不够吗?

三级缓存是存储对象工厂,用于创建对象或者代理对象。一级缓存中的对象通常是原始对象,而代理对象需要通过三级缓存的工厂进行重新创建。如果只有二级缓存,A类如果需要被代理,B类在注入A类时应该得到A的代理对象,直接使用二级缓存就只能得到A类对象而非A的代理对象。

为什么不直接在放入二级缓存时判断是否需要代理,如果需要就在放入二级缓存时放入一个代理对象:在正常的bean生命周期中,代理对象应该是在对象初始化后进入后置处理器去生成代理的(也就是前面bean生命周期的第7步),而此时这样操作就会违背他的生命周期(放入二级缓存阶段应该是处于填充属性注入依赖的阶段,也就是前面bean生命周期的第3步)

具体来说:

通过使用三级缓存,Spring可以确保在循环依赖的情况下,所有引用到的Bean都是最终一致的(可能是代理对象)。

让我们通过一个例子来说明为什么需要三级缓存:

@Component
public class A {
    @Autowired
    private B b;
}

@Component
public class B {
    @Autowired
    private A a;
}

@Aspect
@Component
public class AAspect {
    @Around("execution(* com.example.A.*(..))")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("Before A method execution");
        Object result = pjp.proceed();
        System.out.println("After A method execution");
        return result;
    }
}

在这个例子中,A需要被代理(因为有切面),而B不需要。如果只有二级缓存,那么在解决循环依赖时,B中注入的A可能是原始对象而不是代理对象。

使用三级缓存,Spring可以在需要的时候(即在解决循环依赖时)创建代理对象(代理对象实则就是对一级缓存的原始对象再进行一个包装)。这样可以确保所有地方引用的都是同一个对象(代理对象)。

4. 面向切面编程(AOP)

4.1 什么是AOP?

AOP(Aspect-Oriented Programming,面向切面编程)是一种编程范式,旨在通过分离横切关注点来增加模块化。它允许你在不修改代码自身的情况下,动态地添加额外的行为到现有的代码中。

横切关注点是指那些影响多个类的功能,如日志记录、事务管理、安全检查等。这些功能往往散布在应用程序的多个部分,导致代码重复和难以维护。AOP提供了一种方法来集中处理这些横切关注点。

AOP的主要概念包括:

  1. 目标对象(Target):被增强的对象。

  2. 代理对象(Proxy):对目标对象进行增强后的对象

  3. 连接点(Join Point):目标对象中可以被增强的方法。

  4. 切点(Pointcut):匹配连接点的表达式,也就是实际被增强的方法。

  5. 通知(Advice):在切面的某个特定连接点上执行的动作,即增强部分的代码逻辑。主要类型包括:

    1. 前置通知(Before)
    2. 后置通知(After)
    3. 返回通知(After-returning)
    4. 异常通知(After-throwing)
    5. 环绕通知(Around)
  6. 织入(Weaving):通知和切入点动态组合的过程

  7. 切面(Aspect):通知和切点的组合。

下面是一个简单的AOP示例,使用Spring AOP来实现日志功能:

@Aspect
@Component
public class LoggingAspect {

    @Before("execution(* com.example.service.*.*(..))")
    public void logBefore(JoinPoint joinPoint) {
        System.out.println("Before method: " + joinPoint.getSignature().getName());
    }

    @AfterReturning(pointcut = "execution(* com.example.service.*.*(..))", returning = "result")
    public void logAfterReturning(JoinPoint joinPoint, Object result) {
        System.out.println("After method: " + joinPoint.getSignature().getName());
        System.out.println("Result: " + result);
    }
}

在这个例子中,我们定义了一个切面(LoggingAspect),它包含两个通知:

  1. 一个前置通知(@Before),在com.example.service包中的任何方法执行之前打印日志。
  2. 一个返回通知(@AfterReturning),在方法成功返回后打印日志和返回结果。

通过使用AOP,我们可以在不修改原有业务逻辑的情况下,轻松地为多个类和方法添加日志功能。这大大提高了代码的模块化程度和可维护性。

4.2 Spring AOP默认用的是什么动态代理,两者的区别?

Spring AOP默认使用JDK动态代理。但是,如果目标类没有实现接口,Spring会自动切换到使用CGLIB代理。

关于更多的介绍及区别可参考:Java代理详解:静态代理、动态代理-CSDN博客

在Spring中,如果你想强制使用CGLIB代理,可以通过以下配置:

@EnableAspectJAutoProxy(proxyTargetClass = true)

或者在XML配置中:

<aop:aspectj-autoproxy proxy-target-class="true"/>

4.3 Spring AOP和AspectJ有什么区别?

特性 Spring AOP AspectJ
实现方式 基于代理(JDK动态代理或CGLIB) 编译时或加载时织入
织入时机 运行时(运行时代理) 编译时、类加载时或运行时
支持的切点 方法级切点 方法级、构造函数级、字段级等多种切点
性能 相对较低(由于代理的开销) 较高(直接在字节码中插入切面逻辑)
复杂性 简单易用,适合大多数应用场景 复杂,适合需要高级功能的场景
配置方式 基于XML或注解 需要使用AspectJ特定的语法和工具
支持的功能 事务管理、日志记录、权限控制等 更强大的功能,如字段拦截、构造函数拦截
集成 与Spring框架紧密集成 可以与Spring集成,但需要额外配置
学习曲线 较低,易于上手 较高,需要学习AspectJ的语法和概念

你可能感兴趣的:(Spring,Java,spring,java,后端)