1. SpringBoot之Bean的使用及处理

1.1引入pom依赖

<properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <!--引入springboot父工程依赖-->
    <!--引入依赖作用:
    可以省去version标签来获得一些合理的默认配置
    -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.2.RELEASE</version>
    </parent>

    <dependencies>
        <!--web 相关依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--两个用来做测试的jar包-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>

    </dependencies>


1.2 JavaBean对象

  • 用于后面的注入和使用

1.2.1 Teacher对象

public class Teacher {

    //姓名
    private String name;

    //科目
    private String subject;

    public String getName() {
        return name;
    }

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

    public String getSubject() {
        return subject;
    }

    public void setSubject(String subject) {
        this.subject = subject;
    }

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

1.2.2 Student对象

public class Student {
    //姓名
    private String name;
    //年龄
    private int age;
    //地址
    private String address;

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    public String getAddress() {
        return address;
    }

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

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


1.3配置启动类

  • @ImportResource: 注解主要用于导入Spring的xml配置文件注册的Bean。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ImportResource;

@SpringBootApplication
//注解主要用于导入Spring的xml配置文件注册的Bean。
@ImportResource("classpath*:spring-config.xml")
public class SpringBootBeanApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringBootBeanApplication.class, args);
    }
}


1.4 JavaBean的注入及使用

1.4.1 通过注解式@Bean注入IOC

  • @Configuration: 声明一个类为配置类,用于取代bean.xml配置文件注册bean对象。
  • @Bean: 等价于Spring中的bean标签用于注册bean对象的,给容器中添加组件,一般以方法名作为组件的id(注解中value属性为组件的id),配置类里面使用,默认是单实例的。
  • @Scope: Spring IOC 容器中的一个作用域,在 Spring IOC 容器中,他用来配置Bean实例的作用域对象。
                     作用域:
                             singleton: 单实例的(单例)(默认)   ----全局有且仅有一个实例
                             prototype: 多实例的(多例)            ---- 每次获取Bean的时候会有一个新的实例
                             reqeust:    同一次请求                     ----每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP request内有效
                              session:   同一个会话级别              ----每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP session内有效
import com.it.mhh.entity.Teacher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyConfigurationTeacherBean {

    private String name;

    private String subject;

    @Bean("teacher")
    public Teacher getTeacher(){
        Teacher teacher = new Teacher();
        teacher.setSubject(subject);
        teacher.setName(name);
        return teacher;
    }

    public String getName() {
        return name;
    }

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

    public String getSubject() {
        return subject;
    }

    public void setSubject(String subject) {
        this.subject = subject;
    }
}


1.4.1 通过注解式@Bean注入的使用测试

  • @Value():               “#{}”:表示SpEl表达式通常用来获取bean的属性,或者调用bean的某个方法。当然还有可以表示常量
                  “${}”:表示从配置文件读取值的用法
  • spring boot bean scop spring boot bean scope bean_xml


import com.it.mhh.entity.Student;
import com.it.mhh.entity.Teacher;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import javax.annotation.Resource;

@SpringBootTest
@RunWith(SpringRunner.class)
public class GetBeanTest {

    @Value("#{teacher}")
    private Teacher teacher;


    @Test
    public void getTeacher() {
        System.err.println(teacher);
    }

}


1.4.2 通过实现FactoryBean接口注入IOC

  • @Configuration:声明一个类为配置类,用于取代bean.xml配置文件注册bean对象。
  • @Component:        定义Spring管理Bean(也就是将标注@Component注解的类交由spring管理)
                                    这里两个注解都可以用,他们的value属性就是bean对象的id
import com.it.mhh.entity.Student;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

//@Component("student")
@Configuration("student")
public class MyStudentFactoryBean implements FactoryBean<Student> {

    //姓名
    private String name = "小白";
    //年龄
    private int age = 12;
    //地址
    private String address = "山东济南";

    //返回bean的对象
    @Override
    public Student getObject() throws Exception {
        Student student = new Student();
        student.setAddress(address);
        student.setAge(age);
        student.setName(name);
        return student;
    }

    //返回bean的类型
    @Override
    public Class<?> getObjectType() {
        return Student.class;
    }

    //是不是单例的
    @Override
    public boolean isSingleton() {
        return FactoryBean.super.isSingleton();
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    public String getAddress() {
        return address;
    }

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


1.4.2 通过实现FactoryBean接口注入使用测试

  • @Autowired: 可以对成员变量、方法和构造函数进行标注,来完成自动装配的工作,@Autowired标注可以放在成员变量上,也可以放在成员变量的set方法上,也可以放在任意方法上表示,自动执行当前方法,如果方法有参数,会在IOC容器中自动寻找同类型参数为其传值。
    @Autowired是根据类型进行自动装配的,如果需要按名称进行装配,则需要配合@Qualifier使用;
                             required: 属性为true的时候,注入SpringBean的时候,该bean必须存在,不然会注入失败,启动报错
  • @Qualifier: 在使用Spring框架中@Autowired标签时默认情况下使用 @Autowired 注释进行自动注入时,Spring 容器中匹配的候选 Bean 数目必须有且仅有一个。
import com.it.mhh.entity.Student;
import com.it.mhh.entity.Teacher;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import javax.annotation.Resource;

@SpringBootTest
@RunWith(SpringRunner.class)
public class GetBeanTest {

    @Autowired
    @Qualifier("student")
    private Student student;

    @Test
    public void getStudent() {
        System.err.println(student);
    }


}


1.4.3 通过XML形式注入IOC

  • 在resources包下创建spring-config.xml
  • primary标签: 能够指定配置在xml里面的bean,哪些是作为优先注入的bean。
  • property标签: name 为对应对象中的属性名,value 为该属性的值
<?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:content="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">

    <bean id="beanTeacher" class="com.it.mhh.entity.Teacher" primary="true"/>

    <bean id="beanChangeTeacher" class="com.it.mhh.entity.Teacher">
        <property name="name" value="小小黑"/>
        <property name="subject" value="c++"/>
    </bean>
</beans>


1.4.3 通过XML形式注入使用测试

  • @Autowired:可以对成员变量、方法和构造函数进行标注,来完成自动装配的工作,@Autowired标注可以放在成员变量上,也可以放在成员变量的set方法上,也可以放在任意方法上表示,自动执行当前方法,如果方法有参数,会在IOC容器中自动寻找同类型参数为其传值。
    @Autowired是根据类型进行自动装配的,如果需要按名称进行装配,则需要配合@Qualifier使用;(这里可以看出有两个TeacherBean被注入到ioc没有报错的原因时因为使用了primary,设置了primary则优先注入)
  • @Resource:默认按照名字装配Bean,即会按照name属性的值来找到具有相同id的Bean Definition 并注入。如果@Resource没有指定name属性,则会根据这个将要被注入的属性的名字来进行Bean装配。
                        name: 按照name属性的值来找到具有相同id的Bean
                        type: 使用JavaBeans 属性的类型(Class<?>)自动注入策略,将值作为需要注入bean的类型
import com.it.mhh.entity.Student;
import com.it.mhh.entity.Teacher;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import javax.annotation.Resource;

@SpringBootTest
@RunWith(SpringRunner.class)
public class GetBeanTest {

    @Autowired
    private Teacher beanTeacher;

    @Resource(name = "beanChangeTeacher")
    private Teacher beanChangeTeacher;

    @Test
    public void getTeacher() {
        System.err.println(beanTeacher);
        System.err.println(beanChangeTeacher);
    }
}


1.5 对bean的处理

1.5.1 BeanPostProcessor的使用

  • BeanPostProcessor是针对Bean级别的处理,可以针对某个具体的Bean。
  • 注意在 postProcessBeforeInitialization方法中对bean的使用
  • postProcessBeforeInitialization:                        : bean 实例化之前执行的方法
                           : bean : 这里的bean是还没有实例化的bean,如果是用工厂FactoryBean方式生成的 则就是实现FactoryBean接口的那个对象
                           : beanName : 生成bean的名字(bean的id)
  • postProcessAfterInitialization:                        : bean 实例化之后执行的方法
                           : bean : 这里的bean就是自己需要的已经实例化的了
                           : beanName : 生成bean的名字(bean的id)
import com.it.mhh.entity.Student;
import com.it.mhh.entity.Teacher;
import com.it.mhh.factorybean.MyStudentFactoryBean;
import com.it.mhh.factorybean.MyTeacherFactoryBean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;

//BeanPostProcessor是针对Bean级别的处理,可以针对某个具体的Bean。
@Component //注入到ioc
public class SimpleBeanPostProcessor implements BeanPostProcessor {
    //  bean 实例化之前执行的方法
    //  bean     : 这里的bean是还没有实例化的bean,如果是用工厂FactoryBean方式生成的 则就是实现FactoryBean接口的那个对象
    //  beanName : 生成bean的名字
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof MyStudentFactoryBean) {
            MyStudentFactoryBean myStudentFactoryBean = (MyStudentFactoryBean) bean;
            myStudentFactoryBean.setName("(MyStudentFactoryBean)postProcessBeforeInitialization::" + myStudentFactoryBean.getName());
        }
        if (bean instanceof MyTeacherFactoryBean) {
            MyTeacherFactoryBean myTeacherFactoryBean = (MyTeacherFactoryBean) bean;
            myTeacherFactoryBean.setName("(MyTeacherFactoryBean)postProcessBeforeInitialization::" + myTeacherFactoryBean.getName());
        }
        if (bean instanceof Teacher) {
            Teacher teacher = (Teacher) bean;
            teacher.setName("(Teacher)postProcessBeforeInitialization::" + teacher.getName());
        }
//        System.err.println(bean + "--Before-->" + beanName);
        return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
    }

    //  bean 实例化之后执行的方法
    //  bean     : 这里的bean就是自己需要的已经实例化的了
    //  beanName : bean的名字
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            if (bean instanceof Student) {
                Student student = (Student) bean;
                student.setAddress("postProcessAfterInitialization::" + student.getAddress());
            }
//        System.err.println(bean + "--After-->" + beanName);
        return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
    }
}


1.5.1 BeanPostProcessor的使用测试

  • teacher对象是通过 @Bean方式注入的,并没有使用 FactoryBean,所以postProcessBeforeInitialization方法的实例就是Teacher类
  • student对象使用了实现FactoryBean的方法 所以postProcessBeforeInitialization方法的实例就是实现FactoryBean接口的那个对象

spring boot bean scop spring boot bean scope bean_spring boot_02

import com.it.mhh.entity.Student;
import com.it.mhh.entity.Teacher;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import javax.annotation.Resource;

@SpringBootTest
@RunWith(SpringRunner.class)
public class GetBeanTest {

    @Autowired
    @Qualifier("student")
    private Student student;

    @Value("#{teacher}")
    private Teacher teacher;
    
    @Test
    public void getStudent() {
        System.err.println(teacher);
        System.err.println(student);
    }
    
}


1.5.2 BeanFactoryPostProcessor的使用

  • BeanFactoryPostProcessor : BeanFactory级别的处理,是针对整个Bean的⼯⼚进⾏处理
  • 调⽤ BeanFactoryPostProcessor ⽅法时,这时候bean还没有实例化,此时 bean 刚被解析成BeanDefifinition对象。
import com.it.mhh.factorybean.MyStudentFactoryBean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;

import java.util.Iterator;

//BeanFactoryPostProcessor : BeanFactory级别的处理,是针对整个Bean的⼯⼚进⾏处理,\
//调⽤ BeanFactoryPostProcessor ⽅法时,这时候bean还没有实例化,此时 bean 刚被解析成BeanDefifinition对象。
@Component
public class SimpleBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        //bean的元数据缓存
        configurableListableBeanFactory.clearMetadataCache();
        //冻结所有bean定义,表明注册的bean定义将不再修改或后期处理。
        configurableListableBeanFactory.freezeConfiguration();
        //bean的定义是否被冻结
        boolean configurationFrozen = configurableListableBeanFactory.isConfigurationFrozen();
        System.err.println("bean的定义是否被冻结:"+configurationFrozen);
        //返回所有Bean名称的迭代对象
        Iterator<String> beanNamesIterator = configurableListableBeanFactory.getBeanNamesIterator();
        System.err.println("返回所有Bean名称的迭代对象:"+beanNamesIterator);
        //根据bean名称获取BeanDefinition
        BeanDefinition beanDefinition = configurableListableBeanFactory.getBeanDefinition("student");
        //设置主要
        beanDefinition.setPrimary(true);
        //获取bean类的名称(com.it.mhh.factorybean.MyStudentFactoryBean$$EnhancerBySpringCGLIB$$3b126746)
        System.err.println("获取bean类的名称"+beanDefinition.getBeanClassName());

        //通过自己实例化前的属性set方法来获取和配置参数.
        MyStudentFactoryBean bean = configurableListableBeanFactory.getBean(MyStudentFactoryBean.class);
        bean.setAddress("BeanFactoryPostProcessor->" + bean.getAddress());
    }
}

}


1.5.2 BeanFactoryPostProcessor的使用测试

  • 可以通过实现BeanFactoryPostProcessor 来针对整个Bean的⼯⼚进⾏处理
  • 也可以通过configurableListableBeanFactory.getBean的形式修改指定的bean参数
import com.it.mhh.entity.Student;
import com.it.mhh.entity.Teacher;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import javax.annotation.Resource;

@SpringBootTest
@RunWith(SpringRunner.class)
public class GetBeanTest {

    @Autowired
    @Qualifier("student")
    private Student student;

    @Test
    public void getStudent() {
        System.err.println(student);
    }

}