欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Spring实战拆书--SpringBean

程序员文章站 2023-10-31 13:54:04
https://github.com/wujiachengSH/springBeanDemo ......

代码源码地址:https://github.com/wujiachengsh/springbeandemo

概述:本章将讲解spring对于bean的管理方案。

目录:

  1. 准备工作
  2. 自动装配
  3. 处理装配歧义性
  4. bean的作用域
  5. 注入式声明bean

代码环境:

  1. sts
  2. jdk1.8
  3. spring4

 

1.准备工作

请在github上下载源码结合文章阅读,效果更佳

在创建了springboot项目后,我们首先需要开启组件扫描,如下代码所示。

@configuration
//扫描指定包目录
@componentscan(basepackages="com.wjc")
public class beanconfig {
}

声明一个测试bean的接口,全文的主要内容都是通过此接口的实现类完成的

package com.wjc.spring.bean;

public interface bird {

    void fly();
    void feed();
    void twitter();
    void changetwiter();
}

 


 

2.自动装配

  自动装配是最常见的bean装配形式。

  我们首先写一个bird接口的实现类来展示自动装配,只需一个“@component”注解即可完成。

package com.wjc.spring.bean.impl;

import org.springframework.beans.factory.annotation.qualifier;
import org.springframework.stereotype.component;

import com.wjc.spring.bean.bird;
//这是知更鸟
@component
public class robin implements bird { private string flystr ="知更鸟起飞"; private string feedstr = "不想吃东西"; private string twiterstr = "啊啊啊"; @override public void fly() { system.out.println(flystr); } @override public void feed() { system.out.println(feedstr); } @override public void twitter() { system.out.println(twiterstr); } @override public void changetwiter() { } }

 

在测试时,我们只需要使用“@autowired”注解,就可以拿到对应的对象了

通过junit可以测试装配是否完成

@runwith(springjunit4classrunner.class)
@contextconfiguration(classes=beanconfig.class)
public class beantest {

    @autowired
    private bird bird;
    
    //测试1,查看是否自动装配了知更鸟
    //此时bean.impl只有robin
    @test
    public void beantest1() {
        assertnotnull(bird);
    }
}

 


 

3.处理自动装配的歧义性(@qualifier)

代码源码地址:https://github.com/wujiachengsh/springbeandemo

试想如果我有2个bird接口的实现类,spring在装配时是否会因为不知道具体需要哪个实现类而报错?

此时声明一个“parrot”,也实现bird接口,运行test方法会如何?

package com.wjc.spring.bean.impl;

import org.springframework.beans.factory.annotation.qualifier;
import org.springframework.stereotype.component;

import com.wjc.spring.bean.bird;
//这是鹦鹉
@component
public class parrot implements bird {

    private string flystr ="鹦鹉起飞"; 
    private string feedstr = "啥都吃";
    private string twiterstr = "说人话";
    
    @override
    public void fly() {
        system.out.println(flystr);
    }

    @override
    public void feed() {
        system.out.println(feedstr);
    }

    @override
    public void twitter() {
        system.out.println(twiterstr);
    }

    @override
    public void changetwiter() {
        twiterstr = "你好你好";
    }

}

运行结果如下:

org.springframework.beans.factory.unsatisfieddependencyexception: error creating bean with name 'com.wjc.spring.test.beantest': unsatisfied dependency expressed through field 'bird'; nested exception is org.springframework.beans.factory.nouniquebeandefinitionexception: no qualifying bean of type 'com.wjc.spring.bean.bird' available: expected single matching bean but found 5: parrot,quail,robin,cuckoo1,cuckoo2
	at org.springframework.beans.factory.annotation.autowiredannotationbeanpostprocessor$autowiredfieldelement.inject(autowiredannotationbeanpostprocessor.java:596)

 可以看到,由于spring并不知道应该将哪一个实现类注入到bird中,报出了 “unsatisfieddependencyexception”,我们可以通过注解“@qualifier("parrot")”来解决此问题

//这是鹦鹉
@component
@qualifier("parrot")
public class parrot implements bird {

在获取实现类时使用如下方式,即可获取到自己想要的对象实例了

    @autowired
    @qualifier("parrot")
    private bird parrot;
    
    
    //添加@qualifier("parrot")来解决声明问题
    @test
    public void beantest3() {
        // 此时鹦鹉添加了@primary
        parrot.fly();
        assertnotnull(parrot);
    }

 


 

4.bean的作用域

已知spring默认是单例模式,但在多线程高并发的情况下,单例模式其实未必是最佳选择,如果线程a将bean赋了值,而此时线程b拿取了被a赋值的对象,并返回了对应的结果,此时是不是会出现b返回了预料之外的结果?

本文简单讨论一下原型模式下bean的传递,和会发生的问题,具体的各自作用域请百度“spring作用域”

已知spring作用域如下:singleton / prototype / request  / session /global session

 我们来看一下如下代码,一个原型模式的对象

package com.wjc.spring.bean.impl;

import org.springframework.beans.factory.annotation.qualifier;
import org.springframework.context.annotation.scope;
import org.springframework.stereotype.component;

import com.wjc.spring.bean.bird;

//这是鹌鹑
//这个使用原型模式
@component
@qualifier("quail")
@scope("prototype")
public class quail implements bird {

    private string flystr ="鹌鹑起飞"; 
    private string feedstr = "鹌鹑想吃啥就吃啥";
    private string twiterstr = "鹌鹑不知道怎么叫";
    
    @override
    public void fly() {
        // todo auto-generated method stub
        system.out.println(flystr);
    }

    @override
    public void feed() {
        // todo auto-generated method stub
        system.out.println(feedstr);
    }

    @override
    public void twitter() {
        // todo auto-generated method stub
        system.out.println(twiterstr);
    }

    public void changetwiter() {
        twiterstr = "我大鹌鹑今天就是饿死。。。。";
    }
    
}

看下在test时他的表现如何:

@runwith(springjunit4classrunner.class)
@contextconfiguration(classes=beanconfig.class)
public class beantest3 {

    @autowired
    @qualifier("quail")
    private bird bird;
    
    @autowired
    @qualifier("quail")
    private bird bird2;
    
    //测试原型模式与单例的区别
    @test
    public void beantest1() {
        bird.twitter();
        bird.changetwiter();
        bird.twitter();
        
        bird2.twitter();
        bird2.changetwiter();
        bird2.twitter();
    }
}

 

 

运行结果:

鹌鹑不知道怎么叫
我大鹌鹑今天就是饿死。。。。
鹌鹑不知道怎么叫
我大鹌鹑今天就是饿死。。。。

 

 spring确实将此bean对象变成了原型模式。那么作用域是否就这么简单的完成了?

 我们看一下如下代码

@service
public class birdserviceimpl implements birdservice {

    @autowired
    @qualifier("quail")
    private bird bird;

    public void scoptest() {
        bird.twitter();
        bird.changetwiter();
        bird.twitter();
    }
}

运行测试类:

@runwith(springjunit4classrunner.class)
@contextconfiguration(classes=beanconfig.class)
public class servicetest {

    @autowired
    private birdservice birdservice;
    
    @autowired
    private birdservice birdservice2;
    
    //测试在service上添加和不添加@qualifier("quail")时调用的bean的区别
    @test
    public void servicetest2() {
        birdservice.scoptest();
        birdservice2.scoptest();
    }
}

运行结果:

鹌鹑不知道怎么叫
我大鹌鹑今天就是饿死。。。。
我大鹌鹑今天就是饿死。。。。
我大鹌鹑今天就是饿死。。。。

????????原型模式失效了????

为什么会发生这种情况?因为在此场景下,“birdserviceimpl”是单例模式的,对bean的操作不可避免的变成了单例的,如果添加如下代码结果就会完全不一样

@service
@scope("prototype")
public class birdserviceimpl implements birdservice {

    @autowired
    @qualifier("quail")
    private bird bird;

    public void scoptest() {
        bird.twitter();
        bird.changetwiter();
        bird.twitter();
    }
}

再次运行时:

鹌鹑不知道怎么叫
我大鹌鹑今天就是饿死。。。。
鹌鹑不知道怎么叫
我大鹌鹑今天就是饿死。。。。

 

 假设“servicetest”方法为control层,“birdserviceimpl”方法为service层,“quail”为bean,在实际应用时,应该考虑scop注解是否会可以成功生效。

如下为测试后的结果

    //当service上有@scope("prototype"),bean上有@scope("prototype")时 返回不同对象
    //当service上有@scope("prototype"),bean上无@scope("prototype")时 返回相同对象
    //当service上无@scope("prototype"),bean上有@scope("prototype")时 返回相同对象
    //当service上无@scope("prototype"),bean上无@scope("prototype")时 返回相同对象

 


 

5.注入式声明bean

在上述代码中,我都是通过硬编码的形式在输入一些内容的,那么能否通过读取配置文件的方式完成输出内容呢?(实际运用场景:获取数据库连接对象session)

我们首先定义一个对象,可以看到我没有添加任何注解,因为此对象不需要在这里进行装配!

package com.wjc.spring.bean.impl;

import com.wjc.spring.bean.bird;
//这是杜鹃
public class cuckoo implements bird {

    private string flystr = "fly" ; 
    private string feedstr = "feed";
    private string twiterstr = "twiter";
    
    
    public cuckoo(string flystr, string feedstr, string twiterstr) {
        super();
        this.flystr = flystr;
        this.feedstr = feedstr;
        this.twiterstr = twiterstr;
    }

    @override
    public void fly() {
        // todo auto-generated method stub
        system.out.println(flystr);
    }

    @override
    public void feed() {
        // todo auto-generated method stub
        system.out.println(feedstr);
    }

    @override
    public void twitter() {
        // todo auto-generated method stub
        system.out.println(twiterstr);
    }

    @override
    public void changetwiter() {
        // todo auto-generated method stub
        twiterstr = "杜鹃";
    }

}

 

 

我们将config改造一下,由他来负责装配对象

package com.wjc.spring.config;

import org.springframework.beans.factory.annotation.autowired;
import org.springframework.context.annotation.bean;
import org.springframework.context.annotation.componentscan;
import org.springframework.context.annotation.configuration;
import org.springframework.context.annotation.propertysource;
import org.springframework.core.env.environment;

import com.wjc.spring.bean.impl.cuckoo;


@configuration
//扫描指定包目录
@componentscan(basepackages="com.wjc")
@propertysource("classpath:cuckoo.properties")
public class beanconfig {
    //开启组件扫描
    //获取资源
    @autowired
    private environment env;
    
    //通过配置文件装配cuckoo
    @bean(name="cuckoo1")
    public cuckoo getbird() {
        
          return new cuckoo(env.getproperty("flystr","fly"),
          env.getproperty("feedstr","feed"), env.getproperty("twiterstr","twiter"));
         
        //return new cuckoo("fly","feed", "twiter");
        
    }
    @bean(name="cuckoo2")
    public cuckoo getbird2() {
         
        return new cuckoo("fly","feed", "twiter");
        
    }
    
}

可以看到我声明了2个"cuckoo"对象实例,分别叫“cuckoo1”,“cuckoo2”

使用test方法来执行一下

package com.wjc.spring.test;

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;

import com.wjc.spring.bean.impl.cuckoo;
import com.wjc.spring.config.beanconfig;

@runwith(springjunit4classrunner.class)
@contextconfiguration(classes=beanconfig.class)
public class beantest4 {

    @autowired
    @qualifier("cuckoo2")
    private cuckoo cuckoo;
    
    @autowired
    @qualifier("cuckoo1")
    private cuckoo cuckoo1;
    //测试通过配置文件装配bean
    @test
    public void beantest1() {
        cuckoo1.fly();
        cuckoo1.feed();
        cuckoo1.twitter();
        cuckoo.fly();
        cuckoo.feed();
        cuckoo.twitter();
    }
    
}

执行结果

cuckoo fly
cuckoo feed
cuckoo twiter
fly
feed
twiter

可以看到成功的声明了对象。

 

本文章为在下在查看spring实战一书和工作上发生过的问题结合来完成的。希望各位看官指点错误和不合理的地方。

代码源码地址:https://github.com/wujiachengsh/springbeandemo