Spring Beans

IOC

明确定义组件的接口,独立开发不同的组件,然后按照依赖组装运行。将创建和管理的权利交给spring框架。其核心是Bean工厂(bean factory),能够让各个组件之间保持松散的耦合,降低了业务对象替代的复杂性。

IOC 控制反转与DI 依赖注入

控制的什么被反转了?就是:获得依赖对象的方式反转了

对象@Bean

spring boot 常用的管理Bean的注解

  • name:指定Bean的名称,默认为方法名

生命周期

来源org.springframework.beans.factory.BeanFactory的JavaDoc

创建Bean
  1. BeanNameAware的setBeanName
  2. BeanClassLoaderAware的setBeanClassLoader
  3. BeanFactoryAware的setBeanFactory
  4. EnvironmentAware的setEnvironment
  5. EmbeddedValueResolverAware的setEmbeddedValueResolver
  6. ResourceLoaderAware的setResourceLoader (仅在在应用程序上下文中运行时适用)
  7. ApplicationEventPublisherAware的setApplicationEventPublisher (仅适用于在应用程序上下文中运行的情况)
  8. MessageSourceAware的setMessageSource (仅适用于在应用程序上下文中运行的情况)
  9. ApplicationContextAware的setApplicationContext (仅适用于在应用程序上下文中运行的情况)
  10. ServletContextAware的setServletContext (仅适用于在Web应用程序上下文中运行的情况)
  11. BeanPostProcessors的postProcessBeforeInitialization方法
  12. InitializingBean的afterPropertiesSet
  13. 自定义的初始化方法定义
  14. BeanPostProcessors的postProcessAfterInitialization方法
销毁Bean
  1. DestructionAwareBeanPostProcessors的postProcessBeforeDestruction方法
  2. DisposableBean的destroy
  3. 自定义销毁方法定
package com.example.demo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;

import com.example.demo.domain.MyBean;

@Configuration
public class BeanConfig {

    @Bean
    public MyBean myBean() {
        return new MyBean();
    }
}

作用域 @Scope

指定当前Bean的作用域和@Bean一起使用

作用域描述
singleton单例模式,默认模式
prototype每次都会返回新的Bean
以下几种作用域仅适用于web的Spring WebApplicationContext环境
request每个http request 会创建一个新的Bean
session每个http session 会创建一个新的Bean
application限定一个Bean的作用域为ServletContext的生命周期
package com.example.demo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;

import com.example.demo.domain.MyBean;

@Configuration
public class BeanConfig {

    @Bean
    @Scope("prototype")
    public MyBean myBean() {
        return new MyBean();
    }
}

延迟加载 @Lazy

该注解会让Bean延迟加载,即第一次使用时加载(默认是spring 容器启动时加载)

package com.example.demo.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import com.example.demo.domain.MyBean;

@RestController
public class BeanController {
    public static final Logger LOGGER = LoggerFactory.getLogger(BeanController.class);


    @Autowired
    @Lazy
    private MyBean myBean0;

    @Autowired
    @Lazy
    private MyBean myBean1;

    @GetMapping("printBean")
    public void printBean() {
        LOGGER.info(myBean0.toString());
        LOGGER.info(myBean1.toString());
    }
}

默认的 @Primary

在不使用@Qualifier指定BeanName的时候,默认都会使用@Primary的注解

package com.example.demo.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import com.example.demo.domain.MyBean;

@Configuration
public class BeanConfig {

    public static final Logger LOGGER = LoggerFactory.getLogger(BeanConfig.class);


    @Bean
    @Primary
    public MyBean myBean() {
        final MyBean myBean = new MyBean();
        LOGGER.info("Primary,{}", myBean);
        return myBean;
    }

    @Bean
    public MyBean myBean3() {
        final MyBean myBean = new MyBean();
        LOGGER.info(myBean.toString());
        return myBean;
    }
}

特定的 @Qualifier

通过指定BeanName,使用某一个Bean

package com.example.demo.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import com.example.demo.domain.MyBean;

@RestController
public class BeanController {
    public static final Logger LOGGER = LoggerFactory.getLogger(BeanController.class);


    @Autowired
    private MyBean myBean0;

    @Autowired
    @Qualifier("myBean3")
    private MyBean myBean1;

    @GetMapping("printBean")
    public void printBean() {
        LOGGER.info(myBean0.toString());
        LOGGER.info(myBean1.toString());
    }
}

依赖 @DependsOn

改变Bean加载的顺序,myBean3 会在Mybean 之前加载

package com.example.demo.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

import com.example.demo.domain.MyBean;

@Configuration
public class BeanConfig {

    public static final Logger LOGGER = LoggerFactory.getLogger(BeanConfig.class);

    @Bean
    @DependsOn("myBean3")
    public MyBean myBean() {
        final MyBean myBean = new MyBean();
        LOGGER.info("Primary,{}", myBean);
        return myBean;
    }

    @Bean
    public MyBean myBean3() {
        final MyBean myBean = new MyBean();
        LOGGER.info(myBean.toString());
        return myBean;
    }
}

ApplicationContext

BeanFactory是Spring框架最核心的接口,它提供了高级IOC的配置机制。
ApplicationContext建立在BeanFactory的基础上,提供了更多面向应用的功能, 它提供了国际化支持和框架事件体系。
在这里插入图片描述

区别

描述

  • BeanFactory=>IOC容器
  • ApplicationContext=>应用上下文,或者spring容器

使用

  • BeanFactory是Spring框架的基础设施,面向Spring本身
  • ApplicationContext面向使用Spring框架的开发者,几乎所有的应用场合都可以直接使用ApplicationContext而非底层的BeanFactory

ApplicationContext使用

    @Autowired
    private ApplicationContext applicationContext;

获取Bean

// 从spring容器获取Bean
final BeanController bean = applicationContext.getBean(BeanController.class);

发布事件

主要部分
  1. 事件 继承ApplicationEvent 拓展自己属性
package com.example.demo.event;

import org.springframework.context.ApplicationEvent;

/**
 * @author lb
 */
public class LoginEvent extends ApplicationEvent {

    private String userName;

    /**
     * Create a new {@code ApplicationEvent}.
     *
     * @param source the object on which the event initially occurred or with
     *               which the event is associated (never {@code null})
     */
    public LoginEvent(Object source) {
        super(source);
    }

    public LoginEvent(Object source, String userName) {
        super(source);
        this.userName = userName;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }
}
  1. 发布事件 使用ApplicationContext发布一个事件
applicationContext.publishEvent(new LoginEvent(this, userName));
  1. 监听事件 使用@EventListener 注解,或者实现ApplicationListener<T>接口
    @EventListener(LoginEvent.class)
    public void onLoginEvent(LoginEvent event) {
        LOGGER.info("onLoginEvent,{}", event.getUserName());
    }
监听器
  • 排序 使用@Order 注解,越小优先度越高
  • 继续发布事件,返回一个事件
  • 异步,使用@EnableAsync@Async 注解
    • 注意事项,不能用返回事件的方式继续发布事件,需要手动发布
    • 异常不会传播到时间发布方,需要手动处理
package com.example.demo.event;

import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Component;

/**
 * @author admin
 */
@Component
@EnableAsync
public class LoginListener {

    public static final Logger LOGGER = LoggerFactory.getLogger(LoginListener.class);

    /**
     * 发布LogoutEvent事件
     *
     * @param event 事件
     * @return LogoutEvent事件
     */
    @EventListener(LoginEvent.class)
    @Order(1)
    public LogoutEvent onApplicationEvent(LoginEvent event) {
        LOGGER.info("onApplicationEvent,{}", event.getUserName());
        return new LogoutEvent(event.getSource(), event.getUserName());
    }

    /**
     * 异步处理
     * 优先于{@link this#onApplicationEvent(LoginEvent)}执行
     *
     * @param event 事件
     * @throws InterruptedException 异常
     */
    @EventListener(LoginEvent.class)
    @Order(0)
    @Async
    public void onLoginEvent(LoginEvent event) throws InterruptedException {
        LOGGER.info("onLoginEvent,{}", event.getUserName());
        TimeUnit.SECONDS.sleep(50);
    }

    /**
     * @param event 事件
     */
    @EventListener
    public void onLogoutEvent(LogoutEvent event) {
        LOGGER.info("onLogoutEvent,{}", event.getUserName());
    }
}

访问资源

Resource本质上是JDKjava.net.URL类的功能更丰富的版本。
可以以透明的方式从几乎任何位置获取低级资源,包括从类路径,文件系统位置,可使用标准URL描述的任何位置以及一些其他变体。

    /**
     * resources
     */
    @GetMapping("resources")
    public void login() throws IOException {
        // 系统位置文件
        final Resource resource = applicationContext.getResource(ResourceUtils.FILE_URL_PREFIX + "/data/uuid.txt");
        LOGGER.info("resource:{}:{}", resource.getDescription(), resource.contentLength());
        // 类路径文件,resource文件夹下等
        final Resource classPathResource = applicationContext.getResource(ResourceUtils.CLASSPATH_URL_PREFIX + "resources.txt");
        LOGGER.info("classPathResource:{}:{}", classPathResource.getDescription(), classPathResource.contentLength());
        // 网络资源
        final Resource urlResource = applicationContext.getResource("https://helpimage.paperol.cn//20210120161831.png");
        LOGGER.info("urlResource:{}:{}", urlResource.getDescription(), urlResource.contentLength());
    }
2021-01-25 11:51:01.997  INFO 8204 --- [nio-8080-exec-1] c.e.demo.controller.BeanController       : resource:URL [file:/data/uuid.txt]18278
2021-01-25 11:51:01.998  INFO 8204 --- [nio-8080-exec-1] c.e.demo.controller.BeanController       : classPathResource:class path resource [resources.txt]:1845
2021-01-25 11:51:03.140  INFO 8204 --- [nio-8080-exec-1] c.e.demo.controller.BeanController       : urlResource:URL [https://helpimage.paperol.cn//20210120161831.png]:219156

国际化等 TODO

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值