Spring学习笔记:Spring IoC基于注解的DI详细配置全解

发布于:2025-07-02 ⋅ 阅读:(20) ⋅ 点赞:(0)

基于注解配置概述

Spring的注解配置是替代XML配置的一个替代方法,依赖注解(基于字节码和元数据)来描述元信息。

你可以理解为把之前那个XML的形式改成Java注解的方式来替代,对应的注解以及属性来描述这个Bean的配置元素,再根据这个配置元素去进行初始化和注入依赖。(配置信息从XML到组件类中)

以下有几个问题,关于XML配置文件和注解形式:
(1)那这个基于注解的方式是不是就是一定比XML配置文件强?
不一定,XML你可以不接触源码就来配置,但是大家觉得POJO的形式,这个配置信息是源码的一部分,所以后面很多人觉得应该把这个配置信息移到源码中,这样注解就比较好。

但是注解的问题就是太分散了,这个文件有一个,那个文件有一个,太分散了。你不能很容易找齐注解,和配置文件一个文件集中到一起的相比确实不方便。

(2)如果XML和注解同时存在会怎么样?如果同一个Bean,注解一个,XML配置文件一个,这样这个Bean会怎么样?
实际上二者可以是兼容的,但是同一个Bean两个方式都配置了,会出现什么问题,实际上不会不错。注解注入的方式会在XML注解注入的方式,先注入。但是由于是先注入注解的,但是XML在后面注入会覆盖。

Spring2.5还增加了对JSR-250注解的支持,如@PostConstruct和@PreDstroy。
Spring 3则为javax.inject包中包含的JSR-330(Java依赖注入)注解添加了支持,例如@Inject和@Named。

XML开启注解支持

开启注解支持的快捷方式就是在原始的XML配置文件中加入context Schema约束。

XMl开启注解 添加contrext Schema

原始的Spring XML配置文件,加入context 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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
    
</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
       http://www.springframework.org/schema/beans/spring-beans.xsd 
       http://www.springframework.org/schema/context 
       https://www.springframework.org/schema/context/spring-context.xsd">
    
</beans>

Spring XML文件是Spring框架早期版本中的核心配置文件,主要用于定义和管理应用程序中的组件、依赖关系及框架功能。以下是其核心作用及与注解配置的关系。
(当然后面可以用别的注解,来替代这个context schema来开启注解。)

XML开启注解:开启注解支持

前面只是通过Spring XML打开注解,你还要处理开启检测,处理某些注解的功能。
(实际上,我们现在开的注解的方式是,纯Java配置,也就是说@configuration+@ComponentScan这种纯Java配置)

annotation-config
这个注解就是为了已经在容器注册的bean,进行注解检测激活,对于bean的注册则不检测。
所以需要XML或者其他形式,先把这个bean放入到容器中,annotation-config才会对这些bean相关内部注解,进行激活控制。

这个配置是帮助我们注册4个BeanPostProcessor的bean,主要这4个bean是为:
AutowireAnnotationBeanPostProcessor,CommonAnnotationBeanPostProcessor,
RequiredAnnotationBeanPostProcessor,
PersistenceAnnotationBeanPostProcessor这4个bean,这4个不同的BeanPostProcessor对应处理检测不同类型的注解(相当于每一个BeanPostProcessor对应一个类型的注解,相当于注解处理器。)

常见的context:annotetion-config配置,可激活的注解如下:
(1)Spring的@Required,@Autowired,@Lookup,@Value(@Required 检查依赖 setter方法,@Autowire 依赖注入,@Lookup 动态获取原型bean)
(2)JSR-330@inject(“如果可用”,指的是支持版本,spring3.0版本开始,他对标Spring原生的注解是@Autowired)
(3)JSR-250 @Resource,@PostConstruct和@PreDestor

@Lookup 用于解决单例 Bean 依赖原型 Bean 时的作用域冲突问题,每次调用返回新实例(就是一个原型的bean但是他需要一个单例的bean)
(4)JAW-WS 的 @WebServiceRef

@Component
@Scope("prototype")
public class PrototypeBean { /* 原型 Bean */ }

@Component
public class SingletonBean {
    @Lookup
    public PrototypeBean getPrototypeBean() {
        return null; // 实际由代理实现
    }
}

Spring 在运行时生成代理类,覆盖被 @Lookup 标记的方法。
每次调用该方法时,代理会从容器中获取新的原型 Bean 实例
方法需为 abstract 或返回 null(实际逻辑由代理实现)。
访问权限不能为 private(需可被覆盖)

(1)@PostConstruct
标记一个bean在完成在依赖完成注入,和bean初始化阶段完成,执行你定义的逻辑。和Spring的initializingBean的接口功能类似,

(2)@Autowire,@inject和@Reosurce
这三个都可以依赖注入
在这里插入图片描述
按类型注入

// 三者等效(假设只有一个 UserService 实现类)
@Autowired private UserService userService;      // Spring
@Inject   private UserService userService;      // JSR-330
@Resource private UserService userService;      // JSR-250

按名称注入

// @Autowired + @Qualifier
@Autowired 
@Qualifier("adminUserService") 
private UserService userService;

// @Inject + @Named
@Inject 
@Named("adminUserService") 
private UserService userService;

// @Resource 直接指定 name
@Resource(name = "adminUserService") 
private UserService userService;

构造器注入

// Spring @Autowired (4.3+ 可省略)
@Autowired 
public OrderService(UserService userService) { ... }

// @Inject 必须显式标注
@Inject 
public OrderService(UserService userService) { ... }

// @Resource 不支持构造器注入!
// 错误示例:@Resource public OrderService(UserService userService) { ... }

常见的注解的使用

@Component组件

@Component注解标注在类上,IoC容器启动时,就会在Component-scan范围内扫描标注@Component注解的类,将他们注册在IoC容器中。我们称之为组件,就是相当于xml文件< bean >的标签,就是定义一个bean,把它交给容器。

自Spring2.5起,web分层结构项目中,为了区分组件式负责哪一个部分那一层。根据@Component组件衍生出3个组件注解,@Respository @Service和@Controller,默认是单例。

@Controller注解表示的是表现层注解,@Service表示的是服务层的注解,@Respository表示的是持久层组件。组件不好归类的时候,才会用这个@Component组件。

IoC注解配置第一例

新建一个空的maven工程,引入依赖

<properties>
    <spring-framework.version>5.2.8.RELEASE</spring-framework.version>
</properties>

<dependencies>
    <!--spring 核心组件所需依赖-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring-framework.version}</version>
        <scope>compile</scope>
    </dependency>
</dependencies>

添加spring-config.xml配置文件,开启注解支持:

在这里插入图片描述在这里插入代码片

<?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
       http://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.spring.core.*"/>
</beans>

bean的命名
扫描到一个被托管的组件,它对应的bean名称由BeanNameGenerator策略和@Component,@Respository,@service和@Controller注解指定的value值一起生成。

注解设置了value值,value值是作为bean的名字,外部类的bean的name不能重复。(以下这个就是在@Component组件的value值设置,设置的是componentTwo)。

普通内部类之间是可以value值重复,启动时不会报错,但是不建议,可能会造成bean覆盖,运行时报异常。静态内部类之间bean name 不能重复。因此,指定的bean name都不应该重复。

   @Component("componentTwo")
    public class ComponentTwo {
        public ComponentTwo() {
            System.out.println("ComponentTwo:" + this);
        }
    }

没有设置value的bean name生成的策略:
1 对于外部类,默认的bean name的生成器返回非限定类名,如果类名有多个字符并且前两个字符是大写的,将直接返回简单类名。

2普通内部类,默认bean名称生成器返回全限定类名$内部类名。

3对于静态内部类,默认bean名称生成器将返回小写的非限定外部类名.内部类名。静态内部类之间bean name 不能重复。

自定义bean name生成器
不想依赖设置value和默认的bean命名策略,我们可以自定义一个bean name生成器。

/**
 * bean名称生成器
 *
 * @author lx
 */
public class MyNameGenerator implements BeanNameGenerator {

    /**
     * bean的命名接口
     *
     * @param definition 用于生成名称的bean的定义
     * @param registry   bean定义注册中心
     * @return 自定义的bean name
     */
    @Override
    public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
        //简单的直接返回bean的类名,仅供测试
        return definition.getBeanClassName();
    }
}

在xml生成

<context:component-scan base-package="com.spring.core.*" name-generator="com.spring.core.ann.MyNameGenerator"/>

不建议自定义bean name 生成器。

3.2 @Bean工厂

在Spring的一系列Component组件注解或者@Configuration配置注解的类的内部,可以使用@Bean注解的bean的name,表示向IoC容器添加bean定义。

换个说法就是,向IoC容器中添加bean的两种方式:
(1)@Component组件注解
(2)@Configuration注解配置在类内部中添加@Bean注解

@Bean注解作用类似于<.bean >标签,和这个@Component注解区别就是,相当于使用工厂实例化bean。使用@Bean注册的bean,默认命名策略是取方法作为bean的名字。

@Bean一般用于注册用@Bean的方式了。实际上@Bean注解更多的使用在@Configuration配置注解类内部。

以下是一个样例,一个是@Component组件,在组件里有一个@Bean注解

/**
 * @author lx
 */
@Component
public class BeanFactoryDemo {


//    /**
//     * 由于方法名为beanFactoryDemo,和@Component的命名策略冲突导致名字重复,进而抛出异常
//     */
//    @Bean
//    public BeanFactoryDemo beanFactoryDemo() {
//        return new BeanFactoryDemo();
//    }

    /**
     * 使用@Bean的方式注册bean
     */
    @Bean
    public BeanFactoryDemo getBeanFactoryDemo() {
        return new BeanFactoryDemo();
    }
}

测试代码:

@Test
public void beanFactory() {
    ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("spring-config.xml");
    //这是@Component注册的bean
    System.out.println(ac.getBean("beanFactoryDemo", BeanFactoryDemo.class));
    //这是@Bean注册的bean
    System.out.println(ac.getBean("getBeanFactoryDemo", BeanFactoryDemo.class));
}

返回的结果是两个bean都实例化。

3.3 @Scope组件作用域

基于组件注解的默认作用域是单例的,想不指定作用域,可以使用@Scope注解设置作用域。

@Scope注解是在两种情况下起作用。一个是在@Component组件注解的类,另一个是@Bean注解的标注的工厂方法。类似xml配置文件的< bean >标签的scope属性。

只会配置singleton和prototype两种作用域,甚至基本不会配置,默认就是singleton。

3.3.1生命周期管理

在XML配置文件上,生命周期更长的bean中注入生命周期更短的bean时,可以使用查找方法注入< lookup-method/>来解决短生命周期的bean实例的获取的问题,使得短生命周期的bean能够被正常管理。

同样Spring注解也有这样的设置,@Scope注解也能解决这个问题。主要的就是设置它的proxyMode属性,使得注入的是一个代理对象,生命周期更长的bean获取生命周期更短的bean实例时,实际上是通过代理对象去获取具有正常生命周期的bean实例的。

3.4 @Autowired依赖自动注入