第3章 全注解下的Spring IoC

全注解下的Spring IoC

1.什么是Bean?

在Spring中把每一个需要管理的对象称为Spring Bean(简称Bean)

2.什么是IoC容器?

Spring IoC容器是一个管理Bean的容器,它要求所有的IoC容器都需要实现接口BeanFactory。

下面是BeanFactory接口源码:

package org.springframework.beans.factory;

public interface BeanFactory {

    /**
     * 用来引用一个实例,或把它和工厂产生的Bean区分开,就是说,如果一个FactoryBean的名字为a,那么,&a会得到那个Factory
     */
    String FACTORY_BEAN_PREFIX = "&";

    /*
     * 四个不同形式的getBean方法,获取实例
     */
    Object getBean(String name) throws BeansException;

    <T> T getBean(String name, Class<T> requiredType) throws BeansException;

    <T> T getBean(Class<T> requiredType) throws BeansException;

    Object getBean(String name, Object... args) throws BeansException;

    boolean containsBean(String name); // 是否存在

    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;// 是否为单实例

    boolean isPrototype(String name) throws NoSuchBeanDefinitionException;// 是否为原型(多实例)

    boolean isTypeMatch(String name, Class<?> targetType)
            throws NoSuchBeanDefinitionException;// 名称、类型是否匹配

    Class<?> getType(String name) throws NoSuchBeanDefinitionException; // 获取类型

    String[] getAliases(String name);// 根据实例的名字获取实例的别名

}

默认情况下,Bean都是以单例形式存在,即使用getBean方法返回的都是同一对象。

3.如何从IoC容器中取出Bean

通过上面的源码可以看到,getBean方法有按类型(type)获取Bean的,也有按名称(name)获取Bean的,这对于后面介绍的Spring依赖注入很重要。

4.ApplicationContext和BeanFactory的关系

由于BeanFactory的功能不够强大,Spring在BeanFactory的基础上,设计了一个ApplicationContext接口,是BeanFactory的子接口,他们的关系如下(图片来源于网络):

 

在Spring Boot中,主要通过注解方式装配Bean到Spring IoC容器中(也支持XML).

5.装配Bean注入IoC容器(通过两个实例理解)

(1)@Bean

首先定义一个Java简单对象,文件User.java,代码如下:

package com.springboot.chapter3.pojo;

import java.util.LongSummaryStatistics;

/**
 * Created by lizeyang on 2019/5/7.
 */
public class User {
    private Long id;
    private String userName;
    private String note;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getNote() {
        return note;
    }

    public void setNote(String note) {
        this.note = note;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }
}

然后再定义一个Java配置文件AppConfig.java,代码如下:

package com.springboot.chapter3.config;

import com.springboot.chapter3.pojo.User;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Created by lizeyang on 2019/5/7.
 */
@Configuration
public class AppConfig {
    @Bean(name="user")
    public User initUser(){
        User user = new User();
        user.setId(1L);
        user.setUserName("user_name_1");
        user.setNote("note_1");
        return user;
    }
}

其中@Configuration代表这是一个Java配置文件,Spring容器会根据它来生成IoC容器装配Bean。@Bean代表将initUser方法返回的POJO返回到IoC容器中,属性name定义这个Bean的名称,若不配置,方法名即为Bean名称。

最后,使用AnnotationConfigApplicationContext构建IoC容器,代码如下:

package com.springboot.chapter3.config;

import com.springboot.chapter3.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * Created by lizeyang on 2019/5/7.
 */
public class IoCTest {
    public static void main(String[] args){
        ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
        User user = ctx.getBean(User.class);
        System.out.println(user.getId());
    }
}

运行IoCTest结果如下:

显然,配置在配置文件中名称为user的Bean已经装配到IoC容器中,并且可以通过getBean方法获取对应的Bean,并将Bean的属性输出出来。

(2)@ComponentScan

除了@Bean,还可以使用@Component和@ComponentScan,@Component是标明哪个类被扫描进入Spring IoC容器,@ComponentScan是标明采用何种策略扫描装配Bean。

首先,在包com.springboot.chapter3.config内新建UserCpoy.java文件,代码如下:

package com.springboot.chapter3.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
 * Created by lizeyang on 2019/5/7.
 */
@Component("UserCopy")
public class UserCopy {
    @Value("1")
    private Long id;

    @Value("user_name_1")
    private String userName;

    @Value("note_1")
    private String note;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getNote() {
        return note;
    }

    public void setNote(String note) {
        this.note = note;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }
}

修改配置文件为:AppConfigCopy,代码为:

package com.springboot.chapter3.config;

import com.springboot.chapter3.pojo.User;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

/**
 * Created by lizeyang on 2019/5/7.
 */
@Configuration
@ComponentScan
public class AppConfigCopy {
//    public User initUser(){
//        User user = new User();
//        user.setId(1L);
//        user.setUserName("user_name_1");
//        user.setNote("note_1");
//        return user;
//    }
}

运行IoCTest,结果与上面相同。

 

6.什么是依赖注入?

依赖注入就是Bean之间的依赖,最常用的是@Autoeired注解,它会根据属性的类型(type)找到对应的Bean进行注入(还记得BeanFactory里面getBean方法的两种方式吗?)

另外。可以通过@Primary修改多个不同类型的Bean的优先权,或者将@Autowired和@Qualifier结合使用,通过类型和名称一起找到Bean。

7.Spring IoC初始化和销毁Bean过程——Bean生命周期

分为Bean定义、Bean初始化、Bean生存期和Bean的销毁4个部分。

下图为Spring Bean的初始化流程:

下面是Spring Bean的生命周期:

8.属性文件使用——application.properties

在Spring Boot中,我们一般先在Maven配置文件中加载依赖,Spring Boot将创建读取属性文件的上下文。如:

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

有了依赖,就可以直接使用application.properties文件,通过其机制读取到上下文中,之后便可引用它。如:

spring.datasource.url=jdbc:mysql://localhost:3306/toutiao?useUnicode=true&characterEncoding=utf8&useSSL=false
spring.datasource.username=root
spring.datasource.password=nowcoder
mybatis.config-location=classpath:mybatis-config.xml
#logging.level.root=DEBUG

引用有两种方法,一种是Spring表达式,如通过@Value注解,使用${...}占位符读取配置在属性文件中的内容,如:

@Component
public class DataBaseProperties{
    @Value("${database.drivename}")
    private String driveName = null;
}

另一种方法就是使用@ConfigurationProperties注解,如:

@Component
@ConfigurationProperties("database")

public class DataBaseProperties{
    ......
}

9.单例(isSingleton)和原型(isPrototype)的区别

我们通过一段代码来进行比较两者区别:

@Component
//@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class ScopeBean{
    ...
}

//测试作用域
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
ScopeBean scopeBean1 = ctx.getBean(ScopeBean.class);
ScopeBean scopeBean2 = ctx.getBean(ScopeBean.class);
System.out.println(scopeBean1 == scopeBean2);

在第二行代码注释之后,测试结果为true,说明scopeBean1和scopeBean2变量都指向同一个实例,即为单例。

若将注释消去,则设置Bean的作用域为prototype,让IoC容器每次获取Bean时,都新建一个Bean的实例返回给调用者。

10.XML配置Bean

尽管Sping Boot建议使用注解和扫描配置Bean,但也不拒绝XML配置Bean.

通过一个例子来了解XML配置Bean:

先新建一个松鼠实现类,代码如下:

package com.springboot.other.pojo;

import com.springboot.chapter3.pojo.definition.Animal;


public class Squirrel implements Animal {

	@Override
	public void use() {
		System.out.println("松鼠可以采集松果");
	}

}

这个文件所在的包不在@ComponentScan定义的扫描包com.springboot.chapter3.*之内,也没有标注@Component,所以不会被扫描机制所装配,我们使用XML文件来装配,代码如下:

<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="squirrel" class="com.springboot.other.pojo.Squirrel"/>
</beans>

最后,装配XML定义的Bean,代码如下:

@Configuration
@ComponentScan(basePackages = "com.springboot.chapter3.*")
@ImportResource(value = {"classpath:spring-other.xml"})
public class AppConfig {
    ......
}

这样就可以引入对应的XML,进而将XML定义的Bean装配到IoC容器中。

11.使用Spring EL(了解即可)

Spring EL是一种表达式语言,通过Spring EL可以拥有更强大的运算规则来装配Bean,最常用的是读取属性文件的值,如:

@Value("#{3.14}")
private float pi;

@Value("#{1+2}")
private int run;

总结

Spring Boot关于IoC的部分基本就是这些,本节代码已上传github:

https://github.com/lizeyang18/SpringBoot-2.x/tree/master/Chapter3

学习永不止步,继续加油~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值