Spring面试

@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 的区别?
ApplicationContextBeanFactory 的升级版,功能更多,比如支持国际化、事件发布等。


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;
       }
   }

📌 用途:
2. 发布事件(如用户注册成功后发邮件)
3. 获取任意 Bean
4. 访问资源文件

[@Component 和 @Bean 的区别是什么?](#@Component 和 @Bean 的区别是什么?)

@Component 注解作用于类,而@Bean注解作用于方法。 @Component通常是通过类路径扫描来自动侦测以及自动装配到 Spring 容器中(我们可以使用 @ComponentScan 注解定义要扫描的路径从中找出标识了需要装配的类自动装配到 Spring 的 bean 容器中)。@Bean 注解通常是我们在标有该注解的方法中定义产生这个 bean,@Bean告诉了 Spring 这是某个类的实例,当我需要用它的时候还给我。 @Bean 注解比 @Component 注解的自定义性更强,而且很多地方我们只能通过 @Bean 注解来注册 bean。比如当我们引用第三方库中的类需要装配到 Spring容器时,则只能通过 @Bean来实现。

复制代码

springioc

SpringAOP

  1. 更多关于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 实现了两个关键接口:

  1. BeanFactoryAware:让代理能够访问 Spring 容器中的其他 Bean。

  2. BeanPostProcessor:允许代理在 Bean 生命周期的不同阶段插入自定义逻辑。

  3. postProcessBeforeInstantiation :在这个阶段,Spring 会检查是否有任何带有 @Aspect 注解的类,并将它们的切面方法(如 addSugar()addMilkFoam())封装成 Advisor 对象。每个 Advisor 包含:

    • Advice (通知):即具体的操作(如 addSugar())。
    • Pointcut(切入点):决定哪些方法应该应用这些操作。
  4. postProcessAfterInitialization :一旦所有 Bean 初始化完成,Spring 会根据 Advisor 信息决定是否为某个 Bean 创建代理对象。如果需要,Spring 会选择合适的代理方式(CGLIB 或 JDK 动态代理)并生成代理对象。

BeanFactoryAware :让代理能够访问 Spring 容器中的其他 Bean。 BeanPostProcessor:允许代理在 Bean 生命周期的不同阶段插入自定义逻辑。

这两个接口对应的两个重要方法是:

[#](# Cglib代理的案例) 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。

  1. postProcessBeforeInstantiation :在这个阶段,Spring 会检查是否有任何带有 @Aspect 注解的类,并将它们的切面方法(如 addSugar()addMilkFoam())封装成 Advisor 对象。每个 Advisor 包含:

    • Advice (通知):即具体的操作(如 addSugar())。
    • Pointcut(切入点):决定哪些方法应该应用这些操作。
  2. postProcessAfterInitialization :一旦所有 Bean 初始化完成,Spring 会根据 Advisor 信息决定是否为某个 Bean 创建代理对象。如果需要,Spring 会选择合适的代理方式(CG

    什么是Cglib? SpringAOP和Cglib是什么关系?

    Cglib是一个强大的、高性能的代码生成包,它广泛被许多AOP框架使用,为他们提供方法的拦截。

  3. 最底层是字节码,字节码相关的知识请参考 JVM基础 - 类字节码详解

  4. ASM是操作字节码的工具

  5. cglib基于ASM字节码工具操作字节码(即动态生成代理,对方法进行增强)

  6. 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 特定的范围被继承或覆盖。

相关推荐
Java水解3 分钟前
JAVA经典面试题附答案(持续更新版)
java·后端·面试
洛小豆2 小时前
在Java中,Integer.parseInt和Integer.valueOf有什么区别
java·后端·面试
前端小张同学3 小时前
服务器上如何搭建jenkins 服务CI/CD😎😎
java·后端
ytadpole3 小时前
Spring Cloud Gateway:一次不规范 URL 引发的路由转发404问题排查
java·后端
华仔啊3 小时前
基于 RuoYi-Vue 轻松实现单用户登录功能,亲测有效
java·vue.js·后端
程序员鱼皮3 小时前
刚刚 Java 25 炸裂发布!让 Java 再次伟大
java·javascript·计算机·程序员·编程·开发·代码
闰五月4 小时前
JavaScript执行上下文详解
面试
浮游本尊4 小时前
Java学习第21天 - 微服务架构设计
java
渣哥4 小时前
Java CyclicBarrier 详解:原理、使用方式与应用场景
java