【Spring】IOC(上):构建 Bean 的几种方式(包括xml、注解)

本文详细介绍了在Java Spring框架中,如何通过IoC(控制反转)容器来管理和引用对象,包括构造方法传入、属性设置、运行时传参等不同方式。此外,还讲解了XML配置和JavaConfig配置两种方式创建Bean,以及@Bean、@ComponentScan、@Import等注解的使用,旨在阐明Spring如何简化复杂对象依赖关系的管理。
摘要由CSDN通过智能技术生成

在JAVA的世界中,一个对象A怎么才能调用对象B?通常有以下几种方法:

类别描述时间点
外部传入构造方法传入
属性设置传入设置对象状态时
运行时做为参数传入调用时
内部创建属性中直接创建创建引用对象时
初始化方法创建创建引用对象时
运行时动态创建调用时

上表可以看到, 引用一个对象可以在不同地点(其它引用者)、不同时间由不同的方法完成。如果B只是一个非常简单的对象 如直接new B(),怎样都不会觉得复杂,比如你从来不会觉得创建一个String 是一个件复杂的事情。但如果B 是一个有着复杂依赖的Service对象,这时在不同时机引用B将会变得很复杂。

在这里插入图片描述
无时无刻都要维护B的复杂依赖关系,试想B对象如果项目中有上百过,系统复杂度将会成陪数增加。IOC容器的出现正是为解决这一问题,其可以将对象的构建方式统一,并且自动维护对象的依赖关系,从而降低系统的实现成本。

IOC(Inversion of Control)控制反转:控制反转。就是把原先我们代码里面需要实现的对象创建、依赖的代码,反转给容器来帮忙实现。那么必然的我们需要创建一个容器,同时需要一种描述来让容器知道需要创建的对象与对象的关系。这个描述最具体表现就是我们所看到的配置文件。

1.xml 配置形式

xml 配置形式,顾名思义就是在单独的 xml 文件中对 bean 进行配置,常见的有以下几种形式:

1.1 ClassName反射构建

<!-- 默认构造函数构建 -->
<bean name="hello",class="com.my.spring.HelloSpring"></bean>

这是最常规的方法,其原理是在spring底层会基于 Class 属通过反射进行构建。

Object obj = HelloSpring.class.newInstance();

关于反射可以参考这篇 【JVM】第六篇:堆(Heap)上有什么?Class对象到反射

那我们该如何获取IOC容器中的Bean呢?

public static void main(String[] args)    
{    
     // 传入配置文件
     ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); 
     // getBean 可以传入id、name、Class 来获取唯一个 bean
     // 注:id与name的区别在于,id必须唯一,而name可以不唯一(冲突时后面的对象会覆盖前面的)
  	 System.out.println(ctx.getBean("hello"));  
}

1.2 指定构造方法构建

如果需要基于参数进行构建,就采用构造方法构建,其对应属性如下:

  • name:构造方法参数变量名称
  • type:参数类型(非必须)
  • index:参数索引,从0开始
  • value:参数值,spring 会自动转换成参数实际类型值
  • ref:引用容串的其它对象
public class HelloSpring {
	private String name;
	private int sex;
	
	public HelloSpring(){}
	
	public HelloSpring(String name) {
		this.name = name;
		this.sex = sex;
	}
}
<!-- 相当于是指定构造函数构建,而上面是用默认构造函数构建 -->
<bean class="com.my.spring.HelloSpring">
    <constructor-arg name="name" type="java.lang.String" value="zhangsan"/>
    <constructor-arg index="1" value="1" />
</bean>

1.3.静态工厂方法创建

该模式下必须创建一个静态工厂方法,并且方法返回该实例,spring 会调用该静态方法创建对象。

<!-- 指定Bean的类型,静态工厂方法(未特别指定则在当前类中) -->
<bean class="com.my.spring.HelloSpring" factory-method="build">
	<!-- 给工厂方法传入的参数名称,值。这里是要构造一个B类型的对象 -->
    <constructor-arg name="type" type="java.lang.String" value="B"/>
</bean>
// bulid 方法相当于一个静态工厂,返回已经创建好的对象
public static HelloSpring build(String type) {
	// 如果需要的是A类型的Bean
    if (type.equals("A")) {
        return new HelloSpring("luban",1);// 如果需要的是B类型的Bean
    } else if (type.equals("B")) {
        return new HelloSpring("diaocan", 0);
    // 如果既不是A类型也不是B类型
    } else {
        throw new IllegalArgumentException("type must A or B");
    }
}

使用场景:如果你正在对一个对象进行A/B测试 ,就可以采用静态工厂方法的方式创建,其于策略创建不同的对像或填充不同的属性。

1.4 FactoryBean创建

指定一个Bean工厂来创建对象,对象构建初始化完全交给该工厂来实现。配置Bean时指定该工厂类的类名。

<!-- 构建一个工厂Bean  -->
<bean class="com.my.spring.MYFactoryBean"></bean>
// 实现FactoryBean接口
public class MYFactoryBean implements FactoryBean {
    @Override
    public Object getObject() throws Exception {
        return new HelloSpring();
    }
    @Override
    public Class<?> getObjectType() {
        return HelloSpring.class;
    }
    @Override
    public boolean isSingleton() {
        return false;
    }
}

FactoryBean:具有工厂生产对象能力的 bean,只能生成特定的对象。bean 必须实现 FactoryBean接口,此接口提供方法 getObject() 用于获得特定 bean。所以,使用时先创建FB实例,然后调用 getObject() 方法。

2.JavaConfig配置形式

JavaConfig 不再是单独的配置文件去配置,而是采用标了 @Configuration 注解的配置类去配置bean:

2.1 @Bean(适用于第三方组件)

  • 通过@Bean的形式是使用的话,bean的默认名称是方法名
  • 若@Bean(value=“bean的名称”) 那么bean的名称是指定的
@Configuration 
public class MainConfig { 
    @Bean     
    public Person person(){  
        return new Person(); 
    } 
}

如果是注解形式的,那我该如何获取这个bean呢?

public static void main(String[] args) { 
 // 传入配置类                                          
 AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig.class);
 System.out.println(ctx.getBean("person"));  
}

2.2 @CompentScan(适用于自定义类)

在配置类上写@CompentScan注解来进行包扫描。除此之外还有 @Compent,@Controller,@Service,@Repository

@ComponetScan常用的属性字段有 basepackage(包)、excludeFilters(排除)、includeFilters(包含)。

注意,@CompentScan一般直接写在配置类上。

@Configuration 
// basePackages指定啊哟扫描的包
// 注意:如果要扫描的的包有多个,需要用{}包裹
@ComponentScan(basePackages = {"com.my.testcompentscan"}) 
public class MainConfig { 
}
@Configuration 
// excludeFilters 指定排除扫描的类
// 排除@Controller注解的,和TestService的)
@ComponentScan(basePackages = {"com.my.testcompentscan"}, excludeFilters = { 
    @ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Controller.class}), 
    @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,value = {TestgService.class})
}) 
public class MainConfig { 
}
@Configuration 
// 只注入@Controller 和 @Service
// 注意:若使用包含的用法,需要把useDefaultFilters属性设置为false(true表示扫描全部的)
@ComponentScan(basePackages = {"com.my.testcompentscan"},includeFilters = { 
    @ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Controller.class, Service.class}) 
},useDefaultFilters = false) 
public class MainConfig { 
}

2.3 @Import(适用于配置类)

@Import 就是把多个分散的容器配置合并在一个配置中,类似于 xml 形式下有一个 <import resource/> 形式的注解。

  • @Import 导入是类名.class,可以导入任何类,不要求必须标@Componet交给SpringIOC)
  • 除了静态导入外,也可以实现ImportSelector重写selectImports定义导入类的注解信息
@Configuration
@Import(value = {Person.class, Car.class})
public class MainConfig { 
}
public class MYImportSelector implements ImportSelector { 
    // 可以获取导入类的注解信息     
    @Override   
    public String[] selectImports(AnnotationMetadata importingClassMetadata) { 
    	return new String[]{"com.my.testimport.compent.Dog"};   
    } 
} 

@Configuration 
@Import(value = {Person.class, Car.class, MYImportSelector.class})
public class MainConfig { 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

A minor

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值