Need some help to understand Anotations - Spring a

2019-04-09 04:21发布

I am trying to learn Spring and Hibernate and i am really struggling to understand Annotations and how they work. Most of the example i am seeing on the Internet are annotation based examples so i need to understand how the annotations work first before i can learn Spring or Hibernate

I have an idea of what they are and what they are used for. I know that they replace the xml configuration. I.e. You can configure beans directly within Java code using annotations. What i dont understand is how to use them and when they can be used.

Trying to understand how this can be done i think it would be helpful if i see the difference between the two. I have here a simple Spring program. If i was to convert this sample program to use annotations what would i need to do?

The reason i want to do it this way is because the program i have provided below is one that i understand very well (an example from the Spring in Action book that i am currently reading). If it is converted to an annotations version i will get an idea as to how and where annotations can be used.

Any suggestions?

Thanks in advance


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>

Instrumentalist interface

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

Instrumentalist implementor

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;
    }   
}

Instruments - Piano

package com.sia.ch1.instrumentalist;

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

Instruments - Saxophone

package com.sia.ch1.instrumentalist;

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

Main class

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();
            }
        }   
    }

Exception

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
    }
}

Edit 1

To try and convert the above i am going through these two simple examples:

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

I kind of understand the examples in the first URL but the second one confused me a bit. In the example in the second URL, what is purpose of the SummaryConfig class? It looks as though the SummaryConfig class is a Java version of the XML file. This approach was not used in the example in the first example. What is the difference between the two?

Could it be that when you using annotations you can put the configuration details in a Java class (e.g. SummaryConfig) and you can also put the annotations in the beans themselves as in the examples in the first URL?

Thanks

Edit 2

Here is what i have done so far,

I have modified the xml document to remove the configuration and enable the auto-scan of components (Note: i changed the package name for the modified versions)

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

Added the @Component annotation to the Piano and Saxophone classes. I think this tells the container that this class should be included in the classes to be auto-scanned. right?

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");
    }
}

This is where i am stuck (the Instrumentalist class).

  • Is the @Component annotation required in this class? Or is it only required if the class is to be referenced from another class?

  • I know that i need to @Autowire the instrument and song properties but how do i know if i want to autowire byname or bytype etc

  • How would i autowire the String property if in this class there is no bean that represents it? i.e the instrument property would refer to the piano class but what would the song property be autowired with?


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;
        }   
    }

I think i am right in that no annotations are required on any of the other classes.

Thanks

2条回答
啃猪蹄的小仙女
2楼-- · 2019-04-09 05:02

This is where i am stuck (the Instrumentalist class).

  • Is the @Component annotation required in this class? Or is it only required if the class is to be referenced from another class?

Yes, it is, if you want the annotation scanning to create the bean from the class for you without separate xml-configuration. Since you ask for an Instrumentalist -implementation with the bean name kenny (by name, not by type Instrumentalist) in your main-method, it also needs to be named.

Classes annotated with @Component, @Repository, @Controller and @Service are the ones that Spring scans for when the ApplicationContext is configured. The difference between these four annotations is semantical (differentiating the role of the class in the code), they all do exactly the same thing (unless for example you have some AOP-stuff that handles only certain annotation-types; for now you don't need to care about this).

Annotating a class with of any of the aforementioned annotation is same as declaring a bean in the xml:

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

is same as

@Component
public class Saxophone implements Instrument{

Note that by default the bean is named same as the class, except the first letter of the class name is changed to lowercase (so @Component public class SomeClass would create a bean called "someClass").

If you want to name your bean, you give the name as a parameter to the annotation:

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

is same as

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

Another way to give the parameter to the annotation would be using @Component(value="kenny"). The reason the value= -part is optional, is because the annotations work like this: if there is only one parameter given without telling the field-name and the annotation contains a field called value, the parameter will be set to the value-field. If the field name is something else, or you want to set multiple fields of the annotation, you need to explicitly define the fields: @SomeAnnotation(field1="some string", field2 = 100) or @SomeAnnotation(value="someValue", anotherField="something else"). This is a bit besides the point, but it's good to know, as it can be confusing at first.

So, the @Component-annotation tells the Spring context that you want to create a bean (or beans, see @Scope) from the annotated classes. When there's no @Scope -annotation set, the beans will be created as singletons by default (you can autowire the bean to multiple classes, but they all see the same, single instance). For more information about the different scopes, I suggest reading the official documentation.

  • I know that i need to @Autowire the instrument and song properties
    but how do i know if i want to autowire byname or bytype etc

Usually, if you only have a single implementation of a type (interface), it's more convenient to autowire by type. Autowiring by type works, when there is only a single implementation, because otherwise Spring cannot decide which implementation to instance and inject.

In your case you have two different classes implementing the Instrument-interface: Saxophone and Piano. If you try to autowire by-type the Instrument-field of the Instrumentalist, you will get an exception when Spring is constructing the 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]

Since there are two Instrument-implementations, Spring doesn't have enough information to determine which one you want injected in the Instrumentalist. This is where the @Qualifier -annotation steps in. With the @Qualifier, you tell Spring to inject the autowired dependency by-name. To inject the Piano-implementation of Instrument using the @Qualifier in the Instrumentalist is done by:

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

is same as

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

Note that there's no need (but you can if you want to) to use @Component("piano") in the Piano-class, as the default naming changes the first letter of the class to lowercase and then uses it as the bean-name.

  • How would i autowire the String property if in this class there is no bean that represents it? i.e the instrument property would refer to the piano class but what would > the song property be autowired with?

This is where the line is drawn with annotations (AFAIK); you cannot declare a bean of type String with annotations (Since java.lang.String is not an interface, cannot be extended as it's final and doesn't have an interface). However, with xml-configuration this is possible:

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

You can mix-and-match XML and annotations, and refer to beans declared in xml from annotations and vice versa, to inject this bean into Instrumentalists' song-field:

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

Hope this helped you to understand Springs' annotations in general and get started, I'd still strongly suggest you to read the official documentation, as there's a LOT more. If you prefer to read books instead of from screen, I'd suggest for example Appress's Spring Recipes (but I'm sure there are many other good books too).

查看更多
Explosion°爆炸
3楼-- · 2019-04-09 05:08

Annotations could be used as markers like marker interfaces

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

Serializable is just a marker interface so that your application can know information about the class at runtime (basically by reflection).

The problem with marker interfaces is that you can't use them to mark fields or methods,this why annotations were introduced.

assume that you have this annotation

public @interface myAnnotation{
}

you can simply get methods or fields which are decorated by this marker at run-time.

Hibernate and Spring as many frameworks require some information about your code or classes ,how would you achieve this,if you were the developer of these frameworks ?of course annotations are the best solution(at least the cleaner way)

Don't consider marker interfaces obsolete.There is also some advantages for using markers because they ensure type safety.

 void M(Serializable s)

you can't pass any object to this method unless it implements the Serializable marker.For more details consider reading Effective Java there is a great explanation there.

查看更多
登录 后发表回答