主要有三种装配机制:
- 隐式的bean发现机制和自动装配。
- 在Java中进行显示配置。
- 在xml中进行显示配置。
一、隐式的bean发现机制和自动装配
Spring从两个角度来实现自动化装配
- 组件扫描:Spring会自动发现应用上文中所创建的bean。
- 自动装配:Spring自动满足bean之间的依赖。
创建可发现的bean:
@Component
public class Bean1 {
public Bean1(){
System.out.println("##########Bean1构造成功######################");
}
@Override
public String toString() {
return "Bean1{}";
}
}
Componet :将被注解的类作为bean。
这样Bean1类就是一个组件了,不过组件扫描默认是关闭的,要开启组件扫面有两种方式
- 通过Spring配置类。
- 通过xml配置文件。
通过Spring配置类
@Configuration
@ComponentScan(value = "com.spring")
public class ConScan {
}
Configuration:将被注解的类作为配置类,作用等同配置的XML。
ComponetScan:启动组件扫描,value值表示要扫描的包,如果不写则默认为扫描配置类所在的包。
还可以配置扫描多个包:
@ComponentScan(value ={"com.spring.bean","com.spring.bean2"})
通过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">
<!-- 告知spring在创建容器时要扫描的包 -->
<context:component-scan base-package="com.itheima"></context:component-scan>
</beans>
这样便将可被发现的bean创建完成。
bean之间的自动装配:
@Component
public class Bean2 {
@Autowired(required = false)
private Bean1 bean11;
public Bean2(){
System.out.println("*************Bean2构造成功********");
}
@Autowired(required = false)
public Bean2(Bean1 bean1){
System.out.println("*************Bean2构造成功********有参构造函数");
System.out.println(bean1);
bean1.toString();
}
@Override
public String toString() {
return "Bean2{}";
}
}
Bean2类有一个Bean1变量和一个需要传递Bean1的构造函数和一个无参构造函数。
Autowired:在初始化Bean2之后,Spring会尽可能的从容器里寻找满足条件的bean来满足bean之间的依赖关系。
Autowired可以作用在属性和方法上。如果注解构造方法那么这个bean在初始化时会执行被Autowired注解的构造方 法。如果有两个被Autowired注解的方法会导致运行错误。(注意:Spring默认使用无参构造方法进行初始化bean)
测试类:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath*:/bean.xml"})
//@ContextConfiguration(classes = ConScan.class)
public class Test {
@Autowired
Bean2 bean2;
@org.junit.Test
public void test(){
System.out.println("———————————测试————————");
System.out.println(bean2.getBean11());
}
}
Maven依赖:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
</dependencies>
扩展:
Autowired的required属性:默认值为true
作用:如果Spring找不到匹配的bean进行装配那么会导致运行错误,将required设置为false时,Spring 会尝试进行匹配,如果没有匹配的bean,Spring会将这个bean处于为匹配状态即为null。因此如果将 required设为false在使用到这个bean时要注意null检查。
此外,将Autowired应用到构造方法上并且将required设置为false时,如果Spring找不到匹配的bean时将不会执行这个构造方法而是执行无参构造方法。
为Bean命名:
用Componet创建bean时默认的bean名为类名首字母小写,如果需要自定义名称:@Componet("name")
另一种bean的配置方式,通过Java依赖注入规范中提供的@Named注解。
@Named
public class Bean1 {
public Bean1(){
System.out.println("##########Bean1构造成功######################");
}
@Override
public String toString() {
return "Bean1{}";
}
}
Named也可以自定义bean名称。
Spring支持将@Named 作为@Componet注解的替代方案。
二、在Java中进行显示配置。
自动装配bean只能用于开发者自己编写的类,不能用于jar包的类。通过Bean注解可以将方法的返回值装配为bean,并且ID为方法名。
@Bean
public Bean1 getBean12(){
return new Bean1();
}
注意:Bean注解一般作用于有Configuration注解的类。Spring会拦截带有Bean注解的方法,即当在方法内部调用带有Bean注解的方法时不会执行该方法而是从Spring的bean容器中获取id为该方法名的bean。
列如:
@Configuration
public class ConScan {
@Bean
public Bean1 getBean(){
System.out.println("getBean执行");
return new Bean1();
}
@Bean
public Bean1 getBeanC(){
System.out.println(getBean());
System.out.println(getBean());
return getBean();
}
}
在getBeanC方法里调用了三次getBean方法,但其实际上只有注册bean时调用getBean方法。getBeanC方法里的调用直接被Spring拦截不会正真执行,而是将id为getBean的bean直接返回。
测试类:
public class Main {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(ConScan.class);
Bean1 bean12 = (Bean1) applicationContext.getBean("getBean");
Bean1 bean22 = (Bean1) applicationContext.getBean("getBeanC");
System.out.println(bean12 == bean22);
}
}
结果:
getBean执行
##########Bean1构造成功######################
com.spring.bean.Bean1@6cc7b4de
com.spring.bean.Bean1@6cc7b4de
true
以上结果都是在带有Configuration注解的类里在方法上注解Bean的。在非Configuration类的方法上注解bean,Spring不会拦截该方法而是直接运行:
列如:将Java配置类的方法移到Bean3类里,并在配置类里开启组件扫描。
@Component
public class Bean3 {
public Bean3(){
System.out.println("&&&&&&&&&&bean3构造完成&&&&&&&&&&&");
}
@Bean
public Bean1 getBean(){
System.out.println("getBean执行");
return new Bean1();
}
@Bean
public Bean1 getBeanC(){
System.out.println(getBean());
System.out.println(getBean());
return getBean();
}
}
注意在Java配置类开启组件扫描,不然无法创建bean。
结果:
##########Bean1构造成功######################
&&&&&&&&&&bean3构造完成&&&&&&&&&&&
getBean执行
##########Bean1构造成功######################
getBean执行
##########Bean1构造成功######################
com.spring.bean.Bean1@37574691
getBean执行
##########Bean1构造成功######################
com.spring.bean.Bean1@25359ed8
getBean执行
##########Bean1构造成功######################
false
还要注意的时Bean注解配置的bean的id为方法名,并且是整个扫描包内的,因此如果在扫描包内有不同的类但有方法名相同的带有Bean注解的方法,Spring只会装配其中一个类的方法,其他类的方法不会执行。
三、xml配置bean
由于其内容复杂便直接通过代码讲解。
项目结构:
bean和测试类:
public class Bean1 {
public Bean1(){
System.out.println("##########Bean1构造成功######################");
}
public Bean1(int a){
System.out.println("##########Bean1构造成功################有参构造="+a);
}
}
public class Bean2 {
public Bean2(){
System.out.println("*************Bean2构造成功********");
}
public Bean2(Bean1 bean1){
System.out.println("*************Bean2构造成功********有参构造函数bean1");
System.out.println(bean1);
}
}
public class Bean3 {
Bean2 bean2;
String name;
List<Bean1> bean1List;
List<String> strings;
public List<Bean1> getBean1List() {
return bean1List;
}
public void setBean1List(List<Bean1> bean1List) {
this.bean1List = bean1List;
}
public List<String> getStrings() {
return strings;
}
public void setStrings(List<String> strings) {
this.strings = strings;
}
public Bean2 getBean2() {
return bean2;
}
public void setBean2(Bean2 bean2) {
this.bean2 = bean2;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Bean3(){
System.out.println("&&&&&&&&&&bean3构造完成&&&&&&&&&&&");
System.out.println(bean2+":"+name);
}
}
public class Main {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
@Before
public void test(){
System.out.println("************test***********");
}
@Test
public void defaultId(){
Bean1 bean1 = (Bean1) applicationContext.getBean("com.spring.bean.Bean1#0");
Bean1 bean2 = (Bean1) applicationContext.getBean("com.spring.bean.Bean1#1");
System.out.println("com.spring.bean.Bean1#0: "+bean1);
System.out.println("com.spring.bean.Bean1#1: "+bean2);
}
@Test
public void id(){
Bean1 bean1 = (Bean1) applicationContext.getBean("bean1");
System.out.println("bean1: "+bean1);
}
@Test
public void constructorArg(){
Bean2 bean22 = (Bean2) applicationContext.getBean("bean2");
System.out.println("constructorArg: "+bean22);
}
@Test
public void ByCNamespaces(){
Bean2 bean2 = (Bean2) applicationContext.getBean("bean2Byc");
System.out.println(bean2);
}
@Test
public void property(){
Bean3 bean3 = (Bean3 ) applicationContext.getBean("bean3");
System.out.println(bean3.getBean2()+" : "+bean3.getName());
System.out.println(bean3.getBean1List()+" : "+bean3.getStrings());
}
@Test
public void pNameSpace(){
Bean3 bean3 = (Bean3) applicationContext.getBean("bean3ByP");
System.out.println(bean3.getBean2()+" : "+bean3.getName());
System.out.println(bean3.getBean1List()+" : "+bean3.getStrings());
}
}
xml文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
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
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd
">
<!--默认id为全限定类名+"#0"-->
<!--com.spring.bean.Bean1#0-->
<!--com.spring.bean.Bean1#1-->
<bean class="com.spring.bean.Bean1"/>
<bean class="com.spring.bean.Bean1"/>
<!--自定义ID-->
<bean id="bean1" class="com.spring.bean.Bean1"/>
<!--使用指定构造器创建bean-->
<!--使用默认id注入虽然xml报错但能运行,并且如果仅仅使用全限定类名默认使用#0这个bean。但不建议使用-->
<bean id="bean2" class="com.spring.bean.Bean2">
<!--ref表示通过ID引用其他bean-->
<constructor-arg ref="bean1"/>
</bean>
<!--使用c命名空间注入,需要在顶部声明其模式:xmlns:c="http://www.springframework.org/schema/c"-->
<!--c:构造器参数名 或 _参数索引(0开启)
-ref:如果参数为引用类型则需添加-ref,表示通过ID引用其他bean-->
<bean id="bean2Byc" class="com.spring.bean.Bean2" c:_0-ref="bean1"/>
<!--对成员变量进行注入(赋值)-->
<!--使用property标签进行注入-->
<!--
name:变量名称
value:变量值
ref:其他bean的id值,表示将本id的bean注入(赋值)给这个变量。
list:表示链表。
value表示链元素,只能用于基本变量。
ref:bean的id,将这个bean作为链表元素,bean取值为bean的id
-->
<bean id="bean3" class="com.spring.bean.Bean3">
<property name="name" value="李华"/>
<property name="bean2" ref="bean2Byc"/>
<property name="strings">
<list>
<value>理化</value>
<value>梨花</value>
<value>李华</value>
</list>
</property>
<property name="bean1List">
<list>
<ref bean="bean1"/>
<ref bean="bean1"/>
</list>
</property>
</bean>
<!--util命名空间,需要在顶部声明-->
<!--
xmlns:util="http://www.springframework.org/schema/util"
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd
-->
<!--
util命名空间中的元素
<util:list/> 创建一个java.util.List类型的bean
<util:constant static-field=""/> 引用某个类型的public static域,并将其暴露给bean。
<util:map/> 创建一个java.util.Map类型的bean
<util:properties/> 创建一个java.util.Properties类型的bean
<util:set/> 创建一个java.util.Set类型的bean
<util:property-path path=""/> 引用一个bean的属性,并将其暴露为bean
-->
<!--这是一个类型为List的bean-->
<util:list id="beanlist">
<ref bean="bean1"/>
<ref bean="bean1"/>
</util:list>
<util:list id="strings">
<value>理化</value>
<value>梨花</value>
<value>李华</value>
</util:list>
<!--p命名空间对属性进行注入,用法和c命名空间相似-->
<bean id="bean3ByP" class="com.spring.bean.Bean3"
p:bean2-ref="bean2"
p:name="丽华"
p:bean1List-ref="beanlist"
p:strings-ref="strings"
/>
</beans>
欢迎探讨哦。