Spring IoC容器与Bean管理(四)

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
容器服务 Serverless 版 ACK Serverless,317元额度 多规格
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
简介: Spring IoC容器与Bean管理

8.基于注解与Java Config配置IoC容器


基于注解的优势:

拜托繁琐的XML形式的bean与依赖注入配置。基于”声明式“的原则,更适合轻量级的现代企业应用。让代码可读性变得更好,研发人员拥有更好的开发体验。


Spring三类注解:

组件类型注解:声明当前类的功能与职责。

自动装配注解:根据属性特征自动注入对象

元数据注解:更细化的辅助IoC容器管理对象


组件类型注解


四种组件类型注解


image.png


这些注解如果要被Spring识别的话,还要配置开启组件扫描:


<context:component-scan base-package="项目的包名">
  <!--可选,排除不想被扫描的包-->
  <context:exclude-filter type="regex" expression="包名"/>
</context:component-scan>


下面用一个案例来演示一下组件注解:

创建一个项目,配置好依赖后,在resources目录下创建applicationContext.xml文件。你可可能会有疑问,为什么明明用注解了,还要这个xml文件干什么。这是因为一些最基本的配置,我们还是要写在这个xml配置文件中。而且基于注解的xml约束和之前的xml约束不太一样,我们要复制官网文档1.9的https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-annotation-config的注解的schema配置。


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">
    <context:component-scan base-package="扫描注解的包名"/>
</beans>


下面以是一个案例:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">
    <context:component-scan base-package="com.haiexijun"/>
</beans>


我们指定扫描com.haiexijun包下面的spring注解。

然后,在dao包下面创建一个UserDao的类,我们对其使用@Repository注解。

package com.haiexijun.ioc.dao;
import org.springframework.stereotype.Repository;
//组件类型注解默认的beanId为首字母小写(如:下面UserDao的beanId默认为userDao)
//也可以向@Repository()被传入自定义的beanId(如:@Repository("udao"))
@Repository
public class UserDao {
    public UserDao(){
    }
}


只需要在类名前面书写@Repository就行,这里要注意一个点,组件类型注解默认的beanId为首字母小写(如:下面UserDao的beanId默认为userDao)

我们编写main方法演示一下:


package com.haiexijun.ioc;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringApplication {
    public static void main(String[] args) {
        ApplicationContext context=new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        String[] ids=context.getBeanDefinitionNames();
        for (String id:ids){
            System.out.println(id+":"+context.getBean(id));
        }
    }
}


这个main方法打印了IoC容器内创建的所有的bean对象,运行结果如下:


5a0469b3f3d04ba9a621a168ffa26056.png


注意,这里没有给UserDao设置beanId,所以spring会默认使他的beanId为首字母小写。


我们可以用@Repository(“udao”)为其设置我们自定义的beanId

此时的运行结果如下:


0e4a1aee5ab64514b18ed109978d8a5d.png


如果要给service业务逻辑类添加注解,就用@Service注解


package com.haiexijun.ioc.service;
import org.springframework.stereotype.Service;
@Service
public class UserService {
}


如果要对控制器用注解,就用@controller注解

package com.haiexijun.ioc.controller;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
}


如果要为一个工具类添加注解,spring并没有为工具类的注解,所以用@Component这个注解就可以


package com.haiexijun.ioc.utils;
import org.springframework.stereotype.Component;
@Component
public class StringUtils {
}


所有的注解,都可以为其设置自定义的beanId。而且这些bean在容器中都是单例的。


自动装配注解


两种自动装配注解,自动装配注解就是为了让我们在IoC容器运行的过程中自动地为某个属性注入数据,是为依赖注入所存在的。


b675d150ef45475d8d73e5440f07bd3d.png


在行业中按类型装配不推荐使用,更多鼓励按名称装配。@Named要于前面的@Inject要配合使用。@Resource注解会先按名称进行依赖注入,但名称不满足时,会再按照类型来进行依赖注入。@Resource是功能最强大的自动装配注解了。


下面进入代码演示的环节:

我们回到上面那个案例,再学习完MVC以后我们都知道,作为MVC是采用分层的方式依次地逐层进行调用。就是controller依赖于service,而service依赖于dao。


下面来简单了解一下@Autowired使用就行


package com.haiexijun.ioc.service;
import com.haiexijun.ioc.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService {
    @Autowired
    private UserDao userDao;
}


@Autowired注解用于要注入的属性上使用就行。要注意,这种注解是按类型来注入属性的,如果有两个类的实现于UserDao接口的话,它就会报错,解决方法,是把实现类的@Repository注解去掉,或在要注入的类的@Repository注解后添加一个@primary注解就行,@Autowired会优先注入有@primary注解的类。


下面要演示一下@Resource注解的基本使用:

如果@Resource注解设置name属性,则按name在IoC容器中注入。如果@Resource注解没有设置name属性,会以属性名作为bean的name在IoC容器中匹配bean,如果有匹配,则注入。按属性名未匹配,则按类型进行匹配,同@Autowired一样,需要加入@Primary注解来解决类型冲突。使用建议:在使用@Resource时推荐设置name或者保证属性名与bean名称一致。


package com.haiexijun.ioc.service;
import com.haiexijun.ioc.dao.UserDao;
import javax.annotation.Resource;
import org.springframework.stereotype.Service;
@Service
public class DepService {
    @Resource
    private UserDao userDao;
}


无论是@Autowired还是@Resource,它们都可以基于不使用setter方法来完成对象的注入。他们的本质都是在运行时,采用反射机制,将要注入的属性从private改为public,再完成属性的直接赋值,赋值完以后,再将其改回到private。


元数据注解


它的作用就是为Spring IoC容器管理对象时提供一些辅助信息。


127086fc9e9140aca07ddb6a880720b9.png


@Scope和之前xml里的scope是一个用法,设置单例和多例。singleton和prototype。

这里不方便演示,以后项目中回顾。


9.基于Java Config配置Spring IoC容器


Java Config是再Spring3.0以后推出的一种全新的配置方式,他的主要原理是使用Java代码来替代传统的XML文件。


基于Java Config的优势:完全拜托了XML的束缚,使用独立Java类管理对象与依赖。注解相对分散,利用Java Config可以对配置集中管理。可以在编译时进行依赖检查,不容易出错。基于Java Config的注解配置拥有更好的开发体验,而基于xml的配置则拥有更好的可维护性。我们在实际项目中根据不同的情况,选择不同的配置方案。


Java Config核心注解


0f07b1ad56934701a1368ecce38b7861.png


重新创建一个工程来进行演示基本使用:


分别创建dao,service,controller包,然后创建对应的java类,如下:


UserDao.java


package com.haiexijun.dao;
public class UserDao {
}


UserService.java


package com.haiexijun.service;
import com.haiexijun.dao.UserDao;
public class UserService {
    private UserDao userDao;
    //作为要使用Java Config,要保留getter和setter
    public UserDao getUserDao() {
        return userDao;
    }
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
}


UserController.java

package com.haiexijun.controller;
import com.haiexijun.service.UserService;
public class UserController {
    private UserService userService;
    public UserService getUserService() {
        return userService;
    }
    public void setUserService(UserService userService) {
        this.userService = userService;
    }
}


要使用Java Config,要保留其getter和setter方法。


然后只要再创建一个Config类来替代xml进行配置:

用@configuration注解来标识这是一个Config配置类,里面把要管理的bean用@bean注解标注,Java Config利用方法创建对象,将方法返回对象放入容器中,beanId为方法名。


package com.haiexijun;
import com.haiexijun.controller.UserController;
import com.haiexijun.dao.UserDao;
import com.haiexijun.service.UserService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
//当前类是一个配置类,用于替代applicationContext.xml
@Configuration
public class Config {
    //Java Config利用方法创建对象,将方法返回对象放入容器中,beanId为方法名
    @Bean
    public UserDao userDao(){
        UserDao userDao=new UserDao();
        return userDao;
    }
    @Bean
    public UserService userService(){
        UserService userService=new UserService();
        return userService;
    }
    @Bean
    public UserController userController(){
        UserController userController=new UserController();
        return userController;
    }
}


然后在main方法里面编写代码演示:

package com.haiexijun;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class SpringApplication {
    public static void main(String[] args) {
        //这里使用创建AnnotationConfigApplicationContext对象来初始话IoC容器
        ApplicationContext context=new AnnotationConfigApplicationContext(Config.class);
        String[] ids= context.getBeanDefinitionNames();
        for (String id:ids){
            System.out.println(id+":"+context.getBean(id));
        }
    }
}


这里不再是使用ClassPathXmlApplicationContext来创建IoC容器,而是使用AnnotationConfigApplicationContext来创建IoC容器了。然后我便利了一下IoC容器管理的Bean的相关信息,运行结果如下:


5bf3493085c441c582d99155f8c2c83c.png


上面只是介绍了如何配置使IoC容器管理Bean,下面来学习配置对象依赖注入。


对上面的Config类的代码进行更改:


package com.haiexijun;
import com.haiexijun.controller.UserController;
import com.haiexijun.dao.UserDao;
import com.haiexijun.service.UserService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
//当前类是一个配置类,用于替代applicationContext.xml
@Configuration
public class Config {
    //Java Config利用方法创建对象,将方法返回对象放入容器中,beanId为方法名
    @Bean
    public UserDao userDao(){
        UserDao userDao=new UserDao();
        System.out.println("已创建userDao");
        return userDao;
    }
    @Bean
    //会按name尝试注入,name不存在则按类型注入
    public UserService userService(UserDao userDao){
        UserService userService=new UserService();
        System.out.println("已创建userServices");
        userService.setUserDao(userDao);
        System.out.println("调用setUserDao"+userDao);
        return userService;
    }
    @Bean
    public UserController userController(UserService userService){
        UserController userController=new UserController();
        System.out.println("已创建userController");
        userController.setUserService(userService);
        System.out.println("调用setUserService"+userService);
        return userController;
    }
}


要实现依赖注入,只需要在@Bean的方法里面传入要注入的对象。它会按name尝试注入,name不存在则按类型注入。

然后编写main方法测试:


package com.haiexijun;
import com.haiexijun.controller.UserController;
import com.haiexijun.dao.UserDao;
import com.haiexijun.service.UserService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
//当前类是一个配置类,用于替代applicationContext.xml
@Configuration
public class Config {
    //Java Config利用方法创建对象,将方法返回对象放入容器中,beanId为方法名
    @Bean
    public UserDao userDao(){
        UserDao userDao=new UserDao();
        System.out.println("已创建userDao");
        return userDao;
    }
    @Bean
    //会按name尝试注入,name不存在则按类型注入
    public UserService userService(UserDao userDao){
        UserService userService=new UserService();
        System.out.println("已创建userServices");
        userService.setUserDao(userDao);
        System.out.println("调用setUserDao"+userDao);
        return userService;
    }
    @Bean
    public UserController userController(UserService userService){
        UserController userController=new UserController();
        System.out.println("已创建userController");
        userController.setUserService(userService);
        System.out.println("调用setUserService"+userService);
        return userController;
    }
}

运行结果如下:


b0b039c113844e74ab1b71bad4aaff30.png


相关文章
|
14天前
|
缓存 Java Spring
实战指南:四种调整 Spring Bean 初始化顺序的方案
本文探讨了如何调整 Spring Boot 中 Bean 的初始化顺序,以满足业务需求。文章通过四种方案进行了详细分析: 1. **方案一 (@Order)**:通过 `@Order` 注解设置 Bean 的初始化顺序,但发现 `@PostConstruct` 会影响顺序。 2. **方案二 (SmartInitializingSingleton)**:在所有单例 Bean 初始化后执行额外的初始化工作,但无法精确控制特定 Bean 的顺序。 3. **方案三 (@DependsOn)**:通过 `@DependsOn` 注解指定 Bean 之间的依赖关系,成功实现顺序控制,但耦合性较高。
实战指南:四种调整 Spring Bean 初始化顺序的方案
|
22天前
|
XML 缓存 Java
搞透 IOC、Spring IOC ,看这篇就够了!
本文详细解析了Spring框架的核心内容——IOC(控制反转)及其依赖注入(DI)的实现原理,帮助读者理解如何通过IOC实现组件解耦,提高程序的灵活性和可维护性。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
|
1月前
|
Java 测试技术 Windows
咦!Spring容器里为什么没有我需要的Bean?
【10月更文挑战第11天】项目经理给小菜分配了一个紧急需求,小菜迅速搭建了一个SpringBoot项目并完成了开发。然而,启动测试时发现接口404,原因是控制器包不在默认扫描路径下。通过配置`@ComponentScan`的`basePackages`字段,解决了问题。总结:`@SpringBootApplication`默认只扫描当前包下的组件,需要扫描其他包时需配置`@ComponentScan`。
|
13天前
|
安全 Java 测试技术
Java开发必读,谈谈对Spring IOC与AOP的理解
Spring的IOC和AOP机制通过依赖注入和横切关注点的分离,大大提高了代码的模块化和可维护性。IOC使得对象的创建和管理变得灵活可控,降低了对象之间的耦合度;AOP则通过动态代理机制实现了横切关注点的集中管理,减少了重复代码。理解和掌握这两个核心概念,是高效使用Spring框架的关键。希望本文对你深入理解Spring的IOC和AOP有所帮助。
28 0
|
1月前
|
XML Java 数据格式
Spring IOC容器的深度解析及实战应用
【10月更文挑战第14天】在软件工程中,随着系统规模的扩大,对象间的依赖关系变得越来越复杂,这导致了系统的高耦合度,增加了开发和维护的难度。为解决这一问题,Michael Mattson在1996年提出了IOC(Inversion of Control,控制反转)理论,旨在降低对象间的耦合度,提高系统的灵活性和可维护性。Spring框架正是基于这一理论,通过IOC容器实现了对象间的依赖注入和生命周期管理。
67 0
|
5月前
|
Java 开发者 Spring
解析Spring中Bean的生命周期
解析Spring中Bean的生命周期
56 2
|
1月前
|
Java 开发者 Spring
Spring bean的生命周期详解!
本文详细解析Spring Bean的生命周期及其核心概念,并深入源码分析。Spring Bean是Spring框架的核心,由容器管理其生命周期。从实例化到销毁,共经历十个阶段,包括属性赋值、接口回调、初始化及销毁等。通过剖析`BeanFactory`、`ApplicationContext`等关键接口与类,帮助你深入了解Spring Bean的管理机制。希望本文能助你更好地掌握Spring Bean生命周期。
81 1
|
5月前
|
XML druid Java
Spring5系列学习文章分享---第二篇(IOC的bean管理factory+Bean作用域与生命周期+自动装配+基于注解管理+外部属性管理之druid)
Spring5系列学习文章分享---第二篇(IOC的bean管理factory+Bean作用域与生命周期+自动装配+基于注解管理+外部属性管理之druid)
62 0
|
1月前
|
Java 开发者 Spring
Spring bean的生命周期详解!
本文详细介绍了Spring框架中的核心概念——Spring Bean的生命周期,包括实例化、属性赋值、接口回调、初始化、使用及销毁等10个阶段,并深入剖析了相关源码,如`BeanFactory`、`DefaultListableBeanFactory`和`BeanPostProcessor`等关键类与接口。通过理解这些核心组件,读者可以更好地掌握Spring Bean的管理和控制机制。
85 1
|
4月前
|
Java Spring 容器
Spring Boot 启动源码解析结合Spring Bean生命周期分析
Spring Boot 启动源码解析结合Spring Bean生命周期分析
104 11