我对Spring常用注解的理解

我对Spring常用注解的理解

传统的Spring做法是使用.xml文件来对bean进行注入或者是配置aop、事物,这么做有两个缺点:
1、如果所有的内容都配置在.xml文件中,那么.xml文件将会十分庞大;如果按需求分开.xml文件,那么.xml文件又会非常多。总之这将导致配置文件的可读性与可维护性变得很低。
2、在开发中在.java文件和.xml文件之间不断切换,是一件麻烦的事,同时这种思维上的不连贯也会降低开发的效率。
为了解决这两个问题,Spring引入了注解,通过"@XXX"的方式,让注解与Java Bean紧密结合,既大大减少了配置文件的体积,又增加了Java Bean的可读性与内聚性。

不使用注解:

先看一个不使用注解的Spring示例,在这个示例的基础上,改成注解版本的,这样也能看出使用与不使用注解之间的区别,先定义一个狗:

public class Dog {
    private String DogName="二哈";

    @Override
    public String toString() {
        return "Dog{" +
                "DogName='" + DogName + '\'' +
                '}';
    }
}

 

在定义一个猫咪:

public class Cat {
    private String CatName="猫咪";

    @Override
    public String toString() {
        return "Cat{" +
                "CatName='" + CatName + '\'' +
                '}';
    }
}

 在定义一个动物园:

package demo6.dao;

/**
 * @Author
 * @Date 2020/6/30 9:57
 * @Version 1.0
 */
public class Zoo {
    private Cat cat;
    private Dog dog;

    public Cat getCat() {
        return cat;
    }

    public void setCat(Cat cat) {
        this.cat = cat;
    }

    public Dog getDog() {
        return dog;
    }

    public void setDog(Dog dog) {
        this.dog = dog;
    }

    @Override
    public String toString() {
        return "Zoo{" +
                "cat=" + cat +
                ", dog=" + dog +
                '}';
    }
}

  spring配置文件:

<?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.xsd">

    <bean id="zoo" class="demo6.dao.Zoo">
        <property name="cat" ref="cat"/>
        <property name="dog" ref="dog"/>
    </bean>
    <bean id="cat" class="demo6.dao.Cat"/>
    <bean id="dog" class="demo6.dao.Dog"/>

</beans>

  测试文件:

package demo6.junit;

import demo6.dao.Zoo;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @Author
 * @Date 2020/6/30 10:03
 * @Version 1.0
 */
public class zooTest {
    @Test
    public void test(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("zoo.xml");
        Zoo zoo = (Zoo)applicationContext.getBean("zoo");
        System.out.println(zoo);
    }
}

  打印如下:

 

 

 这是我们比较熟悉的xml配置,

1、@Autowired

@Autowired顾名思义,就是自动装配,其作用是为了消除代码Java代码里面的getter/setter与bean属性中的property。当然,getter看个人需求,如果私有属性需要对外提供的话,应当予以保留。

@Autowired默认按类型匹配的方式,在容器查找匹配的Bean,当有且仅有一个匹配的Bean时,Spring将其注入@Autowired标注的变量中。

因此,引入@Autowired注解,先看一下spring配置文件怎么写:

<?xml version="1.0" encoding="UTF-8"?>
<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.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

   <!-- <bean id="zoo" class="demo6.dao.Zoo">
        <property name="cat" ref="cat"/>
        <property name="dog" ref="dog"/>
    </bean>
    <bean id="cat" class="demo6.dao.Cat"/>
    <bean id="dog" class="demo6.dao.Dog"/>-->

    <context:component-scan base-package="demo6.dao" />
    <bean id="zoo" class="demo6.dao.Zoo"/>
    <bean id="cat" class="demo6.dao.Cat"/>
    <bean id="dog" class="demo6.dao.Dog"/>

</beans>

  使用必须告诉spring一下我要使用注解了,告诉的方式有很多,<context:component-scan base-package="xxx" />是一种最简单的,spring会自动扫描xxx路径下的注解。

现在我们来修改一下Zoo:

package demo6.dao;

import org.springframework.beans.factory.annotation.Autowired;

/**
 * @Author
 * @Date 2020/6/30 9:57
 * @Version 1.0
 */
public class Zoo {
    @Autowired
    private Cat cat;
    @Autowired
    private Dog dog;

    @Override
    public String toString() {
        return "Zoo{" +
                "cat=" + cat +
                ", dog=" + dog +
                '}';
    }
}

  这里@Autowired注解的意思就是,当Spring发现@Autowired注解时,将自动在代码上下文中找到和其匹配(默认是类型匹配)的Bean,并自动注入到相应的地方去。

如果我们在Zoo里面使用了@Autowired但是在xml文件里面没有引用会怎么样?答案是Spring会按照xml优先的原则去Zoo.java中寻找这两个属性的getter/setter,导致的结果就是初始化bean报错。

我们修改一下xml,把引用Dog/Cat的两个属性注释掉,我们会看到Zoo报错,当然运行也一定报错;

 

 

 

 

 

 因为,@Autowired注解要去寻找的是一个Bean,Bean定义都给去掉了,自然就不是一个Bean了,Spring容器找不到也很好理解。那么,如果属性找不到我不想让Spring容器抛出异常,而就是显示null,可以吗?可以的,其实异常信息里面也给出了提示了,就是将@Autowired注解的required属性设置为false即可,这样Zoo就不会报错了

 

 

 运行一下:

 

 

 此时,找不到Cat、Dog两个属性,Spring容器不再抛出异常而是认为这两个属性为null。

2、Qualifier(指定注入Bean的名称)

如果容器中有一个以上匹配的Bean,则可以通过@Qualifier注解限定Bean的名称,看下面的例子:

定义一个Car接口:

package demo7.service;

/**
 * @Author
 * @Date 2020/6/30 10:46
 * @Version 1.0
 */
public interface Car {
    String getCarName();
}

  两个实现类BMWCar和BenzCar:

package demo7.service.impl;

import demo7.service.Car;

/**
 * @Author
 * @Date 2020/6/30 10:48
 * @Version 1.0
 */
public class BAMCar implements Car {
    @Override
    public String getCarName() {
        return "宝马汽车";
    }
}

  

package demo7.service.impl;

import demo7.service.Car;

/**
 * @Author
 * @Date 2020/6/30 10:48
 * @Version 1.0
 */
public class BENZCar implements Car {
    @Override
    public String getCarName() {
        return "奔驰汽车";
    }
}

  编写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"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="demo7.dao" />

    <bean id="carFactory" class="demo7.dao.CarFactory"/>
    <bean id="BAWCar" class="demo7.service.impl.BAMCar"/>
    <bean id="BENZCar" class="demo7.service.impl.BENZCar"/>
 </beans>

  测试类:

package demo7.junit;

import demo7.dao.CarFactory;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @Author
 * @Date 2020/6/30 11:12
 * @Version 1.0
 */
public class CarTest {
    @Test
    public void car(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("car.xml");
        CarFactory carFactory = (CarFactory)applicationContext.getBean("carFactory");
        System.out.println(carFactory.toString());
    }
}

  运行一下,不用说,一定是报错的,Car接口有两个实现类,Spring并不知道应当引用哪个实现类。

 

 出现这种情况通常有两种解决办法:
(1)、在配置文件中删除其中一个实现类,Spring会自动去base-package下寻找Car接口的实现类,发现Car接口只有一个实现类,便会直接引用这个实现类。

 

 (2)、实现类就是有多个该怎么办?此时可以使用@Qualifier注解来指定Bean的名称:

package demo7.dao;

import demo7.service.Car;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

/**
 * @Author
 * @Date 2020/6/30 11:05
 * @Version 1.0
 */
public class CarFactory {
    @Autowired
    @Qualifier("BAWCar")
    private Car car;

    @Override
    public String toString() {
        return "CarFactory{" +
                "car=" + car +
                '}';
    }
}

  此处会注入名为"bmwCar"的Bean。

 

 

 

 两个值保持一致

运行:

 

 

3、Resource

@Resource注解与@Autowired注解作用非常相似,这个就简单说了,看例子:

package demo6.dao;

import org.springframework.beans.factory.annotation.Autowired;

import javax.annotation.Resource;

/**
 * @Author
 * @Date 2020/6/30 9:57
 * @Version 1.0
 */
public class Zoo {
   /* @Autowired(required = false)*/
    @Resource(name="cat")
    private Cat cat;
   /* @Autowired(required = false)*/
   @Resource(name="dog")
    private Dog dog;

    @Override
    public String toString() {
        return "Zoo{" +
                "cat=" + cat +
                ", dog=" + dog +
                '}';
    }

}

  测试:

 

 这是详细一些的用法,说一下@Resource的装配顺序:

(1)、@Resource后面没有任何内容,默认通过name属性去匹配bean,找不到再按type去匹配
(2)、指定了name或者type则根据指定的类型去匹配bean
(3)、指定了name和type则根据指定的name和type去匹配bean,任何一个不匹配都将报错

然后,区分一下@Autowired和@Resource两个注解的区别:
(1)、@Autowired默认按照byType方式进行bean匹配,@Resource默认按照byName方式进行bean匹配
(2)、@Autowired是Spring的注解,@Resource是J2EE的注解,这个看一下导入注解的时候这两个注解的包名就一清二楚了
Spring属于第三方的,J2EE是Java自己的东西,因此,建议使用@Resource注解,以减少代码和Spring之间的耦合。

4、Service

上面这个例子,还可以继续简化,因为spring的配置文件里面还有15行~17行三个bean,下一步的简化是把这三个bean也给去掉,使得spring配置文件里面只有一个自动扫描的标签,增强Java代码的内聚性并进一步减少配置文件。

要继续简化,可以使用@Service。先看一下配置文件,当然是全部删除了:

<?xml version="1.0" encoding="UTF-8"?>
<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.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

   <!-- <bean id="zoo" class="demo6.dao.Zoo">
        <property name="cat" ref="cat"/>
        <property name="dog" ref="dog"/>
    </bean>
    <bean id="cat" class="demo6.dao.Cat"/>
    <bean id="dog" class="demo6.dao.Dog"/>-->

    <context:component-scan base-package="demo6.dao" />

    <!--使用@service注解自动扫描-->
   <!-- <bean id="zoo" class="demo6.dao.Zoo"/>
   <bean id="cat" class="demo6.dao.Cat"/>
    <bean id="dog" class="demo6.dao.Dog"/>-->

</beans>

  来看下Cat/Dog/Zoo如何改动?

package demo6.dao;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

/**
 * @Author
 * @Date 2020/6/30 9:57
 * @Version 1.0
 */
@Service
public class Zoo {
    @Autowired(required = false)

   /* @Resource(name="cat")*/
    private Cat cat;
    @Autowired(required = false)
 /*  @Resource(name="dog")*/
    private Dog dog;

    @Override
    public String toString() {
        return "Zoo{" +
                "cat=" + cat +
                ", dog=" + dog +
                '}';
    }

}

  这样,Zoo.java在Spring容器中存在的形式就是"zoo",即可以通过ApplicationContext的getBean("zoo")方法来得到Zoo.java。@Service注解,其实做了两件事情:
(1)、声明Zoo.java是一个bean,这点很重要,因为Zoo.java是一个bean,其他的类才可以使用@Autowired将Zoo作为一个成员变量自动注入。
(2)、Zoo.java在bean中的id是"zoo",即类名且首字母小写。

我么依次加@service到Cat/Dog中

package demo6.dao;

import org.springframework.stereotype.Service;

/**
 * @Author
 * @Date 2020/6/30 9:54
 * @Version 1.0
 */
@Service
public class Cat {
    private String CatName="猫咪";

    @Override
    public String toString() {
        return "Cat{" +
                "CatName='" + CatName + '\'' +
                '}';
    }
}

  

package demo6.dao;

/**
 * @Author
 * @Date 2020/6/30 9:50
 * @Version 1.0
 */
public class Dog {
    private String DogName="二哈";

    @Override
    public String toString() {
        return "Dog{" +
                "DogName='" + DogName + '\'' +
                '}';
    }
}

  修改junit:

package demo6.junit;

import demo6.dao.Cat;
import demo6.dao.Dog;
import demo6.dao.Zoo;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @Author
 * @Date 2020/6/30 10:03
 * @Version 1.0
 */
public class zooTest {
    @Test
    public void test(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("zoo.xml");
        Zoo zoo = (Zoo)applicationContext.getBean("zoo");
        System.out.println(zoo);
        System.out.println("------------------------------------------");
        Cat cat =(Cat)applicationContext.getBean("cat");
        System.out.println(cat);
        System.out.println("------------------------------------------");
        Dog dog = (Dog)applicationContext.getBean("dog");
        System.out.println(dog);
    }
}

  打印输出:

 

 

这样,就可以通过ApplicationContext的getBean("Zoo")方法来得到Zoo.java了。

 

posted @ 2020-06-30 15:05  LeonardoDiCapprio  阅读(117)  评论(0编辑  收藏  举报