05.spring-ioc-注解方式管理Bean

136 阅读4分钟

前言

spring ioc注解方式管理容器中的bean本质是通过aop来实现的,aop后面章节会讲述到,这里仅仅有个印象即可。所以就需要aop的相关依赖,不过还记得这个图吗?

image-20230131142600228

当我们项目中引入spring-context的依赖时,自动就会依赖spring-aop了。

XML方式开启包扫描

想要spring容器来管理所有的bean,而且又是批注方式。这时就需要告知spring扫描那些包下的类。

<?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
">
    <!-- 进行包扫描 -->
    <context:component-scan base-package="com.mayuanfei.springioc"/>

</beans>

说明

  • 进行包扫描的本质是把有spring注解的类进行实例化,加入的容器中进行管理。
  • context:component-scan就是spring进行包扫描的xml标签
  • base-package后放要扫描的包路径。如果有多个路径可以中间用逗号分割,不过还是建议输入一个比较等层的包作为扫描路径
  • 可以通过注解指定类的实例化对象id。如:@Component("user1")。不指定默认为类名首字母小写如User类,默认情况在Spring容器中对象的id就是user

Spring Ioc 的几个注解

  • @Component 放在类上,用于标记,告诉spring当前类需要由容器实例化bean并放入容器中

    该注解有三个子注解

    • @Controller 用于实例化controller层bean
    • @Service 用于实例化service层bean
    • @Repository 用于实例化持久层bean

理论上这几个注解是可以混着使用的,但是非常不推荐。最好是看到这个注解就知道属于程序逻辑的哪一层。如果不确定是哪一层的,就使用@Component注解。

包扫描过滤

<?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
">
    <!-- 进行包扫描过滤
    use-default-filters默认为true,代表使用默认的扫描过滤器
    默认的扫描过滤器会识别并包含 @Component @Controller @Service @Repository 四个注解
    如果希望使用自己的过滤,把use-default-filters=false

    include-filter: 只扫描的注解 一般和use-default-filters="false"配套使用
    exclude-filter: 排除掉的注解 一般和use-default-filters="true"也就是默认配套使用
    -->
    <!-- 只扫描@Controller注解 -->
    <context:component-scan base-package="com.mayuanfei.springioc" use-default-filters="false">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

    <!-- 不扫描@Controller注解 -->
    <context:component-scan base-package="com.mayuanfei.springioc">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

</beans>

spring中注入的三种姿势

1. 构造注入

/**
* 构造注入。这是非常推荐的一种注入方式。依赖不为空,省去了我们对其检查。如果无该类型的构造参数直接报错。
*/
@Service
@AllArgsConstructor
public class SysUserServcieImpl implements SysUserService {

    private final SysUserMapper userMapper;

    /**
     * 添加用户
     *
     * @param user 用户对象
     */
    public void addUser(SysUser user) {
        this.userMapper.insertSysUser(user);
    }
}

这里采用了lombok的方式来简化构造方法

2. Setter注入

/**
* Setter注入。感觉代码很臃肿,实际项目中很少人用
*/
@Service
public class SysUserServcieImpl2 implements SysUserService {

    private SysUserMapper userMapper;

    /**
     * 添加用户
     *
     * @param user 用户对象
     */
    public void addUser(SysUser user) {
        this.userMapper.insertSysUser(user);
    }

    @Autowired
    public void setUserMapper(SysUserMapper userMapper) {
        this.userMapper = userMapper;
    }
}

3. field注入(属性注入)

注解解释
@Autowired根据属性数据类型自动装配 想使用名称装配可以结合@Qualifier注解
@Qualifier根据属性名称注入依赖。结合@Autowired使用
@Resource如果指定name属性,就只会按照名称进行装配;如果没有指定name属性,首先会以注解的变量名按照名称进行匹配,如果找不到,再以类型进行匹配都找不到,则匹配失败
@Value注解可以用来将外部的值动态注入到 Bean 中,在 @Value 注解中,可以使{} 与 #{} ,它们的区别如下:<br />@Value("{}"):可以获取对应属性文件中定义的属性值。
@Value("#{}"):表示 SpEL 表达式通常用来获取 bean 的属性,或者调用 bean 的某个方法。
@SuppressWarnings("all")
@Component
public class SysUserManager2 {

    @Value("${user.id}")
    private String userId;
    @Value("${user.name}")
    private String userName;
    @Value("${user.address}")
    private String address;
    @Value("${ip}")
    private String ip;
    @Value("${port}")
    private String port;
    // 注入系统属性
    @Value("#{systemProperties['os.name']}")
    private String osName;
    // 注入表达式结果
    @Value("#{T(java.lang.Math).random()*1000}")
    private double randomNum;
    // 注入其他bean执行方法的结果
    @Value("#{user1.userRandomName}")
    private String fromAnotherBean;

    public void showUser() {
        System.out.println(ip+":"+port);
        System.out.println("操作系统:"+osName);
        System.out.println("随机数:"+randomNum);
        System.out.println("其他bean的方法:"+fromAnotherBean);
        SysUser user = new SysUser();
        user.setUserId(""+userId);
        user.setUserName(userName);
        user.setAddress(address);
        System.out.println(user);
    }
}

完全注解方式

上面的例子中,我们都还是通过ClassPathXmlApplicationContext来读取一个配置文件来实现的。这个唯一的配置文件是不是能够被代替呢?回答是肯定的。

  • java配置类

    @ComponentScan("com.mayuanfei.springioc")
    @PropertySource(value={"classpath:user.properties","classpath:abc.properties"}, encoding="utf-8")
    public class SpringConfig {
    }
    
  • 测试代码

    @Test
    public void test7() {
        ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        SysUserManager2 userManager2 = context.getBean(SysUserManager2.class);
        userManager2.showUser();
    }
    

代码地址

gitee.com/mayuanfei/S… 下的SpringIoc04

记忆印记

  • context:component-scan 包扫描
  • include-filter,exclude-filter 包过滤
  • @Component、@Controller、@Service、@Repository加入spring容器的四大注解
  • spring注入的三种姿势,这里强烈推荐构造注入、其次是@Resource注入。
  • 完全注解方式可以替换xml配置文件的包扫描。