[置顶] Spring4实战学习笔记

《Spring4实战 第4版》2016年4月新出版的,之前的第三版看起来还是不错的,所以看到新版就直接买下来。

英文版源码地址:Spring in Action, Fourth Edition Covers Spring 4

[置顶] Spring4实战学习笔记_第1张图片


1.装配Bean

参考【Spring实战4 2.2】

1.1接口只有一个现实类

可以自动装配

public interface CompactDisc {

    void play();
}


import org.springframework.stereotype.Component;

@Component
public class SgtPeppers implements CompactDisc {

    private String title = "Sgt. Pepper's Lonely Hearts Club Band";
    private String artist = "http://blog.csdn.net/unix21";

    public void play() {
        System.out.println("【非常醒目SgtPeppers 】>>>>>>>>>>>>>>>>>Playing " + title + " by " + artist);
    }

}

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan
public class CDPlayerConfig { 
}


单元测试

import static org.junit.Assert.*;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = CDPlayerConfig.class)
public class CDPlayerTest {
    @Autowired
    private CompactDisc cd;
 
    @Test
    public void play() {
        cd.play();
    }
}

[置顶] Spring4实战学习笔记_第2张图片


1.2 接口有多个实现类

【参考 Spring实战4 3.3】
故意再写一个实现类

import org.springframework.stereotype.Component;

@Component
public class SgtPeppersNew implements CompactDisc {

    private String title = "Sgt. Pepper's Lonely Hearts Club Band";
    private String artist = "http://blog.csdn.net/unix21";

    public void play() {
        System.out.println("【非常醒目 SgtPeppersNew】>>>>>>>>>>>>>>>>>Playing " + title + " by " + artist);
    }

}

如果这个时候运行肯定会报错NoUniqueBeanDefinitionException: No qualifying bean of type


解决方法有两种

第一种 在实现类上 标识首选的bean,使用@Primary

import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;

@Component
@Primary
public class SgtPeppers implements CompactDisc {

    private String title = "Sgt. Pepper's Lonely Hearts Club Band";
    private String artist = "http://blog.csdn.net/unix21";

    public void play() {
        System.out.println("【非常醒目SgtPeppers 】>>>>>>>>>>>>>>>>>Playing " + title + " by " + artist);
    }

}


但是这种方法不方便精确定义。


第二种  使用@Qualifier注解

import static org.junit.Assert.*;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = CDPlayerConfig.class)
public class CDPlayerTest {
    @Autowired
    @Qualifier("sgtPeppersNew") 
    private CompactDisc cd;
 
    @Test
    public void play() {
        cd.play();
    }
}

[置顶] Spring4实战学习笔记_第3张图片

需要注意的是bean id的首字母是类名小写。

spring @Qualifier注解


1.3 为组件扫描的bean命名

【参考 Spring实战4  2.2.2】

import org.springframework.stereotype.Component;

@Component("spn")
public class SgtPeppersNew implements CompactDisc {

@Autowired
    @Qualifier("spn") 
    private CompactDisc cd;

也可以使用 @Named效果是一样的,这是java依赖注入规范

import javax.inject.Named;

@Named("spn")
public class SgtPeppersNew implements CompactDisc {

1.4 设定组件扫描的指定包

【参考 Spring实战4  2.2.3】

如果@ComponentScan默认不设置只扫描配置类所在的包作为基础包。

@Configuration
@ComponentScan("blog.csdn.net.unix21")
public class CDPlayerConfigTest {
设置@ComponentScan的value属性就可以指明包名称。


如果想更清晰的表明设置的是基础包
@ComponentScan(basePackages="指定包")


指定多个

@ComponentScan(basePackages={"指定包1","指定包2"})


也可以将其指定为包中所包含的类或者接口

@ComponentScan(basePackages={"XXX.class","XX.class"})


1.5 自动装配

【参考 Spring实战4  2.2.4】

声明自动装配需要@Autowired注解


1.5.1 在构造方法上使用自动装配

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = CDPlayerConfigTest.class)
public class CDPlayerFunTest {

    private CompactDisc cd;

    @Autowired
    @Qualifier("spn")
    public void CDPlayer(CompactDisc cd) {
        this.cd = cd;
    }

    @Test
    public void play() {
        cd.play();
        System.out.println("【占位符】CDPlayerFunTest");
    }
}

[置顶] Spring4实战学习笔记_第4张图片


另一种写法

@Component
public class CDPlayer implements MediaPlayer {
  private CompactDisc cd;

  @Autowired
  public CDPlayer(@Qualifier("spn")CompactDisc cd) {
    this.cd = cd;
  }

  public void play() {
    cd.play();
  }

}


1.5.2 在属性Setter方法上使用自动装配

@Component
public class CDPlayer implements MediaPlayer {
  private CompactDisc cd;
  
  @Autowired
  @Qualifier("spn")
  public void setCompactDisc(CompactDisc cd) {
    this.cd = cd;
  }

  public void play() {
    cd.play();
  }
}

避免异常声明  @Autowired(required = false),如果没有匹配的bean,Spring会让这个bean处于未装配转态,但是需要谨慎对待这个设置,代码需要做null检查。

@Autowired是Spring特有的注解,可以替换为@Inject,@Inject来源自Jave依赖注入规范。


1.6 创建自定义的限定符

【参考 Spring实战4  3.3.2】

@Component
@Qualifier("cold")
public class IceCream implements CompactDisc {

    private String title = "Sgt. Pepper's Lonely Hearts Club Band";
    private String artist = "The Beatles";

    public void play() {
        System.out.println("【非常醒目 IceCream】>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>Playing " + title + " by " + artist);
    }
}


测试用例
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=CDPlayerConfigTest.class)
public class CDPlayerLogTest {

  @Autowired
  private MediaPlayer player;
  
  @Autowired
  @Qualifier("sp")
  private CompactDisc cd;
  
  @Autowired
  @Qualifier("cold")
  private CompactDisc cd2;
  
  @Test
  public void cdShouldNotBeNull() {
    assertNotNull(cd);
  }

  @Test
  public void play() {
    player.play();
    cd.play();
    cd2.play();
  }
}

[置顶] Spring4实战学习笔记_第5张图片


好处:这样做的好处限定符不耦合类名,所以可以随意重构类名。

问题:重复的限定符出现在多个类上这是不允许的,因为Java不允许同一个条目上重复出现相同类型的多个注解


1.7 使用自定义限定符注解

针对上述问题可以创建自定义的限定符注解。

@Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到  
@Target({ElementType.CONSTRUCTOR,ElementType.FIELD,ElementType.METHOD,ElementType.TYPE})//定义注解的作用目标**作用范围字段、枚举的常量/方法  
@Qualifier
public @interface Cold {}

@Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到  
@Target({ElementType.CONSTRUCTOR,ElementType.FIELD,ElementType.METHOD,ElementType.TYPE})//定义注解的作用目标**作用范围字段、枚举的常量/方法  
@Qualifier
public @interface Creamy {}

@Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到  
@Target({ElementType.CONSTRUCTOR,ElementType.FIELD,ElementType.METHOD,ElementType.TYPE})//定义注解的作用目标**作用范围字段、枚举的常量/方法  
@Qualifier
public @interface Fruity {}

@Component
@Cold
@Creamy
public class IceCream implements CompactDisc {

    private String title = "Spring 实现 第4版 读书笔记";
    private String artist = "http://blog.csdn.net/unix21";

    public void play() {
        System.out.println("【非常醒目 IceCream】>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>Playing " + title + " by " + artist);
    }
}

@Component
@Cold
@Fruity
public class Popsicle implements CompactDisc {

    private String title = "Spring 实现 第4版 读书笔记";
    private String artist = "http://blog.csdn.net/unix21";

    public void play() {
        System.out.println("【非常醒目 Popsicle】>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>Playing " + title + " by " + artist);
    }
}

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = CDPlayerConfigTest.class)
public class CDPlayerLogTest {

    @Autowired
    private MediaPlayer player;

    @Autowired
    @Qualifier("sp")
    private CompactDisc cd;

    @Autowired
    @Cold
    @Creamy
    private CompactDisc cd2;

    @Autowired
    @Cold
    @Fruity
    private CompactDisc cd3;

    @Test
    public void cdShouldNotBeNull() {
        assertNotNull(cd);
    }

    @Test
    public void play() {
        player.play();
        cd.play();
        cd2.play();
        cd3.play();
    }
}

[置顶] Spring4实战学习笔记_第6张图片


1.8 bean的作用域

Spring定义了多重作用域,singleton单例,prototype原型等

参考:spring中scope作用域

singleton单例:整个应用中,只创建bean的一个实例,默认Spring上下文中所有的bean都是单例。

prototype原型:每次注入或者通过Spring应用上下文获取的时候,都会创建一个新的bean实例。

@Component
public class Add implements AddI {
    public  int a=0;
   
    public  void Add() {
        a++;
    }
    
    public  void getA() {
        System.out.println("【非常醒目 Add】>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>a= " +a+"");
    }
}

public interface AddI {
void Add();
    void getA();
}

@Component
public class CDPlayer implements MediaPlayer {

    @Autowired
    @Qualifier("sp")
    private CompactDisc cd;
    
    @Autowired
    private AddI a;

    public void play() {
        System.out.println("【非常醒目 CDPlayer】>>>");
        cd.play();
        a.Add();
        a.getA();
        a.Add();
        a.getA();
        System.out.println("【非常醒目 CDPlayer】<<<");
    }
}


测试用例
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = CDPlayerConfigTest.class)
public class CDPlayerLogTest {

    @Autowired
    private MediaPlayer player;

    @Autowired
    @Qualifier("sp")
    private CompactDisc cd;

    @Autowired
    @Cold
    @Creamy
    private CompactDisc cd2;

    @Autowired
    @Cold
    @Fruity
    private CompactDisc cd3;

    @Test
    public void cdShouldNotBeNull() {
        assertNotNull(cd);
    }

    @Autowired
    private AddI a;
    
    @Test
    public void play() {
        player.play();
        cd.play();
        cd2.play();
        cd3.play();
        a.getA();
    }
}

[置顶] Spring4实战学习笔记_第7张图片


再写一个多线程

public class ClientThread extends Thread {

    @Autowired
    private AddI a;

    @Autowired
    public ClientThread(AddI a) {
        this.a = a;
    }

    public void run() {
        a.Add();
        a.getA();
    }
}

调用多线程

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = CDPlayerConfigTest.class)
public class SpringScopeTest {

    @Autowired
    private AddI a;

    @Test
    public void Scope() {
        for (int i = 0; i < 10; i++) {
            ClientThread t = new ClientThread(a);
            t.start();
        }
    }
}

[置顶] Spring4实战学习笔记_第8张图片

改为SCOPE_PROTOTYPE

@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
//@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public class Add implements AddI {
    public  int a=0;
   
    public  void Add() {
        a++;
    }
    
    public  void getA() {
        System.out.println("【非常醒目 Add】>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>a= " +a+"");
    }
}

[置顶] Spring4实战学习笔记_第9张图片

[置顶] Spring4实战学习笔记_第10张图片

看到差异了吧。


补充说明:@Repository、@Service、@Controller 和 @Component 将类标识为Bean,都是一样的,用在不同的地方而已。

你可能感兴趣的:([置顶] Spring4实战学习笔记)