Spring提供的三种装配Bean方式
在spring中,对象不需要自己查找或创建与其所关联的其他对象,spring容器负责把需要相互协作的对象引用赋予各个对象。创建应用对象之间协作关系的行为通常称为装配(wiring),这也是依赖注入(di)的本质。
spring提供了三种主要的装配bean的方式:
- 隐式的bean发现和自动化装配
- 在java中进行显示配置
- 在xml中进行显示配置
隐式的bean发现和自动化装配
spring从两个方面来实现自动化装配:
- 组件扫描:spring会自动发现应用上下文中所创建的bean。
- 自动装配:spring自动满足bean之间的依赖。
示例代码:
package phone; public interface hwphone { void call(); }
package phone; import org.springframework.beans.factory.annotation.qualifier; import org.springframework.stereotype.component; @component("p30phone") @qualifier("p30") public class p30phone implements hwphone { @override public void call() { system.out.println("使用 p30 手机呼叫..."); } }
@component注解表明该类会作为组件类,spring会为这个类创建bean。“p30phone"是定义bean的id。
@qualifier("p30")创建自定义限定符,防止不仅有一个bean能够匹配结果。
package phone; import org.springframework.beans.factory.annotation.qualifier; import org.springframework.stereotype.component; @component("mete30phone") @qualifier("mete30") public class mete30phone implements hwphone { @override public void call() { system.out.println("使用 mete30 手机呼叫..."); } }
package phone; import org.springframework.beans.factory.annotation.autowired; import org.springframework.beans.factory.annotation.qualifier; import org.springframework.stereotype.component; @component("lihua") public class somebody { @autowired @qualifier("p30") private hwphone hwphone; public void usephone() { hwphone.call(); } }
@autowired 自动装配。
@qualifier("p30")使用限定符。
package phone; import org.springframework.context.annotation.componentscan; import org.springframework.context.annotation.configuration; @configuration @componentscan(basepackages = {"phone"}) public class phoneconfig { }
@componentscan启动注解扫描,basepackages={"phone"},指定扫描phone包和phone下的子包;不配置basephckages则扫描当前配置类的包和子包。
package phone; import org.springframework.context.annotation.annotationconfigapplicationcontext; public class main { public static void main(string[] args) { annotationconfigapplicationcontext context = new annotationconfigapplicationcontext(phoneconfig.class); somebody somebody = (somebody) context.getbean("lihua"); somebody.usephone(); } }
运行结果:
使用 p30 手机呼叫...
在java中进行显示配置
想要将第三方库中的组件装配到你的应用中,是没有办法在它的类上添加@component和@autowired注解的,这时就不能使用自动化装配了,必须采用显式配置,而显示配置有两种可选方式:使用java配置或xml配置。javaconfig是配置代码,它不应包含任何业务逻辑,也不应侵入到业务逻辑代码之中。
在上一节的示例代码中,改为如下代码:
package phone; public interface hwphone { void call(); }
package phone; public class p30phone implements hwphone { @override public void call() { system.out.println("使用 p30 手机呼叫..."); } }
package phone; public class mete30phone implements hwphone { @override public void call() { system.out.println("使用 mete30 手机呼叫..."); } }
此处删除了@component注解,和 @qualifier注解。
package phone; public class somebody { private hwphone hwphone; public somebody(hwphone hwphone) { this.hwphone = hwphone; } public void usephone() { hwphone.call(); } }
删除了@autowired注解,以构造器传入hwphone引用对象,也可以通过set方法设置引用对象。
package phone; import org.springframework.beans.factory.annotation.qualifier; import org.springframework.context.annotation.bean; import org.springframework.context.annotation.configuration; @configuration public class phoneconfig { @bean @qualifier("p30") public p30phone p30phone() { return new p30phone(); } @bean @qualifier("mete30") public mete30phone mete30phone() { return new mete30phone(); } @bean(name = "lihua") public somebody somebody() { return new somebody(mete30phone()); } @bean(name = "xiaoming") public somebody someone(@qualifier("p30") hwphone phone) { return new somebody(phone); } }
此处删除了@componentscan注解,以javaconfig方式配置bean。
“lihua”这个bean是通过调用mete30phone()得到的,但情况并非完全如此。因为mete30phone()方法上添加了@bean注解,spring将会拦截所有对它的调用,并确保直接返回该方法所创建的bean,而不是每次都对其进行实际的调用。
“xiaoming”这个bean,someone() 方法请求一个hwphone作为参数。当spring调用someone()创建bean时,它会自动装配一个hwphone到配置方法之中。此处的@qualifier("p30")作为限定符,防止两个bean(p30phone和mete30phone)都能够匹配结果。
package phone; import org.springframework.context.annotation.annotationconfigapplicationcontext; public class main { public static void main(string[] args) { annotationconfigapplicationcontext context = new annotationconfigapplicationcontext(phoneconfig.class); somebody somebody = (somebody) context.getbean("lihua"); somebody.usephone(); somebody someone = (somebody) context.getbean("xiaoming"); someone.usephone(); } }
运行结果:
使用 mete30 手机呼叫... 使用 p30 手机呼叫...
在xml中进行显示配置
在javaconfig中所需要的只是@configuration,但在使用xml时,需要在配置文件的顶部声明多个xml模式(xsd)文件,这些文件定义了配置spring的xml元素。
创建一个application.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-4.3.xsd"> <bean id="p30phone" class="phone.p30phone"/> <bean id="mete30phone" class="phone.mete30phone"/> <bean id="lihua" class="phone.somebody"> <constructor-arg ref="p30phone"/> </bean> <bean id="xiaoming" class="phone.somebody"> <constructor-arg ref="mete30phone"/> </bean> </beans>
基于上一节的示例,不用phoneconfig配置类,并使用classpathxmlapplicationcontext创建spring容器,main方法如下:
package phone; import org.springframework.context.support.classpathxmlapplicationcontext; public class main { public static void main(string[] args) { classpathxmlapplicationcontext context = new classpathxmlapplicationcontext("phone/application.xml"); somebody somebody = (somebody) context.getbean("lihua"); somebody.usephone(); somebody someone = (somebody) context.getbean("xiaoming"); someone.usephone(); } }
运行结果:
使用 p30 手机呼叫... 使用 mete30 手机呼叫...
导入和混合配置
在javaconfig中引用xml
package phone; import org.springframework.context.annotation.bean; import org.springframework.context.annotation.configuration; @configuration public class phoneconfig { @bean public p30phone p30phone() { return new p30phone(); } @bean public mete30phone mete30phone() { return new mete30phone(); } }
此处只配置phone的两个bean。
<?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-4.3.xsd"> <bean id="lihua" class="phone.somebody"> <constructor-arg ref="p30phone"/> </bean> <bean id="xiaoming" class="phone.somebody"> <constructor-arg ref="mete30phone"/> </bean> </beans>
xml配置文件配置了"lihua"和"xiaoming"两个somebody的bean。
package phone; import org.springframework.context.annotation.configuration; import org.springframework.context.annotation.import; import org.springframework.context.annotation.importresource; @configuration @import({phoneconfig.class}) @importresource("classpath:phone/application.xml") public class somebodyconfig { }
配置通过@import引用phone的配置,以及通过@importresource引用xml的配置
package phone; import org.springframework.context.annotation.annotationconfigapplicationcontext; public class main { public static void main(string[] args) { annotationconfigapplicationcontext context = new annotationconfigapplicationcontext(somebodyconfig.class); somebody somebody = (somebody) context.getbean("lihua"); somebody.usephone(); somebody someone = (somebody) context.getbean("xiaoming"); someone.usephone(); } }
运行结果:
使用 p30 手机呼叫... 使用 mete30 手机呼叫...
参考《spring实战(第4版)》