正常Spring通过在xml里对bean进行装配和注入,然后通过@Resource,@Autowired注解来完成自动装配的工作,之前写过一篇两者之间的区别:Spring:自动装配的疑惑,@Autowired和@Resource梳理
我还是比较习惯用@Resource,毕竟是JDK自带,byName也更习惯一些
比如定义一个类
package com.lihuia.spring.hello; /** * Copyright (C), 2018-2019 * FileName: HelloWorld * Author: lihui * Date: 2019-09-07 */ public class HelloWorld { public void sayHelloWorld() { System.out.println("hello world"); } }
XML里定义装配方式
<bean id="helloWorld" class="com.lihuia.spring.hello.HelloWorld"></bean>
最后通过注解自动化装配
package com.lihuia.spring.hello; import javax.annotation.Resource; import org.testng.annotations.Test; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; /** * Copyright (C), 2018-2019 * FileName: HelloTest * Author: lihui * Date: 2019-09-07 */ @ContextConfiguration(locations = {"classpath:hello.xml"}) public class HelloTest extends AbstractTestNGSpringContextTests { @Resource private HelloWorld helloWorld; @Test(description = "测试sayHelloWorld") public void sayHelloWorldTest() { helloWorld.sayHelloWorld(); } }
同样的道理,如果是interface,bean的配置class需要指定实现类,自动化装配方式没变化
<bean id="helloWorld" class="com.lihuia.spring.hello.HelloWorld"></bean> <bean id="hello" class="com.lihuia.spring.hello.HelloImpl"></bean>
如果觉得XML里bean的配置有些麻烦,又是bean id,又是类型,这里有一个@Service注解可以完成自动扫描,大致玩法如下
首先在xml里添加配置
<context:component-scan base-package="com.lihuia.spring"> </context:component-scan>
然后类添加@Service注解
@Service public class HelloWorld { public void sayHelloWorld() { System.out.println("hello world"); } }
最终测试类里没有任何变化
当HelloWorld添加了@Service注解,就声明是一个bean,这样就可以通过@Resource注解来自动注入
同样,如果是接口,@Service注解还是要添加到实现类上
@Service public class HelloImpl implements Hello { @Override public void sayHello(String message) { System.out.println("Hello, " + message); } }
这里还有一个问题,通过@Service注解声明的bean,对应的id是类名第一个字母小写的变量,那么假如两个不同的package里有相同的类都添加了@Service注解,而xml配置里扫描的对象是外层包目录
└── spring/ ├── hello/ │ ├── Hello.java │ ├── HelloImpl.java │ └── HelloWorld.java └── helloworld/ └── HelloWorld.java
扫描的是com.lihuia.spring,而该目录下有两个package:hello和helloworld,这时候两个包里的HelloWorld类都添加了@Service注解,那么@Resource注解要进行自动化装配的时候,由于两个bean id都是helloWorld,因此并不知道要装配哪个类,报错信息如下
Caused by: org.springframework.beans.factory.BeanDefinitionStoreException: Unexpected exception parsing XML document from class path resource [hello.xml]; nested exception is org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name 'helloWorld' for bean class [com.lihuia.spring.helloworld.HelloWorld] conflicts with existing, non-compatible bean definition of same name and class [com.lihuia.spring.hello.HelloWorld] at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:419) at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:336) at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:304) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:188) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:224) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:195) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:257) at org.springframework.test.context.support.AbstractGenericContextLoader.loadBeanDefinitions(AbstractGenericContextLoader.java:257) at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:124) at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:60) at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.delegateLoading
OVER