装配bean
装配Bean
创建应用对象之间协作关系的行为通常称为装配。
一、Spring装配的可选方案
- Spring提供了三种主要的装配机制
- 在XML中进行显示装配
- 在java中进行显示装配
- 隐式的bean发现机制和自动装配
- 每种技术所提供的功能会有一些重叠,所以我们要在特定的场景中,确定使用最合适的技术。
- 三种注入的方式
- 构造器注入(接口注入)
- set方法注入
- 注解注入
二、自动化装配
- 在使用的便利性方面,最强大的还是Spring中的自动化配置,这样我们就不用显式的将bean装配再一起了,Spring从两个角度来实现自动化装配。
- 组件扫描:Spring会自动发现应用上下文中所创建的额bean。(Spring知道有这么个bean存在)
- 自动装配:Spring自动满足bean之间的依赖。(Spring将这些bean联系在一起)
- 如果一个应用类A的完整功能实现上需要使用另一个应用类B的某种功能,那么这里我们使用DI,这样才能完全展现A类的功能
- 示例:
- 创建一个CompactDisc接口(我们常用的构造器注入也就是接口注入)
package spring.com.bean;public interface CompactDisc {
void play();
}
- 创建一个实现类SgtPeppers(这个类用来创建一个组件bean,重写play方法)
package spring.com.bean;import org.springframework.stereotype.Component;
//类上使用该注解会表明该类会作为组价类,并告知Spring为这个类创建bean
@Component
public class SgtPepperts implements CompactDisc {
private String title="抽抽会好起来的!";
private String artist="taeyeon";
@Override
public void play() {
System.out.println("Happy"+artist+";"+title);
}
}
- 创建一个配置类,用于开启Spring来扫描上下文中的bean
package spring.com.bean;import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
//@ComponentScan能在Spring中启用组件扫描,如果没有其他配置的话,@ComponentScan默认会
//会扫描与配置类相同的包。
//Spring会扫描本包中带有@Component注解的类,这样就会使Spring为带有@Component注解的类
//为其自动创建一个bean(扫描组件默认是不启动的)
//这里我们也可以使用xml来启动扫描组件。 <context:component-scan base-package="spring.com"/>
@Configuration
@ComponentScan
public class CDPlayerConfig {
}
- 测试
/** * SpringJUnit4ClassRunner以便在测试开始的时候自动创建Spring的应用上下文。
* 注解ContextConfiguration会告诉它需要在那个类中加载配置。
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = CDPlayerConfig.class)
public class OneTest {
@Autowired
private CompactDisc cd;
@Test
public void cdPlayerTest() {
Assert.assertNotNull(cd);
cd.play();
}
}
- 输出
Happytaeyeon;抽抽会好起来的!
- 现在我们好好解释下这些注解的作用
序号 注解 作用
1
@ComponentScan
在Spring中启用组件扫面,默认扫面与配置类相同的包
2
@Component
用于类,在Spring中自动为其创建一个bean
3
@RunWith(SpringJUnit4ClassRunner.class)
以便在测试开始的时候自动创建Spring的应用上下文
4
@ContextConfiguration(classes = CDPlayerConfig.class)
这个注解会告诉它需要在该类中加载配置
5
@Configuration
配置该类为加载的配置类,表明Spring应用上下文中如何创建bean的细节
- 为组件扫描的bean命名
- 当不指定bean的名称的时候,Spring会默认创建一个名字为类名第一个字母小写的名字;如果要指定bean的id那么使用
@Component("名字")
来配置 - 还有一种为bean命名的方式,是使用java依赖注入规范中所提供的@Named注解来设置id的。
- 两者之间有略微的差别,但是大多数场景中还是可以互相替换的。
- 设置组件扫描的基础包
- 如果你想扫描不同的包,那么你就需要为@ComponentScan设置属性了,有两种方式:
@ComponentScan("包名")@ComponentScan("basePackages="包名")
第二种可以清晰的看出你设置的是基础包。
- 而且我们还可以同时设置多个多个基础包
@ComponentScan("basePackages={"包名","包名",...})
但是上面我们设置的基础包都是String类型的,是类型不安全的。如果你重构代码的话,那么所指定的基础包可能会出现错误了。所以Spring也提供了另一种方法,那么就是将其指定为包中所包含的类或者接口:@ComponentScan("basePackagesClasses={"类名.class","接口名.class"})
7. 当我们使用独立的对象时候,彼此之间没有任何依赖,那么我们所需要的可能就是组件扫描而已。但是实际使用中要实现一个功能,我们就必须使用多个bean,所以我们就需要将有的bean装配在一起,才能完成任务。所以我们需要了解自动化装配,前面只是创建了bean,这里我们需要来将bean装配再一起来使用。
- 自动装配就是让Spring自动满足bean依赖的一种方式,在满足依赖的过程中,会在Spring应用上下文中寻找匹配某个bean需求的其他bean,进行自动装配我们就需要使用借助Spring的@Autowired注解。
- @Autowired可以用所有方法上和字段上。
- 使用@Autowired进行自动装配的时候如果没有相应名字的bean,那么就会抛出异常。为了避免异常的出现,你可以将@Autowired的required属性设置为false:
@Autowired(required=false)
。当没有匹配的bean时会让该bean处于未装配状态,但是当你的代码中有null时,这个处于未装配状态的属性会报空指针异常。 - 当一个接口被多个类实现的时候我们需要引入该接口的bean,即实现类的bean,这时我们使用@Autowired就会报错NoUniqueBeanDefinitionException,表示不知道引入那个bean,因为他们都实现了同一个接口。这时我们就要使用@Qualifier,这个注解的作用就是指定你自动装配的bean是那个bean,不会出现混乱。可以用于类、方法、字段、参数,不可以用于构造方法。
- @Inject不是Spring的特定注解,你要是不愿意使用@Autowired,那么你就可以使用它。
- 示例
- 创建相同实现的CdPlay类
package spring.com.bean;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class CdPlay implements CompactDisc {
// @Autowired
private CompactDisc cd;
@Autowired
public CdPlay(CompactDisc cd) {
this.cd = cd;
}
@Override
public void play() {
System.out.println("CDPlay");
cd.play();
}
}
- 还是利用上面的测试类
@Qualifier("sgtPepperts")//消除注入那个bean的问题,这里我们的接口CompactDisc被两个类实现,并都重写了方法play,这两个类都被创建成bean组件,
// 那么这里在自动装配bean时,就不会知道到底会注入那个bean会报NoUniqueBeanDefinitionException,所以我们要用到这个注解。
@Autowired
private CompactDisc cd;
//经测试发现使用Component注解来创建bean的类(没有指定bean的名字),如果类名第一个字母为大写,第二个为小写,那么他的bean的名字就是第一个字母小写后的。
//但是如果第一个第二个等都是大写,那么bean的名字就是类名。
@Qualifier("cdPlay")
@Autowired
private CompactDisc cp;
@Test
public void cdPlayerTest() {
Assert.assertNotNull(cd);
cd.play();//SgtPepperts
cp.play();//CDPlay
}
- 输出
Happytaeyeon;抽抽会好起来的!CDPlay
Happytaeyeon;抽抽会好起来的!
通过java代码装配bean
- 有时候自动化装配是行不通的,比如你需要将第三方库中的组件装配到你的应用中,在这种情况下你是无法在其他类上添加@Component和@Autowired注解的,因此就不能使用自动化装配方案了。
- 这种情况下我们必须使用显示装配的方法,进行显示装配的时候有两种方案:java和xml。
下来我们就使用java代码来显示配置Spring。
- 创建配置类(创建一个返回对象为JavaBean类的bean)
package spring.com.bean.javabean;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class HardDaysNight {
//该标签会告诉Spring这个方法将会返回一个对象,该对象要注册为Spring应用上下文中的bean。
//默认情况下bean的ID与带有@Bean注解的方法名是一样。也可以设置名字,添加属性name="";
@Bean(name = "setJavaBean")
public JavaBean setJavaBean() {
System.out.println("setJavaBean");
return new JavaBean();
}
}
- 创建实际功能类
package spring.com.bean.javabean;import spring.com.bean.autobean.CompactDisc;
public class JavaBean implements CompactDisc {
public void play() {
System.out.println("安安要开开心心的!");
}
}
- 测试
package spring.com.one;import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import spring.com.bean.javabean.HardDaysNight;
import spring.com.bean.javabean.JavaBean;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = HardDaysNight.class)
public class TwoTest {
@Qualifier("setJavaBean")
@Autowired
private JavaBean javaBean;
@Test
public void hard() {
javaBean.play();
}
}
- 输出
setJavaBean安安要开开心心的!
通过测试类进行JavaBean的自动装配,将bean的id为setJavaBean的bean装配到字段javaBean中,该bean就是JavaBean类的组件bean,然后就可以用该字段去调用其中的play方法了。
该标签会告诉Spring这个方法将会返回一个对象,该对象要注册为Spring应用上下文中的bean。
默认情况下bean的ID与带有@Bean注解的方法名是一样。也可以设置名字,添加属性name="";
通过xml装配
使用xml来装配bean已经拥有很长时间了,但是还是推荐大家使用自动化装配和java配置装配。
- 创建一个Spring的xml文件,在xml文件顶部中我们需要声明大量的xml模式(XSD)文件,这些文件定义了配置Spring的XML元素。
- <beans>节点是该模式中的一个元素,他是所有Spring配置文件的根元素。
- 创建一个Spring配置文件
<?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">
<!--class属性需要全限定名,而且下面这个没有指定ID,所以这个bean会根据全限定名类名来进行命名-->
<!-- 这个bean的名字将会是spring.com.bean.xmlbean.SgtPeppers#0.其中#0是一个计数的,用来区分其他的bean,如果有另一个相同类的bean,那么名字将是spring.com.bean.xmlbean.SgtPeppers#1-->
<bean class="spring.com.bean.xmlbean.SgtPeppers"/>
<!-- 但是一般都会自己指定ID的。-->
<bean id="sgt" class="spring.com.bean.xmlbean.SgtPeppers"/>
</beans>
xml装配bean其实是很不安全的,因为我们在xml中class属性使用的是字符串,在后面我们谁也不能保证我们不会去改变这个类名,那么那个时候Spring容器就会找不到对应的类来创建bean。
借助构造器注入初始化bean
- 具体到构造器注入,有两种基本的配置方案可以选择:
- <constructor-arg></constructor-arg>元素
- 使用Spring3.0所引入的c-命名空间
两者的区别很大程度在于是否冗长烦琐,而且有的功能两者中只有一个能实现。
2. 两者的比较
- 使用c命名空间时需要添加xsd:
xmlns:c="http://www.springframework.org/schema/c"
<!-- 将SgtPeppers对象注入到CDPlay类中--> <bean id="compactDisc" class="spring.com.bean.xmlbean.SgtPeppers"/>
<bean id="cdplay" class="spring.com.bean.xmlbean.CDPlay">
<constructor-arg ref="compactDisc"/>
</bean>
<!-- 使用c命名空间
c:sgtPeppers-ref="compactDisc"
c:命名空间前缀
sgtPeppers:构造器参数名
-ref:引入bean
compactDisc:要注入bean的ID
-->
<bean id="cdplay1" class="spring.com.bean.xmlbean.CDPlay"
c:sgtPeppers-ref="compactDisc"
/>
可以看出来使用c命名空间简化了代码数量,在xml文件中是不允许数字作为属性的第一个字符的。
3. 使用xml进行简单的装配
- 创建注入bean
package spring.com.bean.xmlbean;public class CDPlay {
private SgtPeppers sg;
public CDPlay(SgtPeppers sgtPeppers) {
this.sg=sgtPeppers;
}
public void cd(){
sg.play();
}
}
- 创建被注入的bean
package spring.com.bean.xmlbean;import spring.com.bean.autobean.CompactDisc;
public class SgtPeppers implements CompactDisc {
private String ti;
private String tt;
public SgtPeppers() {
}
public SgtPeppers(String ti, String tt) {
this.ti = ti;
this.tt = tt;
}
@Override
public void play() {
System.out.println("安安答应过不会离开我的");
System.out.println("----------------------");
System.out.println(ti+tt);
}
}
- xml
注入直接赋值的对象时可以用
<constructor-arg name="ti" value="安安"/>
,注入bean时使用<constructor-arg ref="xmlsgt"/>
<bean id="xmlsgt" class="spring.com.bean.xmlbean.SgtPeppers"> <constructor-arg name="ti" value="安安"/>
<constructor-arg name="tt" value="是要好好守护的女孩"/>
</bean>
<bean id="xmlcd" class="spring.com.bean.xmlbean.CDPlay">
<constructor-arg ref="xmlsgt"/>
</bean>
- 测试
@Test public void cdplay() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("one/xmlConfig.xml");
CDPlay cd = (CDPlay)context.getBean("xmlcd");
cd.cd();
context.close();
}
- 输出
安安答应过不会离开我的----------------------
安安是要好好守护的女孩
- 当我们注入一个集合的时候如果这个集合为空那么为了不浪费内存,我们可以这样写
<constructor-arg><null/></constructor-arg>
,将null传递给构造器,在编译的时候不会出问题,但是当在运行一些调用该集合的函数的时候就会出现空指针异常。 - 示例一下集合的注入,比如
- 注入字符串值
<bean id="xmlcd" class="spring.com.bean.xmlbean.CDPlay"> <constructor-arg ref="xmlsgt"/>
<constructor-arg>
<list>
<value>xxxx</value>
<value>xxxx</value>
<value>xxxx</value>
...
</list>
</constructor-arg>
</bean>
- 注入其他bean
<bean id="xmlcd" class="spring.com.bean.xmlbean.CDPlay"> <constructor-arg ref="xmlsgt"/>
<constructor-arg>
<list>
<ref bean=""/>
<ref bean=""/>
<ref bean=""/>
...
</list>
</constructor-arg>
</bean>
- 上面介绍了构造器注入,现在我们了解下Setter方法的依耐注入吧,使用
<property>
属性,这个也有相应的p命名空间的替代方案,记住这两个还是有很大差距的,功能上也不太一样。
package spring.com.bean.xmlbean;import spring.com.bean.autobean.CompactDisc;
public class AsgPeppers implements CompactDisc {
private APlay aPlay;
public APlay getaPlay() {
return aPlay;
}
public void setaPlay(APlay aPlay) {
this.aPlay = aPlay;
}
@Override
public void play() {
System.out.println("04592");
aPlay.ap();
}
}
package spring.com.bean.xmlbean;public class APlay {
private String tt;
public String getTt() {
return tt;
}
public void setTt(String tt) {
this.tt = tt;
}
public void ap(){
System.out.println("傻瓜"+tt);
}
}
<bean id ="xmlap" class="spring.com.bean.xmlbean.APlay"> <property name="tt" value="安安"/>
</bean>
<bean id="xmlasg" class="spring.com.bean.xmlbean.AsgPeppers">
<property name="aPlay" ref="xmlap"/>
</bean>
@Test public void aplay() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("one/xmlConfig.xml");
AsgPeppers cd = (AsgPeppers)context.getBean("xmlasg");
cd.play();
context.close();
}
04592傻瓜安安
可以看出来其实用法和构造器注入用法一样就是注入的时候用的标签不一样,这里我们用的也是Setter方法来注入bean或者常量的。他也可以进行集合的注入。
我们常常使用的也就是xml配置加上java代码配置和自动装配来注入bean。
以上是 装配bean 的全部内容, 来源链接: utcz.com/z/514512.html