Spring框架中,对于不同的bean进行组合实现复杂功能的过程称为装配。
1、装配模式介绍
在Spring中,支持5种装配模式:
no – 缺省情况下,自动配置是通过“ref”属性手动设定。
byType – 按数据类型自动装配。如果一个bean的数据类型是用其它bean属性的数据类型,兼容并自动装配它。
byName – 根据属性名称自动装配。如果一个bean的名称和其他bean属性的名称是一样的,将会自装配它。
constructor – 在构造函数参数的byType方式。
autodetect – 如果找到默认的构造函数,使用“自动装配用构造”; 否则,使用“按类型自动装配”。
接下来,我们通过如下的代码对这几种装配模式做下简单测试。代码结构如下:
structure of the project
代码如下:
pom.xml:
4.0.0 com.SpaceCat.SpringHelloWorld SpringHelloWorld 1.0-SNAPSHOT org.springframework spring-core 4.1.4.RELEASE org.springframework spring-context 4.1.4.RELEASE // Interface HelloWorld public interface HelloWorld { public void sayHello(); }
.
// Class implements HelloWorld public class SpringHelloWorld implements HelloWorld { public void sayHello() { System.out.println("Spring say Hello!"); } } // Other class implements HelloWorld public class StrutsHelloWorld implements HelloWorld { public void sayHello() { System.out.println("Struts say Hello!"); } } public class HelloWorldService { private HelloWorld helloWorld; public HelloWorldService() { } public HelloWorldService(HelloWorld h) { this.helloWorld = h; } public void setHelloWorld(HelloWorld helloWorld) { this.helloWorld = helloWorld; } public HelloWorld getHelloWorld() { return this.helloWorld; } }
HelloWorldProgram.java:
package com.SpaceCat.HelloWorld.spring; import com.SpaceCat.HelloWorld.spring.helloworld.HelloWorld; import com.SpaceCat.HelloWorld.spring.helloworld.HelloWorldService; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * Created by chengxia on 2019/4/30. */ public class HelloWorldProgram { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); HelloWorldService service = (HelloWorldService) context.getBean("helloWorldService"); HelloWorld hw= service.getHelloWorld(); hw.sayHello(); } }
在下面的例子中,我们通过修改beans.xml配置文集爱你,然后,运行HelloWorldProgram.java就可以对这几种装配模式进行举例说明。
1.1 no
缺省情况下,可以通过“ref”属性手动设定bean之间的关联关系。
运行结果:
Spring Say Hello!! Process finished with exit code 0
1.2 byType
按数据类型自动装配。如果一个bean的数据类型是用其它bean属性的数据类型,兼容并自动装配它。
接下来的例子中,我们只定义一个HelloWord类型的bean,然后,在定义bean时,通过autowire属性设置自动装配类型为byType。即可完成bean装配。
运行结果:
Struts Say Hello!! Process finished with exit code 0
1.3 byName
根据属性名称自动装配。如果一个bean的名称和其他bean属性的名称是一样的,将会自装配它。
接下来,我们将一个bean的id设置为与HelloWorldService类的属性名(helloWorld)一致。这样,这个bean就会自动被装配到HelloWorldService。
运行结果:
Spring Say Hello!! Process finished with exit code 0
1.4 constructor
在构造函数参数的byType方式。就是根据构造函数参数的类型进行自动装配,如果有一个bean的类型和HelloWorldService类的构造函数类型一样,就会被自动装配过去。
运行结果:
Struts Say Hello!! Process finished with exit code 0
1.5 autodetect
这种有些资料写的有些歧义,下面是Stack Overflow上面的一个解释:
Autowiring by autodetect uses either of two modes i.e. constructor or byType modes. First it will try to look for valid constructor with arguments, If found the constructor mode is chosen. If there is no constructor defined in bean, or explicit default no-args constructor is present, the autowire byType mode is chosen.
意思就是:如果发现带参数的构造函数,就使用constructor模式进行装配;如果没有定义构造函数,或者是显式定义了没有参数的构造函数,就使用byType模式进行装配。
这里我测试用的环境中,spring的版本是4.1.4.RELEASE,在测试时IDEA提示报错,如下图:
autodetect error tips
可能在高版本的Spring框架中这种装配模式已经不推荐使用了。但是,运行时仍然可以正常运行,如下:
运行结果:
Struts Say Hello!! Process finished with exit code 0
2、更加细粒度的装配控制
2.1 特定属性的自动装配
在前面装配模式介绍的例子中,如果一个bean配置了autowire属性,那么它的所有属性都会被自动装配。但是,有时候,我们只希望某些特定的属性配装配。这时候,就需要用到@Autowired注解。
在Spring框架中,可以使用@Autowired注解通过setter方法,构造函数或字段自动装配Bean。通过它,可以对某个特定的bean属性完成自动装配。@Autowired注解是通过匹配数据类型自动装配Bean的。
启用@Autowired需要注册“AutowiredAnnotationBeanPostProcessor',可以通过两种方式:
在beans.xml文件中,加入(包括xmlns:context="http://www.springframework.org/schema/context")
在beans.xml中添加
这样,我们就可以通过将@Autowired注解用到bean的特定属性、setter方法或者构造函数等,来实现对自动装配更加细粒度的控制。
下面的例子中,三种实现的效果一样:
1. 用在属性上
package com.SpaceCat.HelloWorld.spring.helloworld; import org.springframework.beans.factory.annotation.Autowired; /** * Created by chengxia on 2019/4/30. */ public class HelloWorldService { @Autowired private HelloWorld helloWorld; public HelloWorldService() { } public HelloWorldService(HelloWorld h) { this.helloWorld = h; } public void setHelloWorld(HelloWorld helloWorld) { this.helloWorld = helloWorld; } public HelloWorld getHelloWorld() { return this.helloWorld; } }
2. 用在构造函数上
package com.SpaceCat.HelloWorld.spring.helloworld; import org.springframework.beans.factory.annotation.Autowired; /** * Created by chengxia on 2019/4/30. */ public class HelloWorldService { private HelloWorld helloWorld; public HelloWorldService() { } @Autowired public HelloWorldService(HelloWorld h) { this.helloWorld = h; } public void setHelloWorld(HelloWorld helloWorld) { this.helloWorld = helloWorld; } public HelloWorld getHelloWorld() { return this.helloWorld; } }
3. 用在setter方法上
package com.SpaceCat.HelloWorld.spring.helloworld; import org.springframework.beans.factory.annotation.Autowired; /** * Created by chengxia on 2019/4/30. */ public class HelloWorldService { private HelloWorld helloWorld; public HelloWorldService() { } public HelloWorldService(HelloWorld h) { this.helloWorld = h; } @Autowired public void setHelloWorld(HelloWorld helloWorld) { this.helloWorld = helloWorld; } public HelloWorld getHelloWorld() { return this.helloWorld; } }
运行结果如下:
Struts Say Hello!! Process finished with exit code 0
这里需要注意的是,这个注解并不是只通过byType的方式进行自动装配。准确地说:
@Autowired注入首先根据byType注入,当类型大于1时在根据byName注入。
2.2 指定自动装配特定的bean
在上面介绍的装配模式中,如果有两个符合条件的bean,框架就会报错,如下:
package com.SpaceCat.HelloWorld.spring.helloworld; import org.springframework.beans.factory.annotation.Autowired; /** * Created by chengxia on 2019/4/30. */ public class HelloWorldService { private HelloWorld helloWorld; public HelloWorldService() { } public HelloWorldService(HelloWorld h) { this.helloWorld = h; } @Autowired public void setHelloWorld(HelloWorld helloWorld) { this.helloWorld = helloWorld; } public HelloWorld getHelloWorld() { return this.helloWorld; } }
.
运行如下:
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'helloWorldService': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire method: public void com.SpaceCat.HelloWorld.spring.helloworld.HelloWorldService.setHelloWorld(com.SpaceCat.HelloWorld.spring.helloworld.HelloWorld); nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.SpaceCat.HelloWorld.spring.helloworld.HelloWorld] is defined: expected single matching bean but found 2: strutsHelloWorld,springHelloWorld at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:334) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1202) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:537) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476) at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:303) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:299) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194) at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:762) at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:757) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:480) at org.springframework.context.support.ClassPathXmlApplicationContext.(ClassPathXmlApplicationContext.java:139) at org.springframework.context.support.ClassPathXmlApplicationContext.(ClassPathXmlApplicationContext.java:83) at com.SpaceCat.HelloWorld.spring.HelloWorldProgram.main(HelloWorldProgram.java:14) Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire method: public void com.SpaceCat.HelloWorld.spring.helloworld.HelloWorldService.setHelloWorld(com.SpaceCat.HelloWorld.spring.helloworld.HelloWorld); nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.SpaceCat.HelloWorld.spring.helloworld.HelloWorld] is defined: expected single matching bean but found 2: strutsHelloWorld,springHelloWorld at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:649) at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88) at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:331) ... 13 more Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.SpaceCat.HelloWorld.spring.helloworld.HelloWorld] is defined: expected single matching bean but found 2: strutsHelloWorld,springHelloWorld at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1061) at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:949) at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:606) ... 15 moreProcess finished with exit code 1
这时候,可以通过@Quanlifier告诉Spring哪些bean应当自动装配。如下:
package com.SpaceCat.HelloWorld.spring.helloworld; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; /** * Created by chengxia on 2019/4/30. */ public class HelloWorldService { @Autowired @Qualifier(value = "strutsHelloWorld") private HelloWorld helloWorld; public HelloWorldService() { } public HelloWorldService(HelloWorld h) { this.helloWorld = h; } public void setHelloWorld(HelloWorld helloWorld) { this.helloWorld = helloWorld; } public HelloWorld getHelloWorld() { return this.helloWorld; } }
注意,这里其实要同时修改beans.xml文件,否则,@Qualifier注解不起作用(应该主要是为了添加)。如下:
运行结果:
Struts Say Hello!! Process finished with exit code 0