@Autowired @Resource
@Autowired 是Spring 提供的注解,@Resource是JDK提供的注解。Autowired 默认的注入方式为 byType(根据类型进行匹配),@Resource 默认注入方式为byName(根据名称进行匹配)。
当一个接口存在多个实现类的情况下,@Autowired 和 @Resource 都需要通过名称才能正确匹配到对应的Bean。Autowired 可以通过 @0ualifier 注解来显式指定名称,@Resource 可以通过 name 属性来显式指定名称。
@Autowired 支持在构造函数、方法、字段和参数上使用。@Resource 主要用于字段和方法上的注入,不支持在构造函数或参数上使用。
Bean 的作用域有哪些?
Spring 中Bean 的作用域通常有下面几种:
singleton:loC容器中只有唯一的 bean 实例。Spring 中的 bean 默认都是单例的,是对单例设计模式的应用。
prototype:每次获取都会创建一个新的bean实例。也就是说,连续 getBean()两次,得到的是不同的 Bean 实例。
request(仅Web应用可用):每一次 HTTP 请求都会产生一个新的bean(请求 bean),该bean 仅在当前 HTTP request 内有效。
session(仅Web应用可用):每一次来自新session的 HTTP 请求都会产生一个新的bean(会话bean),该 bean 仅在当前 HTTP session 内有效。
application/global-session(仅Web应用可用):每个Web 应用在启动时创建一个Bean(应用Bean),该bean 仅在当前应用启动时间内有效。
websocket(仅Web 应用可用):每一次 WebSocket 会话产生一个新的 bean。
Spring 的生命周期
1. 实例化 (new)
↓
2. 属性注入 (set values)
↓
3. Aware 接口回调 (知道名字、工厂、上下文)
↓
4. 初始化前处理 (BeanPostProcessor.before)
↓
5. 初始化 (afterPropertiesSet / init-method)
↓
6. 初始化后处理 (BeanPostProcessor.after)
↓
7. Bean 可用了!正常使用
↓
8. 容器关闭 → 销毁 (destroy / destroy-method)
💡 和 BeanFactory 的区别?
ApplicationContext
是 BeanFactory
的升级版,功能更多,比如支持国际化、事件发布等。
6️⃣ 第六步:初始化前的“美容”(postProcessBeforeInitialization)
🔔 如果有
BeanPostProcessor
,Spring 会在初始化前对 Bean 做一些“加工”。
public class MyPostProcessor implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String name) {
System.out.println("准备初始化:" + name);
return bean;
}
}
📌 用途:可以用来做 AOP、日志、权限检查等“增强功能”。
7️⃣ 第七步:正式初始化(InitializingBean 或 init-method)
✅ 当所有属性都设置好了,Spring 会调用初始化方法。
有两种方式:
方式一:实现接口
java
深色版本
public class UserService implements InitializingBean {
public void afterPropertiesSet() {
System.out.println("初始化:连接数据库...");
}
}
方式二:配置文件或注解指定
<bean class="UserService" init-method="myInit"/>
@PostConstruct
public void myInit() {
System.out.println("初始化方法");
}
📌 用途:做一些启动时的准备工作,比如连接数据库、加载缓存。
8️⃣ 第八步:初始化后的“再加工”(postProcessAfterInitialization)
🔔 和第六步对应,这是初始化之后的处理。
public Object postProcessAfterInitialization(Object bean, String name) {
System.out.println("已经初始化完成:" + name);
return bean;
}
📌 用途:Spring 的 AOP(比如加事务)就是在这里实现的!
9️⃣ 第九步:Bean 正式上岗,一直使用
✅ 现在 Bean 已经完全准备好,可以被其他组件使用了,直到应用关闭。
🔟 最后一步:Bean 要“退休”了(DisposableBean 或 destroy-method)
🔔 当 Spring 容器关闭时,会调用销毁方法。
方式一:实现接口
public class UserService implements DisposableBean {
public void destroy() {
System.out.println("销毁:关闭数据库连接...");
}
}
方式二:配置指定
<bean class="UserService" destroy-method="cleanup"/>
📌 用途:释放资源,比如关闭连接、线程池、文件句柄等。
🌱 Spring Bean 的一生(生命周期通俗讲解)
想象一下,Spring 就像一个“工厂”,而 Bean 就是这个工厂里生产出来的“产品”。我们来看看这个产品从出生到销毁经历了哪些阶段。
1️⃣ 第一步:Spring 把 Bean “造出来”(实例化)
✅ 相当于 Java 中的
new User()
Spring 容器通过反射,调用类的构造函数,创建一个空的对象。📌 举个例子:
User user = new User(); // 此时 user 还没设置名字、年龄等属性
2️⃣ 第二步:给 Bean “填资料”(属性注入)
✅ Spring 把配置好的值或引用(比如其他 Bean)设置到这个对象的属性中。
📌 继续上面的例子:
user.setName("张三"); user.setAge(25);
这时候,这个 Bean 才真正“完整”了。
3️⃣ 第三步:告诉 Bean 它叫什么名字(BeanNameAware)
🔔 如果你的 Bean 实现了
BeanNameAware
接口,Spring 会说:“嘿,你的名字是 userDAO”。public class UserDAO implements BeanNameAware { public void setBeanName(String name) { System.out.println("我的名字是:" + name); // 输出:userDAO } }
📌 用途:一般很少用,除非你想在代码里知道自己这个 Bean 叫什么 ID。
4️⃣ 第四步:告诉 Bean 工厂在哪(BeanFactoryAware)
🔔 如果实现了
BeanFactoryAware
,Spring 会把“工厂”(BeanFactory)给你。public class UserService implements BeanFactoryAware { private BeanFactory beanFactory; public void setBeanFactory(BeanFactory beanFactory) { this.beanFactory = beanFactory; } }
📌 用途:你可以通过
beanFactory.getBean(...)
去拿别的 Bean,相当于“反向查找”。
5️⃣ 第五步:告诉 Bean 整个上下文环境(ApplicationContextAware)
🔔 比上一步更强大!如果实现了
ApplicationContextAware
,Spring 会把“整个应用环境”给你。public class EmailService implements ApplicationContextAware { private ApplicationContext context; public void setApplicationContext(ApplicationContext ctx) { this.context = ctx; } }
📌 用途:
- 发布事件(如用户注册成功后发邮件)
- 获取任意 Bean
- 访问资源文件
@Component 和 @Bean 的区别是什么?
@Component
注解作用于类,而@Bean
注解作用于方法。 @Component
通常是通过类路径扫描来自动侦测以及自动装配到 Spring 容器中(我们可以使用 @ComponentScan
注解定义要扫描的路径从中找出标识了需要装配的类自动装配到 Spring 的 bean 容器中)。@Bean
注解通常是我们在标有该注解的方法中定义产生这个 bean,@Bean
告诉了 Spring 这是某个类的实例,当我需要用它的时候还给我。 @Bean
注解比 @Component
注解的自定义性更强,而且很多地方我们只能通过 @Bean
注解来注册 bean。比如当我们引用第三方库中的类需要装配到 Spring
容器时,则只能通过 @Bean
来实现。
springioc
SpringAOP
- 更多关于AspectJ?
了解AspectJ应用到java代码的过程(这个过程称为织入),对于织入这个概念,可以简单理解为aspect(切面)应用到目标函数(类)的过程。
对于这个过程,一般分为动态织入和静态织入:
动态织入的方式是在运行时动态将要增强的代码织入到目标类中,这样往往是通过动态代理技术完成的,如Java JDK的动态代理(Proxy,底层通过反射实现)或者CGLIB的动态代理(底层通过继承实现),Spring AOP采用的就是基于运行时增强的代理技术 ApectJ采用的就是静态织入的方式。ApectJ主要采用的是编译期织入,在这个期间使用AspectJ的acj编译器(类似javac)把aspect类编译成class字节码后,在java目标类编译时织入,即先编译aspect类再编译目标类。
3️⃣ Spring 如何知道要应用这些“特殊服务”?
这就涉及到 Spring 的 AOP 自动代理机制了。Spring 需要知道哪些类需要应用这些“特殊服务”。为此,我们需要告诉 Spring 使用 <aop:aspectj-autoproxy>
标签来启用 AOP 功能。
<aop:aspectj-autoproxy />
这个标签背后的工作流程如下:
解析配置:Spring 会找到 <aop:aspectj-autoproxy>
标签,并使用 AopNamespaceHandler
来处理它。 注册解析器:AopNamespaceHandler
注册了一个 AspectJAutoProxyBeanDefinitionParser
,用于解析和创建代理。 创建代理:AspectJAutoProxyBeanDefinitionParser
使用 AspectJAwareAdvisorAutoProxyCreator
来创建代理对象。
这两个接口对应的两个重要方法是:
4️⃣ 代理是如何工作的?
AspectJAwareAdvisorAutoProxyCreator
实现了两个关键接口:
BeanFactoryAware
:让代理能够访问 Spring 容器中的其他 Bean。BeanPostProcessor
:允许代理在 Bean 生命周期的不同阶段插入自定义逻辑。postProcessBeforeInstantiation
:在这个阶段,Spring 会检查是否有任何带有@Aspect
注解的类,并将它们的切面方法(如addSugar()
和addMilkFoam()
)封装成Advisor
对象。每个Advisor
包含:- Advice(通知):即具体的操作(如
addSugar()
)。 - Pointcut(切入点):决定哪些方法应该应用这些操作。
- Advice(通知):即具体的操作(如
postProcessAfterInitialization
:一旦所有 Bean 初始化完成,Spring 会根据Advisor
信息决定是否为某个 Bean 创建代理对象。如果需要,Spring 会选择合适的代理方式(CGLIB 或 JDK 动态代理)并生成代理对象。
BeanFactoryAware
:让代理能够访问 Spring 容器中的其他 Bean。 BeanPostProcessor
:允许代理在 Bean 生命周期的不同阶段插入自定义逻辑。
这两个接口对应的两个重要方法是:
# Cglib代理的案例
Spring AOP 的 CGLIB 代理 (CglibAopProxy
),通过继承目标类生成子类,在 getCallbacks
中装配一个“拦截器武器库”(callbacks 数组)。核心是 DynamicAdvisedInterceptor
(索引0),它实现 MethodInterceptor
,在 intercept
方法中,同样依据 Advisor
构建 MethodInterceptor
责任链,驱动 MethodInvocation.proceed()
递归执行,将增强逻辑与目标方法调用编织。CallbackFilter
则像调度员,根据方法决定启用哪个“武器”(如主战武器0号或直调武器1号),从而实现对类的代理
Spring AOP 的 JDK 代理 (JdkDynamicAopProxy),通过实现 InvocationHandler 接口,在 invoke 方法中,根据配置的 Advisor 动态生成一个 MethodInterceptor 责任链,利用 MethodInvocation.proceed() 的递归调用机制,将增强逻辑(通知)和目标方法的调用编织在一起,从而实现了 AOP。
postProcessBeforeInstantiation
:在这个阶段,Spring 会检查是否有任何带有@Aspect
注解的类,并将它们的切面方法(如addSugar()
和addMilkFoam()
)封装成Advisor
对象。每个Advisor
包含:- Advice(通知):即具体的操作(如
addSugar()
)。 - Pointcut(切入点):决定哪些方法应该应用这些操作。
- Advice(通知):即具体的操作(如
postProcessAfterInitialization
:一旦所有 Bean 初始化完成,Spring 会根据Advisor
信息决定是否为某个 Bean 创建代理对象。如果需要,Spring 会选择合适的代理方式(CG什么是Cglib? SpringAOP和Cglib是什么关系?
Cglib是一个强大的、高性能的代码生成包,它广泛被许多AOP框架使用,为他们提供方法的拦截。
- 最底层是字节码,字节码相关的知识请参考 JVM基础 - 类字节码详解
- ASM是操作字节码的工具
- cglib基于ASM字节码工具操作字节码(即动态生成代理,对方法进行增强)
- SpringAOP基于cglib进行封装,实现cglib方式的动态代理
DispatcherServlet和ApplicationContext有何关系?
DispatcherServlet 作为一个 Servlet,需要根据 Servlet 规范使用 Java 配置或 web.xml 声明和映射。反过来,DispatcherServlet 使用 Spring 配置来发现请求映射、视图解析、异常处理等等所需的委托组件。那它和ApplicationContext有和关系呢?如下内容可以参考官网-SpringMVC文档在新窗口打开
DispatcherServlet 需要 WebApplicationContext(继承自 ApplicationContext) 来配置。WebApplicationContext 可以链接到ServletContext 和 Servlet。因为绑定了 ServletContext,这样应用程序就可以在需要的时候使用 RequestContextUtils 的静态方法访问 WebApplicationContext。
大多数应用程序只有一个WebApplicationContext,除此之外也可以一个Root WebApplicationContext 被多个 Servlet实例,然后各自拥有自己的Servlet WebApplicationContext 配置。
Root WebApplicationContext 包含需要共享给多个 Servlet 实例的数据源和业务服务基础 Bean。这些 Bean 可以在 Servlet 特定的范围被继承或覆盖。