Spring--注解配置详解

Spring注解配置

从 Spring 2.5 开始就可以使用注解来配置依赖注入。使用注解的方式使我们无需在XML中配置一个Bean引用,更加简单和方便。
首先要引入context名称空间:

xmlns:context="http://www.springframework.org/schema/context" 

声明context命名空间后,即可通过context命名空间的component-scanbase-package属性指定一个需要扫描的基类包,Spring容器将会扫描这个基类包里的所有类,并从类的注解信息中获取Bean的定义信息。若希望仅扫描特定的类而非基包下所有的类,那么可以使用resource-pattern属性过滤出特定的类 。< context:include-filter > 表示要包含的目标类,而 < context:exclude-filter > 表示要抛出在外的目标类。一个< context:component-scan >下可以拥有若干个< context:exclude-filter >和< context:include-filter >元素,这两个元素均支持多种类型的过滤表达式。

applicationContext.xml配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xmlns="http://www.springframework.org/schema/beans" 
        xmlns:context="http://www.springframework.org/schema/context" 
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd ">

    <!-- 指定扫描pers.zhang.bean报下的所有类中的注解.
         注意:扫描包时.会扫描指定报下的所有子孙包
    -->
    <context:component-scan base-package="pers.zhang.bean"></context:component-scan>

</beans>

创建一个User类:

package pers.zhang.bean;

public class User {

    private String name;
    private Integer age;
    
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public void init(){
        System.out.println("我是初始化方法!");
    }
    public void destory(){
        System.out.println("我是销毁方法!");
    }
    @Override
    public String toString() {
        return "User [name=" + name + ", age=" + age + ", car=" + car + "]";
    }
    
}

@Component

@Component 作用在类上,相当于 xml 中的 < bean id="..." name="..." class="..." / >。@Component(str) 可以填写一个参数,相当于 name 属性。
同时,Spring 中还提供了@Component 的三个衍生注解:

  • @Controller: 用于Controller层
  • @Service: 用于业务层
  • @Repository: 用于持久层

这三个注解是为了让注解本身的用途更加清晰,功能上目前来讲是一致的。

@Scope

@Scope 作用于类上,用于指定Bean的作用范围,取值有如下几个:

  • singleton(默认): Spring IOC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id(name)与该bean定义相匹配,则只会返回bean的同一实例,一个容器对应一个bean。
  • prototype: 表示多例,每一次请求(将其注入到另一个bean中,或者以程序的方式调用容器的 getBean()方法)都会产生一个新的bean实例,相当与一个new的操作。
  • request: 表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP request内有效。
  • session: 表示作用域表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP session内有效。
  • global session: 作用域类似于标准的HTTP Session作用域,不过它仅仅在基于portlet的web应用中才有意义。Portlet规范定义了全局Session的概念,它被所有构成某个 portlet web应用的各种不同的portlet所共享。在global session作用域中定义的bean被限定于全局portlet Session的生命周期范围内。如果你在web中使用global session作用域来标识bean,那么web会自动当成session类型来使用。

@PostConstruct 和 @PreDestroy

@PostConstruct 用于在依赖关系注入完成之后需要执行的方法上,以执行任何初始化。相当于 init-method
@PreDestroy 用于在对象销毁之前需要执行的方法上,以执行释放资源等操作。相当于 destroy-method

package pers.zhang.bean;

@Repository("user")
@Scope(scopeName="singleton")
public class User {

    private String name;
    private Integer age;
    
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    @PostConstruct
    public void init(){
        System.out.println("我是初始化方法!");
    }
    @PreDestroy
    public void destory(){
        System.out.println("我是销毁方法!");
    }
    @Override
    public String toString() {
        return "User [name=" + name + ", age=" + age + ", car=" + car + "]";
    }
    
}

测试方法:

package pers.zhang.test;

import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import pers.zhang.bean.User;

public class Demo {
    @Test
    public void fun1(){
        
        //1 创建容器对象
        ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        //2 向容器"要"user对象
        User u1 = (User) ac.getBean("user");
        User u2 = (User) ac.getBean("user");
        
        System.out.println(u1==u2);
        //3 打印user对象
        System.out.println(u1);
        
        ac.close();
            
    }
    
}

运行JUnit测试输出:

我是初始化方法!
true
User [name=null, age=null, car=null]
我是销毁方法!

@Value

@Value 一般作用在属性上或 set 方法上,用于值的注入。@Value的作用位置不同,其实现原理也不同:

  • 作用在属性上: 使用反射技术,操作Field对字段直接赋值,但是破坏了对象的封装性。
  • 作用在set方法上: 使用set方法赋值,但是不够直观,不够一目了然。
package pers.zhang.bean;

@Repository("user")
@Scope(scopeName="singleton")
public class User {

    private String name;
    @Value("18")
    private Integer age;
    
    public String getName() {
        return name;
    }
    @Value("tom")   
    public void setName(String name) {
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    @PostConstruct
    public void init(){
        System.out.println("我是初始化方法!");
    }
    @PreDestroy
    public void destory(){
        System.out.println("我是销毁方法!");
    }
    @Override
    public String toString() {
        return "User [name=" + name + ", age=" + age + ", car=" + car + "]";
    }
    
}

测试方法:

package pers.zhang.test;

import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import pers.zhang.bean.User;

public class Demo {
    @Test
    public void fun1(){
        
        //1 创建容器对象
        ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        //2 向容器"要"user对象
        User u1 = (User) ac.getBean("user");
        User u2 = (User) ac.getBean("user");
        
        System.out.println(u1==u2);
        //3 打印user对象
        System.out.println(u1);
        
        ac.close();
            
    }
    
}

运行JUnit测试输出:

我是初始化方法!
true
User [name=tom, age=18, car=null]
我是销毁方法!

@Autowired 和 @Qualifier

@Autowired ,它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作。 通过 @Autowired的使用来消除 set ,get方法。
但需要注意的是,@Autowired是通过类型来进行匹配的。如果Spring容器中有多个同类型的对象,使用@Autowired装配得到的结果将不确定。
这时就需要使用 @Qualifier,限定描述符能根据名字进行注入,更能进行更细粒度的控制如何选择注入对象。

创建一个Car类,并使用注解配置一个实例:

package pers.zhang.bean;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Repository("car1")
public class Car {
    @Value("car1:二手捷达")
    private String  name;
    @Value("呕吐绿")
    private String color;
    
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getColor() {
        return color;
    }
    public void setColor(String color) {
        this.color = color;
    }
    @Override
    public String toString() {
        return "Car [name=" + name + ", color=" + color + "]";
    }
    
}

在applicationContext.xml中再配置一个Car的实例:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xmlns="http://www.springframework.org/schema/beans" 
        xmlns:context="http://www.springframework.org/schema/context" 
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd ">

    <!-- 指定扫描cn.itcast.bean报下的所有类中的注解.
         注意:扫描包时.会扫描指定报下的所有子孙包
     -->
    <context:component-scan base-package="pers.zhang.bean"></context:component-scan>

    <bean name="car2" class="pers.zhang.bean.Car"  >
        <property name="name" value="car2:9手夏利" ></property>
        <property name="color" value="屎黄色"  ></property>
    </bean>
</beans>

修改User类如下:

package pers.zhang.bean;

@Repository("user")
@Scope(scopeName="singleton")
public class User {
    @Value("tom")   
    private String name;
    @Value("18")
    private Integer age;

    @Autowired //自动装配,问题:如果匹配多个类型一致的对象.将无法选择具体注入哪一个对象.
    @Qualifier("car2")//使用@Qualifier注解告诉spring容器自动装配哪个名称的对象
    private Car car;
    
    public Car getCar() {
        return car;
    }
    public void setCar(Car car) {
        this.car = car;
    }
    public String getName() {
        return name;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    @PostConstruct
    public void init(){
        System.out.println("我是初始化方法!");
    }
    @PreDestroy
    public void destory(){
        System.out.println("我是销毁方法!");
    }
    @Override
    public String toString() {
        return "User [name=" + name + ", age=" + age + ", car=" + car + "]";
    }
    
}

测试方法:

package pers.zhang.test;

import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import pers.zhang.bean.User;

public class Demo {
    @Test
    public void fun1(){
        
        //1 创建容器对象
        ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        //2 向容器"要"user对象
        User u1 = (User) ac.getBean("user");
        User u2 = (User) ac.getBean("user");
        
        System.out.println(u1==u2);
        //3 打印user对象
        System.out.println(u1);
        
        ac.close();
            
    }
    
}

运行JUnit测试输出:

我是初始化方法!
true
User [name=tom, age=18, car=Car [name=car2:9手夏利, color=屎黄色]]
我是销毁方法!

@Resource

@Resource,手动注入,默认是按照byName自动注入。
@Resource有两个重要的属性,name和type:

  • name: name属性解析为bean的名字.
  • type: type属性解析为bean的类型。

如果使用name属性,则使用byName的自动注入策略;而使用type属性则使用byType自动注入策略;如果既不指定name也不指定type属性,这时通过反射机制使用byName自动注入策略。

@Resource装配的顺序:

  • 如果同时指定了name和type属性,则从spring上下问中找到唯一匹配的bean进行装配,如果没有找到,则会抛出异常。
  • 如果指定了name属性,则从spring上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常。
  • 如果指定type属性,则从spring上下文中找到类型匹配的唯一bean进行装配,找不到或者是找到多个,则抛出异常。
  • 如果既没有指定name属性,也没有指定type属性,则默认是按照byName的方式进行装配;如果没有匹配,则返回一个原始的类型进行装配,如果匹配则自动装配。
package pers.zhang.bean;

@Repository("user")
@Scope(scopeName="singleton")
public class User {
    @Value("tom")   
    private String name;
    @Value("18")
    private Integer age;
    
    @Resource(name="car1")//手动注入
    private Car car;
    
    public Car getCar() {
        return car;
    }
    public void setCar(Car car) {
        this.car = car;
    }
    public String getName() {
        return name;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    @PostConstruct
    public void init(){
        System.out.println("我是初始化方法!");
    }
    @PreDestroy
    public void destory(){
        System.out.println("我是销毁方法!");
    }
    @Override
    public String toString() {
        return "User [name=" + name + ", age=" + age + ", car=" + car + "]";
    }
    
}

测试方法:

package pers.zhang.test;

import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import pers.zhang.bean.User;

public class Demo {
    @Test
    public void fun1(){
        
        //1 创建容器对象
        ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        //2 向容器"要"user对象
        User u1 = (User) ac.getBean("user");
        User u2 = (User) ac.getBean("user");
        
        System.out.println(u1==u2);
        //3 打印user对象
        System.out.println(u1);
        
        ac.close();
            
    }
    
}

运行JUnit测试输出:

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

推荐阅读更多精彩内容

  • 来源:关于Spring IOC (DI-依赖注入)你需要知道的一切作者:zejian Dao层(AccountDa...
    杨井阅读 5,324评论 0 27
  • 25个经典的Spring面试问答 本人收集了一些在大家在面试时被经常问及的关于Spring的主要问题,这些问题有可...
    杀小贼阅读 687评论 0 2
  • 1、什么是 Spring 框架?Spring 框架有哪些主要模块?Spring 框架是一个为 Java 应用程序的...
    Java架构阅读 3,284评论 0 77
  • 乞丐坐在街边晒太阳,面前放着一个破旧的碗,偶尔会有人扔进一个铜板。他披散着头发,很少有人能看到他的脸。 一个白发老...
    城外42阅读 1,142评论 7 40
  • (其实文章很早就写好了,一直没发…) 既是桐华小说的爱好者,也是郭碧婷的粉丝一枚,知道这部剧开拍时,简直开心得不行...
    Jeannie_Jun阅读 698评论 1 2