spring注解学习

作者: Dl_毛良伟 | 来源:发表于2017-06-30 16:48 被阅读194次

    采用注解的优势:

    • 注解可以充分利用 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 属性指定了需要扫描的类包,类包及其递归子包中所有的类都会被处理。

    相关文章

      网友评论

        本文标题:spring注解学习

        本文链接:https://www.haomeiwen.com/subject/jbabcxtx.html