Spring之IoC操作Bean

Spring之IoC操作Bean

上一篇博客主要介绍IoC是什么,本篇主要介绍IoC的Bean管理,刚学的时候懵懵懂懂,不知道xml配置是干什么呀,有的类头上还有个注解是什么意思呢,相信很多初学者也是这样,下面带来管理Bean的两种主要方式。(其实有三种)

这里可以先看一下Spring中Bean是如何由容器管理创建并实例化的

在这里插入图片描述

Bean管理的三种方式

Bean管理主要是Spring创建对象和Spring注入属性

1、IoC管理(基于xml)

上一篇博客里的bean.xml里其实已经出现了这样一段

 <bean id="hello" class="com.feng.pojo.Hello">
        <property name="name" value="Spring"/>
    </bean>

这个就是基于xml方式创建对象,

<!--使用Spring来创建对象,在Spring这些都成为Bean

        类型  变量名 = new 类型();
        Hello hello = new Hello();

        id = 变量名
        class = new 的对象
        property 相当于给对象的属性设置一个值
        
        ref:引用Spring容器中创建好的对象
        value:具体的值,基本数据类型
-->

在Spring配置文件中,使用bean标签,标签里添加相应属性,就可以实现对象的创建。bean标签中有很多属性,其中几个重要的属性有,

  • id:唯一标识,不能添加特殊符号
  • class:类的全路径
  • name:定义独享的标识,可以添加特殊符号

还有是

基于xml方式注入属性

1#构造器注入

  1. 使用无参构造创建对象,默认!

  2. 假设我们要使用有参构造创建对象

    1. 下标赋值

      <!--下标赋值-->
      <bean id="user" class="com.feng.pojo.User">
          <constructor-arg index="0" value="歪比歪比"/>
      </bean>
      
    2. 类型

      <!--第二种方式:通过类型创建,不建议使用!-->
      <bean id="user" class="com.feng.pojo.User">
          <constructor-arg type="java.lang.String" value="CBL"/>
      </bean>
      
    3. 参数名

      <!--直接通过参数名-->
      <bean id="user" class="com.feng.pojo.User">
          <constructor-arg name="name" value="歪比巴卜"/>
      </bean>
      

2#Set方式注入【重点】

​ 首先创建一个类,定义属性和对应的set方法(这里创建了两个类,演示几个注入的对象)

Student.java

package com.feng.pojo;

import java.util.*;

public class Student {
    
    

    private String name;
    private Address address;
    private String[] books;
    private List<String> hobbys;
    private Map<String,String> card;
    private Set<String> games;
    private String money;
    private Properties info;

    public String getName() {
    
    
        return name;
    }

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

    public Address getAddress() {
    
    
        return address;
    }

    public void setAddress(Address address) {
    
    
        this.address = address;
    }

    public String[] getBooks() {
    
    
        return books;
    }

    public void setBooks(String[] books) {
    
    
        this.books = books;
    }

    public List<String> getHobbys() {
    
    
        return hobbys;
    }

    public void setHobbys(List<String> hobbys) {
    
    
        this.hobbys = hobbys;
    }

    public Map<String, String> getCard() {
    
    
        return card;
    }

    public void setCard(Map<String, String> card) {
    
    
        this.card = card;
    }

    public Set<String> getGames() {
    
    
        return games;
    }

    public void setGames(Set<String> games) {
    
    
        this.games = games;
    }

    public String getMoney() {
    
    
        return money;
    }

    public void setMoney(String money) {
    
    
        this.money = money;
    }

    public Properties getInfo() {
    
    
        return info;
    }

    public void setInfo(Properties info) {
    
    
        this.info = info;
    }

    @Override
    public String toString() {
    
    
        return "Student{" +
                "name='" + name + '\'' +
                ", address=" + address.getAddress() +
                ", books=" + Arrays.toString(books) +
                ", hobbys=" + hobbys +
                ", card=" + card +
                ", games=" + games +
                ", money='" + money + '\'' +
                ", info=" + info +
                '}';
    }
}

Address.java

package com.feng.pojo;

public class Address {
    
    
    private String address;

    public String getAddress() {
    
    
        return address;
    }

    public void setAddress(String address) {
    
    
        this.address = address;
    }
}

beans.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="address" class="com.feng.pojo.Address">
        <property name="address" value="中国"/>
    </bean>
    <bean id="student" class="com.feng.pojo.Student">
        <!--第一种,普通值注入-->
        <property name="name" value="冯半仙"/>
        <!--第二种,Bean注入,ref-->
        <property name="address" ref="address"/>
        <!--数组注入,ref-->
        <property name="books">
            <array>
                <value>红楼梦</value>
                <value>西游记</value>
                <value>水浒传</value>
                <value>三国演义</value>
            </array>
        </property>
        <!--List-->
        <property name="hobbys">
            <list>
                <value>听歌</value>
                <value>敲代码</value>
                <value>看电影</value>
                <value>吃火锅</value>
            </list>
        </property>
        <!--Map-->
        <property name="card">
            <map>
                <entry key="身份证" value="100111200010112222"/>
                <entry key="银行卡" value="621888888888888888"/>
            </map>
        </property>
        <!--Set-->
        <property name="games">
            <set>
                <value>CS</value>
                <value>OW</value>
                <value>GTA5</value>
            </set>
        </property>
        <!--null值注入,设为空值-->
<!--        <property name="money" value=""/>-->
        <!--若要复制为null,则-->
        <property name="money">
            <null/>
        </property>
        <!--Properties
        key=value
        key=value
        key=value
        -->
        <property name="info">
            <props>
                <prop key="学号">20201808</prop>
                <prop key="性别"></prop>
                <prop key="姓名">小明</prop>
            </props>
        </property>

    </bean>

</beans>

MyTest.java

import com.feng.pojo.Student;
import com.feng.pojo.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    
    
    public static void main(String[] args) {
    
    
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

        Student student = (Student) context.getBean("student");
        System.out.println(student.toString());
    }
    
}

执行结果

/*
     * Student{
         * name='冯半仙',
         * address=中国,
         * books=[红楼梦, 西游记, 水浒传, 三国演义],
         * hobbys=[听歌, 敲代码, 看电影, 吃火锅],
         * card={
         *      身份证=100111200010112222, 银行卡=621888888888888888
         *      },
         * games=[CS, OW, GTA5],
         * money='null',
         * info={
         *      姓名=小明,
         *      学号=20201808,
         *      性别=男
         * }
     * }
     * */

其他几种注入集合、工厂Bean不再赘述

感兴趣的见

https://blog.csdn.net/java_cxrs/article/details/108289664

2、通过注解管理Bean

什么是注解?

(1)代码里特殊的标记,格式:@注解名称(属性名称=属性值…)

(2)可以在类、方法、属性上添加注解。

创建对象用到的注解主要有

  • @Componet
  • @Service
  • @Controller
  • @Repository

其实这四个注解功能一致,只是作用的范围有所区别

注解 含义
@Component 最普通的组件,可以被注入到spring容器进行管理
@Repository 作用于持久层,dao
@Service 作用于业务逻辑层,service
@Controller 作用于表现层,Controller类

例子:User.java

//等价于<bean id="user" class="com.feng.pojo.User"/>
//@Component    组件
@Component
//作用域注解
@Scope("singleton")

public class User {
    
    
    @Value("冯半仙")
    //相当于<property name="name" value="冯半仙"/>
    public String name;
    //使其为空,用属性注入
    //public String name = "冯半仙";

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

属性注入提供的注解:

  • @Autowired
  • @Qualifier
  • @Resource
  • @Value

四个区别如下

注解 含义
@Autowired 根据属性类型进行自动装配
@Qualifier 根据属性名称进行注入
@Resource 可以根据属性、名称进行注入
@Value 注入普通类型的属性

具体例子见

https://blog.csdn.net/java_cxrs/article/details/108331579

3、通过java类管理Bean

实体类

//这个注解的意思,就是说明这个类被Spring注册到了容器中
@Component
public class User {
    
    
    private String name;

    @Value("冯半仙")//属性注入值
    public void setName(String name) {
    
    
        this.name = name;
    }

    public String getName() {
    
    
        return name;
    }

    @Override
    public String toString() {
    
    
        return "User{" +
                "name='" + name + '\'' +
                '}';
    }
}

Javaconfig

//这个也会被Spring注册到容器中,因为它本来就是一个@Component,
// @Configuration代表这是一个配置类,就和我们之前的bean.xml是一样的
@Configuration
@ComponentScan("com.feng.pojo")
@Import(FengConfig2.class)
public class FengConfig {
    
    

    //注册一个bean,就相当于之前写的一个bean标签
    //这个方法的名字就相当于bean标签中的id属性
    //这个方法的返回值就相当于bean标签中的class属性
    @Bean
    public User getUser()
    {
    
    
        return new User();//返回要注入到bean的对象
    }

}

测试类

public class MyTest {
    
    
    public static void main(String[] args) {
    
    
        //如果完全使用了配置类方式去做,我们就只能通过AnnotationConfig 上下文来获取容器
        //通过配置类的class对象加载
        ApplicationContext context = new AnnotationConfigApplicationContext(FengConfig.class);
        User getUser = context.getBean("user", User.class);
        System.out.println(getUser.getName());
    }
}

这种纯Java的配置方式,在SpringBoot中见的比较多。

了解了Bean的三种管理方式以后来讲点理论的东西

Bean的生命周期

Spring IOC容器可以管理Bean的生命周期,Spring允许在Bean生命周期的特定点执行定制的任务。

Spring IOC容器对Bean的生命周期进行管理的过程:

1.通过构造器或工厂方法创建Bean实例
2.为Bean的属性设置值和对其他Bean的引用
3.调用Bean的初始化方法(init-method)
4.Bean可以使用了
5.当容器关闭时,调用Bean的销毁方法(destory-method)

Bean的自动装配

  • 自动装配是Spring满足bean依赖一种方式
  • Spring会在上下文中自动寻找,并自动给bean装配属性

在Spring中有三种自动装配的方式

  1. 在xml中显示的配置
  2. 在java中显示配置
  3. 隐式的自动装配bean【重要】

测试

环境搭建:一个人有两个宠物

ByName 自动装配

<!--
byName:会自动在容器上下文中查找,和自己对象set方法后面的值对应的bean id
    -->
<bean id="people" class="com.feng.pojo.People" autowire="byName">
    <property name="name" value="冯半仙"/>
</bean>

ByType自动装配

<bean id="cat" class="com.feng.pojo.Cat"/>
<bean class="com.feng.pojo.Dog"/>

<!--
byType:会自动在容器上下文中查找,和自己对象属性类型相同的bean!但只能保证全局唯一,例如你只能有一只狗
    -->
<bean id="people" class="com.feng.pojo.People" autowire="byType">
    <property name="name" value="冯半仙"/>
</bean>

小结:

  • byname的时候,需要保证所有的bean的id唯一,并且这个bean需要和自动注入的属性的set方法的值一致
  • bytype的时候,需要保证所有的bean的class唯一,并且这个bean需要和自动注入的属性的类型一致!

使用注解实现自动装配

要使用注解须知:

  1. 导入约束: context约束
  2. 配置注解的支持:
<context:annotation-config/>
<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <context:annotation-config/>

</beans>

@Autowired

直接在属性上使用即可!也可以在set方法上使用

使用Autowired我们可以不用编写Set方法了,前提是你这个自动装配的属性在IOC(Spring)容器中存在,且符合名字byname。

科普:

@Nullable 字段标记了这个注解,说明这个注解可以为null
public @interface Autowired {
    
    
    boolean required() default true;
}

测试代码

public class People{
    
    
    //如果显示定义了Autowired的required属性为false,说明这个对象可以为null,否则不允许为空
    @Autowired(required = false)
    private Cat cat;
    @Autowired
    private Dog dog;
    private String name;
}

如果@Autowired自动装配的环境比较复杂,自动装配无法通过一个注解【@Autowired】完成的时候,我们可以使用@Qualifier(value=“xxx”)去配置@Autowired的使用,指定一个唯一的bean对象注入!

public class People {
    
    
    @Autowired(required = false)
    private Cat cat;
    
    @Autowired
    @Qualifier(value = "dog2")
    private Dog dog;
    private String name;
}

小结:

@Resource 和 @Autowired

  • 相同点:
    • 都是用来自动装配的,都可以放在属性字段上
  • 不同点:
    • @Autowired 通过byType的方式实现,而且必须要求这个对象存在,否则报空指针 【常用】
    • @Resource 默认通过byname的方式实现,如果找不到名字,则通过bytype实现!如果两个都找不到就报错
      false)
      private Cat cat;
      @Autowired
      private Dog dog;
      private String name;
      }

如果@Autowired自动装配的环境比较复杂,自动装配无法通过一个注解【@Autowired】完成的时候,我们可以使用@Qualifier(value="xxx")去配置@Autowired的使用,指定一个唯一的bean对象注入!

```java
public class People {
    @Autowired(required = false)
    private Cat cat;
    
    @Autowired
    @Qualifier(value = "dog2")
    private Dog dog;
    private String name;
}

小结:

@Resource 和 @Autowired

  • 相同点:
    • 都是用来自动装配的,都可以放在属性字段上
  • 不同点:
    • @Autowired 通过byType的方式实现,而且必须要求这个对象存在,否则报空指针 【常用】
    • @Resource 默认通过byname的方式实现,如果找不到名字,则通过bytype实现!如果两个都找不到就报错
    • 执行顺序不同

自定义IoC

到这里IoC就告一段落了,如果要自己实现一个IoC需要做什么?想过具体步骤吗?

前面说到IoC就是控制反转,这也是Spring的核心。换句话说,所谓IoC,对Spring框架来说。就是由Spring来负责管理控制对象的生命周期和对象间的关系。我们要自己实现它,需要做啥,简要的说一下就是:

首先需要一个描述bean配置的Java类,

然后需要解析bean的配置,将bean的配置信息转化为上面的BeanDefinition对象保存在内存中,Spring中采用HashMap进行对象的存储,会用到xml解析技术。

最后遍历存放在BeanDefinition的HashMap对象,逐个取出BeanDefinition独享,获取bean配置信息,利用Java反射机制实例化对象,再将实例化对象保存在另外一个Map中即可。

具体实现见

https://blog.csdn.net/yinbucheng/article/details/79016415

猜你喜欢

转载自blog.csdn.net/weixin_43876186/article/details/108603567