Java Spring开发记(一):IoC容器和Bean的装配
Spring Framework
一种开发Java应用的框架
IoC容器和Bean的装配
IoC (Inversion of Control)
当一个系统拥有大量的组件时,如果仅仅通过new来创建实例,则需要在不同的类中分别对所需的实例进行创建、实例化和维护。这导致当众多不同的组件具有相同的依赖时,需要创建大量相同的实例,使得出现实例化变得复杂、资源空间大量浪费、不易进行测试和维护和实例不易销毁等问题。
解决这类问题,IoC便是最佳方案。它负责实例化所有需要复用的组件,管理组件的生命周期,将组件的创建和配置与其使用相分离。
创建和装配Bean
引入依赖:
1 | |
1. 用XML装配Bean
假如现在有两个类A和B,类B需要调用类A以实现所需功能。首先,类B中需要实现一个setClassA()函数以将类A进行注入。示例如下:
1 | |
之后,需要编写一个配置文件application.xml,以阐明Bean的组装方式:
1 | |
前面的xml格式是固定格式。之后对于每一个需要装配的类,对其分别进行bean的添加。注意这个id是唯一的,用于其他bean对其的引用。对于要引用的类,使用property标签进行注入。注入的内容如果是Bean,则使用ref进行引用;如果只是一些基础类型的数据,可以使用value。下面是对于一个HikariCP连接池创建的例子:
1 | |
接下来,在需要获得引用的AppConfig类中,通过ApplicationContext创建Spring容器:
1 | |
再获取对Bean的引用:
1 | |
上述两种写法均可,但通过类型获取引用的方法1更为常用。
AppConfig类示例如下:
1 | |
2. 用Annotation配置装配Bean
通过注解的标记,可以实现自动扫描配置并进行初始化。
@Component注解就相当于定义了一个Bean,并对其默认生成了一个类名首字母小写的id:
1 | |
根据不同类的不同层次,还可将@Component细分为@Repository、@Service和@Controller三类(它们的作用均与@Component相同),使得工程的层次更清晰明了。
通过@Autowired注解将内部引用的类自动装配成property:
1 | |
注解也可以写在构造函数中,不过通常标于字段之前。
如果我们要注入一个特定的内容,可以使用@Value注解。例如:
1 | |
接下来,对于application.xml文件,我们就可以在beans标签内这么写,以实现自动扫描注解和装配Bean:
1 | |
或者,如果我们连application.xml也不想创建,这时也可以对AppConfig类增加@ComponentScan注解,并在创建Spring容器时使用AnnotationConfigApplicationContext函数,示例如下:
1 | |
注入List
假设我们现在有一个接口,但是在这个接口上进行了一系列不同类的实现。现在需要把这些类都注入Beans,有什么简便的办法呢?
对每一个类都添加一个@Component注解,再在Config中进行如下引用(我们假设Class是一个接口,分别实现了ClassA、ClassB和ClassC三个类):
1 | |
Spring会自动把这三个类装配到这个List中。由于List是有序的,若需指定顺序,可加上@Order注解:
1 | |
创建第三方Bean
如果想要为一个第三方库中存在的类创建Bean,我们不能直接修改对应的库函数,则可以通过编写一个函数方法来实现它,并在实现的函数前添加@Bean注解。
此时,AppConfig类中包含了@Bean注解,则需标记该类是Spring配置类,要在类前再行添加一个@Configuration注解。
例如,注入一个HikariDataSource:
1 | |
指定别名
如果存在多个返回为同类型的Bean,Spring不知道对哪个Bean创建实例(一般情况下相同类型的Bean只能创建一个实例)。这时,我们需要对每一个Bean取别名。
取别名的方法有两种。
一种是在后面直接跟别名:
1 | |
另一种是再添加一个@Qualifier注解:
1 | |
条件装配
Spring提供了一个方式,可以让我们在指定条件或环境下才对某个Bean进行创建。
-
使用Profile
Profile用来表示不同的环境,可以在启动应用程序时通过所传参数(-Dspring.profiles.active=...)进行更改。例如,指定以test环境启动,如果希望在该环境下装配某个Bean,可以这么写:1
@Profile("test")如果希望不要在该环境下装配某个Bean,可以这么写:
1
@Profile("!test")如果希望同时满足
native和test两个条件,则可以这么写:1
@Profile({ "native","test" }) -
使用Conditional
@Conditional注解用于满足某个逻辑条件才进行装配。由于需要额外实现一个判断类较为繁琐,因此实际应用较少。Spring Boot提供了更多的条件装配注解,例如
@ConditionalOnClass和@ConditionalOnProperty等。
可选注入
对于部分引用,有时可能没有符合条件的Bean可供注入,这时Spring会抛出Exception。如果对于一个引用,它的注入不是必须的,这时可以添加一个required=false参数作为标记。
1 | |
注入外部资源
有时我们需要引入固定的外部资源,然而使用InputStream查找并读取文件又显得过于繁琐。这时,Spring为我们提供了一个简洁的方式,通过Resource类将文件“注入”进来。
我们使用@Value进行注入。例如,我们需要读取Classpath下的app.properties文件,可以这样写:
1 | |
此时,Spring会在Classpath下自动搜索该文件。随后,通过调用Resource.getInputStream()函数即可获得输入流,避免了繁琐的手动查找过程。
也可以指定文件的绝对位置并进行读取(并不常用):
1 | |
注入配置
对于一个配置文件,我们可能需要读取它并将其中的内容赋值给对应Beans内的变量。如果通过注入Resource再打开输入流读取的方式仍然显得过于繁琐,于是@PropertySource注解应运而生。
在AppConfig类前,添加如下注解以读取Classpath中app.properties文件的配置:
1 | |
注意此时也需要向Spring声明此类为配置类,因此还需要加上@Configuration注解。
对于AppConfig类或其他Beans,若需要使用当前配置文件的内容初始化变量,仍然使用@Value注解注入。例如,我需要注入app.name变量可以使用如下写法:
1 | |
配置文件app.properties可以这么写:
1 | |
如果配置文件中app.name键值不存在,此时Spring将抛出异常。为此我们可以指定一个默认值,假设app.name的默认值为example:
1 | |
我们也可以注入其他Bean的属性值,例如现在有一个Config类,其中有一个title变量。我们将Config类实例化为Bean,在其他类中我们要将这个值设定到一个其他的变量里,可以使用如下方式(注意实例化Bean后的id中首字母变成了小写):
1 | |