Spring-基于注解的配置[02自动装载bean]

简介: Spring-基于注解的配置[02自动装载bean]

使用@Autowired进行自动注入

Spring通过@Autowired注解实现Bean的依赖注入。

@Autowired默认按照类型(byType)匹配的方式在容器中查找匹配的Bean,当且仅有一个匹配的Bean时,Spring将其注入@Autowired标注的变量中。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Pilot {
    @Autowired
    private Plane plane;
    public void drivePlane() {
        plane.fly();
    }
}

实例

POJO类

package com.xgj.ioc.configuration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Pilot {
    @Autowired
    private Plane plane;
    public void drivePlane() {
        plane.fly();
    }
}

@Component注解将Pilot标注为一个Bean,Spring会扫描加载并实例化该Bean。

通过@Autowired注入plan的Bean。


POJO类

package com.xgj.ioc.configuration;
import org.springframework.stereotype.Component;
@Component
public class Plane {
    private String brand;
    private String color;
    private int speed;
    public String getBrand() {
        return brand;
    }
    public void setBrand(String brand) {
        this.brand = brand;
    }
    public String getColor() {
        return color;
    }
    public void setColor(String color) {
        this.color = color;
    }
    public int getSpeed() {
        return speed;
    }
    public void setSpeed(int speed) {
        this.speed = speed;
    }
    public void introduce() {
        System.out.println("Plane information【 brand:" + brand + ",color:"
                + color + ",speed:" + speed);
    }
    public void fly() {
        System.out.println("Plane begins to  fly");
    }
}

配置文件

<?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 
            http://www.springframework.org/schema/context/spring-context.xsd">
    <!-- (1)声明Context命名空间以及Schema文件   (2)扫描类包以及应用注解定义的bean -->
    <context:component-scan base-package="com.xgj.ioc.configuration"/>
</beans>

测试类

package com.xgj.ioc.configuration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ConfigBeanTest {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext(
                "classpath:com/xgj/ioc/configuration/beans.xml");
        Pilot pilot = ctx.getBean("pilot", Pilot.class);
        pilot.drivePlane();
        Plane plane = ctx.getBean("plane", Plane.class);
        plane.setBrand("A380");
        plane.setColor("White");
        plane.setSpeed(800);
        plane.introduce();
    }
}

运行结果:


使用@Auotwired的required属性

如果容器中没有一个和标注变量类型匹配的Bean,那么Spring启动的时候会报NoSuchBeanDefinitionException异常。 如果希望Spring及时找不到匹配的Bean完成注入也不要抛出异常,那么就可以使用@Autowired(required=false)进行标注。

实例

还是以上面的例子为基础改造下,我们知道上面的扫描包配置的为

<context:component-scan base-package="com.xgj.ioc.configuration"/>

假设在com.xgj.ioc.configuration之外的包目录下 有个Tank类(com.xgj.ioc.inject.construct.type.Tank)

改造下Pilot类,引入Tank,设置自动注入

package com.xgj.ioc.configuration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.xgj.ioc.inject.construct.type.Tank;
@Component
public class Pilot {
    @Autowired
    private Plane plane;
    @Autowired
    private Tank tank;
    public void drivePlane() {
        plane.fly();
    }
}

@Autowired 默认 required =true

运行测试类:

No qualifying bean of type 'com.xgj.ioc.inject.construct.type.Tank' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}• 1

我们改造下@Autowired(required = false)

@Autowired(required = false)
private Tank tank

再此运行: OK


使用@Qualifier指定注入Bean的名称

如果容器中有一个以上匹配的Bean时,则可以通过@Qualifier注解限定Bean的名称。

假设容器中有两个类型为Plane的Bean,一个名为plane,一个名为otherPlane. 因为@Autowired默认是按照类型匹配的方式在容器中查找,plane和otherPlane的类型都是Plane,怎么知道注入哪一个呢?

实例

我们在扫描的基类包下增加个子包 other,包下新增同名Plane类,通过@Component(“otherPlane”)指定Bean的名称。

com.xgj.ioc.configuration.other.Plane代码如下:

package com.xgj.ioc.configuration.other;
import org.springframework.stereotype.Component;
@Component("otherPlane")
public class Plane {
    private String brand;
    private String color;
    private int speed;
    public String getBrand() {
        return brand;
    }
    public void setBrand(String brand) {
        this.brand = brand;
    }
    public String getColor() {
        return color;
    }
    public void setColor(String color) {
        this.color = color;
    }
    public int getSpeed() {
        return speed;
    }
    public void setSpeed(int speed) {
        this.speed = speed;
    }
    public void introduce() {
        System.out.println("OtherPlane information【 brand:" + brand + ",color:"
                + color + ",speed:" + speed);
    }
    public void fly() {
        System.out.println("OtherPlane begins to  fly");
    }
}

如果我们希望注入otherPlane呢?

我们来改造Pilot类

package com.xgj.ioc.configuration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
// 1. 引入其他子包下的Plane  
import com.xgj.ioc.configuration.other.Plane;
import com.xgj.ioc.inject.construct.type.Tank;
@Component
public class Pilot {
    @Autowired
    // 2 通过@Qualifier限定Bean的名称
    @Qualifier("otherPlane")
    private Plane plane;
    @Autowired(required = false)
    private Tank tank;
    public void drivePlane() {
        plane.fly();
    }
}

测试类中改动的地方

记得引入 other子包下的Plane

import com.xgj.ioc.configuration.other.Plane;
.....
Plane plane = ctx.getBean("otherPlane", Plane.class);

然后运行测试类:


对类方法进行标注

@Autowired可以对类成员变量以及方法的入参进行标注。

下面在类的方法上使用@Autowired注解。

实例

package com.xgj.ioc.configuration.method;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Pilot {
    private Plane plane;
    // 自动将Plane类型传给方法入参
    @Autowired
    public void setPlane(Plane plane) {
        this.plane = plane;
    }
    public void drivePlane() {
        plane.fly();
    }
}

如果是下面这种写法

// 自动将名为plane的Bean传给方法入参
    @Autowired
    @Qualifier("plane")
    public void setPlane(Plane plane) {
        this.plane = plane点内容**

如果一个方法拥有多个入参,在默认情况下,Spring将自动选择匹配入参类型的Bean进行注入。 Spring允许对方法入参标注@Qualifier以指定注入Bean的名称。

比如

@Autowired
    public void setPlane(Plane plane, @Qualifier("tank") Tank tank) {
        this.plane = plane;
    }

在这种情况下,Plane的入参注入为Plane类型的Bean,而Tank的入参注入的则为名称为tank的Bean.

实例代码如下:

Pilot POJO类

package com.xgj.ioc.configuration.method;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Pilot {
    private Plane plane;
    // 自动将Plane类型传给方法入参
    @Autowired
    public void setPlane(Plane plane) {
        this.plane = plane;
    }
    public void drivePlane() {
        plane.fly();
    }
}

Plane POJO类

package com.xgj.ioc.configuration.method;
import org.springframework.stereotype.Component;
@Component // 通过注解标注为一个Bean,以便Spring扫描并实例化
public class Plane {
    public void fly() {
        System.out.println("Plane begins to  fly");
    }
}

配置文件

<?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 
            http://www.springframework.org/schema/context/spring-context.xsd">
    <!-- (1)声明Context命名空间以及Schema文件   (2)扫描类包以及应用注解定义的bean -->
    <context:component-scan base-package="com.xgj.ioc.configuration.method"/>
</beans>

测试类

package com.xgj.ioc.configuration.method;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ConfigMethodTest {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext(
                "classpath:com/xgj/ioc/configuration/method/beans.xml");
        Pilot pilot = ctx.getBean("pilot", Pilot.class);
        pilot.drivePlane();
    }
}

运行结果

小结

一般情况下,Spring容器中大部分的Bean是单实例的,所以一般无需通过@Repository、@Service等注解的value属性指定Bean的名称,也无须使用@Qualifier注解按照名称进行注入。

虽然Spring支持在属性和方法上标注自动注入注解@Autowired,但在实际项目开发中建议采用在方法上标注@Autowired,因为这样更加“面向对象”,也方便单元测试的编写, 如果将注解标注在私有属性上,则在单元测试的时候就很难用编程的办法设置属性值。


集合类进行标注

如果对类中集合类的变量或者方法入参进行@Autowired标注,那么Spring会将容器中类型所有匹配的Bean都自动注入进来。

实例

接口 Plugin

package com.xgj.ioc.configuration.lstmpSupport;
public interface Plugin {
}

POJO

package com.xgj.ioc.configuration.lstmpSupport;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Component
@Order(value = 1)
public class Plugin1 implements Plugin {
    /**
     * 
     * 
     * @Title:Plugin1
     * 
     * @Description:构造函数
     */
    public Plugin1() {
        super();
        System.out.println("Pligin1 Init");
    }
}

POJO

package com.xgj.ioc.configuration.lstmpSupport;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Component
@Order(value = 2)
public class Plugin2 implements Plugin {
    /**
     * 
     * @Title:Plugin2
     * 
     * @Description:构造函数
     * 
     * */
    public Plugin2() {
        super();
        System.out.println("Pligin2 Init");
    }
}

通过@Component标注为Bean,Spring会将 Plugin1和Plugin2这两个Bean都注入到plugins中。 在默认情况下,这两个bean的加载顺序是不确定,在Spring4.0中可以通过@Order注解或者实现Ordered接口来决定Bean加载的顺序,值越小,优先被加载。

POJO

package com.xgj.ioc.configuration.lstmpSupport;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class MyComponent {
    private List<Plugin> plugins;
    private Map<String, Plugin> pluginMap;
    // Spring会将容器中所有类型为Plugin的Bean注入到这个变量中
    @Autowired(required = false)
    public void setPlugins(List<Plugin> plugins) {
        this.plugins = plugins;
    }
    // 将Plugin类型的Bean注入到Map中
    @Autowired
    public void setPluginMap(Map<String, Plugin> pluginMap) {
        this.pluginMap = pluginMap;
    }
    /**
     * 
     * 
     * @Title: getPlugins
     * 
     * @Description: 获取Plugins
     * 
     * @return
     * 
     * @return: List<Plugin>
     */
    public List<Plugin> getPlugins() {
        return plugins;
    }
    /**
     * 
     * 
     * @Title: getPluginMap
     * 
     * @Description: 获取Map
     * 
     * @return
     * 
     * @return: Map<String,Plugin>
     */
    public Map<String, Plugin> getPluginMap() {
        return pluginMap;
    }
}

Spring如果发现变量是一个List和一个Map的集合类,则它会将容器中匹配集合元素类型的所有Bean都注入进来。

private Map

<?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 
            http://www.springframework.org/schema/context/spring-context.xsd">
    <!-- (1)声明Context命名空间以及Schema文件   (2)扫描类包以及应用注解定义的bean -->
    <context:component-scan base-package="com.xgj.ioc.configuration.lstmpSupport"/>
</beans>

测试类:

package com.xgj.ioc.configuration.lstmpSupport;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ListMapSupportTest {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext(
                "classpath:com/xgj/ioc/configuration/lstmpSupport/beans.xml");
        // 从容器中获取bean
        MyComponent myComponent = ctx.getBean("myComponent", MyComponent.class);
        // 获取Map集合
        Map<String, Plugin> map = myComponent.getPluginMap();
        // Map遍历key和value
        for (Entry<String, Plugin> entry : map.entrySet()) {
            System.out.println("key:" + entry.getKey());
            System.out.println("value:" + entry.getValue());
        }
        // 获取list集合
        List<Plugin> list = myComponent.getPlugins();
        // 遍历list
        for (int i = 0; i < list.size(); i++) {
            System.out.println("list中的元素" + i + "为" + list.get(i));
        }
    }
}

运行结果:


对延迟依赖注入的支持

Spring4.0支持延迟依赖注入,即在Spring容器的时候,对已在Bean上标注了@Lazy和@Autowired注解的属性,不会立即注入属性值。 而是延迟到调用此属性的时候才会注入属性值。

对于Bean实施延迟依赖注入,要注意@Lazy注解必须同时标注在属性及目标Bean上,二者缺一不可,否则延迟注入无效。

实例

package com.xgj.ioc.configuration.lazyLoad;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
@Component
public class Pilot {
    private Plane plane;
    // 延迟注入
    @Lazy
    @Autowired(required = false)
    public void setPlane(Plane plane) {
        this.plane = plane;
    }
}

POJO

package com.xgj.ioc.configuration.lazyLoad;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
@Lazy
// @Lazy 目标Bean 延迟注入
@Component
// 通过注解标注为一个Bean,以便Spring扫描并实例化
public class Plane {
    /**
     * 
     * 
     * @Title:Plane
     * 
     * @Description:无参构造函数
     */
    public Plane() {
        super();
        System.out.println("Plan init ");
    }
    public void fly() {
        System.out.println("Plane begins to  fly");
    }
}

配置文件:

<?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 
            http://www.springframework.org/schema/context/spring-context.xsd">
    <!-- (1)声明Context命名空间以及Schema文件   (2)扫描类包以及应用注解定义的bean -->
    <context:component-scan base-package="com.xgj.ioc.configuration.lazyLoad"/>
</beans>

测试

package com.xgj.ioc.configuration.lazyLoad;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class LazyLoadTest {
    public static void main(String[] args) {
        // 初始化容器
        ApplicationContext ctx = new ClassPathXmlApplicationContext(
                "classpath:com/xgj/ioc/configuration/lazyLoad/beans.xml");
    }
}

运行结果:

我们可以看到,并没有实例化Plane这个Bean.

我们将@Lazy去掉,再次运行下

可以看到 启动容器的时候,Plane bean已经被加载实例化了。


对标准注解的支持

Spring还支持JSR-250中定义的@Resource和JSR-330中定义的@Inject注解,这两个标准注解和@Autowired注解的功能类似,都能对类变更及方法入参提供自动注入功能。

@Resource注解要求提供一个Bean名称的属性,如果属性为空,则自动采用标注处的变量名或者方法名作为Bean的名称。

实例

POJO

package com.xgj.ioc.configuration.standard;
import javax.annotation.Resource;
import org.springframework.stereotype.Component;
@Component
public class Pilot {
    private Plane plane;
    @Resource(name = "plane")
    public void setPlane(Plane plane) {
        this.plane = plane;
    }
    public void drivePlane() {
        plane.fly();
    }
}

POJO

package com.xgj.ioc.configuration.standard;
import org.springframework.stereotype.Component;
@Component
public class Plane {
    public void fly() {
        System.out.println("plane begins to fly");
    }
}

配置文件

<?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 
            http://www.springframework.org/schema/context/spring-context.xsd">
    <!-- (1)声明Context命名空间以及Schema文件   (2)扫描类包以及应用注解定义的bean -->
    <context:component-scan base-package="com.xgj.ioc.configuration.standard"/>
</beans>

测试类

package com.xgj.ioc.configuration.standard;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class StandardTest {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext(
                "classpath:com/xgj/ioc/configuration/standard/beans.xml");
        ctx.getBean("pilot", Pilot.class).drivePlane();
    }
}

运行结果

小结

如果@Resource未指定plane属性,则也可以根据属性方法得到需要注入的Bean名称。

@Autowired默认按照类型匹配注入bean, @Resource默认按照名称匹配注入Bean。而@Inject和@Autowired同样也是按照类型匹配注入Bean的,只不过它没有required属性。

可见不管是@Resource或者@Inject注解,其功能都没有@Autowired丰富,因此除非有不要,大可不必在乎这两个注解

相关文章
|
1月前
|
Java 关系型数据库 MySQL
Spring Boot自动配置:魔法背后的秘密
Spring Boot 自动配置揭秘:只需简单配置即可启动项目,背后依赖“约定大于配置”与条件化装配。核心在于 `@EnableAutoConfiguration` 注解与 `@Conditional` 系列条件判断,通过 `spring.factories` 或 `AutoConfiguration.imports` 加载配置类,实现按需自动装配 Bean。
|
28天前
|
缓存 监控 Java
SpringBoot @Scheduled 注解详解
使用`@Scheduled`注解实现方法周期性执行,支持固定间隔、延迟或Cron表达式触发,基于Spring Task,适用于日志清理、数据同步等定时任务场景。需启用`@EnableScheduling`,注意线程阻塞与分布式重复问题,推荐结合`@Async`异步处理,提升任务调度效率。
400 128
|
1月前
|
人工智能 Java 开发者
【Spring】原理解析:Spring Boot 自动配置
Spring Boot通过“约定优于配置”的设计理念,自动检测项目依赖并根据这些依赖自动装配相应的Bean,从而解放开发者从繁琐的配置工作中解脱出来,专注于业务逻辑实现。
|
1月前
|
XML 安全 Java
使用 Spring 的 @Aspect 和 @Pointcut 注解简化面向方面的编程 (AOP)
面向方面编程(AOP)通过分离横切关注点,如日志、安全和事务,提升代码模块化与可维护性。Spring 提供了对 AOP 的强大支持,核心注解 `@Aspect` 和 `@Pointcut` 使得定义切面与切入点变得简洁直观。`@Aspect` 标记切面类,集中处理通用逻辑;`@Pointcut` 则通过表达式定义通知的应用位置,提高代码可读性与复用性。二者结合,使开发者能清晰划分业务逻辑与辅助功能,简化维护并提升系统灵活性。Spring AOP 借助代理机制实现运行时织入,与 Spring 容器无缝集成,支持依赖注入与声明式配置,是构建清晰、高内聚应用的理想选择。
319 0
|
14天前
|
XML Java 应用服务中间件
【SpringBoot(一)】Spring的认知、容器功能讲解与自动装配原理的入门,带你熟悉Springboot中基本的注解使用
SpringBoot专栏开篇第一章,讲述认识SpringBoot、Bean容器功能的讲解、自动装配原理的入门,还有其他常用的Springboot注解!如果想要了解SpringBoot,那么就进来看看吧!
148 2
|
1月前
|
XML Java 数据格式
常用SpringBoot注解汇总与用法说明
这些注解的使用和组合是Spring Boot快速开发和微服务实现的基础,通过它们,可以有效地指导Spring容器进行类发现、自动装配、配置、代理和管理等核心功能。开发者应当根据项目实际需求,运用这些注解来优化代码结构和服务逻辑。
200 12
|
1月前
|
Java 测试技术 数据库
使用Spring的@Retryable注解进行自动重试
在现代软件开发中,容错性和弹性至关重要。Spring框架提供的`@Retryable`注解为处理瞬时故障提供了一种声明式、可配置的重试机制,使开发者能够以简洁的方式增强应用的自我恢复能力。本文深入解析了`@Retryable`的使用方法及其参数配置,并结合`@Recover`实现失败回退策略,帮助构建更健壮、可靠的应用程序。
172 1
使用Spring的@Retryable注解进行自动重试
|
1月前
|
缓存 Java 应用服务中间件
Spring Boot配置优化:Tomcat+数据库+缓存+日志,全场景教程
本文详解Spring Boot十大核心配置优化技巧,涵盖Tomcat连接池、数据库连接池、Jackson时区、日志管理、缓存策略、异步线程池等关键配置,结合代码示例与通俗解释,助你轻松掌握高并发场景下的性能调优方法,适用于实际项目落地。
309 4
|
1月前
|
传感器 Java 数据库
探索Spring Boot的@Conditional注解的上下文配置
Spring Boot 的 `@Conditional` 注解可根据不同条件动态控制 Bean 的加载,提升应用的灵活性与可配置性。本文深入解析其用法与优势,并结合实例展示如何通过自定义条件类实现环境适配的智能配置。
113 0
探索Spring Boot的@Conditional注解的上下文配置
|
1月前
|
智能设计 Java 测试技术
Spring中最大化@Lazy注解,实现资源高效利用
本文深入探讨了 Spring 框架中的 `@Lazy` 注解,介绍了其在资源管理和性能优化中的作用。通过延迟初始化 Bean,`@Lazy` 可显著提升应用启动速度,合理利用系统资源,并增强对 Bean 生命周期的控制。文章还分析了 `@Lazy` 的工作机制、使用场景、最佳实践以及常见陷阱与解决方案,帮助开发者更高效地构建可扩展、高性能的 Spring 应用程序。
Spring中最大化@Lazy注解,实现资源高效利用