【SSM详细教程】-04-Spring基于注解的组件扫描

 精品专题:

01.《C语言从不挂科到高绩点》课程详细笔记

https://blog.csdn.net/yueyehuguang/category_12753294.html?spm=1001.2014.3001.5482icon-default.png?t=O83Ahttps://blog.csdn.net/yueyehuguang/category_12753294.html?spm=1001.2014.3001.5482

02. 《SpringBoot详细教程》课程详细笔记

https://blog.csdn.net/yueyehuguang/category_12789841.html?spm=1001.2014.3001.5482icon-default.png?t=O83Ahttps://blog.csdn.net/yueyehuguang/category_12789841.html?spm=1001.2014.3001.548203.《SpringBoot电脑商城项目》课程详细笔记

https://blog.csdn.net/yueyehuguang/category_12752883.html?spm=1001.2014.3001.5482icon-default.png?t=O83Ahttps://blog.csdn.net/yueyehuguang/category_12752883.html?spm=1001.2014.3001.548204.《VUE3.0 核心教程》课程详细笔记

https://blog.csdn.net/yueyehuguang/category_12769996.html?spm=1001.2014.3001.5482icon-default.png?t=O83Ahttps://blog.csdn.net/yueyehuguang/category_12769996.html?spm=1001.2014.3001.5482

================================

||   持续分享系列教程,关注一下不迷路  ||

||   视频教程:小破站:墨轩大楼             ||

================================

1. 什么是组件扫描

指定一个包路径,Spring会自动扫描该包及其子包所有组件类,当发现组件类定义前有特定的注解标记时,就将该组件纳入到Spring容器中。等价于原有XML配置中的<bean>定义功能。

组件扫描可以代替大量XML配置的<bean>定义。

1.1. 指定扫描类路径

使用组件扫描,首先需要在applicationContext.xml配置文件中指定扫描类路径,如下所示:

<context:component-scan base-package="com.moxuan" />

上面配置,容器实例化时会自动扫描com.moxuan包及其子包下面所有组件类。

1.2. 自动扫描的注解标记

指定扫描类路径后,并不是该路径下所有组件类都扫描到Spring容器的,只有在组件类定义前面有以下注解标记时,才会扫描到spring容器中:

注解标记

描述

@Component

通用注解

@Name

通用注解

@Repository

持久化层组件注解

@Service

业务层组件注解

@Controller

控制层组件注解

1.3. 自动扫描组件的命名

当一个组件在扫描过程中被检测到时,会生成一个默认id值,默认id为小写开头的类名,也可以在注解标记中自定义id。看下面案例:

首先在application.xml中配置注解扫描包路径:

 <context:component-scan base-package="com.moxuan"></context:component-scan>

然后在com.moxuan的子包entity中新建Cat类 ,使用默认id值,具体代码如下:

package com.moxuan.entity;

import lombok.Data;
import org.springframework.stereotype.Component;

@Component  // 此处不加名字,采用默认id
@Data
public class Cat {
    private String name;
    private String color;
}

然后在com.moxuan的子包entity中新建Dog类 ,指定一个id名字,具体代码如下:

package com.moxuan.entity;

import lombok.Data;
import org.springframework.stereotype.Component;

@Component("myDog")  // 此处指定了名字,就不再使用默认的id了
@Data
public class Dog {
    private String name;
    private String type;
}

分别在测试方法中获取cat和dog对象,具体如下:

/**
* 自动扫描组件的命名
*/
@Test
public void test01(){
  AbstractApplicationContext context =
    new ClassPathXmlApplicationContext("application.xml");
  Cat cat = context.getBean("cat",Cat.class);
  System.out.println(cat);

  Dog dog = context.getBean("myDog",Dog.class);
  System.out.println(dog);
}

运行效果:

2. 指定组件的作用域

通常受Spring管理的组件,默认的作用域是Singleton,如果需要其他的作用域可以使用@Scope注解,只要在注解中提供作用域的名称即可。看下面代码:

Cat类代码不变:

package com.moxuan.entity;

import lombok.Data;
import org.springframework.stereotype.Component;

@Component  // 此处不加名字,采用默认id
@Data
public class Cat {
    private String name;
    private String color;
}

Dog类新增@Scope注解,指定为prototype

package com.moxuan.entity;

import lombok.Data;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Scope("prototype")
@Component("myDog")  // 此处指定了名字,就不再使用默认的id了
@Data
public class Dog {
    private String name;
    private String type;
}

编写测试方法:

@Test
public void test02(){
    AbstractApplicationContext context =
            new ClassPathXmlApplicationContext("application.xml");
    Cat cat1 = context.getBean("cat",Cat.class);
    Cat cat2 = context.getBean("cat",Cat.class);
    System.out.println(cat1==cat2); // true

    Dog dog1 = context.getBean("myDog",Dog.class);
    Dog dog2 = context.getBean("myDog",Dog.class);
    System.out.println(dog1==dog2); //false

}

cat未指定作用域,默认的作用域是Singleton,所以可以看到虽然我们获取了两次对象,但是由于默认的作用域是Singleton,单例模式,只有一个对象,所以比较的时候,值为true。而Dog上面我使用@Scope指定了非单例模式,两次获取到的对象不是同一个,所以比较结果为false。

3. 初始化和销毁回调的控制

@PostConstruct @PreDestroy 注解标记分别用于指定初始化和销毁回调函数,使用示例如下:

首先添加ExampleBean,分别添加初始化和销毁方法,并使用两个注解,具体如下:

package com.moxuan.entity;

import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

@Component
public class ExampleBean {

    public ExampleBean(){
        System.out.println("构造函数执行。..");
    }

    @PostConstruct
    public void init(){
        System.out.println("初始化方法被调用了");
    }

    @PreDestroy
    public void destroy(){
        System.out.println("销毁方法被调用了");
    }
}

编写测试方法,具体代码如下:

/**
* 测试初始化和销毁注解
*/
@Test
public void test03(){
  AbstractApplicationContext context =
    new ClassPathXmlApplicationContext("application.xml");
  ExampleBean eb = context.getBean("exampleBean",ExampleBean.class);
  System.out.println(eb);
  System.out.println("-------------");
  context.close();// 关闭容器,测试销毁
}

运行效果如图所示:

4. 指定依赖注入关系

具有依赖关系的Bean对象,利用下面任意一种注解都可以实现关系注入:

@Resource

@Autowired / @Qulifier

@Inject / @Named

4.1. @Resource 注解

@Resource 注解标记可以用在字段定义或setter方法定义前面,默认首先按名称匹配注入,如果匹配不到再按照类型匹配注入。

首先创建武器类Weapon.java,代码如下:

package com.moxuan.entity;

import lombok.Data;
import org.springframework.stereotype.Component;

@Component
@Data
public class Weapon {
    private String name;
    public Weapon(){
        this.name = "擎天柱";
    }
}

再创建一个装备类Equip.java,代码如下:

package com.moxuan.entity;

import lombok.Data;
import org.springframework.stereotype.Component;

@Data
@Component
public class Equip {

    private String name;

    public Equip(){
        this.name = "皇帝的新衣";
    }
}

接下来创建Hero.java,使用@Resource进行属性注入:

package com.moxuan.entity;

import lombok.Data;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

@Component
@Data
public class Hero {

    @Resource  //根据属性名去匹配
    private Weapon weapon;

    @Resource(name="weapon")  // 指定属性名去匹配
    private Weapon weapon01;

    @Resource  // 根据类型匹配
    private Weapon weapon02;

    private Equip equip;

    @Resource  // 作用在setter方法上
    public void setEquip(Equip equip) {
        this.equip = equip;
    }
}

最后编写测试方法,代码如下:

/**
* 测试@Resource
*/
@Test
public void test04(){
  AbstractApplicationContext context =
    new ClassPathXmlApplicationContext("application.xml");
  Hero hero = context.getBean("hero", Hero.class);
  System.out.println(hero);
}

运行效果:

4.2. @Autowired 注解

@Autowired 注解标记也可以用在字段定义或setter方法定义前面,默认按类型匹配注入

首先新建一个Computer类,添加相应属性

package com.moxuan.entity;

import lombok.Data;
import org.springframework.stereotype.Component;

@Data
@Component
public class Computer {
    private String mainBoard;
    private String hdd;
    private String ram;

    public Computer(){
        this.mainBoard = "技嘉";
        this.hdd = "希捷";
        this.ram = "金士顿";
    }
}

然后添加Programmer类,分别定义两个Computer属性,并分别对两个属性在属性前和setter方法前使用@Autowired注解。

package com.moxuan.entity;

import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
@Data
public class Programmer {
    @Autowired
    private Computer com1;

    private Computer com2;

    @Autowired
    public void setCom2(Computer com2) {
        this.com2 = com2;
    }
}

最后编写测试方法,代码如下:

/**
* 测试@Autowired
*/
@Test
public void test05(){
  AbstractApplicationContext context =
    new ClassPathXmlApplicationContext("application.xml");
  Programmer pro = context.getBean("programmer",Programmer.class);
  System.out.println(pro);
}

运行效果如图所示:

此时发现两种方式都能够成功注入属性值。

由于@Autowired 注解方式是按照类型去匹配注入的,如果出现两个类型相同的Bean,会根据属性名去匹配Bean,如果匹配得到就能注入成功,反之匹配不到就会报错。

首先,去掉Computer类上面的注解

package com.moxuan.entity;

import lombok.Data;
import org.springframework.stereotype.Component;

@Data
//@Component
public class Computer {
    private String mainBoard;
    private String hdd;
    private String ram;

    public Computer(){
        this.mainBoard = "技嘉";
        this.hdd = "希捷";
        this.ram = "金士顿";
    }
}

在applicationContext.xml中添加bean配置,配置两个Computer类型的bean,注入不同的属性,代码如下:

<?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.moxuan"></context:component-scan>
    <bean id="com1" class="com.moxuan.entity.Computer">
        <property name="mainBoard" value="技嘉" />
        <property name="hdd" value="希捷" />
        <property name="ram" value="金士顿"/>
    </bean>

    <bean id="com2" class="com.moxuan.entity.Computer">
        <property name="mainBoard" value="华硕" />
        <property name="hdd" value="西部" />
        <property name="ram" value="金士顿"/>
    </bean>
</beans>

Programmer类中保留之前的代码,代码如下:

package com.moxuan.entity;

import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
@Data
public class Programmer {
    @Autowired
    private Computer com1;

    private Computer com2;

    @Autowired
    public void setCom2(Computer com2) {
        this.com2 = com2;
    }
}

测试方法如下:

/**
* 测试@Autowired
*/
@Test
public void test05(){
  AbstractApplicationContext context =
    new ClassPathXmlApplicationContext("application.xml");
  Programmer pro = context.getBean("programmer",Programmer.class);
  System.out.println("com1:"+pro.getCom1());
  System.out.println("com2:"+pro.getCom2());
}

运行效果:

此时会发现,application.xml中配置的id为com1的bean 注入给了Programmer中com1属性。而com2的bean注入给了Programmer中的com2属性。不难看出,当有多个类型相同的bean时@Autowired会自动根据名称去匹配。

但是当没有匹配到名字相同的bean时,就会出错,比如下面,我们将配置文件中两个Bean的id分别修改为computer1和computer2,代码如下:

<?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.moxuan"></context:component-scan>
    <bean id="computer1" class="com.moxuan.entity.Computer">
        <property name="mainBoard" value="技嘉" />
        <property name="hdd" value="希捷" />
        <property name="ram" value="金士顿"/>
    </bean>

    <bean id="computer2" class="com.moxuan.entity.Computer">
        <property name="mainBoard" value="华硕" />
        <property name="hdd" value="西部" />
        <property name="ram" value="金士顿"/>
    </bean>
</beans>

再次运行测试方法时,会出现下图问题:

4.3. @Qualifier 注解

在前面的操作中,如果使用@Autowired 注解,会优先根据类型去匹配,如果存在多个类型相同的bean时,会根据名称去匹配,如果名称匹配不成功,就会报错。而@Qualifier 注解可以用来和@Autowired 进行配合,解决当属性名和bean中的id不同时注入错误的问题。

修改前面的Programmer类如下:

package com.moxuan.entity;

import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

@Component
@Data
public class Programmer {
    @Autowired
    @Qualifier("computer1")
    private Computer com1;

    private Computer com2;

    @Autowired
    public void setCom2(@Qualifier("computer2") Computer com2) {
        this.com2 = com2;
    }
}

运行测试方法,结果如下:

我们可以发现,这回也注入成功了。所以使用@Autowired 注入时,需要注入指定名称的Bean时,可以使用@Qualifier 去注入指定名称的Bean

总结:@Autowired 自动装配的流程如下图所示:

首先根据所需要的组件类型到IOC容器中查找

  • 如果能够找到唯一的bean:直接执行装配
  • 如果完全找不到匹配这个类型的bean:装配失败

如果匹配到的bean不止一个

  • 没有@Qualifier注解:根据@Autowired标记位置成员变量的变量名作为bean的id进行匹配
  • 如果能够找到:执行装配
  • 如果找不到:装配失败
  • 使用@Qualifier注解:根据@Qualifier注解中指定的名称作为bean的id进行匹配
  • 能够找到:执行装配
  • 找不到:装配失败

4.4. @Value注解

@Value 注解可以注入Spring表达式的值,使用方法步骤如下:

  1. 首先在项目resources目录中添加properties文件,比如:mysql.properties,内容如下:
driver=com.mysql.jdbc.Driver
url=jdbc:mysql:///moxuan
username=root
password=123456
  1. 在application.xml配置中,引入mysql.properties文件
<util:properties location="classpath:mysql.properties" id="jdbc"></util:properties>
  1. 添加MySQLUtils类,代码如下:
package com.moxuan.entity;

import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
@Data
public class MySQLUtils {

    @Value("#{jdbc.driver}")
    private String driver;

    @Value("#{jdbc.url}")
    private String url;

    @Value("#{jdbc.username}")
    private String user;

    @Value("#{jdbc.password}")
    private String password;
}
  1. 添加测试方法如下:
@Test
public void test06(){
    AbstractApplicationContext context =
            new ClassPathXmlApplicationContext("application.xml");
    MySQLUtils utils = context.getBean("mySQLUtils",MySQLUtils.class);
    System.out.println(utils);
}

运行结果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

听潮阁

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值
>