Spring:@Service注解

正常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

发表回复