Spring注解驱动开发之自动装配

本文深入解析Spring框架中的注解驱动开发,包括组件注册、生命周期管理、属性赋值、配置文件读取、自动装配及环境配置。通过实例演示如何使用@Autowired、@Qualifier、@Resource、@Inject注解进行依赖注入,以及@Profile注解实现环境切换。

回顾

Spring注解驱动开发之组件注册

Spring注解驱动开发之生命周期

前奏: 属性赋值

对Person类进行赋值

public class Person {

    @Value("#{2.3*10}")
    private Integer age;
    @Value("张三")
    private String name; //setter和getter方法自行脑补...}

创建配置类

@Configuration
public class MyConfigOfPropertyValues {

    @Bean
    public Person person(){
        return new Person();
    }
}

测试

@Test
public void test01(){
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfigOfPropertyValues.class);
    Person person = (Person) applicationContext.getBean("person");
    System.out.println(person);
}

结果如下

 取出配置文件中的值并赋值

① 创建一个配置文件person.properties

person.nickName=小六子

② 为了不影响之前的测试代码, 新建了一个Person1

public class Person1 {

    @Value("赵六")//基本数据类型都可
    private String name;
    @Value("#{20+3}")//SpEL表达式
    private Integer age;
    @Value("${person.nickName}")//EL表达式
    private String nickName;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getNickName() {
        return nickName;
    }

    public void setNickName(String nickName) {
        this.nickName = nickName;
    }

    @Override
    public String toString() {
        return "Person1{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", nickName='" + nickName + '\'' +
                '}';
    }
}

配置文件加入@PropertySource注解

@Configuration
@PropertySource({"classpath:person.properties"})//加载配置文件位置
public class MyConfigOfPropertyValues {

    @Bean
    public Person1 person1(){
        return new Person1();
    }
}

测试

@Test
public void test01(){
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfigOfPropertyValues.class);
    Person1 person1 = (Person1) applicationContext.getBean("person1");
    System.out.println(person1);
}

 结果如下

由于配置文件一旦被加载, 即进入到了Spring的容器环境中, 所以也可以在Java代码中直接通过key来获取 

@Test
public void test01(){
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfigOfPropertyValues.class);
    Person1 person1 = (Person1) applicationContext.getBean("person1");
    ConfigurableEnvironment environment = applicationContext.getEnvironment();//获取环境信息
    String nickName = environment.getProperty("person.nickName");//根据key获取值
    System.out.println(person1);
    System.out.println(nickName);
}

结果如下

压轴: 自动装配

Spring利用依赖注入(DI), 完成对IOC容器中的各个组件的依赖关系赋值

@Autowired注解(Spring提供)

● 默认优先按照类型去容器中找对应的组件

① 创建dao / service / controller  并添加@Autowired注解

以BookService为例

@Service
public class BookService {

    @Autowired
    private BookDao bookDao;

    @Override
    public String toString() {
        return "BookService{" +
                "bookDao=" + bookDao +
                '}';
    }
}

② 配置类

@ComponentScan({"com.spring.annotation.controller", "com.spring.annotation.dao", "com.spring.annotation.service"})
@Configuration
public class MyConfigOfAutowired {
}

③ 测试

@Test
public void test01(){
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfigOfAutowired.class);
    BookService bookService = applicationContext.getBean(BookService.class);
    BookDao bookDao = applicationContext.getBean(BookDao.class);
    System.out.println(bookService);
    System.out.println(bookDao);
}

结果如下

两个BookDao对象是一样的

● 如果存在两个类型相同的bean, 则按照注入时的属性名来容器中找对应的bean

BookDao

@Repository
public class BookDao {//默认注入容器的id是类名首字母小写
    
    private String label = "1";//添加一个标识符

    public String getLabel() {
        return label;
    }

    public void setLabel(String label) {
        this.label = label;
    }
}

配置类(此时容器中有两个BookDao类型的bean)

@ComponentScan({"com.spring.annotation.controller", "com.spring.annotation.dao", "com.spring.annotation.service"})
@Configuration
public class MyConfigOfAutowired {
    
    @Bean(name = "bookDao2")//手动添加bean, 这样容器中就有两个BookDao的bean了
    public BookDao bookDao2(){
        BookDao bookDao = new BookDao();
        bookDao.setLabel("2");
        return bookDao;
    }
}

测试

@Test
public void test01(){
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfigOfAutowired.class);

    BookService bookService = applicationContext.getBean(BookService.class);
    System.out.println(bookService);

    BookDao bookDao = (BookDao) applicationContext.getBean("bookDao2");
    System.out.println(bookDao);

    applicationContext.close();
}

结果如下: 说明@Autowired的注入结果是根据属性名bookDao去容器中找对应的label=1的bean

● 也可以使用@Qualifier注解强行指定使用具体的bean

@Qualifier("bookDao2")
@Autowired
BookDao bookDao;

结果如下

 ● 如果容器中完全没有相对应的bean, 此时注入就会报错, 原因在于@Autowired的required属性默认是true, 将其设置为false即可

@Qualifier("bookDao2")
@Autowired(required = false)
BookDao bookDao;

● 当有多个注入需要使用@Qualifier时就会显得比较麻烦, 此时可以使用@Primary(在自动装配时默认首选为该注解指定的bean)

配置类

@ComponentScan({"com.spring.annotation.controller", "com.spring.annotation.service", "com.spring.annotation.dao"})
@Configuration
public class MyConfigOfAutowired {

    @Primary//默认首选使用
    @Bean(name = "bookDao2")
    public BookDao bookDao(){
        BookDao bookDao = new BookDao();
        bookDao.setLabel("2");
        return bookDao;
    }
}

 结果如下

 如果此时使用@Qualifier明确指定, 那还是按照@Qualifier注解为准, 此时的属性名就不管用了

@Service
public class BookService {

    @Qualifier("bookDao")//绝对指定使用
    @Autowired(required = false)
    BookDao bookDao2;

    @Override
    public String toString() {
        return "BookService{" +
                "bookDao2=" + bookDao2 +
                '}';
    }
}

结果如下

 

@Resource注解(JSR250规范)

● 一样可以实现自动注入的功能, 但跟@Autowired的区别在于@Resource注解只能根据id名称来选择注入具体的bean, 如果name属性没有给出, 默认是按照字段bookDao去容器中查找的, 一旦给出name属性, 则根据name属性的值去容器中查找.

@Service
public class BookService {

    @Resource(name = "bookDao2")
    BookDao bookDao;

    @Override
    public String toString() {
        return "BookService{" +
                "bookDao=" + bookDao +
                '}';
    }
}
@ComponentScan({"com.spring.annotation.controller", "com.spring.annotation.service", "com.spring.annotation.dao"})
@Configuration
public class MyConfigOfAutowired {

    @Bean(name = "bookDao2")
    public BookDao bookDao(){
        BookDao bookDao = new BookDao();
        bookDao.setLabel("2");
        return bookDao;
    }
}

 结果如下 

 

@Service
public class BookService {

    @Resource
    BookDao bookDao;

    @Override
    public String toString() {
        return "BookService{" +
                "bookDao=" + bookDao +
                '}';
    }
}

结果如下

 

@Service
public class BookService {

    @Resource
    BookDao bookDao2;

    @Override
    public String toString() {
        return "BookService{" +
                "bookDao2=" + bookDao2 +
                '}';
    }
}

结果如下

 ps: 该注解不是Spring提供的注解, 所以只能单独使用, 无法与@Primary注解 / @Qualifier注解等配合使用, 也没有required属性.

@Inject注解(JSR330规范)

需要导入依赖
<dependency>
    <groupId>javax.inject</groupId>
    <artifactId>javax.inject</artifactId>
    <version>1</version>
</dependency>
● @Inject注解需要在导入依赖后才能使用, 功能跟@Autowired一样, 区别在于@Inject注解没有required属性

@Service
public class BookService {

    @Qualifier("bookDao")//绝对指定使用
    @Inject
    BookDao bookDao;

    @Override
    public String toString() {
        return "BookService{" +
                "bookDao=" + bookDao +
                '}';
    }
}
@ComponentScan({"com.spring.annotation.controller", "com.spring.annotation.service", "com.spring.annotation.dao"})
@Configuration
public class MyConfigOfAutowired {

    @Primary//默认首选使用
    @Bean(name = "bookDao2")
    public BookDao bookDao(){
        BookDao bookDao = new BookDao();
        bookDao.setLabel("2");
        return bookDao;
    }
}

以上的配置默认优先使用bookDao2, 当有@Qualifier绝对指定后使用bookDao

结果如下

 小结: 通过以上对比, 相信它们三个注解的区别一目了然, 网上好多的说明都没有将三个注解的区别讲得比较详细, 通常我们做web项目使用@Autowired注解即可, 功能也最全面.

 @Autowired所在的位置

属性, 构造器, 方法, 参数

以上的测试都是标注在属性上, 接下来测试标注在其他位置

● 标注在方法

新建一个Boss类

public class Boss {
    
    private Car car;

    public Car getCar() {
        return car;
    }

    public void setCar(Car car) {
        this.car = car;
    }

    @Override
    public String toString() {
        return "Boss{" +
                "car=" + car +
                '}';
    }
}

Car类

public class Car {

    public Car() {
        System.out.println("Car对象被创建...");
    }

    /**
     * 自定义一个初始化对象方法
     */
    public void init(){
        System.out.println("Car对象被初始化...");
    }

    /**
     * 自定义一个销毁对象方法
     */
    public void destroy(){
        System.out.println("Car对象被销毁...");
    }
}

配置类

@ComponentScan({"com.spring.annotation.controller", "com.spring.annotation.service", "com.spring.annotation.dao"})
@Configuration
public class MyConfigOfAutowired {

    @Primary//默认首选使用
    @Bean(name = "bookDao2")
    public BookDao bookDao(){
        BookDao bookDao = new BookDao();
        bookDao.setLabel("2");
        return bookDao;
    }

    @Bean
    public Boss boss(){
        return new Boss();
    }

    @Bean
    public Car car(){
        return new Car();
    }
}

  测试

@Test
    public void test01(){
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfigOfAutowired.class);

        BookService bookService = applicationContext.getBean(BookService.class);
        System.out.println(bookService);

        Boss boss = applicationContext.getBean(Boss.class);
        Car car = applicationContext.getBean(Car.class);
        System.out.println(boss+"  <==是否相等==>  "+car);
    }

结果如下

 

 小结: 将注解标注在setter方法上时, 容器创建当前对象时就会调用该方法, 完成赋值, 值是从ioc容器中获取的, 也就是结果中显示的两个对象是相等的.

ps:  使用@Bean将组件加入容器中后, 如果想要获取Car, 可以从参数中直接获取(此时加不加@Autowired都可以), 如下

@Bean
public Boss boss(@Autowired Car car){//此时的@Autowired加不加都会自动从ioc容器中获取当前的car对象
    Boss boss = new Boss();
    boss.setCar(car);
    return boss;
}

● 标注在有参构造器上

ps: 标注在setter方法上时, 默认在容器启动时会调用无参构造器创建对象, 再进行初始化操作

配置类

@ComponentScan({"com.spring.annotation.controller", "com.spring.annotation.service", "com.spring.annotation.dao"})
@Configuration
public class MyConfigOfAutowired {

    @Autowired
    private Car car;

    @Primary//默认首选使用
    @Bean(name = "bookDao2")
    public BookDao bookDao(){
        BookDao bookDao = new BookDao();
        bookDao.setLabel("2");
        return bookDao;
    }

    @Bean
    public Boss boss(){
        return new Boss(car);
    }

    @Bean
    public Car car(){
        return new Car();
    }
}

Boss类, 标注在有参构造器上

public class Boss {

    private Car car;

    @Autowired
    public Boss(Car car) {
        this.car = car;
        System.out.println("Boss的有参构造器被调用...");
    }

    public Car getCar() {
        return car;
    }

    public void setCar(Car car) {
        this.car = car;
    }

    @Override
    public String toString() {
        return "Boss{" +
                "car=" + car +
                '}';
    }
}

结果如下

ps: 除此之外, 如果Boss类中只有一个有参构造器, 此时的@Autowired注解可以省略, 容器默认会找它进行注入  

● 放在参数上(有参构造器的参数 / setter方法的参数)的效果是一样的, 就不再贴出来

@Profile注解(重点)

● Spring提供的可以根据当前环境动态激活和切换一系列组件的功能

开发环境 / 测试环境 / 生产环境...

例如: 数据源切换: A数据库(开发环境) / B数据库(测试环境) / C数据库(生产环境)

配置步骤

① 引入c3p0数据源依赖
<dependency>
    <groupId>com.mchange</groupId>
    <artifactId>c3p0</artifactId>
    <version>0.9.5.2</version>
</dependency>
② 引入数据库驱动依赖
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.44</version>
</dependency>
③ 安装好数据库(我是在虚拟机中使用docker容器创建的数据库)

④ 配置文件, 存放于classpath目录下

db.user=root
db.password=root
db.driverClass=com.mysql.jdbc.Driver
db.testUrl=jdbc:mysql://192.168.5.134:3306/test
db.devUrl=jdbc:mysql://192.168.5.134:3306/dev
db.prodUrl=jdbc:mysql://192.168.5.134:3306/prod

⑤ 配置类MyConfigOfProfile

@PropertySource("classpath:/dbconfig.properties")
@Configuration
public class MyConfigOfProfile implements EmbeddedValueResolverAware {

    @Value("${db.user}")//第一种方式
    private String user;

    private StringValueResolver valueResolver;//第二种方式

    private String driverClass;

    @Bean(name = "testDataSource")
    public DataSource dataSourceTest(/*第三种方式*/@Value("${db.password}") String pwd, @Value("${db.testUrl}") String url) throws PropertyVetoException {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser(user);
        dataSource.setPassword(pwd);
        dataSource.setJdbcUrl(url);
        dataSource.setDriverClass("com.mysql.jdbc.Driver");
        return dataSource;
    }

    @Bean(name = "devDataSource")
    public DataSource dataSourceDev(@Value("${db.password}") String pwd, @Value("${db.devUrl}") String url) throws PropertyVetoException {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser(user);
        dataSource.setPassword(pwd);
        dataSource.setJdbcUrl(url);
        dataSource.setDriverClass("com.mysql.jdbc.Driver");
        return dataSource;
    }

    @Bean(name = "prodDataSource")
    public DataSource dataSourceProd(@Value("${db.password}") String pwd, @Value("${db.prodUrl}") String url) throws PropertyVetoException {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser(user);
        dataSource.setPassword(pwd);
        dataSource.setJdbcUrl(url);
        dataSource.setDriverClass(driverClass);
        return dataSource;
    }

    @Override
    public void setEmbeddedValueResolver(StringValueResolver resolver) {
        this.valueResolver = resolver;
        driverClass = valueResolver.resolveStringValue("${db.driverClass}");
    }
}

⑥ 测试

public class IOCTest_Profile {

    @Test
    public void test01(){
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfigOfProfile.class);
        String[] names = applicationContext.getBeanNamesForType(DataSource.class);
        for (String name: names){
            System.out.println(name);
        }
        applicationContext.close();
    }
}

 ⑦ 结果如下

 激活数据源(结合@Profile注解)

ps: 不指定的情况下, 任何环境下都能注册该组件; @Profile动态指定组件在具体的环境下才能被注册到容器中; 加了@Profile标志的bean只有被激活才能注入容器中.

1. @Profile注解在bean上

配置类

@PropertySource("classpath:/dbconfig.properties")
@Configuration
public class MyConfigOfProfile implements EmbeddedValueResolverAware {

    @Value("${db.user}")//第一种方式
    private String user;

    private StringValueResolver valueResolver;//第二种方式

    private String driverClass;

    @Bean
    public Blue blue(){
        return new Blue();
    }

    @Profile("test")//标志为test
    @Bean(name = "testDataSource")
    public DataSource dataSourceTest(/*第三种方式*/@Value("${db.password}") String pwd, @Value("${db.testUrl}") String url) throws PropertyVetoException {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser(user);
        dataSource.setPassword(pwd);
        dataSource.setJdbcUrl(url);
        dataSource.setDriverClass("com.mysql.jdbc.Driver");
        return dataSource;
    }

    @Profile("dev")//标志为dev
    @Bean(name = "devDataSource")
    public DataSource dataSourceDev(@Value("${db.password}") String pwd, @Value("${db.devUrl}") String url) throws PropertyVetoException {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser(user);
        dataSource.setPassword(pwd);
        dataSource.setJdbcUrl(url);
        dataSource.setDriverClass("com.mysql.jdbc.Driver");
        return dataSource;
    }

    @Profile("prod")//标志为prod
    @Bean(name = "prodDataSource")
    public DataSource dataSourceProd(@Value("${db.password}") String pwd, @Value("${db.prodUrl}") String url) throws PropertyVetoException {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser(user);
        dataSource.setPassword(pwd);
        dataSource.setJdbcUrl(url);
        dataSource.setDriverClass(driverClass);
        return dataSource;
    }

    @Override
    public void setEmbeddedValueResolver(StringValueResolver resolver) {
        this.valueResolver = resolver;
        driverClass = valueResolver.resolveStringValue("${db.driverClass}");
    }
}

1) 方法一: 指定IDE的VM启动参数(我使用的是idea, eclipse也大同小异)

参数: -Dspring.profiles.active=<profile的值>

测试

@Test
public void test01(){
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfigOfProfile.class);
    String[] names = applicationContext.getBeanNamesForType(DataSource.class);
    for (String name: names){
        System.out.println(name);
    }
    applicationContext.close();
}

结果如下

2) 方法二: 代码指定

@Test
public void test01(){
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();//使用无参方法
    applicationContext.getEnvironment().setActiveProfiles("dev", "prod");//指定环境
    applicationContext.register(MyConfigOfProfile.class);//注册配置类
    applicationContext.refresh();//刷新容器启动
    String[] names = applicationContext.getBeanNamesForType(DataSource.class);
    for (String name: names){
        System.out.println(name);
    }
    applicationContext.close();
}

结果如下

ps: 可以指定@Profile("default")来指定默认的环境, 在没有任何配置的情况下注入该默认bean

 2. @Profile注解在配置类上

配置类

@Profile("test")
@PropertySource("classpath:/dbconfig.properties")
@Configuration
public class MyConfigOfProfile implements EmbeddedValueResolverAware {...}

此时的配置环境方式跟在bean上是一样的, 不同的是一旦配置的环境跟配置类上的注解指定的环境不匹配, 则该配置类都不会加载

3. 没有标注环境标志的bean在任何环境下都是加载的(除非该配置类没有加载)

@Bean
public Blue blue(){
    return new Blue();
}

测试类中多加两行代码

Blue blue = applicationContext.getBean(Blue.class);
System.out.println(blue.getClass());

 结果如下

【SCI复现】含可再生能源与储能的区域微电网最优运行:应对不确定性的解鲁棒性与非预见性研究(Matlab代码实现)内容概要:本文围绕含可再生能源与储能的区域微电网最优运行展开研究,重点探讨应对不确定性的解鲁棒性与非预见性策略,通过Matlab代码实现SCI论文复现。研究涵盖多阶段鲁棒调度模型、机会约束规划、需求响应机制及储能系统优化配置,结合风电、光伏等可再生能源出力的不确定性建模,提出兼顾系统经济性与鲁棒性的优化运行方案。文中详细展示了模型构建、算法设计(如C&CG算法、大M法)及仿真验证全过程,适用于微电网能量管理、电力系统优化调度等领域的科研与工程实践。; 适合人群:具备一定电力系统、优化理论和Matlab编程基础的研究生、科研人员及从事微电网、能源管理相关工作的工程技术人员。; 使用场景及目标:①复现SCI级微电网鲁棒优化研究成果,掌握应对风光负荷不确定性的建模与求解方法;②深入理解两阶段鲁棒优化、分布鲁棒优化、机会约束规划等先进优化方法在能源系统中的实际应用;③为撰写高水平学术论文或开展相关课题研究提供代码参考和技术支持。; 阅读建议:建议读者结合文档提供的Matlab代码逐模块学习,重点关注不确定性建模、鲁棒优化模型构建与求解流程,并尝试在不同场景下调试与扩展代码,以深化对微电网优化运行机制的理解。
个人防护装备实例分割数据集 一、基础信息 数据集名称:个人防护装备实例分割数据集 图片数量: 训练集:4,524张图片 分类类别: - Gloves(手套):工作人员佩戴的手部防护装备。 - Helmet(安全帽):头部防护装备。 - No-Gloves(未戴手套):未佩戴手部防护的状态。 - No-Helmet(未戴安全帽):未佩戴头部防护的状态。 - No-Shoes(未穿安全鞋):未佩戴足部防护的状态。 - No-Vest(未穿安全背心):未佩戴身体防护的状态。 - Shoes(安全鞋):足部防护装备。 - Vest(安全背心):身体防护装备。 标注格式:YOLO格式,包含实例分割的多边形坐标和类别标签,适用于实例分割任务。 数据格式:来源于实际场景图像,适用于计算机视觉模型训练。 二、适用场景 工作场所安全监控系统开发:数据集支持实例分割任务,帮助构建能够自动识别工作人员个人防护装备穿戴状态的AI模型,提升工作环境安全性。 建筑与工业安全检查:集成至监控系统,实时检测PPE穿戴情况,预防安全事故,确保合规性。 学术研究与创新:支持计算机视觉在职业安全领域的应用研究,促进AI与安全工程的结合。 培训与教育:可用于安全培训课程,演示PPE识别技术,增强员工安全意识。 三、数据集优势 精准标注与多样性:每个实例均用多边形精确标注,确保分割边界准确;覆盖多种PPE物品及未穿戴状态,增加模型鲁棒性。 场景丰富:数据来源于多样环境,提升模型在不同场景下的泛化能力。 任务适配性强:标注兼容主流深度学习框架(如YOLO),可直接用于实例分割模型开发,支持目标检测和分割任务。 实用价值高:专注于工作场所安全,为自动化的PPE检测提供可靠数据支撑,有助于减少工伤事故。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值