【问题标题】:Need some help to understand Anotations - Spring annotations需要一些帮助来理解注解——Spring注解
【发布时间】:2012-02-10 09:14:04
【问题描述】:

我正在努力学习 Spring 和 Hibernate,但我真的很难理解 Annotations 以及它们是如何工作的。我在 Internet 上看到的大多数示例都是基于注释的示例,因此我需要先了解注释的工作原理,然后才能学习 Spring 或 Hibernate

我知道它们是什么以及它们的用途。我知道他们替换了 xml 配置。 IE。您可以使用注释直接在 Java 代码中配置 bean。我不明白的是如何使用它们以及何时可以使用它们。

试图了解如何做到这一点,我认为如果我看到两者之间的区别会很有帮助。我这里有一个简单的 Spring 程序。如果我要将此示例程序转换为使用注释,我需要做什么?

我想这样做的原因是因为我在下面提供的程序是我非常了解的程序(我目前正在阅读的 Spring in Action 书中的一个示例)。如果将其转换为注释版本,我将了解如何以及在何处使用注释。

有什么建议吗?

提前致谢


instrumentalist.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-2.0.xsd">

    <bean id="saxophone" class="com.sia.ch1.instrumentalist.Saxophone" />
    <bean id="piano" class="com.sia.ch1.instrumentalist.Piano" />

    <!--  Injecting into bean properties Ken 1 -->
    <bean id="kenny" class="com.sia.ch1.instrumentalist.Instrumentalist">
        <property name="song" value="Jingle Bells"/>
        <property name="instrument" ref="piano"/>       
    </bean> 
</beans>

乐器界面

package com.sia.ch1.instrumentalist;
public interface Instrument {
    void play();
}

器乐实现者

package com.sia.ch1.instrumentalist;

import com.sia.ch1.performer.PerformanceException;
import com.sia.ch1.performer.Performer;

public class Instrumentalist implements Performer{

    private Instrument instrument;
    private String song;

    public Instrumentalist(){}

    public void perform() throws PerformanceException{
        System.out.print("Playing " + song + " : ");        
        instrument.play();
    }

    public void setInstrument(Instrument instrument) {
        this.instrument = instrument;
    }   

    public void setSong(String song) {
        this.song = song;
    }   
}

乐器 - 钢琴

package com.sia.ch1.instrumentalist;

public class Piano implements Instrument{
    public Piano(){}
    public void play(){
        System.out.println("PLINK PLINK");
    }
}

乐器 - 萨克斯管

package com.sia.ch1.instrumentalist;

public class Saxophone implements Instrument{
    public Saxophone(){}
    public void play(){
        System.out.println("TOOT TOOT TOOT");
    }
}

主类

package com.sia.ch1.instrumentalist;

    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.FileSystemXmlApplicationContext;

    import com.sia.ch1.performer.PerformanceException;
    import com.sia.ch1.performer.Performer;

    public class InstrumentalistApp {

        public static void main(String[] args){
            ApplicationContext ctx = new FileSystemXmlApplicationContext("c:\\projects\\test\\conf\\instrumentalist.xml");

            Performer performer = (Performer) ctx.getBean("kenny");

            try {
                performer.perform();            
            } catch (PerformanceException e) {
                e.printStackTrace();
            }
        }   
    }

例外

package com.sia.ch1.performer;

public class PerformanceException extends Exception {

    public PerformanceException() {
        super();
        // TODO Auto-generated constructor stub
    }

    public PerformanceException(String message, Throwable cause) {
        super(message, cause);
        // TODO Auto-generated constructor stub
    }

    public PerformanceException(String message) {
        super(message);
        // TODO Auto-generated constructor stub
    }

    public PerformanceException(Throwable cause) {
        super(cause);
        // TODO Auto-generated constructor stub
    }
}

编辑 1

为了尝试转换上述内容,我将通过以下两个简单示例:

Ex1:http://jroller.com/habuma/entry/reducing_xml_with_spring_2

Ex2:http://www.theserverside.com/tutorial/Spring-Without-XML-The-Basics-of-Spring-Annotations-vs-Spring-XML-Files

我有点理解第一个 URL 中的示例,但第二个 URL 让我有点困惑。在第二个 URL 的示例中,SummaryConfig 类的用途是什么?看起来SummaryConfig 类是 XML 文件的 Java 版本。在第一个示例中的示例中未使用此方法。两者有什么区别?

难道当您使用注解时,您可以将配置详细信息放在 Java 类中(例如 SummaryConfig),并且您还可以将注解放在 bean 本身中,就像第一个 URL 中的示例一样?

谢谢

编辑 2

这是我到目前为止所做的,

我已经修改了xml文档以移除配置并启用组件的自动扫描(注意:我更改了修改版本的包名称)

<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-2.5.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-2.5.xsd">

    <context:component-scan base-package="com.sia.ch1.instrumentalist.annotate" />

</beans>

在 Piano 和 Saxophone 类中添加了 @Component 注解。我认为这告诉容器这个类应该包含在要自动扫描的类中。正确的?

package com.sia.ch1.instrumentalist.annotate;

import org.springframework.stereotype.Component;

@Component
public class Piano implements Instrument{

    public Piano(){}
    public void play(){
        System.out.println("PLINK PLINK");
    }
}

package com.sia.ch1.instrumentalist.annotate;

import org.springframework.stereotype.Component;

@Component
public class Saxophone implements Instrument{
    public Saxophone(){}
    public void play(){
        System.out.println("TOOT TOOT TOOT");
    }
}

这就是我卡住的地方(乐器演奏家)。

  • 这个类需要@Component注解吗?还是仅在要从另一个类引用该类时才需要?

  • 我知道我需要 @Autowire 乐器和歌曲属性,但我怎么知道我是否要自动连接 byname 或 bytype 等

  • 如果在此类中没有代表它的 bean,我将如何自动装配 String 属性?即乐器属性将引用钢琴类,但歌曲属性将自动连接到什么?


package com.sia.ch1.instrumentalist.annotate;
//
    import org.springframework.stereotype.Component;
    import com.sia.ch1.performer.PerformanceException;
    import com.sia.ch1.performer.Performer;
    //
        @Component
        public class Instrumentalist implements Performer{

        private Instrument instrument;
        private String song;

        public Instrumentalist(){}

        public void perform() throws PerformanceException{
            System.out.print("Playing " + song + " : ");        
            instrument.play();
        }

        public void setInstrument(Instrument instrument) {
            this.instrument = instrument;
        }   

        public void setSong(String song) {
            this.song = song;
        }   
    }

我认为我是对的,因为其他任何类都不需要注释。

谢谢

【问题讨论】:

  • 至少尝试自己做而不是问。这就是你将如何学习。它会迫使你阅读文档。您将需要 Component、Autowired 和 Qualifier。
  • 感谢@JB Nizet。我正在浏览一些文档,但我正在努力解决它。你能推荐一些好的教程吗?我先看看你提到的那三个注释。
  • 我实际上已经完成了另一个注释教程并运行了程序。我想了解的是哪个注释替换了 XML 配置的哪个位。使用上面的示例会更容易,因为我了解配置。
  • Spring 参考文档是了解 Spring 的最佳场所。组件将一个类定义为一个 bean,并允许给它一个名字。 Autowired 告诉 Spring 必须调用某些字段或构造函数或设置器来注入 bean。限定符允许指定要注入的 bean 的名称,以防您有多个相同类型的 bean。

标签: java spring oop dependency-injection annotations


【解决方案1】:

这就是我卡住的地方(乐器演奏者类)。

  • 这个类需要@Component注解吗?还是只有 如果要从另一个类引用该类,是否需要?

是的,如果您希望注释扫描为您从类中创建 bean,而无需单独的 xml 配置。由于您在主方法中要求使用 bean 名称 kenny 的 Instrumentalist 实现(按名称,而不是按类型 Instrumentalist),因此它也需要命名。

使用@Component、@Repository、@Controller 和@Service 注解的类是 Spring 在配置 ApplicationContext 时扫描的类。这四个注释之间的区别是语义上的(区分类在代码中的角色),它们都做同样的事情(除非例如你有一些只处理某些注释类型的 AOP 东西;现在你没有'不需要关心这个)。

使用上述任何注释对类进行注释与在 xml 中声明 bean 相同:

<bean id="saxophone" class="com.sia.ch1.instrumentalist.Saxophone"> ... </bean>

与

相同
@Component
public class Saxophone implements Instrument{

请注意,默认情况下,bean 的名称与类相同,只是将类名的第一个字母更改为小写(因此@Component public class SomeClass 将创建一个名为“someClass”的 bean)。

如果你想给你的bean命名,你可以把名字作为参数给注解:

@Component("kenny")
public class Instrumentalist implements Performer {

与

相同
 <bean id="kenny" class="com.sia.ch1.instrumentalist.Instrumentalist">

为注解提供参数的另一种方法是使用@Component(value="kenny")。 value= 部分是可选的,因为注解是这样工作的:如果只给出一个参数而不告诉字段名称,并且注解包含一个名为 value,参数将设置为 value-字段。如果字段名是别的,或者要设置注解的多个字段,需要显式定义字段:@SomeAnnotation(field1="some string", field2 = 100)或@SomeAnnotation(value="someValue", anotherField="something else")。这有点题外话,但很高兴知道,因为一开始可能会造成混淆。

因此,@Component-annotation 告诉 Spring 上下文您要从带注释的类中创建一个 bean(或 bean,请参阅 @Scope)。当没有 @Scope -annotation 集时,bean 将默认创建为单例(您可以将 bean 自动装配到多个类,但它们都看到相同的单个实例)。 For more information about the different scopes, I suggest reading the official documentation.

  • 我知道我需要@Autowire 乐器和歌曲属性
    但是我怎么知道我是要自动装配 byname 还是 bytype 等

通常,如果您只有一个类型(接口)的实现,则按类型自动装配会更方便。当只有一个实现时,按类型自动装配有效,否则 Spring 无法决定实例化和注入哪个实现。

在您的情况下,您有两个不同的类实现Instrument-接口:Saxophone 和Piano。如果您尝试通过类型自动装配 Instrumentalist 的 Instrument-field,当 Spring 构造 Instrumentalist-bean 时,您将收到异常:

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [com.sia.ch1.instrumentalist.annotate.Instrument] is defined: expected single matching bean but found 2: [piano, saxophone]

由于有两个Instrument 实现,Spring 没有足够的信息来确定您要在Instrumentalist 中注入哪一个。这是@Qualifier -annotation 介入的地方。使用@Qualifier,你告诉Spring注入自动装配的依赖by-name。要使用Instrumentalist 中的@Qualifier 注入Instrument 的Piano 实现,请执行以下操作:

@Component(value="kenny")
public class Instrumentalist implements Performer
{
    @Autowired
    @Qualifier("piano")
    private Instrument instrument;

与

相同
<bean id="kenny" class="com.sia.ch1.instrumentalist.Instrumentalist">
    <property name="instrument" ref="piano"/>       
</bean>

请注意,在Piano-class 中没有必要(但如果您愿意也可以)使用@Component("piano"),因为默认命名会将类的第一个字母更改为小写,然后将其用作 bean -名字。

  • 如果在这个类中没有 bean,我将如何自动装配 String 属性 代表它?即,乐器属性将引用钢琴类,但 > 歌曲属性会自动关联什么?

这是用注释画线的地方(AFAIK);您不能使用注释声明 String 类型的 bean(因为 java.lang.String 不是接口,因此不能扩展,因为它是最终的并且没有接口)。但是,使用 xml 配置是可能的:

<bean id="songName" class="java.lang.String">   
    <constructor-arg value="Valley of the Queens"/>
</bean>

您可以混合匹配 XML 和注解,并从注解引用在 xml 中声明的 bean,反之亦然,以将此 bean 注入 Instrumentalists' song-field:

@Autowired
@Qualifier("songName")
private String song;

希望这能帮助您大致了解 Springs 的注释并开始使用,我仍然强烈建议您阅读官方文档,因为还有很多内容。如果您更喜欢看书而不是从屏幕上看书,我建议例如Appress's Spring Recipes(但我相信还有很多其他好书)。

【讨论】:

    【解决方案2】:

    注释可以用作标记,如标记接口

    class Foo implements java.io.Serializable{
     ...
    }
    

    Serializable 只是一个标记接口,以便您的应用程序可以了解有关信息 运行时的类(基本上是通过反射)。

    标记接口的问题是您不能使用它们来标记字段或方法,这就是引入注释的原因。

    假设你有这个注解

    public @interface myAnnotation{
    }
    

    您可以在运行时简单地获取由该标记修饰的方法或字段。

    Hibernate 和 Spring 一样多的框架需要一些关于您的代码或类的信息,如果您是这些框架的开发者,您将如何实现这一点?当然注解是最好的解决方案(至少是更简洁的方式)

    不要认为标记接口已过时。使用标记还有一些优点,因为它们确保类型安全。

     void M(Serializable s)
    

    您不能将任何对象传递给此方法,除非它实现了 Serializable 标记。有关更多详细信息,请考虑阅读Effective Java,那里有很好的解释。

    【讨论】:

    • 有用的答案,但您可能会提到并非所有注释都在运行时可用。见底部this page,里面讲的是Retention。
    猜你喜欢
    • 2012-02-17
    • 1970-01-01
    • 2012-02-02
    • 2016-02-10
    • 1970-01-01
    • 2022-09-24
    • 1970-01-01
    • 1970-01-01
    • 2020-08-06
    相关资源
    最近更新 更多