01-Bean注解

配置Bean对象

xml配置文件方式

流程

  1. 写配置文件
  2. bean标签注入
  3. 测试获取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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--配置一个Person对象-->
    <beans>
        <bean class="com.xiong.bean.Person" id="person">
            <property name="name" value="root"/>
            <property name="age" value="18"/>
        </bean>
    </beans>
</beans>

测试代码

package com.xiong;


import com.xiong.bean.Person;
import com.xiong.config.MainConfig;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MainTest {
    @Test
    public void getBeanByXmlFileTest() {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        Person person = context.getBean("person", Person.class);
        System.out.println(person);
    }
}

结果展示

image-20221118022738220

注解方式

  • @Configuration: 设置配置类, 用以取代配置文件
  • @Bean: 设置Bean对象, 用以取代配置文件中的bean标签
  1. 使用@Configuration设置配置类
  2. 通过@Bean设置bean方法
    1. 在bean方法中, new对象
    2. 设置对象属性
    3. 返回return对象
    4. 完成将bean对象注入到容器中
  3. 测试获取bean对象

配置类

package com.xiong.config;

import com.xiong.bean.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MainConfig {
    @Bean
    Person person() {
        Person person = new Person();
        person.setName("person");
        person.setAge(20);
        return person;
    }
}

测试类

package com.xiong;


import com.xiong.bean.Person;
import com.xiong.config.MainConfig;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MainTest {
    @Test
    public void getBeanByConfigClassTest() {
        ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
        Person person = context.getBean("person", Person.class);
        System.out.println(person);
    }
}

结果展示

image-20221118022720610

@Bean注解解析

package org.springframework.context.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.core.annotation.AliasFor;


@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean {

    @AliasFor("name")
    String[] value() default {};

    @AliasFor("value")
    String[] name() default {};

    boolean autowireCandidate() default true;

    String initMethod() default "";

    String destroyMethod() default AbstractBeanDefinition.INFER_METHOD;
}

value属性

作为bean对象的id

默认情况下是bean方法的方法名. 例如person()getBean(“person”, Person.class)

默认情况

image-20221118024407013

显示指明bean对象的id

image-20221118024601637

实验效果

image-20221118024740434

Bean 的声明周期

  1. 默认空参构造器
  2. setXXX() 属性赋值方法
  3. BeanPostProcessor 接口中的 postProcessBeforeInitialization() 后置处理器方法
  4. initMethod() 初始化方法
    1. InitializingBean 接口的 afterPropertiesSet() 方法
    2. @PostConstruct 注解
    3. @Bean 注解中的 init-method 属性
  5. postProcessAfterInitialization() 后置处理器方法
  6. destroyMethod() 销毁方法

手工实现 @MyBean 注解

仿注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyBean {
}

仿配置类

public class MyConfiguration {

    @MyBean
    public void show(Person person, Cat cat) {
        System.out.println(person + " -[own]-> " + cat);
    }
}

辅助打印输出的实体类

public class Cat {
    private String name;

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "Cat{" +
                "name='" + name + '\'' +
                '}';
    }
}
public class Person {
    private String name;
    private Integer age;

    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;
    }

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

@MyBean 注解解析类(后置处理器)的简易实现

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;

/**
 * 可以看做是处理@MyBean注解的一个后置处理器
 */
public class MyBeanPostProcessorImpl {
    public static void main(String[] args) throws Exception {
        Person person = new Person();
        person.setName("root");
        person.setAge(18);

        Cat cat = new Cat();
        cat.setName("kitty");

        // 模拟Spring容器
        HashMap<Class<?>, Object> context = new HashMap<>();
        context.put(Person.class, person);
        context.put(Cat.class, cat);

        // 0. 通过扫描获取获取配置类(略)

        // 1. 解析注解@MyBean
        Class<?> clazz = Class.forName("prepared.MyConfiguration");
        Object configuration = clazz.newInstance();

        Method[] methods = clazz.getDeclaredMethods();
        for (Method method : methods) {
            // 这里写死MyBean.class说明是解析@MyBean, 不同的后置处理器本质的区别: 针对哪个注解进行解析并实现相应的功能
            if (method.isAnnotationPresent(MyBean.class)) {
                Class<?> returnType = method.getReturnType();

                // 解析参数, 从容器中赋值
                Class<?>[] parameterTypes = method.getParameterTypes();
                ArrayList<Object> objects = new ArrayList<>();
                for (Class<?> parameterType : parameterTypes) {
                    // 根据解析得到的参数类型, 从容器中获取值, 赋值给方法的形参, 然后再调用该方法
                    Object parameterBean = context.get(parameterType);
                    objects.add(parameterBean);
                }

                //TODO: 如果能将其当做某个属性保存起来就更好了, 现在这种实现在解析的时候就调用了
                Object[] parameterBeans = objects.toArray();

                // TODO: 这里获取到返回结果类型为returnType, 但是如何实现强制类型转换呢?
                Object result = method.invoke(configuration, parameterBeans);

                System.out.println("result = " + result);
            }
        }
    }
}

   转载规则


《01-Bean注解》 熊水斌 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
@Value 不仅仅局限于值注入,或者说一切的高级功能最后都落地于值注入。下面我们使用 @Value 来完成一些更高级的注入。 ${}:解析环境(Environment)中的值,本质上是通过 key 获取 value #{}:解析 Sp
2023-05-14
下一篇 
02-ComponentScan注解 02-ComponentScan注解
@ComponentScan包扫描配置文件方式 在配置文件中配置包扫描 测试 通过包扫描自动注入@Configuration配置类组件 在@Configuration组件中创建bean对象 通过配置文件启动spring程序, 从容器中获取@
2023-05-13
  目录