spring注解学习

采用注解的优势:

  • 注解可以充分利用 Java 的反射机制获取类结构信息,这些信息可以有效减少配置的工作。如使用 JPA 注释配置 ORM 映射时,我们就不需要指定 PO 的属性名、类型等信息,如果关系表字段和 PO 属性名、类型都一致,您甚至无需编写任务属性映射信息——因为这些信息都可以通过 Java 反射机制获取。
  • 注解和 Java 代码位于一个文件中,而 XML 配置采用独立的配置文件,大多数配置信息在程序开发完成后都不会调整,如果配置信息和 Java 代码放在一起,有助于增强程序的内聚性。而采用独立的 XML 配置文件,程序员在编写一个功能时,往往需要在程序文件和配置文件中不停切换,这种思维上的不连贯会降低开发效率。

以前配置bean的方法及在bean之间建立依赖关系的做法

以用户购买商品为例主要有四个实体类(items,orderdateil,user)
商品信息 Items.java

public class Items {
    private Integer id;
    private String name;
    private Float price;
    private String pic;
    private Date createtime;
    private String detail;

//省略 get/setter

   @Override
    public String toString() {
        return "Items{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", price=" + price +
                ", pic='" + pic + '\'' +
                ", createtime=" + createtime +
                ", detail='" + detail + '\'' +
                '}';
    }
}

订单明细(包含用户信息与商品信息) Orderdetail.java

public class Orderdetail {
    private int id;
    private Items items;
    private User user;

//省略get/setter方法
    @Override
    public String toString() {
        return "Orderdetail{" +
                "id=" + id +
                ", items=" + items +
                ", user=" + user +
                '}';
    } 
}

用户 User.jav

public class User {
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
//省略get/setter方法
 @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", birthday=" + birthday +
                ", sex='" + sex + '\'' +
                ", address='" + address + '\'' +
                ", iId=" + iId +
                '}';
    }
}

在spring容器中我们将User和Order两个类声明为bean,并注入到Orderdetail这个bean中,因此创建一个bean.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-3.0.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-3.0.xsd
       http://www.springframework.org/schema/mvc">

    <bean id="items" class="pojo.Items">
        <property name="name" value="苹果手机"></property>
        <property name="price" value="10000"></property>
    </bean>
    <bean id="user" class="pojo.User">
        <property name="username" value="张三"></property>
        <property name="address" value="陝西西安"></property>
    </bean>
    <bean id="orderdetail" class="pojo.Orderdetail">
        <property name="items" ref="items"></property>
        <property name="user" ref="user"></property>
    </bean>

</beans>

测试 Test.java (输出订单明细时,成功时就会打印用户与商品的相关信息)

package test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import pojo.Orderdetail;

/**
 * Created by admin on 2017/6/30.
 */
public class Test {
    public static void main(String[] args) {
        String path = "bean.xml";
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext(path);
        Orderdetail orderdetail = (Orderdetail) applicationContext.getBean("orderdetail");
        System.out.println(orderdetail);
    }
}

图片.png

使用 @Autowired 注释

  • @Autowired可以对成员变量、方法和构造函数进行标注,来完成自动装配的工作,这里必须明确:@Autowired是根据类型进行自动装配的,如果需要按名称进行装配,则需要配合@Qualifier[1]使用;
  • @Autowired标注可以放在成员变量上,也可以放在成员变量的set方法上。前者,Spring会直接将UserDao类型的唯一一个bean赋值给userDao这个成员变量;后者,Spring会调用setUserDao方法来将UserDao类型的唯一一个bean装配到userDao这个属性。
  • Spring 2.5 引入了 @Autowired 注释,它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作。 通过 @Autowired的使用来消除 set ,get方法。
使用@Autowired注释Orderdetail
  • Spring 通过一个 BeanPostProcessor 对 @Autowired 进行解析,所以要让 @Autowired 起作用必须事先在 Spring 容器中声明 AutowiredAnnotationBeanPostProcessor Bean。
  • 在bean.xml中 移除 boss Orderdetail 的属性注入配置的信息,并声明 AutowiredAnnotationBeanPostProcessor Bean。
<?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.5.xsd">

    <bean id="items" class="pojo.Items" scope="singleton">
        <property name="name" value="三星手机"></property>
        <property name="price" value="10000"></property>
    </bean>
    <bean id="user" class="pojo.User">
        <property name="username" value="李四"></property>
        <property name="address" value="陝西西安"></property>
    </bean>
    <!-- 移除 boss Orderdetail 的属性注入配置的信息 -->
    <bean id="orderdetail" class="pojo.Orderdetail">
    </bean>

    <bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"></bean>
</beans>
  • 修改在原来注入spring容器中的bean的方法。在域变量上加上标签@Autowired,并且去掉 相应的get 和set方法

Orderdetail.java

package pojo;

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

public class Orderdetail {
    private int id;
    @Autowired
    private Items items;
    @Autowired
    private User user;


    @Override
    public String toString() {
        return "Orderdetail{" +
                "id=" + id +
                ", items=" + items +
                ", user=" + user +
                '}';
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }
}

用测试类测试:

图片.png
  • 在默认情况下使用 @Autowired 注释进行自动注入时,Spring 容器中匹配的候选 Bean 数目必须有且仅有一个。当找不到一个匹配的 Bean 时,Spring 容器将抛出BeanCreationException 异常,并指出必须至少拥有一个匹配的 Bean。

  • 当不能确定 Spring 容器中一定拥有某个类的 Bean 时,可以在需要自动注入该类 Bean 的地方可以使用 @Autowired(required = false),这等于告诉 Spring:在找不到匹配 Bean 时也不报错。

在 beans.xml 中配置两个 User类型的 Bean时

    <bean id="user" class="pojo.User">
        <property name="username" value="李四"></property>
        <property name="address" value="陝西西安"></property>
    </bean>
    <bean id="user1" class="pojo.User">
        <property name="username" value="张三"></property>
        <property name="address" value="陝西西安"></property>
    </bean>

这样配置时,就会发生异常,因为Spring 容器将无法确定到底要用哪一个 Bean,Spring 允许我们通过 @Qualifier 注释指定注入 Bean 的名称,这样歧义就消除了。

    @Autowired
    @Qualifier("user")
    private User user;
  • @Qualifier("office") 中的 office 是 Bean 的名称,所以 @Autowired 和@Qualifier 结合使用时,自动注入的策略就从 byType 转变成 byName 了。@Autowired 可以对成员变量、方法以及构造函数进行注释,而@Qualifier 的标注对象是成员变量、方法入参、构造函数入参。正是由于注释对象的不同,所以 Spring 不将 @Autowired 和@Qualifier 统一成一个注释类。

使用 @Resource 注释

@Resource 的作用相当于 @Autowired,只不过 @Autowired 按 byType 自动注入,面@Resource 默认按 byName 自动注入罢了。@Resource 有两个属性是比较重要的,分别是 name 和 type,Spring 将@Resource 注释的 name 属性解析为 Bean 的名字,而 type 属性则解析为 Bean 的类型。所以如果使用 name 属性,则使用 byName 的自动注入策略,而使用 type 属性时则使用 byType 自动注入策略。如果既不指定 name 也不指定 type 属性,这时将通过反射机制使用 byName 自动注入策略。

    // 自动注入类型为 Items 的 Bean
    @Resource
    private Items items;
    // 自动注入 bean 名称为 user 的 Bean
    @Resource(name = "user")
    private User user;

使用 <context:annotation-config/> 简化配置

Spring 2.1 添加了一个新的 context 的 Schema 命名空间,该命名空间对注释驱动、属性文件引入、加载期织入等功能提供了便捷的配置。我们知道注释本身是不会做任何事情的,它仅提供元数据信息。要使元数据信息真正起作用,必须让负责处理这些元数据的处理器工作起来。

而我们前面所介绍的 AutowiredAnnotationBeanPostProcessor 和 CommonAnnotationBeanPostProcessor 就是处理这些注释元数据的处理器。但是直接在 Spring 配置文件中定义这些 Bean 显得比较笨拙。Spring 为我们提供了一种方便的注册这些BeanPostProcessor 的方式,这就是 <context:annotation-config/>。

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

    <context:annotation-config/>
    
    <bean id="items" class="pojo.Items" scope="singleton">
        <property name="name" value="三星手机"></property>
        <property name="price" value="10000"></property>
    </bean>
    <bean id="user" class="pojo.User">
        <property name="username" value="李四"></property>
        <property name="address" value="陝西西安"></property>
    </bean>
    <bean id="user1" class="pojo.User">
        <property name="username" value="张三"></property>
        <property name="address" value="陝西西安"></property>
    </bean>
    <!-- 移除 boss Orderdetail 的属性注入配置的信息 -->
    <bean id="orderdetail" class="pojo.Orderdetail">
    </bean>

<!--    <!–@Autowired–>
    <bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"></bean>
    <!–@Resource–>
    <bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor"/>-->
</beans>

<context:annotationconfig/> 将隐式地向 Spring 容器注册 AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor、PersistenceAnnotationBeanPostProcessor 以及equiredAnnotationBeanPostProcessor 这 4 个 BeanPostProcessor。

在配置文件中使用 context 命名空间之前,必须在 <beans> 元素中声明 context 命名空间。

使用 @Component

虽然我们可以通过 @Autowired 或 @Resource 在 Bean 类中使用自动注入功能,但是 Bean 还是在 XML 文件中通过 <bean> 进行定义 —— 也就是说,在 XML 配置文件中定义 Bean,通过@Autowired 或 @Resource 为 Bean 的成员变量、方法入参或构造函数入参提供自动注入的功能。能否也通过注释定义 Bean,从 XML 配置文件中完全移除 Bean 定义的配置呢?答案是肯定的,我们通过 Spring 2.5 提供的@Component 注释就可以达到这个目标了。

为什么 @Repository 只能标注在 DAO 类上呢?这是因为该注解的作用不只是将类识别为 Bean,同时它还能将所标注的类中抛出的数据访问异常封装为 Spring 的数据访问异常类型。 Spring 本身提供了一个丰富的并且是与具体的数据访问技术无关的数据访问异常结构,用于封装不同的持久层框架抛出的异常,使得异常独立于底层的框架。

Spring 2.5 在 @Repository 的基础上增加了功能类似的额外三个注解:@Component、@Service、@Constroller,它们分别用于软件系统的不同层次:

@Component 是一个泛化的概念,仅仅表示一个组件 (Bean) ,可以作用在任何层次。
@Service 通常作用在业务层,但是目前该功能与 @Component 相同。
@Constroller 通常作用在控制层,但是目前该功能与 @Component 相同。

通过在类上使用 @Repository、@Component、@Service 和 @Constroller 注解,Spring 会自动创建相应的 BeanDefinition 对象,并注册到 ApplicationContext 中。这些类就成了 Spring 受管组件。这三个注解除了作用于不同软件层次的类,其使用方式与 @Repository 是完全相同的。

接下来完全使用注释定义 Bean 并完成 Bean 之间装配:

Items .java

package pojo;

import org.springframework.stereotype.Component;

import java.util.Date;

@Component  //使用 @Component 注释就可以将一个类定义为 Spring 容器中的 Bean
public class Items {
    private Integer id;
    private String name;
    private Float price;
    private String pic;
    private Date createtime;
    private String detail;
    @Override
    public String toString() {
        return "Items{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", price=" + price +
                ", pic='" + pic + '\'' +
                ", createtime=" + createtime +
                ", detail='" + detail + '\'' +
                '}';
    }

//省略get/set方法
}

User.java

@Component
public class User {
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", birthday=" + birthday +
                ", sex='" + sex + '\'' +
                ", address='" + address + '\'' +
                ", iId=" + iId +
                '}';
    }

Orderdetail.java

package pojo;

import javax.annotation.Resource;

public class Orderdetail {
    private int id;
    // 自动注入类型为 Items 的 Bean
    @Resource
    private Items items;
    // 自动注入 bean 名称为 user 的 Bean
    @Resource(name = "user")
    private User user;


    @Override
    public String toString() {
        return "Orderdetail{" +
                "id=" + id +
                ", items=" + items +
                ", user=" + user +
                '}';
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }
}

升级之后的配置文件beanUp.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-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <context:component-scan base-package="pojo"/>
</beans>

这里,所有通过 <bean> 元素定义 Bean 的配置内容已经被移除,仅需要添加一行 <context:component-scan/> 配置就解决所有问题了——Spring XML 配置文件得到了极致的简化(当然配置元数据还是需要的,只不过以注释形式存在罢了)。<context:component-scan/> 的 base-package 属性指定了需要扫描的类包,类包及其递归子包中所有的类都会被处理。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,830评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,992评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,875评论 0 331
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,837评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,734评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,091评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,550评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,217评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,368评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,298评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,350评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,027评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,623评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,706评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,940评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,349评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,936评论 2 341

推荐阅读更多精彩内容