小白的Spring框架学习之路(2)——SpringIoC容器(下)

延迟初始化
Spring默认的容器在启动时会将
所有的Bean都初始化完成,
所以我们设置延迟初始化只是告诉容器
在第一次使用Bean时完后初始化,而非启动时完成。
<!--延迟初始化-->
    <bean id="lazyExampleBean" class="com.dyy.springcore.LazyExampleBean" lazy-init="true"/>
LazyExampleBean lazyExampleBean = (LazyExampleBean) context.getBean("lazyExampleBean");
        System.out.println(lazyExampleBean);
当我们在上述代码的执行前后分别打断点,进行Debug调试时
会发现除了设置延迟初始化的Bean之外所有的都在启动时初始化好了。(如下图所示)

这里写图片描述
这里写图片描述

自动装配
在此前我们进行了手工完成Bean之间依赖关系的装配,除此之外,Bean可以进行自动装配。
自动装配功能的定义:
不需要在Spring配置文件中描述Bean之间的依赖关系,如:配置<property>、<constructor-arg>
而IoC容器会自动建立Bean之间的联系
public class Customer {
    private final Bar bar;

    public Customer(Bar bar) {
        this.bar = bar;
    }

    public Bar getBar() {
        return bar;
    }

    @Override
    public String toString() {
        return "Customer{" +
                "bar=" + bar +
                '}';
    }
}
public class Customer2 {
    private Bar bar;

    public Bar getBar() {
        return bar;
    }

    public void setBar(Bar bar) {
        this.bar = bar;
    }

    @Override
    public String toString() {
        return "Customer{" +
                "bar=" + bar +
                '}';
    }
}
<!--自动装配-->
    <bean id="customer" class="com.dyy.springcore.auto.Customer " autowire="constructor"/>
    <bean id="customer2" class="com.dyy.springcore.auto.Customer2" autowire="byName"/>
Spring的四种自动装配模式
(1)no:缺省情况下,自动配置通过“ref”属性自动设定
(2)byName:根据属性名称自动装配,如果属性名和其他Bean的属性名相同,都会自动装配。
(3)byType:根据参数类型进行自动装配,如果一个bean的数据类型是用其他bean属性的数据类型,
则兼容并自动进行装配,但是如果同一个类型bean有多个,则无法进行自动装配。
(4)constructor:构造函数参数是通过byType方式进行自动装配。

Bean的作用域

Bean的作用域是描述一个Bean实例在SpringIoC容器中的状态。
Spring所支持的作用域
singleton
这是Spring默认的作用域
此作用域下的Bean在IoC容器中只创建一个实例,
所有的对象对其的引用都返回同一个。
prototype
在这个作用域下的进行的Bean请求每次都会创建新的实例。每次调用的返回值均不同
request
每次http请求会创建新的Bean实例,类似于prototype。(限定于在SpringMVC中使用)
session
在一个http session中,定义一个Bean实例(限定于在SpringMVC中使用)
global session
类似于http session,但是限定于在protlet web中使用
application session

这里写图片描述

自定义作用域
上面我们看到了Spring自带的作用域,除此之外我们还可以通过自定义作用域
来实现更多的作用域
我们通过实现一个简单的线程作用域为例
实现Scope接口
package com.dyy.springcore.auto;

import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.Scope;
import org.springframework.core.NamedThreadLocal;

import java.util.HashMap;
import java.util.Map;

/**
*@Description: 用于线程的自定义作用域
*@Author: dyy
*/

public class SingleThreadScope implements Scope {

    private ThreadLocal<Map<String,Object>> threadLocal = new NamedThreadLocal<Map<String, Object>>("SingleThreadScope"){
        @Override
        protected Map<String, Object> initialValue() {
            return super.initialValue();
        }
    };
    public Object get(String s, ObjectFactory<?> objectFactory) {
        Map<String ,Object> map = this.threadLocal.get();
        Object beanName = map.get(s);
        if(beanName==null){
            beanName = objectFactory.getObject();
            map.put(s,beanName);
        }
        return beanName;
    }

    public Object remove(String s) {
        return this.threadLocal.get().remove(s);
    }

    public void registerDestructionCallback(String s, Runnable runnable) {

    }

    public Object resolveContextualObject(String s) {
        return null;
    }

    public String getConversationId() {
        return null;
    }
}
注册作用域
(1)使用代码方式注册自定义作用域
 context.getBeanFactory().registerScope("thread",new SingleThreadScope());
(2)使用配置XML的方式注册自定义作用域
<!--配置方式设置自定义作用域-->
    <bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
        <property name="scopes">
            <map>
                <entry key="thread" value-ref="scope"/>
            </map>
        </property>
    </bean>
编码验证
我们使用ExampleBean4类进行多线程下的验证,在XML中对类进行配置
<!--配置用于多线程验证的类-->
<bean id="bean44" class="com.dyy.springcore.ExampleBean4" scope="thread">
        <property name="name" value="lemon"/>
        <property name="id">
            <null/>
        </property>
    </bean>
进行验证
package com.dyy.springcore.auto;

import com.dyy.springcore.ExampleBean4;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.concurrent.CountDownLatch;
/**
*@Description: 此类用于多线程下自定义作用域的验证
*@Author: dyy
*/
public class IoCAutoApplication {
    public static void main(String[] args) {
        final  ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application-content.xml");
        //此语句是使用代码注册方式定义作用域,与使用XML进行配置具有等同效果
        //        context.getBeanFactory().registerScope("thread",new SingleThreadScope());
        final int count = 3;
        final CountDownLatch countDownLatch = new CountDownLatch(count);
        for(int i = 0; i < count ; i++) {
            new Thread(new Runnable() {
                public void run() {
                    ExampleBean4 exampleBean4 = (ExampleBean4) context.getBean("bean44");
                    System.out.println("currentThread " + Thread.currentThread().getName() + " " + exampleBean4.hashCode());
                    countDownLatch.countDown();
                }
            }).start();
        }
            try {
                countDownLatch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    }
}
通过结果我们可以看出,每个线程从容器中获取的实例都是不同的,达到了我们的预期目标。

基于注解的配置

不论我们通过XML还是通过注解进行配置,它们都是表达Bean的载体,
本质都是为Spring容器提供Bean定义的信息,在表现形式上都是将XML定义的内容通过注解进行描述。
#
Spring容器启动成功地三要素:
(1)Bean的定义
(2)Bean实现类
(3)Spring本身
使用注解定义Bean
采用基于注解配置文件,则Bean的定义信息时通过在Bean上使用注解标识实现的。
而采用XML的配置,则Bean定义信息和Bean实现类是分离的。

#

基于注解的配置
@Component
public class Circle implements Shape {
}
实则上述代码等同于基于XML配置中的
<bean class="com.dyy.springcore.auto.Circle"/>
使用@Component标识的类会被Spring容器识别,自动转化为被容器管理的Bean。

在我们使用注解进行配置时,同样需要在配置文件中加入如下字样来告诉Spring容器开启自动Bean扫描

<!--使用注解进行配置时需要加入-->
    <contex:component-scan base-package="com.dyy.springcore"/>
//注解的验证
public class IoCAutoApplication2 {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application-content.xml");
        ShapeService shapeService = context.getBean(ShapeService.class);
        System.out.println(shapeService);
    }
}
与@Compent注解等同的注解来进行类作用的标识:
(1)@Repository:标识类属于应用
(2)@Serivce:标识类负责业务逻辑
(3)@Controller:标识类属于数据访问呢
使用注解装配
进行自动装配@Autowired
@Autowired默认是按照类型进行装配(byType)在容器中查找匹配的Bean
当有且仅有一个匹配的Bean四,Spring会将其注入到@Autowired标注的变量中
若含有多个即产生歧义时,则应该使用@Qualifier来进行装配
@Service
public class ShapeService {

    @Autowired
    private Shape shape;

    public void setShape(Shape shape) {
        this.shape = shape;
    }

    public Shape getShape() {
        return shape;
    }

    @Override
    public String toString() {
        return "ShapeService{" +
                "shape=" + shape +
                '}';
    }
}
若在进行自动装配时未找到匹配的Bean,则Spring容器会报出NoSuchBeanDefinitionException异常信息。
当我们希望即使未找到也不要给我们进行错误提示信息时,则应使用@Autowired(required=false)来进行标注(默认requried为true)
指定注入Bean的名称@Qualifier
当采用自动注入时会有多个与之类型匹配的Bean时,则我们需要对其进行限定,则应该使用
@Qualifier注解来进行指定名称的标注

#

如:当我们同时具有triangle和circle满足shape的需求时,则进行自动装配时会
出现歧义,我们可以使用如下指定名称来进行装配。
@Service
public class ShapeService {

    @Autowired
    @Qualifier(value = "triangle")
    private Shape shape;

    public void setShape(Shape shape) {
        this.shape = shape;
    }

    public Shape getShape() {
        return shape;
    }

    @Override
    public String toString() {
        return "ShapeService{" +
                "shape=" + shape +
                '}';
    }
}
其他注解
@Scope:指定作用域注解
@PostConstruct:此注解标明在实例化之后就要做的操作
@PreDestory:此注解标明在对象销毁之前要做的操作。
package com.dyy.springcore.auto;

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

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.time.LocalDate;

@Component
@Scope(value = "prototype")
public class ChineseCurrence {

    public ChineseCurrence() {
        System.out.println("构造方法");
    }
    public LocalDate localDateTime(){
        return LocalDate.now();
    }
    @PostConstruct
    public void init(){
        System.out.println("对象实例化完成后执行");
    }
    @PreDestroy
    public void destory(){
        System.out.println("对象销毁前执行");
    }
}
public class IoCAutoApplication2 {

    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application-content.xml");
        ShapeService shapeService = context.getBean(ShapeService.class);
        System.out.println(shapeService);

        ChineseCurrence chineseCurrence = context.getBean(ChineseCurrence.class);
        System.out.println(chineseCurrence);
        context.getBeanFactory().destroyBean(chineseCurrence);
    }
}

这里写图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值