Spring扩展点:让你的Bean“活”出精彩人生!

82 阅读15分钟

你是否曾经觉得Spring就像是一个“魔法工厂”,它负责创建、管理、销毁Bean,而你只需要坐享其成?但有一天,你突然想:“嘿,Spring,能不能让我也参与一下Bean的生命周期?我想给我的Bean加点‘私房菜’!”——恭喜你,Spring扩展点就是你的“厨房”!

今天,我们就来聊聊Spring的扩展点,看看如何让你的Bean“活”出精彩人生!准备好了吗?系好安全带,我们出发!

1.Spring生命周期:Bean的“人生”阶段

在Spring的世界里,每个Bean都有自己的“人生轨迹”。从出生(实例化)到死亡(销毁),Spring为Bean安排了多个“人生阶段”。而作为开发者,你可以通过扩展点在每个阶段“插手”,给Bean加点“调味料”。

Bean的“人生”阶段:

  1. 出生:实例化(Instantiation)
  2. 成长:属性赋值(Population of Properties)
  3. 青春期:初始化(Initialization)
  4. 成熟期:使用(In Use)
  5. 退休:销毁(Destruction)

2.扩展点:Bean的“人生导师”

Spring提供了多个扩展点,允许你在Bean的每个“人生阶段”介入。这些扩展点就像是Bean的“人生导师”,帮助Bean更好地成长。

2.1BeanPostProcessor:Bean的“私人教练”

BeanPostProcessor是Spring中最强大的扩展点之一。它可以在Bean初始化前后“调教”Bean,比如修改属性、生成代理等。

代码示例:

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;

@Component
public class MyBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("Before初始化:" + beanName);
        // 在这里可以对Bean进行修改
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("After初始化:" + beanName);
        // 在这里可以对Bean进行修改
        return bean;
    }
}

思考题:

  • 1、如果你想让所有的Bean在初始化时都打印日志,你会怎么做?
  • 2、BeanPostProcessor和AOP(面向切面编程)有什么区别?它们可以一起使用吗?

可以通过 BeanPostProcessor 或 InitializingBean 接口实现。在 Bean 初始化完成后,将其注册到某个服务中。

应用场景:

假设你有一个 UserService,你希望在它初始化时自动将其注册到一个全局的 ServiceRegistry 中。

代码示例:

// 1. 定义一个服务注册中心
@Service
public class ServiceRegistry {
    private Map<String, Object> services = new HashMap<>();

    public void registerService(String name, Object service) {
        services.put(name, service);
    }

    public Object getService(String name) {
        return services.get(name);
    }
}

// 2. 定义一个 UserService
@Service
public class UserService {
    public void sayHello() {
        System.out.println("Hello from UserService!");
    }
}

// 3. 使用 BeanPostProcessor 自动注册
@Component
public class ServiceRegistryBeanPostProcessor implements BeanPostProcessor {

    @Autowired
    private ServiceRegistry serviceRegistry;

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof UserService) {
            serviceRegistry.registerService("userService", bean);
            System.out.println("UserService 已注册到 ServiceRegistry!");
        }
        return bean;
    }
}

运行结果:

当 Spring 容器启动时,UserService 会被自动注册到 ServiceRegistry 中。你可以通过 ServiceRegistry 获取 UserService 实例并调用其方法。


思考题 2:BeanPostProcessor和 AOP 有什么区别?它们可以一起使用吗?

答案:

  • BeanPostProcessor:是 Spring 提供的一个扩展点,允许在 Bean 初始化前后对 Bean 进行修改或增强。它的作用范围更广,可以操作任何 Bean。
  • AOP(面向切面编程) :是一种编程范式,用于将横切关注点(如日志、事务、权限等)从业务逻辑中分离出来。AOP 通常通过动态代理实现,只对匹配的 Bean 生效。

区别:

  1. 作用范围
  2. BeanPostProcessor 可以操作所有 Bean。
  3. AOP 只对匹配的 Bean 生效(如通过切入点表达式匹配)。
  4. 实现方式
  5. BeanPostProcessor 是 Spring 的核心扩展点。
  6. AOP 是基于动态代理实现的。
  7. 使用场景
  8. BeanPostProcessor 更适合对 Bean 进行全局处理(如修改属性、生成代理等)。
  9. AOP 更适合实现横切关注点(如日志、事务等)。

是否可以一起使用?

可以!BeanPostProcessor 和 AOP 可以协同工作。例如,你可以通过 BeanPostProcessor 生成代理,再通过 AOP 添加额外的逻辑。

代码示例:

// 1. 定义一个 BeanPostProcessor,生成代理
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof MyService) {
            ProxyFactory proxyFactory = new ProxyFactory(bean);
            proxyFactory.addAdvice(new MyMethodInterceptor());
            return proxyFactory.getProxy();
        }
        return bean;
    }
}

// 2. 定义一个 AOP 切面
@Aspect
@Component
public class MyAspect {

    @Before("execution(* com.example.MyService.*(..))")
    public void beforeMethod(JoinPoint joinPoint) {
        System.out.println("AOP: 方法执行前");
    }
}

// 3. 定义一个 Service
@Service
public class MyService {
    public void doSomething() {
        System.out.println("MyService 正在执行...");
    }
}

运行结果:

  • MyService 会被 BeanPostProcessor 生成代理。
  • AOP 切面会在方法执行前打印日志。

2.2InitializingBean & DisposableBean:Bean的“自我修养”

InitializingBean和DisposableBean是两个接口,分别用于Bean的初始化和销毁阶段。它们就像是Bean的“自我修养”课程,让Bean自己管理自己的生命周期。

代码示例:

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;

@Component
public class MyBean implements InitializingBean, DisposableBean {

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("MyBean初始化完成!");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("MyBean销毁了,再见!");
    }
}

思考题:

  • 如果你不想实现InitializingBean接口,还有什么其他方式可以实现初始化逻辑?
  • @PostConstruct和@PreDestroy注解与InitializingBean和DisposableBean有什么区别?

2.2.1. 实现初始化逻辑的其他方式

2.2.1.1 使用@PostConstruct注解

@PostConstruct 是 JSR-250 规范中的注解,用于标记一个方法在 Bean 初始化完成后执行。Spring 支持该注解。

代码示例:

import javax.annotation.PostConstruct;
import org.springframework.stereotype.Component;

@Component
public class MyService {

    @PostConstruct
    public void init() {
        System.out.println("MyService 初始化完成!");
    }
}

特点:

  • 方法名可以任意,只需标注 @PostConstruct。
  • 不需要实现任何接口,代码更简洁。
  • 是标准的 Java 注解,不依赖于 Spring。

2.2.1.2 使用init-method属性

在 XML 配置中,可以通过 init-method 属性指定初始化方法。

XML 配置示例:

<bean id="myService" class="com.example.MyService" init-method="init"/>

Java 类示例:

public class MyService {
    public void init() {
        System.out.println("MyService 初始化完成!");
    }
}

特点:

  • 适用于 XML 配置方式。
  • 方法名可以任意,只需在 XML 中指定。

2.2.1.3 使用@Bean注解的initMethod属性

在 Java 配置类中,可以通过 @Bean 注解的 initMethod 属性指定初始化方法。

代码示例:

@Configuration
public class AppConfig {

    @Bean(initMethod = "init")
    public MyService myService() {
        return new MyService();
    }
}

public class MyService {
    public void init() {
        System.out.println("MyService 初始化完成!");
    }
}

特点:

  • 适用于 Java 配置类。
  • 方法名可以任意,只需在 @Bean 注解中指定。

2.2.1.4 使用BeanPostProcessor

通过实现 BeanPostProcessor 接口,可以在 Bean 初始化前后执行自定义逻辑。

代码示例:

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;

@Component
public class MyBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("Before Initialization: " + beanName);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("After Initialization: " + beanName);
        return bean;
    }
}

特点:

  • 可以全局处理所有 Bean 的初始化逻辑。
  • 适用于需要统一处理的场景。

2.2.2. 实现销毁逻辑的其他方式

2.2.2.1 使用@PreDestroy注解

@PreDestroy 是 JSR-250 规范中的注解,用于标记一个方法在 Bean 销毁前执行。

代码示例:

import javax.annotation.PreDestroy;
import org.springframework.stereotype.Component;

@Component
public class MyService {

    @PreDestroy
    public void cleanup() {
        System.out.println("MyService 销毁完成!");
    }
}

特点:

  • 方法名可以任意,只需标注 @PreDestroy。
  • 不需要实现任何接口,代码更简洁。
  • 是标准的 Java 注解,不依赖于 Spring。

2.2.2.2 使用destroy-method属性

在 XML 配置中,可以通过 destroy-method 属性指定销毁方法。

XML 配置示例:

<bean id="myService" class="com.example.MyService" destroy-method="cleanup"/>

Java 类示例:

public class MyService {
    public void cleanup() {
        System.out.println("MyService 销毁完成!");
    }
}

特点:

  • 适用于 XML 配置方式。
  • 方法名可以任意,只需在 XML 中指定。

2.2.2.3 使用@Bean注解的destroyMethod属性

在 Java 配置类中,可以通过 @Bean 注解的 destroyMethod 属性指定销毁方法。

代码示例:

@Configuration
public class AppConfig {

    @Bean(destroyMethod = "cleanup")
    public MyService myService() {
        return new MyService();
    }
}

public class MyService {
    public void cleanup() {
        System.out.println("MyService 销毁完成!");
    }
}

特点:

  • 适用于 Java 配置类。
  • 方法名可以任意,只需在 @Bean 注解中指定。

2.2.3.@PostConstruct和@PreDestroy注解与InitializingBean和DisposableBean接口的区别

2.2.3.1 代码侵入性

  • InitializingBean 和 DisposableBean:需要实现 Spring 的接口,代码与 Spring 框架耦合。
  • @PostConstruct 和 @PreDestroy:只需标注注解,代码与 Spring 解耦,更符合面向对象设计原则。

2.2.3.2 标准化

  • InitializingBean 和 DisposableBean:是 Spring 特有的接口。
  • @PostConstruct 和 @PreDestroy:是 JSR-250 规范中的标准注解,适用于任何支持该规范的框架(如 Java EE)。

2.2.3.3 灵活性

  • InitializingBean 和 DisposableBean:方法名固定(afterPropertiesSet 和 destroy),灵活性较低。
  • @PostConstruct 和 @PreDestroy:方法名可以任意,灵活性更高。

2.2.3.4 执行顺序

  • @PostConstruct:在 InitializingBean.afterPropertiesSet 之前执行。
  • @PreDestroy:在 DisposableBean.destroy 之后执行。

2.2.4. 总结

初始化逻辑的实现方式:

  1. 实现 InitializingBean 接口。
  2. 使用 @PostConstruct 注解。
  3. 使用 init-method 属性(XML 配置)。
  4. 使用 @Bean 注解的 initMethod 属性(Java 配置)。
  5. 使用 BeanPostProcessor。

销毁逻辑的实现方式:

  1. 实现 DisposableBean 接口。
  2. 使用 @PreDestroy 注解。
  3. 使用 destroy-method 属性(XML 配置)。
  4. 使用 @Bean 注解的 destroyMethod 属性(Java 配置)。

@PostConstruct和@PreDestroy的优势:

  • 代码与 Spring 解耦,更符合标准化。
  • 方法名可以任意,灵活性更高。
  • 适用于任何支持 JSR-250 规范的框架。

2.3BeanFactoryPostProcessor:Bean工厂的“幕后黑手”

BeanFactoryPostProcessor允许你在Bean实例化之前修改Bean的定义。它就像是Bean工厂的“幕后黑手”,悄悄地改变Bean的命运。

代码示例:

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;

@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        System.out.println("BeanFactory准备就绪,开始搞事情!");
        // 在这里可以修改Bean的定义
    }
}

思考题:

  • 如果你想让所有的Bean都自动注入某个属性,你会怎么做?
  • BeanFactoryPostProcessor和BeanPostProcessor有什么区别?它们的使用场景分别是什么?

2.3.1. 让所有的 Bean 自动注入某个属性

实现方式:使用BeanPostProcessor

BeanPostProcessor 允许你在 Bean 初始化前后对 Bean 进行修改。通过实现 BeanPostProcessor,你可以为所有的 Bean 自动注入某个属性。

代码示例:

假设我们有一个 CommonProperty 类,我们希望所有的 Bean 都自动注入这个属性。

// 1. 定义 CommonProperty
@Component
public class CommonProperty {
    private String value = "Default Value";

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }
}

// 2. 实现 BeanPostProcessor
@Component
public class CommonPropertyInjector implements BeanPostProcessor {

    @Autowired
    private CommonProperty commonProperty;

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        // 在初始化之前注入属性
        if (bean instanceof CommonPropertyAware) {
            ((CommonPropertyAware) bean).setCommonProperty(commonProperty);
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}

// 3. 定义一个接口,用于标记需要注入 CommonProperty 的 Bean
public interface CommonPropertyAware {
    void setCommonProperty(CommonProperty commonProperty);
}

// 4. 实现 CommonPropertyAware 接口
@Service
public class MyService implements CommonPropertyAware {

    private CommonProperty commonProperty;

    @Override
    public void setCommonProperty(CommonProperty commonProperty) {
        this.commonProperty = commonProperty;
    }

    public void printCommonProperty() {
        System.out.println("CommonProperty: " + commonProperty.getValue());
    }
}

运行结果:

当 MyService 初始化时,CommonProperty 会被自动注入。调用 printCommonProperty 方法会输出:

CommonProperty: Default Value

2.3.2.BeanFactoryPostProcessor和BeanPostProcessor的区别

2.3.2.1BeanFactoryPostProcessor

  • 作用阶段:在 Bean 实例化之前,Spring 容器会调用 BeanFactoryPostProcessor 的 postProcessBeanFactory 方法。
  • 功能:允许修改 Bean 的定义(如修改属性值、添加新的 Bean 定义等)。
  • 使用场景
    • 修改 Bean 的定义(如修改属性值)。
    • 动态注册新的 Bean。
    • 在 Bean 实例化之前对容器进行全局配置。

代码示例:

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;

@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        System.out.println("BeanFactoryPostProcessor 正在处理 BeanFactory...");
        // 在这里可以修改 Bean 的定义
    }
}

2.3.2.2BeanPostProcessor

  • 作用阶段:在 Bean 初始化前后,Spring 容器会调用 BeanPostProcessor 的 postProcessBeforeInitialization 和 postProcessAfterInitialization 方法。
  • 功能:允许修改 Bean 的实例(如修改属性、生成代理等)。
  • 使用场景
    • 在 Bean 初始化前后执行自定义逻辑。
    • 为 Bean 生成动态代理。
    • 全局处理 Bean 的初始化逻辑。

代码示例:

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;

@Component
public class MyBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("Before Initialization: " + beanName);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("After Initialization: " + beanName);
        return bean;
    }
}

2.3.2.3 区别总结

特性BeanFactoryPostProcessorBeanPostProcessor
作用阶段Bean 实例化之前Bean 初始化前后
操作对象Bean 的定义(BeanDefinition)Bean 的实例
使用场景修改 Bean 的定义、动态注册 Bean修改 Bean 的属性、生成代理、全局处理逻辑
执行时机在 Bean 实例化之前在 Bean 初始化前后
是否依赖 Bean 实例不依赖 Bean 实例依赖 Bean 实例

2.3.3. 使用场景示例

2.3.3.1BeanFactoryPostProcessor的使用场景

  • 动态注册 Bean:在运行时根据条件动态注册新的 Bean。
  • 修改 Bean 的定义:例如,将所有 Bean 的某个属性设置为默认值。

代码示例:动态注册 Bean

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;

@Component
public class DynamicBeanRegistrar implements BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        // 动态注册一个 Bean
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(MyDynamicBean.class);
        beanFactory.registerBeanDefinition("myDynamicBean", builder.getBeanDefinition());
    }
}

public class MyDynamicBean {
    public void sayHello() {
        System.out.println("Hello from MyDynamicBean!");
    }
}

2.3.3.2BeanPostProcessor的使用场景

  • 全局属性注入:为所有 Bean 自动注入某个属性。
  • 生成动态代理:为某些 Bean 生成代理对象。
  • 日志记录:在 Bean 初始化前后打印日志。

代码示例:生成动态代理

import org.springframework.aop.framework.ProxyFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;

@Component
public class ProxyBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof MyService) {
            ProxyFactory proxyFactory = new ProxyFactory(bean);
            proxyFactory.addAdvice(new MyMethodInterceptor());
            return proxyFactory.getProxy();
        }
        return bean;
    }
}

2.3.3.4. 总结

  • BeanFactoryPostProcessor:用于在 Bean 实例化之前修改 Bean 的定义或动态注册 Bean。
  • BeanPostProcessor:用于在 Bean 初始化前后修改 Bean 的实例或执行自定义逻辑。

3.扩展点的实际应用:让Bean“活”出精彩

3.1动态代理:给Bean穿上“马甲”

通过BeanPostProcessor,你可以轻松地为Bean生成动态代理。比如,你想给所有的Service层方法加上事务管理,就可以通过BeanPostProcessor来实现。

代码示例:

import org.springframework.aop.framework.ProxyFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;

@Component
public class TransactionalBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof MyService) {
            ProxyFactory proxyFactory = new ProxyFactory(bean);
            proxyFactory.addAdvice(new TransactionalAdvice());
            return proxyFactory.getProxy();
        }
        return bean;
    }
}

思考题:

  • 动态代理和AOP有什么区别?它们可以一起使用吗?
  • 如果你想让某个Bean不被代理,你会怎么做?

3.2自定义注解:给Bean打上“标签”

通过BeanFactoryPostProcessor,你可以扫描所有的Bean,并根据自定义注解为它们添加额外的逻辑。

代码示例:

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;

@Component
public class CustomAnnotationProcessor implements BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        for (String beanName : beanFactory.getBeanDefinitionNames()) {
            Object bean = beanFactory.getBean(beanName);
            if (bean.getClass().isAnnotationPresent(MyCustomAnnotation.class)) {
                System.out.println("发现自定义注解:" + beanName);
                // 在这里可以为Bean添加额外的逻辑
            }
        }
    }
}

思考题:

  • 自定义注解和Spring的@Component注解有什么区别?它们可以一起使用吗?
  • 如果你想让某个Bean在初始化时自动注册到某个服务中,你会怎么做?

3.2.1. 动态代理和 AOP 的区别

3.2.1.1 动态代理

  • 定义:动态代理是一种设计模式,允许在运行时动态创建代理对象,并将方法调用转发到实际对象或拦截器。
  • 实现方式
    • JDK 动态代理:基于接口的代理,要求目标类必须实现接口。
    • CGLIB 动态代理:基于子类的代理,可以对普通类进行代理。
  • 使用场景
    • 在方法调用前后执行额外逻辑(如日志、事务、权限检查等)。
    • 动态生成代理对象,减少手动编码。

代码示例:JDK 动态代理

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class JdkDynamicProxyDemo {

    public static void main(String[] args) {
        MyService target = new MyServiceImpl();
        MyService proxy = (MyService) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new MyInvocationHandler(target)
        );
        proxy.doSomething();
    }
}

interface MyService {
    void doSomething();
}

class MyServiceImpl implements MyService {
    @Override
    public void doSomething() {
        System.out.println("MyService is doing something...");
    }
}

class MyInvocationHandler implements InvocationHandler {
    private final Object target;

    public MyInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Before method: " + method.getName());
        Object result = method.invoke(target, args);
        System.out.println("After method: " + method.getName());
        return result;
    }
}

3.2.1.2 AOP(面向切面编程)

  • 定义:AOP 是一种编程范式,用于将横切关注点(如日志、事务、权限等)从业务逻辑中分离出来。
  • 实现方式
    • 基于动态代理实现。
    • 通过切面(Aspect)、切入点(Pointcut)、通知(Advice)等概念组织代码。
  • 使用场景
    • 统一处理多个 Bean 的横切关注点。
    • 减少重复代码,提高代码的可维护性。

代码示例:Spring AOP

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class MyAspect {

    @Before("execution(* com.example.MyService.doSomething(..))")
    public void beforeMethod() {
        System.out.println("Before method: doSomething");
    }
}

3.2.1.3 动态代理 vs AOP

特性动态代理AOP
定义一种设计模式,用于动态生成代理对象一种编程范式,用于分离横切关注点
实现方式JDK 动态代理、CGLIB 动态代理基于动态代理实现
使用场景方法调用前后的逻辑增强统一处理多个 Bean 的横切关注点
代码组织手动编写代理逻辑通过切面、切入点、通知等组织代码
耦合性与业务逻辑耦合与业务逻辑解耦

3.2.2. 动态代理和 AOP 可以一起使用吗?

可以!  AOP 的实现依赖于动态代理。Spring AOP 默认使用动态代理来创建代理对象,并在代理对象中执行切面逻辑。

示例:AOP 使用动态代理

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class MyAspect {

    @Before("execution(* com.example.MyService.doSomething(..))")
    public void beforeMethod() {
        System.out.println("Before method: doSomething");
    }
}

在这个例子中,Spring 会为 MyService 创建动态代理,并在 doSomething 方法执行前调用 beforeMethod。


3.2.3. 如何让某个 Bean 不被代理?

在某些场景下,你可能希望某个 Bean 不被代理(例如,避免代理链过长或性能问题)。以下是几种实现方式:

3.2.3.1 使用@NonAspect注解(自定义注解)

Spring 本身没有提供直接禁用代理的注解,但你可以通过自定义注解和条件判断来实现。

代码示例:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface NonAspect {
}

在 Bean 上标注 @NonAspect:

@Service
@NonAspect
public class MyService {
    public void doSomething() {
        System.out.println("MyService is doing something...");
    }
}

在 BeanPostProcessor 中跳过被标记的 Bean:

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;

@Component
public class NonAspectBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean.getClass().isAnnotationPresent(NonAspect.class)) {
            return bean; // 跳过被标记的 Bean
        }
        // 其他 Bean 的处理逻辑
        return bean;
    }
}

3.2.3.2 使用@Primary注解

如果某个 Bean 有多个实现,可以通过 @Primary 注解指定默认的 Bean,避免被代理。

代码示例:

@Service
@Primary
public class MyServiceImpl implements MyService {
    @Override
    public void doSomething() {
        System.out.println("MyServiceImpl is doing something...");
    }
}

3.2.3.3 使用@Scope(proxyMode = ScopedProxyMode.NO)

如果 Bean 是作用域代理(如 @Scope("prototype")),可以通过 proxyMode = ScopedProxyMode.NO 禁用代理。

代码示例:

@Service
@Scope(proxyMode = ScopedProxyMode.NO)
public class MyService {
    public void doSomething() {
        System.out.println("MyService is doing something...");
    }
}

3.2.3.4 使用BeanPostProcessor排除特定 Bean

在 BeanPostProcessor 中排除特定 Bean。

代码示例:

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;

@Component
public class ExcludeBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof MyService) {
            return bean; // 跳过 MyService
        }
        // 其他 Bean 的处理逻辑
        return bean;
    }
}

3.2.4. 总结

  • 动态代理:是一种设计模式,用于在运行时生成代理对象。
  • AOP:是一种编程范式,基于动态代理实现,用于分离横切关注点。
  • 动态代理和 AOP 可以一起使用:AOP 的实现依赖于动态代理。
  • 让某个 Bean 不被代理
    • 使用自定义注解(如 @NonAspect)。
    • 使用 @Primary 注解。
    • 使用 @Scope(proxyMode = ScopedProxyMode.NO)。
    • 在 BeanPostProcessor 中排除特定 Bean。

4.总结:让你的Bean“活”出精彩人生!

Spring的扩展点就像是Bean的“人生导师”,帮助Bean在每个阶段都能“活”出精彩。通过BeanPostProcessor、InitializingBean、BeanFactoryPostProcessor等扩展点,你可以轻松地介入Bean的生命周期,实现各种自定义逻辑。

希望这篇文章能让你对Spring的扩展点有更深入的理解,也希望你能通过这些扩展点,让你的Bean“活”出精彩人生!


相关推荐

  • Spring AOP:让你的代码更优雅!
  • Spring源码解析:Bean的生命周期
  • 动态代理 vs AOP:谁才是真正的“幕后黑手”?

互动时间:你用过哪些Spring扩展点?有没有遇到过什么有趣的问题?欢迎在评论区分享你的经验!