什么是装配
在spring中,对象无需自己查找或创建与其关联的其他对象,容器负责把需要相互协作的对象引用赋予各个对象。而创建对象之间协作关系的行为通常称为装配。
Spring 装配的方式
在Spring中,有三种主要的方式用来装配Bean:
1. 隐式Bean的发现机制和自动装配
2. 显式装配:
- 在Java中显式装配
- 在XML中显式装配
三种方式的选择
在《Spring in action》一书中,作者这样建议道:
首选自动配置的机制,显示配置越少越好。当必须要显式装配Bean时,优先选择类型安全更好的JavaConfig方式。只有想使用XML便利的命名空间并且JavaCofig没有相同的实现的时候,才使用XML的方式显式装配。
Bean自动扫描创建
我们按照推荐的顺序来整理Spring下Bean的装配。首先就是自动装配的方式。
Spring从两个角度来实现自动化配置:
- Component Scanning:Spring会自动发现应用上下文中所创建的Bean
- Autowiring:Spring自动满足Bean之间的依赖。
接下来我们通过程序的方式来了解这种装配方式:
首先导入Spring-4.2.8的相关jar包.文章中,我使用Eclipse来作为开发工具。我们所要创建的类大概就只有这些。
![]()
我们需要创建一个Bean类,这个Bean类的特点就只有可被发现,这样在自动装配的时候,可以被自动的扫描到,并且被装配。
- 创建Human接口
package com.blaze.sherlock.interfaces public interface Human{ void fuck(); }
创建该接口的目的是为了降低耦合,它定义了人类对一个人所能做的事情。同时,创建一个该接口的实现,该接口可以有很多实现,现在我们就只创建一个。
- 创建Male实现
package com.blaze.sherlock.implementsclass; import org.springframework.stereotype.Component; import com.blaze.sherlock.interfaces.Human; @Component public class Male implements Human{ private String name= "LeiOu"; private String object = "Small monster"; @Override public void fuck(){ System.out.println(name + " fucked " + object); } }
这个地方,我们很容易可以注意到Male类被@Component注解标示着,这个注解表示该类会被作为一个组件类,并且由Spring为这个类创建Bean。
既然我们标示了一个类,告诉Spring,你要搞定它并且自动帮我创建Bean,不要老夫亲自去弄。但是,组件扫描默认是不启用的,也就是说,我们必须显示的配置Spring,让它知道扫描带有@Component注解的类,并为它创建bean。
这里我们使用一个简单的配置。
- 创建HumanFuckConfig配置类
@Configuration @ComponentScan("com.blaze.sherlock.implementsclass") public class HumanFuckConfig { }
这里同样有个地方需要注意一下,对于注解@ComponentScan,这个注解是用于启动Spring中组件扫描,帮助我们去扫描被注解@Component标示的类,在仅有@ComponentScan注解没有其他配置的情况下,默认是扫描当前配置类所在的包。
我们可以通过类似上面的方式来指定所要扫描的包,其实上面@ComponentScan的写法是“`java
@ComponentScan(basePackages=”com.blaze.sherlock.implementsclass”)
> 的简写,注意,这里的basePackages并不一定代表一个字符串,我们可以通过数组的方式来指定需要扫描的包,例如:
> ```java
@ComponentScan(basePackages={"implementsclass","anotherimplementsclass"})
同样,我们也可以通过
@ComponentScan(basePackageClasses = Human.class)
的注解方式来锁定所要扫描的类。
这个时候,我们基本已经做到了一个简单的实例,即Spring可以自己查找到我想要它自动创建的bean类,我们需要通过测试来判断Human是不是真的被创建出来了。
- 创建HumanFuckTest类
package comb.blaze.sherlock.testclass; import static org.junit.Assert.*; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import com.blaze.sherlock.configuration.HumanFuckConfig; import com.blaze.sherlock.implementsclass.Male; import com.blaze.sherlock.interfaces.Human; @RunWith(SpringJUnit4ClassRunner.class) //@ContextConfiguration(classes=com.blaze.sherlock.configuration.HumanFuckConfig.class) @ContextConfiguration(classes = Male.class) public class HumanFuckTest { @Autowired private Male h; @Test public void testH(){ assertNotNull(h); System.out.println(h); } }
![]()
这里,我们成功创建了Male的Bean。这里我们简单介绍一下HumanFuckTest中的内容。SpringJUnit4ClassRunner,可以让在测试开始的时候自动创建Spring的应用上下文。而ContextConfiguraion告诉该Test需要在HumanFuckConfig中加载配置,虽然我们现在什么都没有写。
补充
我们不仅可以通过HumanFuckConfig来启动自动扫描,还可以通过XML的方式来启动自动扫描。
主要使用的就是这么一个标签。
<context:component-scan base-package="com.blaze.sherlock.implementclasses"/>
自动装配!!
在应用程序中,如果所有的对象都是孤立的,那就搞不起来事情。很多对象只有依赖其他对象才能搞出事情,这里,我们通过为bean类添加注解,让组件扫描得到的bean类和它们的依赖装配在一起,为了搞事情。
到了这里,最重点的仍然是@Autowired注解,它不仅可以用于构造方法上,还可以用于Setter或是其他方法。话说多容易乱,先上代码:
- MetalHuman 接口
package com.blaze.sherlock.interfaces; public interface MetalHuman { void fuck_02(); }
- MetalMale 类
package com.blaze.sherlock.implementsclass; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import com.blaze.sherlock.interfaces.Human; import com.blaze.sherlock.interfaces.MetalHuman; @Component public class MetalMale implements MetalHuman{ private Human h; @Autowired(required = false) public MetalMale(Human h){ this.h = h; } @Override public void fuck_02() { // TODO Auto-generated method stub if(h != null){ h.fuck(); } } @Autowired(required = false) public void setHuman(Human h){ this.h = h; } public void fuck() { // TODO Auto-generated method stub } }
这里我们看到,MetalMale类仍然被@Component注解标记着,同时,在该类的构造方法上,我们写上了
@Autowired(required = false)
这样一段代码。通过它,我们完成了对Human的自动装配,实现了MetalMale对Human的依赖。
通过这样,Spring会尝试满足方法参数上所声明的依赖,假如有且只有一个bean匹配这样的依赖需求的话,那么这个bean类就会被装配进来。而如果没有匹配依赖需求的bean,Spring就会抛出异常,这样,通过@Autowired后括号中 required = false 表达式,让Spring在没有匹配依赖需求的bean时可以让这个bean处于未装配状态,而不会去抛出相关异常。这个时候,我们就需要对装配的bean进行判空,所以在通过Human对象h调用fuck方法时,我们先对 h 进行了判空。
写完了装配代码,接着就是对装配是否成功的测试。
- 编写 HumanFuckTest 类
package comb.blaze.sherlock.testclass; import static org.junit.Assert.*; import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.StandardOutputStreamLog; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import com.blaze.sherlock.configuration.HumanFuckConfig; import com.blaze.sherlock.interfaces.Human; import com.blaze.sherlock.interfaces.MetalHuman; @RunWith(SpringJUnit4ClassRunner.class) //@ContextConfiguration(classes=com.blaze.sherlock.configuration.HumanFuckConfig.class) //@ContextConfiguration(classes = Male.class) @ContextConfiguration(classes = HumanFuckConfig.class) public class HumanFuckTest { @Rule public final StandardOutputStreamLog log = new StandardOutputStreamLog(); @Autowired private MetalHuman mh; @Autowired private Human h; @Test public void hMustNotBeNull(){ assertNotNull(h); System.out.println(h); } @Test public void fuck(){ mh.fuck_02(); System.out.println(log.getLog()); } }
总结
至此,spring通过隐式的发现机制和自动装配来配置bean的简单实现就完成了。接下面几个博客会依次贡献Java显式装配、XML显式装配的内容。
最后
欢迎大家访问我的博客