小白新手web开发简单总结(六)-Spring的IoC容器

目录

一 前言

二 什么是IoC容器

三 实例

1.通过XML配置规则

2.通过注解

3.Spring中的其他几种注解

四 JavaBean生命周期的监听

五 总结


一 前言

接上篇小白新手web开发简单总结(四)-web应用开发中的MVC总结下IoC容器的相关内容。

IoC容器是Spring框架的一个核心功能。通过该容器来管理所有的JavaBean组件,提供组件的生命周期管理、配置和组装、AOP支持、以及建立在AOP基础上的声明事务服务等。

二 什么是IoC容器

IoC(Inversion of Control 控制反转)容器

通常Java组件通过new一个实例的方式实现组件之间的引用,并且组件中的两个实例都含有共享组件的时候,也不能共享该组件。如果系统中有大量的组件的时候,不仅要维护组件的生命周期,还要维护组件之间的依赖关系,大大增加了系统的复杂度,而且组件之间会相互耦合,这些控制流程完全有开发者自行控制。

而IoC容器就是将控制权进行反转,将控制权交给了IoC容器,由IoC容器负责组件的创建和配置,负责组件的生命周期管理,而开发者只需要定义装配规则,这样就将组件的创建配置和组件的使用进行分离。并且如果在一个组件的多个实例中含有共享组件的时候,是可以相互共享的,不需要多次注入。

在Spring的IoC容器中,所有的组件都被成为JavaBean组件,每个组件即一个Bean。

通常有两种方式来定义组件的配置规则:

  • (1)XML
  • (2)注解

三 实例

通过一个简单的实例来看下这个JavaBean组件是怎么定义配置规则。

1.通过XML配置规则

  • 1.在项目pom.xml中引入spring-context依赖
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
  • 2.定义项目中需要使用的JavaBean

(1)定义一个RegisterService:通过输入的手机号码和密码,来注册该用户,并将注册成功之后给用户发送注册成功的短信。

public class RegisterService {
    private MessageService messageService;

    public void setMessageService(MessageService service) {
        this.messageService = service;
    }

    public User register(String phone, String password) {
        User user = new User();
        user.phone = phone;
        user.password = password;
        //随机生成一个用户名
        user.name = "小刘";
        //需要将该信息写入数据库
        SqlDataBase sql = new SqlDataBase();
        sql.insert(user);
        //注册成功之后,给用户发送短息
        if (messageService == null) {
            System.err.println("还没有初始化MessageService");
            return user;
        }
        messageService.sendRegisterSuccessMessage(phone);
        return user;

    }

}

(2)定义MessageService: 向用户的手机发送短信(逻辑省略)

public class MessageService {
    /**
     * 发送短信
     *
     * @param phone
     */
    public void sendRegisterSuccessMessage(String phone) {
        String message = "您已经成功注册该网站账户";
        System.out.println(String.format("已经成功的向%s发送注册短信:%s", phone, message));
    }
}
  • 3.定义配置JavaBean的规则

(1)在resouce目录下添加application-context.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="registerService" class="com.wj.spring.RegisterService">
        <property name="messageService" ref="messageService"/>
    </bean>

    <bean id="messageService" class="com.wj.spring.MessageService">
    </bean>
</beans>

其中id为该bean的唯一识别 ,通过 <property name="messageService" ref="messageService"/>的方式在RegisterService中增加了MessageService的引用,其实上面的XML配置的内容就等价于下面的java代码:

    MessageService messageService = new MessageService();
    RegisterService registerService = new RegisterService();
    registerService.setMessageService(messageService);
  • 4.通过读取XML文件,获取到ApplicationContext,也就是IoC容器
public class RegisterApplication {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("config/application-context.xml");
        RegisterService service = context.getBean(RegisterService.class);
        //RegisterService service = (RegisterService)context.getBean("registerService");
        User user = service.register("12345678901", "123456");
        System.out.println("注册之后的用户名为:" + user.name);      
    }
}

当 ClassPathXmlApplicationContext要传入的是这个application-context.xml的相对路径。这样就可以context.getBean()的方式获取到定义的JavaBean。运行项目如下:

小白新手web开发简单总结(六)-Spring的IoC容器

相关的代码已经上传到github:https://github.com/wenjing-bonnie/build-spring.git:对应的com/wj/spring下的相关代码。

2.通过注解

使用XML文件的方式,可以把所有的JavaBean的都一一列举出来,并且通过配置注入能直观的了解到每个JavaBean的依赖关系,但是缺点也就显现出来,这种写法非常繁琐,每增加一个组件,就必须在XML将该组件添加进去。所以就有了注解这种方式,可以让IoC容器自动扫描所有的Bean并组装他们。

  • 1.同上面的1
  • 2.同样定义同上面的JavaBean,只不过在JavaBean增加@Component注解,来标示是一个JavaBean组件

(1)定义一个RegisterService

@Component
public class RegisterAnnotationService {
    //   @Autowired将指定类型的Bean直接注入到指定的字段
    @Autowired
    private MessageAnnotationService messageAnnotationService;

}

通过 @Autowired将指定类型的Bean直接注入到指定的字段。当然@Autowired也可以注解List<JavaBean>集合中,这样每增加一个同类型的JavaBean,都会被IoC容器自动装配到该集合中。如果要设置JavaBean被装配的顺序,则可通过在组件上增加@Order(1)、@Order(2)……

默认情况下,当给定字段标记了 @Autowired,如果IoC容器没有找到对应的JavaBean,则会抛出NoSuchBeanDefinitionException异常,所以可以增加@Autowired(required=false)来避免该异常。

(2)定义MessageService

@Component
public class MessageAnnotationService {

}

通过增加上面的两个注解的方式,就完成了JavaBean之间的配置规则。

默认的将JavaBean标记为@Component,那么IoC容器会自动创建一个单例,即容器初始化的时候创建JavaBean,容器销毁的时候销毁。在整个容器运行期间,getBean()获取的是一个实例。

其实可以通过@Scope来标记该实例的作用域:即@Scope(value = "singleton")或@Scope( "singleton"),该value对应的有四种作用域

singleton :唯一的bean实例,默认的为单例。即getBean()获取的是一个实例。

prototype:每次容器返回的都是一个新的实例。即getBean()获取的都是一个新的实例。

request:每次Http请求的时候都会产生一个新的实例,仅在当前request请求中有效。

session:每次Http请求的时候都会产生一个新的实例,仅在当前session内有效。

  • 3.通过读取注解来获得到IoC容器
@Configuration  //用来标明是一个配置类,在创建AnnotationConfigApplicationContext,需要传入该类
@ComponentScan //用来告诉容器,自动扫描当前类所在的包以及子包,把所有标注为@Component的Bean都自动创建出来,并根据@Autowired进行装配
public class RegisterAnnotationApplication {

    public static void main(String[] args) {
       ApplicationContext context = new  AnnotationConfigApplicationContext(RegisterAnnotationApplication.class);
        RegisterAnnotationService service = context.getBean(RegisterAnnotationService.class);
        //RegisterService service = (RegisterService)context.getBean("registerService");
        User user = service.register("12345678901", "123456");
        System.out.println("注册之后的用户名为:" + user.name);
    }
}

不同于通过XML文件的配置方式,在读取ApplicationContext的时候,需要将该类增加一个 @Configuration  用来标明是一个配置类(其实@Configuration可以用@Component来代替,也就是如果分不清这个组件具体什么作用,那就直接用@Component),在创建AnnotationConfigApplicationContext的时候,需要传入该类;增加@ComponentScan 用来告诉容器,自动扫描当前类所在的包以及子包,把所有标注为@Component的Bean都自动创建出来,并根据@Autowired进行装配。

运行项目结果和上面的一致。相关代码已经上传github:https://github.com/wenjing-bonnie/build-spring.git:对应的com/wj/springannotation下的相关代码。

3.Spring中的其他几种注解

1.JavaBean组件相关的注解

像之前大体有印象的一些@Controller/@RestController、@Service、@Respository等这些都是用来注解为JavaBean,只不过这是在后面的Spring MVC具有一些特殊功能的JavaBean,如果分不清该组件属于哪一种,就直接使用@Component来注解。

2.读取配置文件、资源文件等相关注解

  • (1)读取Resources下面的一些普通的文件

使用IoC容器,可以很方便的把这些文件引入进来,方便程序读取,省掉了很多繁琐的代码来获取InputStream。在Maven项目中,经常会将这些文件放置到Resources目录下,所以Spring框架就提供了org.springframework.core.io.Resource来可以直接读取到文件的InputStream

    @Value("readme.txt")
    private Resource readmeTxt;

那么在项目中就可以通过readmeTxt.getInputStream()获取到该txt的inputstream。

  •  (2)读取Resouces下的一些key/value结构的文件

经常有些配置文件是key/value形式的存在文件中,在使用上面的方式,显得不太方便,那么IoC容器还提供了更简便的方式来读取。通过添加@PropertySource注解的方式,就可以直接根据key来获取到对应的value。例如有一个配置文件config/application.properties的内容如下:

application.ipaddress = 192.168.110.118

我们只需要在需要读取该配置文件的组件上添加 @PropertySource("config/application.properties"),那么就可以通过key来得到对应的value值。

@Configuration
@PropertySource("config/application.properties")
public class ApplicationConfig {
    @Value("${application.ipaddress}")
    public String ipAddress;
}

 @Value("${application.ipaddress}"):为没有默认值的value,如果想给定默认值的话,则 @Value("${application.ipaddress:192.168.0.0}")。

  • (3)在其他组件中引用配置文件的相关内容

还是上面的例子,如果在其他地方也要引用config/application.properties里面的配置项,其实可以直接通过@Value("#{bean.property}")的方式来得到对应的配置项,bean就是ApplicationConfig的实例applicationConfig(该实例不需要引入,只是组件名的首字母小写)的ipAddress属性值引入到OtherAnnotation。

@Component
public class OtherAnnotation {

    @Value("#{applicationConfig.ipAddress}")
    public String ipAddress;
}

通过该注解方式,在OtherAnnotation中可以不用引入config/application.properties就可以读取到定义在ApplicationConfig里面的配置项。与(2)不同的是,在(2)中使用的是@Value("${key:defaultValue}")的形式读取配置文件中的值,而在OtherAnnotation中通过@Value("#{bean.property}")读取在ApplicationConfig中读到的配置项。

四 JavaBean生命周期的监听

当我们将一个JavaBean添加到IoC容器时,有的时候可能需要在容器初始化该JavaBean的时候监听消息等,在容器关闭的时候清理资源。那么就可以通过在该JavaBean只能够定义两个方法,在项目中引入javax.annotation依赖,通过javax.annotation的@PostContruct和@PreDestroy添加到上述的两个方法中。其过程如下:

(1)在pom.xml添加javax.annotation依赖

        <dependency>
            <groupId>javax.annotation</groupId>
            <artifactId>javax.annotation-api</artifactId>
            <version>1.3.2</version>
        </dependency>

(2)在MessageAnnotationService中需要承载初始化和销毁的方法上添加@PostContruct和@PreDestroy

public class MessageAnnotationService {

    @PostConstruct
    public void init() {
        System.out.println("MessageAnnotationService init");
    }

    @PreDestroy
    public void destroy() {
        System.out.println("MessageAnnotationService destroy");
    }
}

这样在IoC容器初始化和销毁的时候,就会看到对应的方法的调用 。

五 总结

1.Spring的核心功能就是IoC容器,提供组件的创建和装配,只需要定义配置规则,IoC容器会自动完成所有的过程;

2.可以通过XML文件和注解两种方式来定义组件之间的配置规则;

3.终于搞明白了项目中为什么有那么多bean的配置文件;

4.使用@Component来注解一个类为组件,那么IoC容器在启动的时候,就会自动初始化该组件;

5.可以用@Scope来指定组件实例的作用域,是单例还是每次都是一个新的实例;

6.一个ApplicationContext就是一个IoC容器,可以从ApplicationContext中通过getBean()来获取到对应的组件实例;

7.使用javax.annotation可以在组件初始化或者销毁的时候做一些事情;

8.可以使用Resource轻松得到resouces下的文件的inputStream;

9.可以通过 @PropertySource的方式轻松得到配置文件的key/value;

10.之前有点印象的@Controller/@RestController、@Service、@Respository等其实都是一些组件,只不过是一些拥有特殊功能的组件。

每天都在进步一点,加油!!! 

上一篇:JavaBean


下一篇:JavaWEB——JSP—JavaBean