你是否曾经觉得Spring就像是一个“魔法工厂”,它负责创建、管理、销毁Bean,而你只需要坐享其成?但有一天,你突然想:“嘿,Spring,能不能让我也参与一下Bean的生命周期?我想给我的Bean加点‘私房菜’!”——恭喜你,Spring扩展点就是你的“厨房”!
今天,我们就来聊聊Spring的扩展点,看看如何让你的Bean“活”出精彩人生!准备好了吗?系好安全带,我们出发!
1.Spring生命周期:Bean的“人生”阶段
在Spring的世界里,每个Bean都有自己的“人生轨迹”。从出生(实例化)到死亡(销毁),Spring为Bean安排了多个“人生阶段”。而作为开发者,你可以通过扩展点在每个阶段“插手”,给Bean加点“调味料”。
Bean的“人生”阶段:
- 出生:实例化(Instantiation)
- 成长:属性赋值(Population of Properties)
- 青春期:初始化(Initialization)
- 成熟期:使用(In Use)
- 退休:销毁(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 生效。
区别:
- 作用范围:
- BeanPostProcessor 可以操作所有 Bean。
- AOP 只对匹配的 Bean 生效(如通过切入点表达式匹配)。
- 实现方式:
- BeanPostProcessor 是 Spring 的核心扩展点。
- AOP 是基于动态代理实现的。
- 使用场景:
- BeanPostProcessor 更适合对 Bean 进行全局处理(如修改属性、生成代理等)。
- 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. 总结
初始化逻辑的实现方式:
- 实现 InitializingBean 接口。
- 使用 @PostConstruct 注解。
- 使用 init-method 属性(XML 配置)。
- 使用 @Bean 注解的 initMethod 属性(Java 配置)。
- 使用 BeanPostProcessor。
销毁逻辑的实现方式:
- 实现 DisposableBean 接口。
- 使用 @PreDestroy 注解。
- 使用 destroy-method 属性(XML 配置)。
- 使用 @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 区别总结
| 特性 | BeanFactoryPostProcessor | BeanPostProcessor |
|---|---|---|
| 作用阶段 | 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扩展点?有没有遇到过什么有趣的问题?欢迎在评论区分享你的经验!