spring中产生bean的几种方式

Posted Hide on jdk

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring中产生bean的几种方式相关的知识,希望对你有一定的参考价值。

  1. @Bean

  1. @Import

  1. MyImportSelector implements ImportSelector

  1. MyImportBeanDefinitionRegistarimplements ImportBeanDefinitionRegistrar

  1. FactoryBean

这里着重讲解FactoryBean

如何判断当前bean是否是FactoryBean

org.springframework.beans.factory.support.AbstractBeanFactory#isFactoryBean(java.lang.String)

org.springframework.beans.factory.support.AbstractBeanFactory#isFactoryBean(java.lang.String, org.springframework.beans.factory.support.RootBeanDefinition)

查看mbd中的属性即可。

手动拼接一个&走生命周期

注意上图doGetBean的时候会把beanname里面的&去掉,包括多个&的beanname也会把多个&去掉。

如果该bean是factorybean的话,那么第一次加上&的话走了生命周期,单例池就会有该bean,如果是SmartFactoryBean的话第二次执行就会走下面标红的代码来处理getObject逻辑。

看下如何处理?

org.springframework.beans.factory.support.AbstractBeanFactory#getObjectForBeanInstance

先去缓存中拿:由于是factorybean是不会变动的,所以放入缓存提高查询效率

缓存中第一次肯定拿不到,继续执行标红代码

org.springframework.beans.factory.support.FactoryBeanRegistrySupport#doGetObjectFromFactoryBean

放入缓存。

spring初识--bean的几种注册方式

spring彻底改变了java世界观。spring解决了java 对象管理问题,今天我们来看看spring创建对象的方式有哪些至今还不知道的吧

注册bean的几种方式(IOC)

BeanDefinition

  • 我们查看类图可以看出,BeanDefinitionRegistry下有三个实现类。spring为我们提供了一个默认的BeanDefinition注册工厂DefaultListableFactory 。 为什么说他是默认的不仅仅是因为名字里出现了Default字样。而是在AnnotationConfigApplicationContext中默认使用的就是DefaultListableFactory。在注解式开发汇总AnnotationConfigApplicationContext上下文是经常使用的。他也可以理解成spring容器。

配置类Config

  • 相信大家都是用过springboot。在springboot中首先需要一个启动类。这个启动类上会添加各种各样的注解进行修饰。其实他就是一个配置的入口。相当于spring中的xml配置文件。
  • 为了演示我们这里也需要一个这样的配置类。告诉spring哪些类他需要进行装载解析。

@ComponentScan(value = "com.zxhtom.cloud.order.spring")
public class Config 


  • Config的作用就是告诉spring去扫描com.zxhtom.cloud.order.spring包下带有spring注解的类及配置类

生成bean

  • 相信大家对@Bean@Component@Service这些注解都不陌生。这些都是spring bean的注解。但是这些注解的背后又是什么呢。对!就是本章节的主题BeanDefinition 。 spring的bean的注册都是通过它来完成的。
//首先获取容器上下文
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
//生成java类对应的BeanDefinitionBuilder
BeanDefinitionBuilder builder = BeanDefinitionBuilder
        .genericBeanDefinition(Student.class);
//将BeanDefinition注册到该spring容器上
context.registerBeanDefinition("student",builder.getBeanDefinition());
//尝试获取
Object orderController = context.getBean("student");
System.out.println(orderController);

  • 上面简单几行就完成了springbean的注册。在AnnotationConfigApplicationContext中,我们看到他是继承了GenericApplicationContext这个类的。而这个类中默认的是上面我们提到的DefaultListableFactory

  • 这也是我们在开始放置的一张图。到这里我们知道BeanDefinition是spring注册bean的元素。而BeanDefinitionRegistry是注册真正的工作者。他负责解析BeanDefinition将对应的Java对象转换成spring的bean。这中间还有很多很多的细节处理。这里我们不做展开啦。

FactoryBean

  • FactoryBean是一个接口,在java8中我们只需要实现getObjectgetObjectType两个方法。前者是生成对象后者是返回对象Class对象。

  • 下面我们通过FactoryBean来创建一个springbean .


@Data
public class User 
    private String name;


  • ①首先我们编写一个普通的Java类User 。不要添加spring注册bean的注解

public class UserFactoryBean implements FactoryBean<User> 
    @Override
    public User getObject() throws Exception 
        User user = new User();
        user.setName("hello");
        return user;
    

    @Override
    public Class<?> getObjectType() 
        return User.class;
    


  • ②然后在编写一个实现了FactoryBean接口的实现类。在getObject方法中我们构造一个User对象并返回。

public class BeanTest 
    public static void main(String[] args) 
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        BeanDefinitionBuilder builder = BeanDefinitionBuilder
                .genericBeanDefinition(UserFactoryBean.class);
        context.refresh();
        context.registerBeanDefinition("user", builder.getBeanDefinition());
        System.out.println("获取user:"+context.getBean("user"));
        System.out.println("获取userFactoryBean:"+context.getBean("&user"));
    


  • ③最后我们构建一个UserFactoryBean对应的BeanDefinition对象。值得注意的是UserFactoryBean的BeanDefinition会向spring注册两个对象到spring容器中。一个是getObject方法中的对象。另外一个就是UserFactoryBean自己本身。

  • 在spring容器中是通过KV形式保存对象信息的,两个对象是不可能对应同一个key的。上面注册进来的名称是user 。

key对象
userUser对象
&userUserFactoryBean对象
  • FactoryBean作用就是将负责的bean生成过程进行代码话。 比如上面是构建一个User对象注册到spring容器。这种需求我们直接在User类上添加@Component并在Springboot启动类上添加扫描路径就可以了。
  • 但是如果User对象中的name在启动是需要将当前时间赋值给name, 这种需求我们就不好通过spring提供的注解配置了。但是通过FactoryBean就可以很好的解决了。因为我们通过代码很容器就获取到时间并进行赋值。
  • FactoryBean在spring中的地位也是很高的。在mybatis框架中如何将Mapper接口注册到spring容器就是利用他的功能。因为spring中是无法注册接口的。mybatis将接口生成代理类注册到spring容器。在执行这些代理类的时候在根据Mapper对象的xml里的sql进行SqlSession执行sql进行数据的解析。

Supplier

  • GenericApplicationContext类中我们发现registryBean方法中有个重载的方法需要参数Class、Supplier、BeanDefinitionCustomer。

public static void main(String[] args) 
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
    context.refresh();
    context.registerBean("user", User.class);
    User user = (User) context.getBean("user");
    System.out.println(user.getName());


  • 还是上面的需求,我需要注册是将当前时间赋值给User#name属性。这个时候常规操作是没法满足需求的。这个时候除了上面提到的FactoryBean以外,我们还可以通过Supplier来初始化。

public static void main(String[] args) 
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
    context.refresh();
    context.registerBean(User.class, new Supplier<User>() 
        @Override
        public User get() 
            User user = new User();
            user.setName(new Date().toString());
            return user;
        
    , new BeanDefinitionCustomizer() 
        @Override
        public void customize(BeanDefinition bd) 
            bd.setPrimary(true);
            System.out.println(bd);
        
    );
    User user = (User) context.getBean("user");
    System.out.println(user.getName());


  • 上述代码实现了User对象属性初始化注册。并对BeanDefinition进行加强更新。在Spring中Customizer往往代码封装的意思。BeanDefinitionCustomizer就是对BeanDefinition对象的封装方便对他们进行二次操作。

spring AOP

  • AOP全称Aspect Oriented Programming的缩写。在spring中我们已经习惯了面向切面编程了。切面真的帮助我们实现了很多帮助。他将我们重复性的工作进行抽离,是的我们整体的业务不再线性的编程了。最终我们在开发过程就会变成模块–>aop–>模块

概念

  • 在aop中我们需要了解到几个专有名词

  • Aspect : 申明切面

  • Joint point : 表示连接点。对异常处理

  • Pointcut : 切点。定义拦截点。由点生面

  • Advice : 在切点上进行增强,对执行点进行包装

  • Target : 代理对象的真实对象

  • Weaving: 将Aspect连接起来

spring容器的认识

  • 上面我们演示的spring的两大特性。IOC+AOP。 其中IOC就是通过反射将java对象注册到容器中。那么这个容器到底是什么个东西。这里我们就简单的理解成KV 。 内部其实就是一个Map. key就是java对象在容器中的beanName。value就是java对象本身.

授人以鱼不如授人以渔,Spring的强大相信做过Java开发的都是知道。今天我们开始Spring相关课程的第一话–纵观全局
今后我们也是从这个五个方面进行入手,由于探讨框架本身存在很多未知数,里面的总结也肯定是参考别的文献的。个人总结

Spring结构

Core Container

Spring中的Core Container(核心容器)包含有Core、Beans、Context和Expression Language模块。Core和Beans模块是框架的基础部分,提供IoC(反转控制)和依赖注入特性。这里的基础概念是BeanFactory,它对Factory模式的金典实现来消除对程序性单利模式的需要,并真正地允许你从程序逻辑中分离出依赖关系和配置。

Core

core模块主要包含Spring框架基本的核心工具类,Spring的其他组件都需要使用这个包里的类。
core相当于是底层文件,任何spring的产品都是建立在core上的。

Beans

Beans在Core的基础上进行了功能的扩展它包含访问配置文件,创建和管理bean以及进行控制反转、依赖注入操作相关的所有类。
Beans相当于是功能性的提供。所有的spring项目都用到这个功能。这里引入了Spring重大特性之一的依赖注入(控制反转)

Context

Context 模块构建于 Core 和 Beans 模块基础之上,提供了一种类似于刑DI 注册器的框
架式的对象访问方法 。 Context 模块继承了 Beans 的特性,为 Spring 核心提供了大量
扩展,添加了对国际化(例如资源绑定)、事件传播、资源加载和对 Co ntext 的 透明创
建的支持 。 Context 模块同时也支持 J2EE 的一些特性, 171) :Ji.口 EJB 、几仪和基础的远程
处理 。 ApplicationContext 接口是 Context 模块的关键 。

Expression Language模块提供了一个强大的表达式语言用于在运行时查询和操作对象。

Expression Language 模块提供了强大的表达式语言,用于在运行时查询和操纵对象 。
它是 JSP 2.1 规范中定义的 unifed expression language 的扩展 。 该语言支持设直/获取属
性的值,属性的分配,方法的调用,访问数组上下文(
accessiong the context of arrays )、
容器和索引器、逻辑和算术运算符、命名变量以及从 S prit屯的 IoC 容器中根据名称检
索对象 。 它也支持 list 投影、选择和一般的 list 聚合

Data Access/Integration

Data Access/Integration 层包含而BC 、 ORM 、 OXM 、几础和 Transaction 模块 。

  • JDBC模块提供了一个JDBC抽象层,它可以消除冗长的JDBC编码和解析数据库厂商特有的错误代码。 这个模块包含了Spring对JDBC数据访问进行封装的所有类。
  • ORM模块为流行的对象-关系映射API,如JPA、JDO、Hibernate、Mybatis.提供了一个交互层。利用ORM封装包,可以混合使用所有Springboot提供的特性进行OR映射
    Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括 JDO 、Hibernate 和 iBatisSQL Map 。 所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构
  • OXM 模块提供了一个对 ObjecνXML 映射实现的抽象层, Object/XML 映射实现包括JAXB 、 Castor 、 XMLBeans 、 JiBX 和 XStrearn 。。
  • JMS ( Java Messaging Service )模块主要包含了 一些制造和消 费消息的特性 。
  • Transaction 模块支持编程和声明性的事务 管理,这些事务类必须实现特定的接 口,并且对所有的 POJO 都适用 。

Web

Web上下文模块建立在应用程序上下文模块之上。为基于Web的应用程序提供了上下文。所以Spring框架支持与Jakarta Struts集成。Web模块还简化了处理大部分请求以及讲请求参数绑定到域对象工作,Web层包含了Web,Servlet、Struts、Porlet模块。

  • Web模块:童工基础的面向web的集成特性。多文件上传、使用listeners初始化IOC容器、容器上下文。还包括Spring远程支持中Web的相关部分。
  • Servlet模块: 该模块包含 Spring 的 model-view-controller ( MVC)实现 。 Spring 的 MVC 框架使得模型范围内的代码和 web forms 之间能够清楚地分离开
    来,并与 Spring 框架的其他特性集成在一起
  • Struts模块:该模块提供了对Struts支持,是的类在Springboot容器中能够与一个典型的Struts web 层集成在一起,spring3.0之后已被抛弃
  • Porlet模块: 提供了用于Portlet环境和Web——servlet模块的Mvc的实现

Aop

AOP 模块提供了 一个符合 AOP 联盟标准的面向切面编程的实现,它让你可以定义例如方
法拦截器和切点,从而将逻辑代码分开,降低它们之间的调合性 。 利用 source-level 的元数据
功能,还可以将各种行为信息合并到你的代码中,这有点像 .Net 技术中的 attribute 概念 。
通过配置管理特性, SpringAOP 模块直接将面向切面的编程功能集成到了 Spring 框架中,
所以可以很容易地使 Spring 框架管理的任何对象支持 AOP 。 Spring AOP 模块为基于 Spring 的
应用程序中的对象提供了事务管理服务 。 通过使用 SpringAOP ,不用依赖 EJB 组件,就可以将
声 明性事务管理集成到应用程序中 。
Aspects 模块提供了对 AspectJ 的集成支持 。

Instrumentation 模块提供了 class instrumentation 支持和 classloader 实现, 使得可以在特
定的应用服务器上使用 。

Test

Test模块就是在容器内进行单元测试。没啥好说的。

总结

  • spring是个框架,但是现在spring已经不仅仅是框架了。我们可以把spring理解成一个生态。
  • 在spring基础上已经衍生出脚手架springboot 。 还有在微服务上的springcloud
  • 而在springcloud中有衍生出很多的组件。大多都是借鉴了netflix 。 比如说eureka、 zuul 等 。 其中zuul已被springcloud gateway取代
  • 因为spring的设计优秀已经和mybatis、redis、rabbitmq这些都是完美的整合了。
  • 本章主要是介绍spring . 后面也会继续更新springcloud专题。 因为在cloud中发现对spring的基础要求还是很高的。
  • 这个五一先放放cloud 。 来个spring爽爽 。 cloud五一后再见
    务上的springcloud
  • 而在springcloud中有衍生出很多的组件。大多都是借鉴了netflix 。 比如说eureka、 zuul 等 。 其中zuul已被springcloud gateway取代
  • 因为spring的设计优秀已经和mybatis、redis、rabbitmq这些都是完美的整合了。
  • 本章主要是介绍spring . 后面也会继续更新springcloud专题。 因为在cloud中发现对spring的基础要求还是很高的。
  • 这个五一先放放cloud 。 来个spring爽爽 。 cloud五一后再见

以上是关于spring中产生bean的几种方式的主要内容,如果未能解决你的问题,请参考以下文章

spring注入bean的几种方式

spring初识--bean的几种注册方式

spring初识--bean的几种注册方式

spring Bean装配的几种方式简单介绍

Spring构建bean实例的几种方式

C#中产生SQL语句的几种方式