Spring注解版–spring给容器注册组件的四种方法:
这是我看网上视频教程之后,自己整理的,加强记忆。
文章目录
- Spring注解版--spring给容器注册组件的四种方法:
- 一. `@Configuration`&`@Bean`-自动扫描组件、扫描规则组件
- 二. `@Bean`[导入的第三方包里面的组件]
- 三. `@Import`[快速给容器中导入一个组件]
- 四. 使用Spring提供的`FactoryBean`工厂(bean)
一. @Configuration
&@Bean
-自动扫描组件、扫描规则组件
1、创建一个webquick项目,内部添加pom依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.2.RELEASE</version>
</dependency>
给maven工程添加spring-context
的依赖。
2、比较xml注册bean与注解注册bean
(1)xml配置注册bean
IOC在项目中添加一个resources文件夹,内部存放所有的配置文件以及资源。现在我们在内部创建一个bean.xml这个IOC容器的xml配置文件。
bean.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 http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--<bean></bean>-->
<bean id="user" class="com.xiaojian.bean.User">
<property name="username" value="xiaojian"></property>
<property name="userage" value="11"></property>
</bean>
</beans>
如图,我们给容器添加了一个bean,指向我们的一个User的实体类。此处的User的实体类,我们存放在java目录下的bean文件夹中。
User实体类如下:
package com.xiaojian.bean;
public class User {
private String username;
private Integer userage;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Integer getUserage() {
return userage;
}
public void setUserage(Integer userage) {
this.userage = userage;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", userage='" + userage + '\'' +
'}';
}
public User(String username, Integer userage) {
this.username = username;
this.userage = userage;
}
public User() {
}
}
我们在test目录下面创建MainTest01测试类,测试获取IOC容器中的bean。
测试类MainTest01:
package com.xiaojian.test;
import com.xiaojian.bean.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainTest01 {
public static void main(String[] args) {
ApplicationContext ApplicationContext = new ClassPathXmlApplicationContext("bean.xml");
User user = (User) ApplicationContext.getBean("user");
System.out.println(user);
}
}
测试结果:
(2)注解注册bean
在config目录下创建MainConfig类,此类为创建bean的配置类。
package com.xiaojian.config;
import com.xiaojian.bean.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
//配置类==配置文件
@Configuration //告诉spring这是一个配置类
public class MainConfig {
//给容器注册一个bean;类型为返回值的类型;id默认是用方法名作为id(不过我们也可以自己指定id,就是value值)
@Bean(value = "user01")
public User user01(){
return new User("小贱",13);
}
}
此class类文件中,@Configuration
注解告诉spring这是一个配置类,配置类的作用就是xml配置文件的作用。
@Bean
注解是给容器注册一个bean,类型为返回值的类型,id默认是用方法名作为id,不过我们可以自己定义id,也就是@Bean
注解下value
属性值。
@Bean
注解接口(系统内部的注解,我们直接使用):
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
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.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 {};
/** @deprecated */
@Deprecated
Autowire autowire() default Autowire.NO;
boolean autowireCandidate() default true;
String initMethod() default "";
String destroyMethod() default "(inferred)";
}
此注解接口中定义了value
这个属性,就是bean的id。当使用value
这个属性的时候,所有通过id想要获取此bean,必须通过value
定义的id,而不是方法名。
我们在test目录下再定义一个MainTest02这个类文件,测试注解方式注册bean。
测试类MainTest02:
package com.xiaojian.test;
import com.xiaojian.bean.User;
import com.xiaojian.config.MainConfig;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MainTest02 {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
User user = (User) applicationContext.getBean("user01");
System.out.println(user);
//此处的方法是通过类型来获取所有的bean的集合。
Map<String, User> beansOfType = applicationContext.getBeansOfType(User.class);
System.out.println(beansOfType);
//此处的方法是通过类型来获取Bean的名称,此处是通过User这个类,来获取Bean的名称。
String[] beanNamesForType = applicationContext.getBeanNamesForType(User.class);
for (String name : beanNamesForType) {
System.out.println(name);
}
//applicationContext下面还有好多的方法,可以有时间可以自己看看。
}
}
运行结果:
3.@ComponentScan
包扫描
(1)创建相关的类
首先在xiaojian这个文件夹下面分别创建controller、service、dao这三个文件夹,里面在分别创建对应的BookController、BookService、BookDao这三个类文件,同时添加对应的注解。
BookController:
package com.xiaojian.controller;
import org.springframework.stereotype.Controller;
@Controller //此注解说明这是一个控制器
public class BookController {
}
BookService:
package com.xiaojian.service;
import org.springframework.stereotype.Service;
@Service //此注解作用是给容器扫描到,说明此类事服务层
public class BookService {
}
BookDao:
package com.xiaojian.dao;
import org.springframework.stereotype.Repository;
@Repository //此注解是为了给容器扫描到,说明此类是dao层。
public class BookDao {
}
(2)xml配置中的包扫描方式
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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 包扫描,只要标注了@Controller、@Service、@Repository、@Component中的任何一个,都会被扫描-->
<context:component-scan base-package="com.xiaojian"></context:component-scan>
<!--<bean></bean>-->
<bean id="user" class="com.xiaojian.bean.User">
<property name="username" value="xiaojian"></property>
<property name="userage" value="11"></property>
</bean>
</beans>
xml文件中通过<context:component-scan base-package="" />
来扫描配置的bean。
(3)注解实现包扫描方式
我们只需要在@Configuration
主配置类中添加@ComponentScan( value="com.xiaojian")
就可以自动扫描xiaojian这个包下面的所有的添加了@Controller
、@Service
、@Repository
、@Component
。
MainConfig主配置类:
package com.xiaojian.config;
import com.xiaojian.bean.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
//配置类==配置文件
@Configuration //告诉spring这是一个配置类
@ComponentScan(value = "com.xiaojian")
//包扫描路径,与在xml文件中配置 <context:component-scan base-package="com.xiaojian"></context:component-scan> 作用一样。
public class MainConfig {
//给容器注册一个bean;类型为返回值的类型;id默认是用方法名作为id(不过我们也可以自己指定id,就是value值)
@Bean(value = "user01")
public User user01(){
return new User("小贱",13);
}
}
小的测试
在test目录下添加一个测试类——MainTest03。
MainTest03:
package com.xiaojian.test;
import com.xiaojian.config.MainConfig;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MainTest03 {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
//此方法可以获取所有的bean定义的名字
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String name:beanDefinitionNames){
System.out.println(name);
}
}
}
测试结果:
(4)@ComponentScan
属性值
-
value
值:value
值是说明此扫描的扫描路径,系统只会扫描路径下的所有的添加了@Controller
、@Service
、@Repository
、@Component
注解的类。 -
excludeFilters
属性:产生过滤规则,此指定扫描的时候按照什么规则排除哪些组件。此写法是一个Filter数组。
MainConfig:
package com.xiaojian.config; import com.xiaojian.bean.User; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.FilterType; import org.springframework.stereotype.Controller; import org.springframework.stereotype.Service; //配置类==配置文件 @Configuration //告诉spring这是一个配置类 @ComponentScan(value = "com.xiaojian",excludeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class, Service.class}) }) //此处的type中的 FilterType.ANNOTATION表明排除注解方式的配置bean,classes表明排除的配置bean添加的注解的类型(此处主要排除@Controller、@Service两个注解的配置类) //包扫描路径,与在xml文件中配置 <context:component-scan base-package="com.xiaojian"></context:component-scan> 作用一样。 public class MainConfig { //给容器注册一个bean;类型为返回值的类型;id默认是用方法名作为id(不过我们也可以自己指定id,就是value值) @Bean(value = "user01") public User user01(){ return new User("小贱",13); } }
运行MainTest03测试类,测试结果:
系统的打印日志中少了BookController、BookService这两个。
-
includeFilters
属性:产生过滤规则,此指定扫描的时候按照什么规则只扫描哪些组件。此写法是一个Filter数组。
(与上面相反)
不过此处需要注意,因为在
@ComponentScan
中有个属性useDefaultFilters
,默认为true,而我们使用includeFilters
属性的时候,需要将useDefaultFilters
设置为false。MainConfig:
package com.xiaojian.config; import com.xiaojian.bean.User; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.FilterType; import org.springframework.stereotype.Controller; import org.springframework.stereotype.Service; //配置类==配置文件 @Configuration //告诉spring这是一个配置类 @ComponentScan(value = "com.xiaojian",includeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class}) },useDefaultFilters = false) //必须把useDefaultFilters设置为false才能使用includeFilters //包扫描路径,与在xml文件中配置 <context:component-scan base-package="com.xiaojian"></context:component-scan> 作用一样。 public class MainConfig { //给容器注册一个bean;类型为返回值的类型;id默认是用方法名作为id(不过我们也可以自己指定id,就是value值) @Bean(value = "user01") public User user01(){ return new User("小贱",13); } }
运行MainTest03测试类,测试结果:
此处只有一个bookController,因为includeFilters中只有一个Controller通行。
@ComponentScan
是一个可重复写的注解,我们可以在一个类上多次添加此注解。或者我们可以使用
@ComponentScans
注解,内部添加多个@ComponentScan
(5)@Filter注解type属性指定扫描规则
FilterType.ANNOTATION
:按照注解的方式(最常用)FilterType.ASPECTJ
:使用ASPECTJ
表达式(几乎不常用)FilterType.REGEX
:使用正则表达式FilterType.ASSIGNABLE_TYPE
:按照给定的类型
@ComponentScan(value = "com.xiaojian",includeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class}),
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,classes = {BookService.class})
},useDefaultFilters = false)
在MainConfig类文件的@ComponentScan
的includeFilters
属性中添加一个FilterType.ASSIGNABLE_TYPE的Filter,此Filter通过BookService这个指定的类文件。
运行MainTest03测试类,测试结果:
FilterType.CUSTOM
:使用自定义规则
使用自定义规则的时候,这个值必须是一个TypeFilter规则的实现类。
首先我们在xiaojian这个文件夹下创建一个impls文件夹,在此文件夹下创建一个MyTypeFilter类去实现TypeFilter这个接口,接口中有一个返回boolean类型值的方法。
package com.xiaojian.impls;
import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;
import java.io.IOException;
public class MyTypeFilter implements TypeFilter{
/**
*
*
* @param metadataReader 读取到的当前正在扫描的类的星系
* @param metadataReaderFactory 可以获取到其他任何类的信息
* @return
* @throws IOException
*/
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
//获取当前类注释的信息
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
//获取当前正在扫描的类的类信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();
//获取当前类资源(类的路径)
Resource resource = metadataReader.getResource();
//获取类名
String className = classMetadata.getClassName();
//获取父类名
String superClassName = classMetadata.getSuperClassName();
//获取子类名
String[] memberClassNames = classMetadata.getMemberClassNames();
//获取接口名
String[] interfaceNames = classMetadata.getInterfaceNames();
//上面那些方法我们有时间可以自己去看看。
System.out.println("---->"+className);
//判断逻辑,为true,则放行,为false则拦截。
if (className.contains("vice")){
return true;
}
return false;
}
}
MainConfig类文件也做出相应的改变
package com.xiaojian.config;
import com.xiaojian.bean.User;
import com.xiaojian.impls.MyTypeFilter;
import com.xiaojian.service.BookService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;
//配置类==配置文件
@Configuration //告诉spring这是一个配置类
@ComponentScan(value = "com.xiaojian",includeFilters = {
@ComponentScan.Filter(type = FilterType.CUSTOM,classes = {MyTypeFilter.class})
},useDefaultFilters = false)
//FilterType.CUSTOM是使用自定义规则,其实现的是MyTypeFilter这个类中放行的内容。
//包扫描路径,与在xml文件中配置 <context:component-scan base-package="com.xiaojian"></context:component-scan> 作用一样。
public class MainConfig {
//给容器注册一个bean;类型为返回值的类型;id默认是用方法名作为id(不过我们也可以自己指定id,就是value值)
@Bean(value = "user01")
public User user01(){
return new User("小贱",13);
}
}
运行MainTest03测试类,测试结果:
如图,只有一个bookService被放行,注册到了IOC的容器中,因为判断逻辑只给包含"vice"的放行。而其他的两个,是不在这个过滤器管辖范围内的。
4.@Scope指定作用范围
(1)默认情况(单实例)
- 创建MainConfig2
我们重新创建一个MainConfig类,类名为MainConfig2,同时标注注解@Configuration
。
MainConfig2:
package com.xiaojian.config;
import com.xiaojian.bean.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MainConfig2 {
//默认的都是单实例的
@Bean
public User user(){
return new User("小贱",333);
}
}
- 创建测试类
在test文件夹下面创建一个MainTest04这个测试类。
MainTest04:
package com.xiaojian.test;
import com.xiaojian.bean.User;
import com.xiaojian.config.MainConfig2;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MainTest04 {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
//此方法可以获取所有的bean定义的名字
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String name:beanDefinitionNames){
System.out.println(name);
}
User user = (User) applicationContext.getBean("user");
User user1 = (User) applicationContext.getBean("user");
//比较两者是否为同一个对象,观察所有的bean的创建的默认情况下是单例还是多例。
System.out.println(user==user1);
}
}
运行MainTest04测试类,测试结果:
说明所有的bean默认情况下都是单例模式,就是所有创建的bean都是同一个。
(2)多实例案例
- 修改MainConfig2.java类文件,使得User这个bean为多实例bean。
MainConfig2:
package com.xiaojian.config;
import com.xiaojian.bean.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
@Configuration
public class MainConfig2 {
/**
* @Scope的作用范围:
* singleton:单实例(系统默认情况下都是单实例的)
* prototype:多实例
* request:同一次请求创建一个实例
* session:同一个session创建一个实例
*
* 注意:
* - 系统默认的是单实例,IOC容器启动会调用方法创建对象放到IOC容器中,以便以后每次获取,就可以直接从容器中获取
* - 多实例:IOC容器获取的时候才会创建,且获取几次容器会调用方法创建对象几次。IOC容器启动的时候并不会调用该方法创建对象到IOC容器中。
*/
//默认的都是单实例的
@Scope("prototype")
@Bean
public User user(){
return new User("小贱",333);
}
}
- 测试结果
运行MainTest04测试类,测试结果:
所以当bean被定义为多实例的时候,每次调用都会创建一个bean对象,并且这些bean对象不是同一个。
(3)单实例与多实例的创建时间对比
- 单实例情况下:
修改MainConfig2.java文件:
MainConfig2:
package com.xiaojian.config;
import com.xiaojian.bean.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
@Configuration
public class MainConfig2 {
/**
* @Scope的作用范围:
* singleton:单实例(系统默认情况下都是单实例的)
* prototype:多实例
* request:同一次请求创建一个实例
* session:同一个session创建一个实例
*
* 注意:
* - 系统默认的是单实例,IOC容器启动会调用方法创建对象放到IOC容器中,以便以后每次获取,就可以直接从容器中获取
* - 多实例:IOC容器获取的时候才会创建,且获取几次容器会调用方法创建对象几次。IOC容器启动的时候并不会调用该方法创建对象到IOC容器中。
*/
//默认的都是单实例的
// @Scope("prototype")
@Bean
public User user(){
System.out.println("给容器中添加User。。。");
return new User("小贱",333);
}
}
将User这个bean还原为单实例对象。
修改测试类MainTest04.java:
MainTest04:
package com.xiaojian.test;
import com.xiaojian.bean.User;
import com.xiaojian.config.MainConfig2;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MainTest04 {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
System.out.println("IOC容器启动完毕");
}
}
此时系统只是简单的运行了,创建了IOC容器,但是容器中并没有做任何的操作。
运行MainTest04测试类,测试结果:
如图,在系统启动IOC容器的时候,系统已经给我们创建了user这个bean,也就是说,系统默认的是单实例,IOC容器启动会调用方法创建对象放到IOC容器中,以便以后每次获取,就可以直接从容器中获取。
- 多实例情况下:
修改MainConfig2.java文件:
MainConfig2:
package com.xiaojian.config;
import com.xiaojian.bean.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
@Configuration
public class MainConfig2 {
/**
* @Scope的作用范围:
* singleton:单实例(系统默认情况下都是单实例的)
* prototype:多实例
* request:同一次请求创建一个实例
* session:同一个session创建一个实例
*
* 注意:
* - 系统默认的是单实例,IOC容器启动会调用方法创建对象放到IOC容器中,以便以后每次获取,就可以直接从容器中获取
* - 多实例:IOC容器获取的时候才会创建,且获取几次容器会调用方法创建对象几次。IOC容器启动的时候并不会调用该方法创建对象到IOC容器中。
*/
//默认的都是单实例的
@Scope("prototype")
@Bean
public User user(){
System.out.println("给容器中添加User。。。");
return new User("小贱",333);
}
}
将User这个bean设置为多实例对象。
修改测试类MainTest04.java:
MainTest04:
package com.xiaojian.test;
import com.xiaojian.bean.User;
import com.xiaojian.config.MainConfig2;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MainTest04 {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
System.out.println("IOC容器启动完毕");
User user = (User) applicationContext.getBean("user");
User user1 = (User) applicationContext.getBean("user");
System.out.println(user==user1);
}
}
运行MainTest04测试类,测试结果:
如图,在系统启动IOC容器的时候,系统并没有给我们创建User这个bean,而是我们去调用这个bean的时候,系统会给我创建这个bean。也就是说,多实例的情况下,IOC容器获取的时候才会创建,且获取几次容器会调用方法创建对象几次。IOC容器启动的时候并不会调用方法创建对象到IOC容器中。
(4)@Scope
指定作用范围
@Scope
的作用范围:
- singleton:单实例(系统默认情况下都是单实例的)
- prototype:多实例
- request:同一次请求创建一个实例
- session:同一个session创建一个实例
上面的四个作用范围,我们实际上用的比较多的是singleton以及prototype这两种。
注意:
- 系统默认的是单实例,IOC容器启动会调用方法创建对象放到IOC容器中,以便以后每次获取,就可以直接从容器中获取
- 多实例:IOC容器获取的时候才会创建,且获取几次容器会调用方法创建对象几次。IOC容器启动的时候并不会调用方法创建对象到IOC容器中。
5.@Lazy
懒加载
(1)懒加载含义
单实例bean的情况下,系统会在IOC容器启动的时候创建单实例的bean,而懒加载的含义是让容器在启动的时候,不创建对象,而是等到第一次调用的时候在创建。
(2)案例
- 修改MainConfig2.java文件:
MainConfig2:
package com.xiaojian.config;
import com.xiaojian.bean.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
@Configuration
public class MainConfig2 {
/**
* @Scope的作用范围:
* singleton:单实例(系统默认情况下都是单实例的)
* prototype:多实例
* request:同一次请求创建一个实例
* session:同一个session创建一个实例
*
* 注意:
* - 系统默认的是单实例,IOC容器启动会调用方法创建对象放到IOC容器中,以便以后每次获取,就可以直接从容器中获取
* - 多实例:IOC容器获取的时候才会创建,且获取几次容器会调用方法创建对象几次。IOC容器启动的时候并不会调用该方法创建对象到IOC容器中。
*/
//默认的都是单实例的
// @Scope("prototype")
@Lazy
@Bean
public User user(){
System.out.println("给容器中添加User。。。");
return new User("小贱",333);
}
}
我们在User这个bean上面添加@Lazy
这个注解,标明这个User的bean是懒加载,告诉容器其在本身启动的时候不先创建此单实例的bean,等到调用的时候再创建。
- 修改测试类
修改MainTest04.java测试类:
MainTest04:
package com.xiaojian.test;
import com.xiaojian.bean.User;
import com.xiaojian.config.MainConfig2;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MainTest04 {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
System.out.println("IOC容器启动完毕");
User user = (User) applicationContext.getBean("user");
}
}
我们在系统启动IOC容器完成后再调用这个User的bean,看看这个单实例的bean在什么时候被创建的。
- 测试运行
运行MainTest04测试类,测试结果:
如图,加上@Lazy
注解的User这个bean,在容器IOC启动的时候并没有被创建,而是在IOC调用这个bean的时候才被IOC容器创建。且以后再调用这个bean的时候,不会再创建这个bean。
6.@Conditional条件注解
@Conditional
:按照一定的条件进行判断,满足条件给容器中注册bean
(1)创建类
- 创建主题类
我们在config文件夹下面创建MainConfig3.java类文件,以作新的案例。
MainConfig3:
package com.xiaojian.config;
import com.xiaojian.bean.User;
import com.xiaojian.impls.LinuxCondition;
import com.xiaojian.impls.WindowsCondition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MainConfig3 {
/**
* @Conditional:按照一定的条件进行判断,满足条件给容器中注册bean
*
* @Conditional注解:
* 可以标注在类上,也可以标注在方法上
*
* 要求:
* 如果系统是windows,给容器中注册xiaojian
* 如果系统是linux,给容器中注册dashu
*/
@Conditional({WindowsCondition.class})
@Bean("xiaojian")
public User user01(){
return new User("xiaojian",22);
}
@Conditional({LinuxCondition.class})
@Bean("dashu")
public User user02(){
return new User("dashu",23);
}
}
- 创建WindowsCondition.java
我们在impls这个文件包下面创建WindowsCondition.java这个类文件。
WindowsCondition:
package com.xiaojian.impls;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;
//判断系统是否为windows
public class WindowsCondition implements Condition{
/**
*
* @param conditionContext 判断条件能使用的上下文(环境)
* @param annotatedTypeMetadata 注释信息
* @return
*/
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
//1.能获取到IOC使用的beanfactory
ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();
//2.获取类加载器
ClassLoader classLoader = conditionContext.getClassLoader();
//3.获取当前环境信息
Environment environment = conditionContext.getEnvironment();
//4.获取到bean定义的注册类
BeanDefinitionRegistry registry = conditionContext.getRegistry();
//我们可以通过registry这个对象,判断容器中对的bean的注册情况,同时也可以更新一些信息
//判断操作系统
String property = environment.getProperty("os.name");
boolean windows = property.contains("Windows");
if (windows){
System.out.println("此电脑是windows系统");
return true;
}
return false;
}
}
- 创建LinuxCondition.java
我们在impls这个文件包下面创建LinuxCondition.java这个类文件。
LinuxCondition:
package com.xiaojian.impls;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;
//判断系统是否为linux
public class LinuxCondition implements Condition{
/**
*
* @param conditionContext 判断条件能使用的上下文(环境)
* @param annotatedTypeMetadata 注释信息
* @return
*/
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
//1.能获取到IOC使用的beanfactory
ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();
//2.获取类加载器
ClassLoader classLoader = conditionContext.getClassLoader();
//3.获取当前环境信息
Environment environment = conditionContext.getEnvironment();
//4.获取到bean定义的注册类
BeanDefinitionRegistry registry = conditionContext.getRegistry();
//我们可以通过registry这个对象,判断容器中对的bean的注册情况,同时也可以更新一些信息
//判断操作系统
String property = environment.getProperty("os.name");
boolean linux = property.contains("Linux");
if (linux){
System.out.println("此电脑是linux系统");
return true;
}
return false;
}
}
- 测试类
我们在test的文件夹下创建MainTest05。
MainTest05:
package com.xiaojian.test;
import com.xiaojian.bean.User;
import com.xiaojian.config.MainConfig3;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.env.Environment;
import java.util.Map;
public class MainTest05 {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig3.class);
System.out.println("IOC容器启动完成");
String[] beanNamesForType = applicationContext.getBeanNamesForType(User.class);
for (String name:beanNamesForType){
System.out.println(name);
}
//获取User类型的所有的bean
Map<String, User> beansOfType = applicationContext.getBeansOfType(User.class);
System.out.println(beansOfType);
}
}
(2)测试结果
运行测试类MainTest05.java类文件中的主函数。
测试结果:
如图所示,在IOC容器启动过程中,系统会默认的去创建单实例的bean,而我们两个bean上面都标注了条件注解,所以在创建bean的时候,它们都会先去启动条件判断执行的类,所以第一条是“这是windows系统”,之后才是“IOC容器启动完成”,而后我们可以看出系统打印出现在系统中已经存在的bean的名称,因为此系统是windows系统,所以只有"xiaojian"这个bean被注册。
二. @Bean
[导入的第三方包里面的组件]
此部分省略。
三. @Import
[快速给容器中导入一个组件]
1.导入单个组件
(1)创建Color.java类文件
我们在bean目录下创建一个Color.java类文件。
Color:
package com.xiaojian.bean;
public class Color {
}
此类文件将作为本次的bean对象。
(2)创建MainConfig4.java类文件
我们创建MainConfig4.java类文件,此文件作为此案例的配置类。
MainConfig4:
package com.xiaojian.config;
import com.xiaojian.bean.Color;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@Import(Color.class) //@Import导入组件,id默认是组件的全类名
public class MainConfig4 {
}
此处我们使用@Import
注解来给IOC容器注册bean,组件的id默认是组件的全类名。
(3)创建测试类
我们在test包下面创建一个MainTest06.java测试类。
MainTest06:
package com.xiaojian.test;
import com.xiaojian.config.MainConfig4;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MainTest06 {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig4.class);
//获取系统中的所有定义的bean名称
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String name:beanDefinitionNames){
System.out.println(name);
}
}
}
(4)测试运行
运行MainTest06.java测试类,测试结果如下:
如图,系统中已经注册了Color这个bean,只不过其id是Color.java这个类文件的全路径。
2.导入多个组件
(1)创建Animal.java类文件
我们在bean目录下创建一个Animal.java类文件,内部为空,只是简单的作为一个bean创建对象。
package com.xiaojian.bean;
public class Animal {
}
(2)修改MainConfig4.java类文件
导入多个组件,其实就是修改@Import
的导入的值,而这个注解本身是可以导入一个数组的,所以我们将上面的那个之后改成一个数组就行。
MainConfig4:
package com.xiaojian.config;
import com.xiaojian.bean.Animal;
import com.xiaojian.bean.Color;
import com.xiaojian.bean.User;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@Import({Color.class, Animal.class, User.class}) //@Import导入组件,id默认是组件的全类名
public class MainConfig4 {
}
(3)测试运行
运行MainTest06.java测试类,测试结果如下:
如图,配置类中导入了三个组件,此处显示三个组件的全类名。
3.ImportSelector
接口实现(第二种导入方法)
此方法是导入一个类,该类去实现ImportSelector
接口,这个接口内部有一个selectImports
方法需要方法重载,其会返回一个带有系统需要导入的组件的全类名的数组,告诉IOC容器去创建哪些bean对象。
(1)修改MainConfig4.java类文件
MainConfig4:
package com.xiaojian.config;
import com.xiaojian.bean.Animal;
import com.xiaojian.bean.Color;
import com.xiaojian.bean.User;
import com.xiaojian.impls.MyImportSelector;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
//@Import({Color.class, Animal.class, User.class}) //@Import导入组件,id默认是组件的全类名
@Import({MyImportSelector.class})
//导入一个自定义的组件选择控制类,其实现了ImportSelector这个接口,接口返回一个需要导入的组件的全类名的数组
public class MainConfig4 {
}
(2)创建MyImportSelector.java类文件
我们在impls这个包下面创建一个MyImportSelector.java类文件,去实现ImportSelector
这个接口。
MyImportSelector:
package com.xiaojian.impls;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
//自定义逻辑,返回需要导入的组件。
public class MyImportSelector implements ImportSelector {
//返回值就是要导入到容器中的组件的全类名
//AnnotationMetadata:当前标注@Import注解的类的所有注解信息
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
//方法不能返回一个null,否则会出现空指针异常。
return new String[]{"com.xiaojian.bean.Color","com.xiaojian.bean.User"};
}
}
此处的selectImports
方法返回一个数组,其内部包含Color、User这两个实例对象,目的就是告诉IOC容器在启动的时候去创建这两个bean对象。
(3)测试运行
运行MainTest06.java测试类,测试结果如下:
如图,控制台打印出了Color以及User这两个实体类的bean对象。
4.ImportBeanDefinitionRegistrar
接口实现(第三种导入方法)
此方法是导入一个类,该类去实现ImportBeanDefinitionRegistrar
接口,这个接口内部有一个registerBeanDefinitions
方法需要方法重载,我们可以在这个方法的内部注册我们所需要的bean,同时我们也可以在这个方法中做一些其他的事情。
(1)创建MyImportBeanDefinitionRegistrar.java类文件
我们在impls这个包下面创建一个MyImportBeanDefinitionRegistrar.java类文件,去实现registerBeanDefinitions
这个接口。
MyImportBeanDefinitionRegistrar:
package com.xiaojian.impls;
import com.xiaojian.bean.Animal;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
public class MyImportBeanDefinitonRegistrar implements ImportBeanDefinitionRegistrar {
/**
*
* @param annotationMetadata 当前类的注解信息以及其他的信息
* @param beanDefinitionRegistry BeanDefinition注册类
*
* 把所有的需要添加到容器中的bean:调用
* BeanDefinitionRegistry.registerBeanDefinition手动注册进来
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
//判断内部是否有User这个bean的信息
boolean user = beanDefinitionRegistry.containsBeanDefinition("User");
//判断内部是否有Color这个bean的信息
boolean color = beanDefinitionRegistry.containsBeanDefinition("Color");
if (!user && !color){
//registerBeanDefinition这个方法用于注册bean。
// 第一个参数是这个bean的名称
// 第二个参数是一个BeanDefinition,我们用RootBeanDefinition这个实现
//指定bean定义信息:(bean的类型,bean的作用域。。。)
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Animal.class);
//注册一个bean,指定bean的名称
beanDefinitionRegistry.registerBeanDefinition("Animal", rootBeanDefinition);
}
}
}
(2)修改MainConfig4.java类文件
MainConfig4:
package com.xiaojian.config;
import com.xiaojian.bean.Animal;
import com.xiaojian.bean.Color;
import com.xiaojian.bean.User;
import com.xiaojian.impls.MyImportBeanDefinitonRegistrar;
import com.xiaojian.impls.MyImportSelector;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
//@Import({Color.class, Animal.class, User.class}) //@Import导入组件,id默认是组件的全类名
//@Import({MyImportSelector.class})
//导入一个自定义的组件选择控制类,其实现了ImportSelector这个接口,接口返回一个需要导入的组件的全类名的数组
@Import(MyImportBeanDefinitonRegistrar.class)
//导入一个实现ImportBeanDefinitionRegistrar接口的类,该接口把所有需要注册的bean通过内部的方法手动注册进来。
public class MainConfig4 {
}
(3)测试运行
运行MainTest06.java测试类,测试结果如下:
如图,控制台只打印了一个Animal这个bean,因为我们在方法中只注册了一个这个bean。
四. 使用Spring提供的FactoryBean
工厂(bean)
1.创建ColorFactoryBean.java类文件
我们在impls这个包下面创建一个ColorFactoryBean.java类文件,此类实现FactoryBean
这个接口。
ColorFactoryBean:
package com.xiaojian.impls;
import com.xiaojian.bean.Color;
import org.springframework.beans.factory.FactoryBean;
//创建一个Spring定义的FactoryBean
public class ColorFactoryBean implements FactoryBean {
//返回一个Color对象,这个对象会添加到容器中
@Override
public Object getObject() throws Exception {
//此处返回一个Color对象,意思就是告诉IOC容器,我们注册一个Color这个bean。
return new Color();
}
@Override
public Class<?> getObjectType() {
//此处是返回上面getObject()创建的bean对象的类型
return Color.class;
}
@Override
public boolean isSingleton() {
//此处是返回上面getObject()方法创建的bean对象是否为单例模式
//true则为单实例,容器中只保存一份;false,多实例,每次获取都会返回一份。
return false;
}
}
此类实现FactoryBean
,我们需要重写三个方法:
- getObject():此方法返回的是一个bean对象,就是将会在IOC容器中创建的对象
- getObjectType():此方法返回的是getObject()方法返回的对象的类型
- isSingleton():此方法返回的是getObject()方法返回的bean对象作用域范围,就是是单例模式还是多例模式
2.创建MainConfig5.java类文件
我们在config包下面创建MainConfig5.java类文件,在此类文件中创建ColorFactoryBean这个bean对象的声明。
MainConfig5:
package com.xiaojian.config;
import com.xiaojian.impls.ColorFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MainConfig5 {
//此处声明了ColorFactoryBean这个bean的对象,告诉IOC容器
@Bean
public ColorFactoryBean colorFactoryBean(){
return new ColorFactoryBean();
}
}
3.创建测试类
在test包下面创建一个MainTest07.java类文件,进行相干的测试。
MainTest07:
package com.xiaojian.test;
import com.xiaojian.config.MainConfig5;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MainTest07 {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig5.class);
//此处查看我们所有的bean对象的名称
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String name:beanDefinitionNames){
System.out.println("此方法为:"+name);
}
//工厂Bean获取的时调用getObject方法创建的对象
//此处是为了查看我们获取的bean实际上的名称
Object colorFactoryBean = applicationContext.getBean("colorFactoryBean");
System.out.println("bean的名称"+colorFactoryBean.toString());
System.out.println("bean的类型:"+colorFactoryBean.getClass());
}
}
4.运行测试
运行MainTest07.java测试类,测试结果如下:
如图,为测试结果。图中的1,是IOC容器创建的bean对象,而2是colorFactoryBean这个对象的实际的指向。也就是说,我们在MainConfig5.java类文件中创建的那个bean,我们在IOC容器中获取的却是Color,因为我们在ColorFactoryBean的方法中返回了Color这个bean。