使用Spring的IOC进行Bean管理

Spring 是一个为了解决企业应用开发的复杂性而创建的开源框架。是一个轻量级的控制反转( IOC)和面向切面( AOP )的容器框架:

  • 从大小与开销两方面而言 Spring 都是轻量
  • 通过控制反转( IOC )的技术达到松耦合的目的
  • 提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务进行内聚性的开发
  • 包含并管理应用对象的配置和生命周期的容器
  • 将简单组件组合称为复杂的框架

1、创建maven项目

首先通过IDEA创建一个Maven类型的Spring项目,点击File->new->Project弹出如下界面,选择maven,点击next

之后为项目选择名称和位置,并且可以设置项目所属公司名GroupId、输出名ArtfactId、版本名Version,最后生成的目录结构如下右图所示, 在src目录下有main和test两个文件夹,main用于存放源文件,test/java测试模块,通过JUnit生成的测试文件会自动保存到该目录。main下的java文件夹为Source Root,resources为Resources Root文件夹存放配置文件。最外层pom.xml为maven的配置文件。

              

2、使用接口降低耦合度

如下所示我创建了两个汽车类Audi和Toyota,分别有三个方法start()、run()、stop()。还有一个人XiaoWang,驾驶汽车回家goHome(),首先需要创建汽车类对象audi,然后再通过audi调用方法回家。

public class Audi {
    public void start(){
        System.out.println("奥迪启动!");
    }
    public void run(){
        System.out.println("奥迪行驶中...");
    }
    public void stop(){
        System.out.println("奥迪停车!");
    }
}

public class XiaoWang {
    private Audi audi=new Audi();

    public void goHome(){
        audi.start();
        audi.run();
        audi.stop();
    }
}

这时如果Xiaowang希望更换车辆Toyota,他就需要再创建一个新的对象toyota,并且重写一个goHome方法去调用相同的start()、run()、stop()方法,这是十分繁琐与不便的。造成这种情况的原因是XiaoWang与汽车类Audi之间的耦合度过高。通过使用接口将耦合在一起的整体拆分为两部分,可以将汽车Audi、Toyota抽象为一个接口Car,然后XiaoWang通过传入Car对象来使用不同的汽车。

//公共接口
public interface Car {
    void start();

    void run();

    void stop();
}

//汽车类实现接口
public class Toyota implements Car{
    public void start(){
        System.out.println("丰田启动!");
    }
    public void run(){
        System.out.println("丰田行驶中...");
    }
    public void stop(){
        System.out.println("丰田停车!");
    }
}

//使用接口调用汽车
public class XiaoWang {
    private Car car;

    public XiaoWang(Car car) {
        this.car = car;
    }

    public void goHome(){        //通过Car接口来调用汽车类的方法
        car.start();
        car.run();
        car.stop();
    }
}

public void main() {
    Car car =new Toyota();    //创建传入不同的car对象来使用
    XiaoWang xiaoWang=new XiaoWang(car);    
    xiaoWang.goHome();
}

3、IOC

控制反转(IOC,Inversion of Controll)是一个重要的面向对象编程的规则,用以削弱计算机程序的耦合问题,也是轻量级 Spring 框架的核心。所谓控制反转是指应用程序本身不负责所需对象的创建和维护,而是由外部容器完成,应用程序直接拿来使用。如下图所示,我们将一个普通Java对象(POJOs,Plain Ordinary Java Objects)传入Spring容器,通过读取配置元数据,之后就会生产一个符合我们系统需要的并且可以直接使用的对象。通过使用IOC,我们不必再手动创建和管理对象了,并且可以直接使用一个对象

依赖注入(Dependency Injection)是Spring实现IOC的一种方式,即在容器运行期间,动态地将依赖关系注入到对象。

手动实现IoC容器

如下所示为手动实现的Ioc容器,它利用一个ConcurrentHashMap来保存字符串类型的beanId到对应Bean类的映射关系。其getBean()方法即通过beanId查找Map中对应的Bean并且返回。setBean()方法来创建新的bean存入到Map中,参数除了需要传入bean所属类型clazz和beanId标识外,还需要传入其构造方法所需要的bean,例如构造一个XiaoWang对象,需要传入一个Car作为参数。之后根据参数beanId从Map中取出对应的bean,将其传入clazz的构造方法,遍历clazz所有的构造方法找到合适的实例化bean。最后将实例化的bean存入Map。

public class IocContainer {
    private Map<String, Object> beans = new ConcurrentHashMap<String, Object>();

    /**
     * 根据beanId返回一个Bean
     * @param beanId 要获取的bean的Id
     * @return 返回找到的bean
     */
    public Object getBean(String beanId) {
        return beans.get(beanId);
    }

    /**
     * 创建一个bean
     * @param clazz 要创建的bean的类型
     * @param beanId 对应的beanId
     * @param paramBeanIds 对应构造方法所需要的参数的beanId列表
     */
    public void setBeans(Class clazz, String beanId, String... paramBeanIds) {
        //1、获取构造方法所需的参数bean
        Object[] paramBeans = new Object[paramBeanIds.length];
        for (int i = 0; i < paramBeanIds.length; i++) {
            paramBeans[i] = beans.get(paramBeanIds[i]);
        }
        //2、调用构造方法实例化bean
        Object bean = null;
        for (Constructor constructor : clazz.getConstructors()) {
            try {       // 依次遍历clazz类的所有构造方法找到合适的来实例化bean
                bean = constructor.newInstance(paramBeans);
            } catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
                e.printStackTrace();
            }
        }
        if (bean == null)
            throw new RuntimeException("没有合适的构造方法实例化" + beanId);
        //3、将bean保存到beans中
        beans.put(beanId, bean);
    }
}

通过一个Test来测试上面的IOC容器,在使用之前需要创建bean,然后在testIoc()方法中就可以直接使用bean而不需要创建和维护对象。

class IocContainerTest {
    private IocContainer iocContainer=new IocContainer();

    @BeforeEach
    void setUp() {
        //在使用之前创建bean
        iocContainer.setBeans(Audi.class,"audi");
        iocContainer.setBeans(Toyota.class,"toyota");
        //传入Bean参数audi构造XiaoWang
        iocContainer.setBeans(XiaoWang.class,"xiaowang","audi");
    }

    @Test
    void testIoc() {
        XiaoWang x1=(XiaoWang)iocContainer.getBean("xiaowang");     //直接获取并使用bean
        x1.goHome();
    }
}

Spring中的IOC

spring中已经集成了Ioc容器供我们使用。其使用步骤如下:

1、首先在maven的配置文件pom.xml中引入Spring的依赖,只需要输入<artifactId>标签spring-core、spring-context,IDEA会提示补全对应的<groupId>和<version>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>5.2.4.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.4.RELEASE</version>
        </dependency>
    ......

2、在项目的src/main/resources文件夹下创建配置文件spring-ioc.xml,在其中注册所需要交友Ioc容器管理的Bean类,在<bean>标签内输入bean的id和对应的类

<?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">

    <bean id="audi" class="com.spring.ioc.Audi"/>
</beans>

3、获取并使用bean。首先通过ApplicationContext加载resources目录下的配置文件spring-ioc.xml,然后通过getBean()从Ioc容器获取bean,需要传入bean的Id和Class作为参数

class AudiTest {

    @Test
    void run() {
        //通过上下文管理类获取配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-ioc.xml");
        //获取Bean并使用
        Audi audi = context.getBean("audi", Audi.class);
        audi.run();
    }
}

4、使用Bean

实例化

Spring实例化一个bean有三种方法:

  • 第一种是通过构造方法实例化Bean,即之前使用的方法。如下所示,首先创建一个Bean类
public class Bean {
    public Bean() {
        System.out.println("Bean被创建");
    }
}

之后在配置文件中注册:

    <bean id="bean1" class="com.spring.ioc.Bean"/>

最后在代码中使用Bean如下

ApplicationContext context = new ClassPathXmlApplicationContext("spring-ioc.xml");
Bean bean1=context.getBean("bean1",Bean.class);
  • 第二种是通过工厂的静态方法来实例化Bean。在Bean的基础上再创建一个BeanFactory来返回bean对象
public class BeanFactory {
    public static Bean getBean(){
        return new Bean();
    }
}

接着在配置文件中注册,这里的factory-method要求必须为静态方法

<bean class="com.spring.ioc.BeanFactory" factory-method="getBean" id="bean2"/>

最后采用相同的方式获取bean

Bean bean2=context.getBean("bean2",Bean.class);
  • 第三种方法是通过实例化工厂来实例Bean,首先工厂的getBean()方法是非静态的
public class BeanFactory {
    public Bean getBean(){
        return new Bean();
    }
}

 接着在配置文件中同时注册Bean和BeanFactory,并在bean中配置其对应的factory-bean和factory-method

<bean id="bean3Factory" class="com.spring.ioc.BeanFactory"/>
<bean id="bean3" class="com.spring.ioc.Bean" factory-bean="bean3Factory" factory-method="getBean"/>

最后采用相同的方法获取bean

Bean bean3=context.getBean("bean3",Bean.class);

为Bean添加别名可以通过name属性,也可以通过<alias>标签。通过别名创建的bean其实指向同一个bean对象。

<bean id="bean1" class="com.spring.ioc.Bean" name="bean1_1,bean1_2"/>
<alias name="bean1" alias="bean1_3"/>

属性注入

对Bean的属性进行注入有两种方法,第一种是通过构造函数进行注入,第二种是通过setXxxv()设置属性。

如下所示为一个bean类XiaoWang,他有两个属性age、audi,其中audi为自定义的汽车Audi对象,并且设置其构造方法和get/set方法如下

public class XiaoWang {
    private  int age;
    private Audi audi;

    public XiaoWang(int age, Audi audi) {
        this.age = age;
        this.audi = audi;
    }

    public int getAge() { return age; }
    public void setAge(int age) {this.age = age; }
    public Audi getAudi() {return audi; }
    public void setAudi(Audi audi) {this.audi = audi; }
}

由于这里的构造方法需要传入两个参数,因此需要在配置文件中通过<constructor-arg>完成参数的传入。其中属性index代表是第几个参数,name代表成员变量的名称,type指定参数的类型,这里使用index即可指明对应哪个属性,因此name和type可以省略。在为参数赋值时,如果是Java自带数据类型,则使用value,如果是我们自己创建的类,则需要使用ref="",并传入对应的bean。这里第一个为age赋值为24,第二个赋值bean为之前创建的audi1

除了使用构造方法,还可以使用<property>对Bean的属性赋值。它会调用bean的setXxx()方法完成对属性的赋值。

    <bean id="audi1" class="com.spring.ioc.Audi"/>
    <bean id="xiaowang" class="com.spring.ioc.XiaoWang">
        <constructor-arg index="0" name="age" value="24"/>
        <constructor-arg index="1" name="audi" type="com.spring.ioc.Audi" ref="audi1"/>
        <property name="age" value="21"/>
        <property name="audi" ref="audi1"/>
    </bean>

可以采用<constructor>和<property>的简便写法,首先在xml配置文件头中引入xmlns:c、xmlns:p,接着就可以在<bean>的属性中使用c:、p:来表示构造方法和属性。例如c:age代表传入属性age的构造方法,c:audi-ref代表传入属性audi的构造方法,p:age代表传入age的setAge()方法完成对age的赋值。注意由于audi并非Java的基本数据类型,所以其使用一般会带有“ref”之类的标识。

    <bean id="audi1" class="com.spring.ioc.Audi"/>
    <bean id="xiaowang" class="com.spring.ioc.XiaoWang"
          c:age="22" c:audi-ref="audi1"
          p:age="23" p:audi-ref="audi1"/>

Spring可以通过自动装配完成属性的注入,设置<beans default-autowire="byName" ...>代表通过beanId名完成自动装配,例如XiaoWang有一个属性audi,spring会自动查找Id为audi的Bean并且通过setAudi()方法注入到属性。如果beanId为audi111则查找失败。如果default-autowire="byType"代表依据类型查找,这时会查找类型为Audi的bean并完成注入。

如果Bean的属性是Java复合数据类型使用<property>赋值如下。在Bean XiaoWang中有string类型的List和Car类型的List属性,并且具有对应的get/set方法。在配置文件中赋值如下,常规类型的值使用<value>,自定义类型用<ref>。Set类型和List类似,只需要将<list>换为<set>即可。

        <property name="stringList">
            <list>
                <value>strig1</value>
                <value>字符串2</value>
            </list>
        </property>
        <property name="carList">
            <list>
                <ref bean="audi1"/>
                <ref bean="toyota1"/>
            </list>
        </property>

Map类型赋值如下,Bean中有Map类型的属性stringMap和carMap,分别使用<map>标签赋值

        <property name="stringMap">
            <map>
                <entry key="sk1" value="string1"/>
            </map>
        </property>
        <property name="carMap">
            <map>
                <entry key="car1" value-ref="audi1"/>
            </map>
        </property>

如果希望赋空值,只需要shiyong<null/>即可,<property name="carMap">    <null/>    </property>

在使用属性时可以创建内部Bean,类似Java的内部类,供临时bean属性赋值使用

<bean id="xiaowang" class="com.spring.ioc.XiaoWang">
    <property name="age" value="21"/>
    <property name="audi">
       <bean class="com.spring.ioc.Audi"/>            <!--内部bean-->
    </property>
</bean>

属性继承

如果两个Bean之间存在继承关系,可以使用parent属性指明Bean的父标签,从而可以继承父类的属性。例如下面有父类、子类两个Bean:

public class ParentBean {
    protected String parentString;

    public String getParentString() {
        return parentString;
    }

    public void setParentString(String parentString) {
        this.parentString = parentString;
    }
}

public class ChildBean extends ParentBean {        //继承自父类
    private String childString;

    public String getChildString() {
        return childString;
    }

    public void setChildString(String childString) {
        this.childString = childString;
    }

    @Override
    public String toString() {                    //打印父类和子类的属性
        return "ChildBean{" +
                "childString='" + childString + '\'' +
                ", parentString='" + parentString + '\'' +
                '}';
    }
}

在配置文件中配置继承关系,父类<bean>设置abstract="true"代表不需要实例化该Bean,子类<bean>通过parent="paren"将父类指向了id为parent的<bean>。打印输出子类child:ChildBean{childString='子类字符串', parentString='父类字符串'},可以看到父类和子类的属性都被输出。

<bean id="parent" class="com.spring.ioc.ParentBean" abstract="true">
    <property name="parentString" value="父类字符串"/>
</bean>
<bean id="child" class="com.spring.ioc.ChildBean" parent="parent">
    <property name="childString" value="子类字符串"/>
</bean>

初始化和销毁

如果我们希望在Bean的初始化和销毁之前执行一些操作有两种方法,

第一是在<bean>中使用init-method、destroy-method来指定Bean在初始化和销毁时执行的方法,也可以在<beans>中通过属性default-init-method、default-destroy-method为所用的bean设置默认的方法。例如在Bean类中定义onInit()、onDestroy()方法,并在spring文件中进行配置:

<bean id="bean" class="com.spring.ioc.Bean" init-method="onInit" destroy-method="onDestroy" />

之后通过上下文对象context获取bean对象并且在关闭context时销毁

AbstractApplicationContext context=new ClassPathXmlApplicationContext("spring-ioc.xml");
Bean bean1=context.getBean("bean",Bean.class);
System.out.println(bean1);
context.close();                    //关闭上下文对象context时会调用bean的销毁方法

 第二种方法是通过实现InitializingBean、DisposableBean接口中的方法来执行创建、销毁之前的操作

public class Bean implements DisposableBean, InitializingBean {
    @Override
    public void destroy() throws Exception {
        System.out.println("实现接口,Bean销毁之前");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("实现接口,Bean创建之前");
    }
}

5、作用域

单例/多例模式

在<bean>标签中的scope属性可以设置bean的作用域,其默认值为singleton单例模式,与之对应的是prototype多例模式。所谓单例模式就是指在一个上下文对象context中只会实例化一个bean,而多例模式会创建多个不同的bean。

如下所示为一个Bean类,它有一个InnerBean类的成员变量

public class Bean {
    private InnerBean innerBean;

    public Bean() {
        System.out.println("Bean被创建");
    }

    public InnerBean getInnerBean() {
        return innerBean;
    }

    public void setInnerBean(InnerBean innerBean) {
        this.innerBean = innerBean;
        System.out.println("内部bean:"+innerBean);
    }
}

public class InnerBean {
    public InnerBean() {
        System.out.println("InnerBean被创建");
    }
}

在配置文件中配置innerBean和bean,并且将innerBean设置为bean的子属性,这里innerBean的scope为单例模式singleton

    <bean id="innerBean" class="com.spring.ioc.InnerBean"  scope="singleton"/>
    <bean id="bean" class="com.spring.ioc.Bean">
        <property name="innerBean" ref="innerBean"/>
    </bean>

在测试模块中先创建两个innerBean实例,之后再创建一个bean实例

    @Test
    void testScope() {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-ioc.xml");
        InnerBean inner1 = context.getBean("innerBean", InnerBean.class);
        System.out.println("innerBean1:" + inner1);
        InnerBean inner2 = context.getBean("innerBean", InnerBean.class);
        System.out.println("innerBean2:" + inner2);
        Bean bean = context.getBean("bean", Bean.class);
        System.out.println("bean:" + bean);
    }

运行结果如下所示,InnerBean被创建了一次,并且inner1、inner2以及bean内部innerBean所使用的都是同一个InnerBean

将配置文件中的innerBean设置为多例模式scope="prototype",再次运行代码结果如下,可见InnerBean被创建了三次,并且inner1、inner2以及bean内部innerBean所使用的是不同的InnerBean对象

Web作用域

web中常用的为request、session、application三个作用域。如下所示为定义一个Servlet的Controller,定义请求映射的路径为requestScope,并向页面返回本对象的toString()结果

@Controller
public class RequestController {
    @RequestMapping("requestScope")
    @ResponseBody
    public String testScope(){
        return this.toString();
    }
}

将上面的Controller交由Spring管理并配置其作用域为request,部署服务器后访问该Controller页面,刷新页面可以看到两次request请求访问返回的对象不同,这是因为RequestController的作用域为request

    <bean class="com.spring.ioc.controller.RequestController" scope="request"/>

 

同理定义一个SessionController,并定义其作用域为session。刷新页面返回的SessionController对象相同,但是在另一个浏览器访问sessionScope对象不同,这样由于同一个浏览器session相同,换一个浏览器会打开一个新的session

    <bean class="com.spring.ioc.controller.RequestController" scope="request"/>

定义一个ApplicationController,其作用域为application。不论是刷新页面还是在不同的浏览器,都会返回相同的对象,因为只要服务器没有关闭,就属于同一个application

    <bean class="com.spring.ioc.controller.ApplicationController" scope="application"/>

自定义作用域

spring支持用户自定义作用域,类需要实现springframework的Scope接口。接口中的get()方法用于从作用域中返回对象,remove()方法用于从作用于删除并返回对象。之后在配置文件中注册自定义scope,例如spring为我们提供了一个自定义好的scope--SimpleThreadScope用于在一个线程中返回一个实例,其注册如下:

<!--引入自定义scope-->
    <bean id="simpleThreadScope" class="org.springframework.context.support.SimpleThreadScope"/>
<!--配置scope-->
    <bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
        <property name="scopes">
            <map>
                <entry key="simpleThread" value-ref="simpleThreadScope"/>
            </map>
        </property>
    </bean>
<!--使用自定义scope-->
    <bean id="bean1" class="com.spring.ioc.Bean" scope="simpleThread"/>

Bean的懒加载

如果bean的作用域为默认的singleton,则spring会在启动时就会实例化该bean。可以通过设置lazy-init="true"来使Bean懒加载,即当我们使用getBean()方法获取时才会创建bean。如果希望所有的bean都是懒加载,在<beans>标签中设置default-lazy-init="true"

    <bean id="bean" class="com.spring.ioc.Bean" lazy-init="true"/>

6、使用注解

为了避免使用繁琐的配置文件对Bean进行管理,Spring在2.5版本之后引入了注解的方式对Bean进行配置和管理。例如有一个类Bean1,我们首先创建Spring的上下文管理器BeanConfiguration用于管理Bean,为其添加注解@Configuration。在其中定义方法myBean1用于返回一个被管理的Bean1类,这里方法名myBean1就是默认的bean的Id,也可以通过注解@Bean的属性value为其添加别名,并且将新创建的Bean1对象作为返回值。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class BeanConfiguration {
    @Bean(value = "myBeanId")
    public Bean1 myBean1() {
        return new Bean1();
    }
}

在代码中获取Bean1如下,首先通过AnnotationConfigApplicationContext获取上下文对象context,其传入参数为刚定义的管理类BeanConfiguration。之后通过getBean()得到bean对象b1。

ApplicationContext context=new AnnotationConfigApplicationContext(BeanConfiguration.class);
Bean1 b1=context.getBean("myBean1",Bean1.class);

上面的方法为每一个Bean都要定义一个返回方法,这样也很繁琐。可以添加注解@ComponentScan来扫描包的方式将Bean自动添加到BeanConfiguration类中,这时它会自动扫描指定包目录下添加了@Component注解的Bean,并将其类名首字母小写作为默认bean的Id。例如下面BeanConfiguration会扫描com.spring.ioc.annotation,添加将其中的Bean1,并将bean1作为其id。也可以通过@Component的value属性为其添加自定义Id--”myBeanId“。类似@Component的注解还有@Controller用于标注控制层组件、@Service标注服务处、@Repository标注Dao层

//被管理的类
package com.spring.ioc.annotation;

import org.springframework.stereotype.Component;

@Component(value = "myBeanId")
public class Bean1 {
}

//Bean的管理类
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan(value = "com.spring.ioc.annotation")            //自动扫描包
public class BeanConfiguration {

}

属性注入

简单数据类型

简单的数据类型可以通过@Value()注解来进行属性注入,并且完成从string到对应类型的转换,结果为:

@Component
public class MyBean {
    @Value("101")
    private int simpleInt;

自定义类型的属性注入

对Bean的属性进行注入有三种方法,第一种是通过构造函数,如下所示,使用@Autowired注解修饰MyBean的构造方法,它会自动去BeanConfiguration管理的上下文中查找ParamBean并完成对属性paramBean的注入。

@Component
public class MyBean {
    private ParamBean paramBean;

    @Autowired
    public MyBean(ParamBean paramBean) {
        this.paramBean = paramBean;
    }
}

第二种方法是通过setXxx()方法完成对属性paramBean的注入

@Component
public class MyBean {
    private ParamBean paramBean;

    @Autowired
    public void setParamBean(ParamBean paramBean) {
        this.paramBean = paramBean;
    }
}

第三种方法是直接为属性添加@Autowired,Spring会自动查找该类型的Bean完成注入

@Component
public class MyBean {
    @Autowired
    private ParamBean paramBean;
}

复合类型的属性注入

复合类型的属性注入也是通过@Autowired修饰,不同的是需要定义对应的Bean来返回相关类型。例如下面定义了一个String类型的List属性,并在Spring上下文管理器中定义了一个Bean来返回stringList,Spring会将返回的List注入到stringList属性。我们还可以通过@Component注解创建一个Bean返回stringList,效果相同。

@Component
public class MyBean {
    private List<String> stringList;

    @Autowired
    public void setStringList(List<String> stringList) {
        this.stringList = stringList;
    }
}

@Configuration
@ComponentScan(value = "com.spring.ioc.annotation")
public class BeanConfiguration {

    @Bean
    public List<String> stringList(){
        List<String> list=new ArrayList<String>();
        list.add("string1");
        list.add("字符串2");
        return list;
    }
}

如果在上下文管理器中存在String类型的Bean,那么会被自动作为元素添加到String类型的List属性中,并且可以通过@Order注解来指定元素添加的顺序,顺序的数字不必连续,也不必从1开始。运行结果如下所示:

@Configuration
@ComponentScan(value = "com.spring.ioc.annotation")
public class BeanConfiguration {
    @Bean
    @Order(36)
    public String stringBean1() {
        return "string3";
    }

    @Bean
    @Order(21)
    public String stringBean2() {
        return "字符串4";
    }
}

如果同时存在stringList、string类型的Bean,则会优先将String类型的Bean作为元素添加到stringList属性。这时如果仍然希望使用stringList注入,则需要@Qualifier来指定需要注入的beanId

    @Autowired
    @Qualifier("stringList")
    public void setStringList(List<String> stringList) {
        this.stringList = stringList;
    }

同理可以使用@Autowired来修饰Map数据类型,并定义返回Map类型的Bean来完成注入。

作用域

通过注解的方式为Bean规定作用域只需要通过@Scope()即可,如下定义MyBean作用域为多例模式

@Component
@Scope("prototype")
public class MyBean {
    ......

可以通过CustomScopeConfigurer来添加自定义作用域,如下所示添加自定义作用域SimpleThreadScope

    @Bean
    public SimpleThreadScope MyScope() {
        return new SimpleThreadScope();
    }

    @Bean
    public CustomScopeConfigurer customScopeConfigurer() {
        CustomScopeConfigurer configurer = new CustomScopeConfigurer();
        configurer.addScope("simpleThread", MyScope());
        return configurer;
    }

初始化和销毁

通过注解的方式定义在Bean的初始化和销毁之前的执行函数有三种方法,第一种是实现InitializingBean、DisposalBean接口,这种方法和之前的XML配置一样。第二种方法是通过@PostConstruct、@PreDestroy注解来标注方法

@Component
public class MyBean {
    ......
    @PostConstruct
    public void onInit(){
        System.out.println("创建Bean...");
    }

    @PreDestroy
    public void onDestroy(){
        System.out.println("销毁Bean...");
    }

第三种方法是通过@Bean的属性initMethod和destroyMethod来指定方法

    @Bean(initMethod = "onInit",destroyMethod = "onDestroy")
    public MyBean myBean(){
        return new MyBean();
    }

在注解中可以通过@lazy来实现Bean的懒加载功能

发布了124 篇原创文章 · 获赞 65 · 访问量 13万+

猜你喜欢

转载自blog.csdn.net/theVicTory/article/details/105059153