分享两道java面试题

59 阅读6分钟

1.SpringBean依赖注入(DI)的方式有哪些?

1. 构造器注入 (Constructor Injection)

通过类的构造方法来注入依赖。Spring容器在创建Bean时,会调用其构造方法,并将所需的依赖作为参数传入。

  • 优点

    1. 不可变性 (Immutability) :依赖项通常被声明为 final,确保了它们在Bean的整个生命周期内不可变,避免了不可预料的修改,线程安全。
    2. 完全初始化的状态:对象在构造完成后,所有必需的依赖都已经就绪,状态是完整和一致的,避免了NPE(空指针异常)。
    3. 代码清晰,易于测试,可以直接在测试中通过 new 来创建对象并传入mock依赖。

2. Setter方法注入 (Setter Injection)

通过类的Setter方法进行注入。Spring容器在调用无参构造器或工厂方法创建Bean实例后,再调用对应的Setter方法来注入依赖。

  • 代码示例

    @Service
    public class OrderService {
        private UserService userService;
        private ProductService productService;
    
        @Autowired
        public void setUserService(UserService userService) {
            this.userService = userService;
        }
    
        @Autowired
        public void setProductService(ProductService productService) {
            this.productService = productService;
        }
    }
    
  • 优点

    1. 灵活性:允许在Bean创建后再改变依赖(尽管实践中很少这么做),更适合可选依赖的注入。
    2. 与传统的JavaBean规范兼容。
  • 缺点

    1. 对象在构造后可能处于不完整的状态(因为依赖可能还没被注入)。
    2. 无法将依赖项声明为 final

3. 字段注入 (Field Injection)

直接在字段上使用 @Autowired 注解。这是最简洁但也是最不推荐的方式。

  • 代码示例

    java

    @Service
    public class OrderService {
        @Autowired
        private UserService userService;
    
        @Autowired
        private ProductService productService;
    }
    
    • 优点

    1. 代码极其简洁,减少了大量的模板代码。
  • 缺点

    1. 不易测试:如果不使用Spring的测试框架,你无法直接通过 new 来创建对象并初始化这些依赖,必须通过反射来注入mock对象,非常不方便。
    2. 隐藏了依赖关系:一个类到底依赖了什么,无法通过公共的接口(构造方法或Setter方法)清晰地看到,违反了“显式优于隐式”的原则。
    3. 无法声明为final:导致依赖可以被修改,破坏了不变性。
    4. 容易违反单一职责原则,因为可以很方便地注入很多依赖,导致类变得臃肿。

2.Spring中常用的注解有哪些?

1. 核心配置与Bean声明注解

这些注解用于定义和配置Spring容器中的Bean。

  • @Configuration: 声明一个类为配置类,其内部会包含使用@Bean注解定义Bean的方法。它取代了传统的XML配置文件。

  • @ComponentScan: 配合@Configuration使用,用于自动扫描指定包路径下的组件(如@Component@Service等),并将其注册为Bean。可配置扫描路径和过滤器。

  • @Bean: 在@Configuration类的方法上使用。它将方法的返回值定义为一个Bean,并交给Spring容器管理。常用于导入第三方库的类。

  • @Component: 一个通用的组件注解,表示一个类会被Spring自动扫描并创建为Bean。它是一个泛化的概念。

  • @Repository: 是@Component的特化,用于标注数据访问层(DAO层)  的类。其额外的一个作用是能够将平台特定的异常(如SQLException)转换为Spring的统一DataAccessException

  • @Service: 是@Component的特化,用于标注业务服务层(Service层)  的类。主要用于逻辑上的区分,表明这个类持有业务逻辑。

  • @Controller /  @RestController: 用于标注控制层(Web层)  的类。

    • @Controller: 传统的Spring MVC控制器,通常返回一个视图名称。
    • @RestController = @Controller + @ResponseBody。是RESTful Web服务的专用控制器,其所有方法返回的数据都会直接写入HTTP响应体,而不是视图名。这是开发API的首选注解。
  • @Autowired: 用于实现自动依赖注入。可以用在构造器、Setter方法、字段上。(如前一个问题所述,推荐用于构造器)。

  • @Qualifier: 当有多个相同类型的Bean时,用此注解来指定要注入的具体Bean的名称,与@Autowired配合使用,解决歧义性问题。

  • @Primary: 设置一个Bean为首选项。当有多个相同类型的Bean且没有指定@Qualifier时,优先注入被@Primary标注的Bean。

  • @Value: 用于注入外部属性值(如从application.properties文件),也可以注入SpEL表达式结果。

    @Value("${server.port}")
    private int port;
    
    @Value("#{systemProperties['user.home']}")
    private String userHome;
    
  • @PropertySource /  @PropertySources: 用于指定外部属性文件(如.properties)的位置,将其中的属性加载到Spring的Environment中,供@Value使用。

    @Configuration
    @PropertySource("classpath:app.properties")
    public class AppConfig { ... }
    
  • @Scope: 指定Bean的作用域,如单例(singleton)、原型(prototype)、请求(request)、会话(session)等。

    @Bean
    @Scope("prototype") // 每次都会创建一个新的实例
    public MyBean myBean() {
        return new MyBean();
    }
    

2. Web MVC相关注解

这些注解主要用于处理Web请求。

  • @RequestMapping: 一个通用的请求映射注解,可以指定URL路径、HTTP方法(GET, POST等)。通常用其变体:

    • @GetMapping : 等价于 @RequestMapping(method = RequestMethod.GET)
    • @PostMapping
    • @PutMapping
    • @DeleteMapping
    • @PatchMapping
  • @RequestParam: 用于从请求参数中提取值。

    public String getUser(@RequestParam("id") Long userId) { ... }
    
  • @PathVariable: 用于从URL模板变量中提取值。

    @GetMapping("/users/{id}")
    public String getUser(@PathVariable("id") Long userId) { ... }
    
  • @RequestBody: 将HTTP请求体(通常是JSON)反序列化为一个Java对象。

    @PostMapping("/users")
    public User createUser(@RequestBody User user) { ... }
    
  • @ResponseBody: 将方法的返回值直接序列化后写入HTTP响应体(如返回JSON)。@RestController已经默认包含了它。

  • @ModelAttribute: 用于将请求参数绑定到模型对象,常用于表单处理。也可以用于在方法上,表示该方法会在控制器每个方法执行前被调用,用于准备模型数据。

  • @ExceptionHandler: 在控制器内部定义一个处理特定异常的方法,用于做局部的异常处理。

  • @ControllerAdvice /  @RestControllerAdvice: 定义一个全局的异常处理、数据绑定、数据初始化的类。可以看作是全局的@ExceptionHandler,能够处理所有控制器抛出的异常。

3. 事务管理注解

  • @Transactional: 声明一个方法或类需要事务管理。这是Spring声明式事务管理的核心注解。可以配置事务的传播行为、隔离级别、超时时间、回滚条件等。

    @Transactional
    public void transferMoney(Long fromId, Long toId, BigDecimal amount) {
        // ... 业务逻辑
    }
    

4. 测试相关注解

  • @SpringBootTest: 用于Spring Boot应用的集成测试,它会加载完整的应用程序上下文。
  • @DataJpaTest@WebMvcTest@JsonTest 等:用于切片测试(Slice Test) ,只加载应用程序的特定部分,使得测试更快、更专注。
  • @MockBean: 在测试环境中,向Spring容器中添加一个Mockito mock对象,以替换掉原有的Bean,用于隔离测试。