Spring Bean基础-4
创始人
2024-02-07 09:10:55
0

1. 定义Bean: 什么是BeanDefinition?

  • 什么是BeanDefinition?
  • BeanDefinition 是 Spring Framework 中定义 Bean 的配置元信息接口, 包含:
    • Bean 的类名
    • Bean 行为配置元素, 如作用域、自动绑定的模式、生命周期回调等
    • 其他 Bean 引用, 又可称作合作者 (Collaborators) 或者依赖 (Dependencies)
    • 配置设置, 比如 Bean 属性 (Properties)

2. BeanDefinition元信息: 除了Bean名称和类名, 还有那些Bean元信息值的关注?

  • BeanDefinition 元信息

    属性 (Property)说明
    ClassBean 全类名, 必须是具体类, 不能用抽象类或接口
    NameBean 的名称或者 ID
    ScopeBean 的作用域 (如: singleton、prototype 等)
    Constructor argumentsBean 构造器参数 (用于依赖注入)
    PropertiesBean 属性设置 (用于依赖注入)
    Autowiring modeBean 自动绑定模式 (如: 通过名称 byName)
    Lazy initialization modeBean 延迟初始化模式 (延迟和非延迟)
    initialization methodBean 初始化回调方法名称
    Destruction methodBean 销毁回调方法名称
  • BeanDefinition 构建

    • 通过 BeanDefinitionBuilder

    • 通过 AbstractBeanDefinition 以及派生类

      User.class

      package org.xiaoge.thinking.in.spring.ioc.overview.domain;/*** @Classname User* @Date 2022/10/17 14:57* @Created by ZhangXiao* @Description TODO*/
      public class User {private Long id;private String name;public Long getId() {return id;}public void setId(Long id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "User{" +"id=" + id +", name='" + name + '\'' +'}';}
      }

      BeanDefinitionCreationDemo.class

      package org.xiaoge.thinking.in.spring.bean.definition;import org.springframework.beans.MutablePropertyValues;
      import org.springframework.beans.factory.config.BeanDefinition;
      import org.springframework.beans.factory.support.AbstractBeanDefinition;
      import org.springframework.beans.factory.support.BeanDefinitionBuilder;
      import org.springframework.beans.factory.support.GenericBeanDefinition;
      import org.xiaoge.thinking.in.spring.ioc.overview.domain.User;/*** {@link org.springframework.beans.factory.config.BeanDefinition} 构建示例** @Classname BeanDefinitionCreationDemo* @Date 2022/11/2 16:05* @Created by ZhangXiao* @Description TODO*/
      public class BeanDefinitionCreationDemo {public static void main(String[] args) {// 1. 通过 BeanDefinitionBuilder 构建BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(User.class);// 通过属性设置beanDefinitionBuilder.addPropertyValue("id", 1).addPropertyValue("name", "xiaoge");// 获取 BeanDefinition 实例BeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();// BeanDefinition 并非 Bean 终态, 可以自定义修改// 2. 通过 AbstractBeanDefinition 以及派生类GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();// 设置 Bean 类型genericBeanDefinition.setBeanClass(User.class);// 通过 MutablePropertyValues 批量操作属性MutablePropertyValues propertyValues = new MutablePropertyValues();// propertyValues.addPropertyValue("id", 1);// propertyValues.addPropertyValue("name", "xiaoge");propertyValues.add("id", 1).add("name", "xiaoge");// 通过 set MutablePropertyValues 批量操作属性genericBeanDefinition.setPropertyValues(propertyValues);}}
      

3. 命名Spring Bean: id和name属性命名Bean, 那个更好?

  • Bean名称

    • 每个Bean 拥有一个或多个标识符(identifiers) ,这些标识符在Bean 所在的容器必须是唯一的。通常,一个 Bean仅有一个标识符,如果需要额外的,可考虑使用别名(Alias) 来扩充。

    • 在基于XML的配置元信息中,开发人员可用id或者name属性来规定 Bean的标识符。通常Bean 的标识符由字母组成,允许出现特殊字符。如果要想引入 Bean 的别名的话,可在
      name 属性使用半角逗号(“,”)或分号(“;”)来间隔。

    • Bean 的id 或name属性并非必须制定,如果留空的话,容器会为Bean自动生成一个唯一的名称。Bean的命名尽管没有限制, 不过官方建议采用驼峰的方式,更符合Java的命名约定。

    • Bean名称生成器(BeanNameGenerator)

    • 由Spring Framework 2.0.3引入,框架内建两种实现:

      • DefaultBeanNameGenerator:默认通用BeanNameGenerator 实现
    • AnnotationBeanNameGenerator:基于注解扫描的 BeanNameGenerator实现,
      起始于SpringFramework 2.5,关联的官方文档:

      With component scanning in the classpath,Spring generates bean names for unnamed components,following the rules described earlier: essentially,taking the simple class name and turnined components,following the rules described earlier: essentially,taking the simple class name and turning its initial character to lower-case.However, in the (unusual) special case when there is more than one character and both the first and second characters are upper case, the original casing gets preserved.These are the same rules as defined by java.beans.Introspector.decapitalize (which Spring uses herre).通过在类路径中扫描component,Spring为未命名的组件生成bean名称,遵循前面描述的规则:实质上,采用简单的类名和翻转的组件,遵循前面描述的规则:实质上,采用简单的类名并将其初始字符改为小写。但是,在(不寻常的)特殊情况下,当有多个字符并且第一个和第二个字符都是大写字母时,原始的大小写将被保留。这些规则与java.beans.Introspector.decapitalize (Spring在这里使用)定义的规则相同。
      

      关于Bean的命名, Bean的命名有两种方式, 一种是Spring容器自动帮你生成, 当你没有设置名称/id的时候, 另一种是自己去定义所需要的名称, 一般XML是自己定义名称, 而注解我们一般很少会自己去定义名称。

4. Spring Bean的别名: 为什么命名Bean 还需要别名?

  • Bean别名(Alias)的价值

    • 复用现有的 BeanDefinition

    • 更具有场景化的命名方法,比如:

      • Bean别名(Alias)的价值

        
        
        
      • bean-deginitions-context.xml

        
        
        
      • BeanAliasDemo

        package org.xiaoge.thinking.in.spring.bean.definition;import org.springframework.beans.factory.BeanFactory;
        import org.springframework.context.support.ClassPathXmlApplicationContext;
        import org.xiaoge.thinking.in.spring.ioc.overview.domain.User;/*** {@link BeanAliasDemo} 别名示例** @Classname BeanAliasDemo* @Date 2022/11/2 17:02* @Created by ZhangXiao* @Description TODO*/
        public class BeanAliasDemo {public static void main(String[] args) {// 配置xml配置文件// 启动spring应用上下文BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/bean-deginitions-context.xml");// 通过id和类型取User user = beanFactory.getBean("user", User.class);// 通过别名 获取 曾用命 user 的 BeanUser xiaogeUser = beanFactory.getBean("xiaoge-user", User.class);System.out.println("xiaogeUser 是否与 user Bean 相同: " + (xiaogeUser == user));}}/*运行结果xiaogeUser 是否与 user Bean 相同: true
        */
        

5. 注册Spring Bean: 如何讲BeanDefinition注册到IoC容器?

  • BeanDefinition 注册

    • XML 配置元信息
    • Java 注解配置元信息
      • @Bean
      • @Component
      • @Import
    • Java API 配置元信息
      • 命名方式:BeanDefinitionRegistry#registerBeanDefinition(String,BeanDefinition)
      • 非命名方式:BeanDefinitionReaderUtils#registerWithGeneratedName(AbstractBeanDefinition,BeandefinitionRegistry)
      • 配置类方式: AnnotatedBeanDefinitionReader#register(Class…)
  • XML这里就不写了, 主要写注解 和 JavaAPI 方式

    package org.xiaoge.thinking.in.spring.bean.definition;import org.springframework.beans.factory.support.AbstractBeanDefinition;
    import org.springframework.beans.factory.support.BeanDefinitionBuilder;
    import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
    import org.springframework.beans.factory.support.BeanDefinitionRegistry;
    import org.springframework.context.annotation.AnnotatedBeanDefinitionReader;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Import;
    import org.springframework.stereotype.Component;
    import org.springframework.util.StringUtils;
    import org.xiaoge.thinking.in.spring.ioc.overview.domain.User;/**** 注解 BeanDefinition 示例*      1. 通过 @Bean 方式定义*      2. 通过 @Component 方式定义*      3. 通过 @Import 方式定义* Java API 配置元信息*      1. 通过 BeanDefinitionRegistry 方式定义*      2. 通过 BeanDefinitionReaderUtils 方式定义*      3. 通过 AnnotatedBeanDefinitionReader 方式定义** @Classname AnnotationBeanDefinitionDemo* @Date 2022/11/3 9:22* @Created by ZhangXiao* @Description TODO*/
    @Import(AnnotationBeanDefinitionDemo.Config.class) // 3. 通过 @Import 方式定义
    public class AnnotationBeanDefinitionDemo {public static void main(String[] args) {// 创建 ApplicationContext 容器AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();// 注册 Configuration Class 配置类applicationContext.register(AnnotationBeanDefinitionDemo.class);// 通过 BeanDefinition 注册 API 实现// 1. 命名 Bean 的注册方式registerUserBeanDefinition(applicationContext, "xiaoge-niubi");// 2. 非命名 Bean 的注册方式registerUserBeanDefinition(applicationContext);// 3. 配置类方式AnnotatedBeanDefinitionReader annotatedBeanDefinitionReader = new AnnotatedBeanDefinitionReader(applicationContext);annotatedBeanDefinitionReader.register(Config.class);// 启动应用上下文applicationContext.refresh();// 按照类型依赖查找System.out.println("Config 类型的所有 Beans: " + applicationContext.getBeansOfType(Config.class));System.out.println("User 类型的所有 Beans: " + applicationContext.getBeansOfType(User.class));// 关闭应用上下文applicationContext.close();}/*** 带名 注册到 ioc 容器* @param register* @param beanName*/public static void registerUserBeanDefinition(BeanDefinitionRegistry register, String beanName) {BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(User.class);beanDefinitionBuilder.addPropertyValue("id", 1).addPropertyValue("name", "xiaoge");// 构建 BeanDefinitionAbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();// 判断如果beanName存在时if (StringUtils.isEmpty(beanName)) {// 注册 BeanDefinition  非命名方式(不带名称) 注册BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, register);} else {// 注册 BeanDefinition  命名方式(带名称) 注册register.registerBeanDefinition(beanName, beanDefinition);}}public static void registerUserBeanDefinition(BeanDefinitionRegistry register) {registerUserBeanDefinition(register,null);}// 定义当前类做为 Spring Bean (组件)@Component // 2. 通过 @Component 方式定义public static class Config {/*** 通过 Java 注解的方式, 定义了一个 Bean* @return*/@Bean(name = {"user", "user-xiaoge"}) // 1. 通过 @Bean 方式定义public User user() {User user = new User();user.setId(1L);user.setName("xiaoge");return user;}}}/*运行结果:Config 类型的所有 Beans: {annotationBeanDefinitionDemo.Config=org.xiaoge.thinking.in.spring.bean.definition.AnnotationBeanDefinitionDemo$Config@fa4c865}User 类型的所有 Beans: {xiaoge-niubi=User{id=1, name='xiaoge'}, org.xiaoge.thinking.in.spring.ioc.overview.domain.User#0=User{id=1, name='xiaoge'}, user=User{id=1, name='xiaoge'}}*/
    

6. 实例化Spring Bean: Bean实例化的姿势有多少种?

  • Bean实例化(Instantiation)

    • 常规方式:
      • 通过构造器(配置元信息: XML、Java 注解 和 Java APl )
      • 通过静态工厂方法(配置元信息: XML 和 Java API )
      • 通过Bean工厂方法(配置元信息: XML 和 Java APl )
      • 通过FactoryBean(配置元信息: XML、Java 注解 和 Java API )
    • 特殊方式:
      • 通过ServiceLoaderFactoryBean(配置元信息∶XML、Java注解 和 Java API )
      • 通过AutowireCapableBeanFactory#createBean(java.lang.Class, int, boolean)
      • 通过 BeanDefinitionRegistry#registerBeanDefinition(String,BeanDefinition)
  • bean-instantiation-context.xml

    
    
    
  • special-bean-instantiation-context.xml

    
    
    
  • org.xiaoge.thinking.in.spring.bean.factory.UserFactory

    org.xiaoge.thinking.in.spring.bean.factory.DefaultUserFactory
    org.xiaoge.thinking.in.spring.bean.factory.DefaultUserFactory
    org.xiaoge.thinking.in.spring.bean.factory.DefaultUserFactory
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dlMENwbx-1669085413218)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20221121095520604.png)]

  • User.java

    package org.xiaoge.thinking.in.spring.ioc.overview.domain;/*** @Classname User* @Date 2022/10/17 14:57* @Created by ZhangXiao* @Description TODO*/
    public class User {private Long id;private String name;public Long getId() {return id;}public void setId(Long id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "User{" +"id=" + id +", name='" + name + '\'' +'}';}public static User createUser() {User user = new User();user.setId(1L);user.setName("xiaoge");return user;}
    }
  • UserFactory.java

    package org.xiaoge.thinking.in.spring.bean.factory;import org.xiaoge.thinking.in.spring.ioc.overview.domain.User;/**** {@link User} 工厂类** @Classname DefaultUserFactory* @Date 2022/11/8 17:19* @Created by ZhangXiao* @Description*/
    public interface UserFactory {default User createUser() {return User.createUser();}}
    
  • DefaultUserFactory.java

    package org.xiaoge.thinking.in.spring.bean.factory;/*** @Classname DefaultUserFactory* @Date 2022/11/8 17:23* @Created by ZhangXiao* @Description TODO*/
    public class DefaultUserFactory implements UserFactory {
    }
  • UserFactoryBean.java

    package org.xiaoge.thinking.in.spring.bean.factory;import org.springframework.beans.factory.FactoryBean;
    import org.xiaoge.thinking.in.spring.ioc.overview.domain.User;/*** {@link User} Bean 的 {@link FactoryBean} 实现** 用FactoryBean 相当于 又 实例化 又 初始化** @Classname UserFactoryBean* @Date 2022/11/8 17:32* @Created by ZhangXiao* @Description TODO*/
    public class UserFactoryBean implements FactoryBean {@Overridepublic Object getObject() throws Exception {return User.createUser();}@Overridepublic Class getObjectType() {return User.class;}}
    
  • BeanInstantiationDemo.java

    package org.xiaoge.thinking.in.spring.bean.definition;import org.springframework.beans.factory.BeanFactory;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    import org.xiaoge.thinking.in.spring.ioc.overview.domain.User;/*** @Classname BeanInstantiationDemo* @Date 2022/11/8 17:15* @Created by ZhangXiao* @Description Bean 实例化  示例** 实例化:是对象创建的过程。比如使用构造方法new对象,为对象在内存中分配空间。* 初始化:是为对象中的属性赋值的过程。** 1. 通过构造器方法这里就不写了, 之前写过* 2. 通过静态工厂方法* 3. 通过Bean工厂方法* 4. 通过FactoryBean**/
    public class BeanInstantiationDemo {public static void main(String[] args) {// 配置xml配置文件// 启动spring应用上下文BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/bean-instantiation-context.xml");// 1. 通过静态工厂方法User user = beanFactory.getBean("user-by-static-method", User.class);// 2. 通过Bean工厂方法User userByInstanceMethod = beanFactory.getBean("user-by-instance-method", User.class);// 3. 通过FactoryBeanUser userByFactoryBean = beanFactory.getBean("user-by-factory-bean", User.class);System.out.println(user);System.out.println(userByInstanceMethod);System.out.println(userByFactoryBean);System.out.println(user == userByInstanceMethod);System.out.println(user == userByFactoryBean);}}// 运行结果
    User{id=1, name='xiaoge'}
    User{id=1, name='xiaoge'}
    User{id=1, name='xiaoge'}
    false
    false
    
  • SpecialBeanInstantiationDemo.java

    package org.xiaoge.thinking.in.spring.bean.definition;import org.springframework.beans.factory.BeanFactory;
    import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    import org.xiaoge.thinking.in.spring.bean.factory.DefaultUserFactory;
    import org.xiaoge.thinking.in.spring.bean.factory.UserFactory;import java.util.Iterator;
    import java.util.ServiceLoader;/*** 特殊的 Bean 实例化 示例*  1. ServiceLoaderFactoryBean*  2. AutowireCapableBeanFactory*  3. BeanDefinitionRegistry这里就不写了, 之前写过**  实例化:是对象创建的过程。比如使用构造方法new对象,为对象在内存中分配空间。*  初始化:是为对象中的属性赋值的过程。** @Classname SpecialBeanInstantiaionDemo* @Date 2022/11/8 17:42* @Created by ZhangXiao* @Description TODO*/
    public class SpecialBeanInstantiationDemo {public static void main(String[] args) {// 配置xml配置文件// 启动spring应用上下文ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:/META-INF/special-bean-instantiation-context.xml");// 通过 ApplicationContext 获取 AutowireCapableBeanFactoryAutowireCapableBeanFactory beanFactory = applicationContext.getAutowireCapableBeanFactory();ServiceLoader userFactoryServiceLoader = beanFactory.getBean("userFactoryServiceLoader", ServiceLoader.class);displayServiceLoad(userFactoryServiceLoader);demoServiceLoader();// 创建 UserFactory 对象, 通过 AutowireCapableBeanFactoryUserFactory userFactory = beanFactory.createBean(DefaultUserFactory.class);System.out.println(userFactory.createUser());}/*** 这种方式可以实例化多个bean, 默认去找META-INF/services* 而我们写的是三个同样的Bean * org.xiaoge.thinking.in.spring.bean.factory.DefaultUserFactory* org.xiaoge.thinking.in.spring.bean.factory.DefaultUserFactory* org.xiaoge.thinking.in.spring.bean.factory.DefaultUserFactory* 所以这里只会创建一个**/private static void demoServiceLoader() {ServiceLoader serviceLoader = ServiceLoader.load(UserFactory.class, Thread.currentThread().getContextClassLoader());displayServiceLoad(serviceLoader);}/*** 这种方式只会找一个, 找我们关注的设置了serviceType的* @param serviceLoader*/private static void displayServiceLoad(ServiceLoader serviceLoader) {Iterator iterator = serviceLoader.iterator();while(iterator.hasNext()) {UserFactory userFactory = iterator.next();System.out.println(userFactory.createUser());}}}// 运行结果
    User{id=1, name='xiaoge'}
    User{id=1, name='xiaoge'}
    User{id=1, name='xiaoge'}
    

7. 初始化Spring Bean: Bean初始化有那些方式?

  • Bean初始化(Initialization)

    • @PostConstruct 标注方法
    • 实现 InitializingBean 接口的 afterPropertiesSet() 方法
    • 自定义初始化方法
      • XML 配置:
      • Java 注解: @Bean(initMethod=”init”)
      • Java API: AbstractBeanDefinition#setlnitMethodName(String)
    • 问题: 假设以上三种方式在同一 Bean 中定义, 那么这些方法的执行顺序是怎样?
      1. @PostConstruct
      2. InitializingBean 接口的 afterPropertiesSet() 方法
      3. 自定义初始化方法
  • User.java

    package org.xiaoge.thinking.in.spring.ioc.overview.domain;/*** @Classname User* @Date 2022/10/17 14:57* @Created by ZhangXiao* @Description TODO*/
    public class User {private Long id;private String name;public Long getId() {return id;}public void setId(Long id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "User{" +"id=" + id +", name='" + name + '\'' +'}';}public static User createUser() {User user = new User();user.setId(1L);user.setName("xiaoge");return user;}
    }
    
  • UserFactory.java

    package org.xiaoge.thinking.in.spring.bean.factory;import org.xiaoge.thinking.in.spring.ioc.overview.domain.User;/**** {@link User} 工厂类** @Classname DefaultUserFactory* @Date 2022/11/8 17:19* @Created by ZhangXiao* @Description*/
    public interface UserFactory {default User createUser() {return User.createUser();}}
  • DefaultUserFactory.java

    package org.xiaoge.thinking.in.spring.bean.factory;import org.springframework.beans.factory.InitializingBean;import javax.annotation.PostConstruct;/*** @Classname DefaultUserFactory* @Date 2022/11/8 17:23* @Created by ZhangXiao* @Description TODO*/
    public class DefaultUserFactory implements UserFactory, InitializingBean {// 基于 @PostConstruct 注解@PostConstructpublic void init() {System.out.println("@PostConstruct : UserFactory 初始化中.....");}public void initUserFactory() {System.out.println("自定义初始化方法 initUserFactory() : UserFactory 初始化中.....");}@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("InitializingBean#afterPropertiesSet() : UserFactory 初始化中.....");}
    }
    
  • BeanInitializationDemo.java

    package org.xiaoge.thinking.in.spring.bean.definition;import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.xiaoge.thinking.in.spring.bean.factory.DefaultUserFactory;
    import org.xiaoge.thinking.in.spring.bean.factory.UserFactory;/*** @PACKAGE_NAME: org.xiaoge.thinking.in.spring.bean.definition* @Classname BeanInitializationDemo* @Date 2022/11/21 10:26* @Author zhangxiao* @Description Bean 初始化 示例** 实例化:是对象创建的过程。比如使用构造方法new对象,为对象在内存中分配空间。* 初始化:是为对象中的属性赋值的过程。** 1. @PostConstruct 标注方法* 2. InitializingBean 接口* 3. 自定义方法*    1. xml 这里就不说了*    2. @Bean 初始化 方法*    3. AbstractBeanDefinition#setlnitMethodName(String) 初始化方法*/
    @Configuration
    public class BeanInitializationDemo {public static void main(String[] args) {// 1. 创建 ApplicationContext 容器AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();// 2. 注册 Configuration Class 配置类applicationContext.register(BeanInitializationDemo.class);// 3. 开启 spring 应用上下文applicationContext.refresh();// 4. 依赖查找applicationContext.getBean(UserFactory.class);// 5. 关闭 spring 应用上下文applicationContext.close();}@Bean(initMethod  = "initUserFactory")public UserFactory userFactory(){return new DefaultUserFactory();}}// 运行结果
    @PostConstruct : UserFactory 初始化中.....
    InitializingBean#afterPropertiesSet : UserFactory 初始化中.....
    自定义初始化方法 initUserFactory() : UserFactory 初始化中.....
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-75VFpexr-1669085413219)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20221121104247595.png)]

8. 延迟初始化 Spring Bean: 延迟初始化的 Bean 会影响依赖注入吗

  • Bean 延迟初始化 (Lazy Initialization)

    • XML配置:
    • Java 注解: @Lazy(true)
  • 问题: 当某个Bean定义为延迟初始化,那么,Spring容器返回的对象与非延迟的对象存在怎样的差异?

    1. 延迟加载和非延迟加载在Bean定义的时候, 也就是Bean注册的时候是没有区别的, 按照你的需要来进行注册, 但是在依赖查找和依赖注入的时候它的区别就体现出来了。
    2. 非延迟查找实在上下文启动之前就已经初始化了。
    3. 延迟加载就必须是在初始化完成之后来进行加载 。
  • User.class

    package org.xiaoge.thinking.in.spring.ioc.overview.domain;/*** @Classname User* @Date 2022/10/17 14:57* @Created by ZhangXiao* @Description TODO*/
    public class User {private Long id;private String name;public Long getId() {return id;}public void setId(Long id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "User{" +"id=" + id +", name='" + name + '\'' +'}';}public static User createUser() {User user = new User();user.setId(1L);user.setName("xiaoge");return user;}
    }
    
  • UserFactory.class

    package org.xiaoge.thinking.in.spring.bean.factory;import org.xiaoge.thinking.in.spring.ioc.overview.domain.User;/**** {@link User} 工厂类** @Classname DefaultUserFactory* @Date 2022/11/8 17:19* @Created by ZhangXiao* @Description*/
    public interface UserFactory {default User createUser() {return User.createUser();}}
  • DefaultUserFactory.java

    package org.xiaoge.thinking.in.spring.bean.factory;import org.springframework.beans.factory.InitializingBean;import javax.annotation.PostConstruct;/*** @Classname DefaultUserFactory* @Date 2022/11/8 17:23* @Created by ZhangXiao* @Description TODO*/
    public class DefaultUserFactory implements UserFactory, InitializingBean {// 基于 @PostConstruct 注解@PostConstructpublic void init() {System.out.println("@PostConstruct : UserFactory 初始化中.....");}public void initUserFactory() {System.out.println("自定义初始化方法 initUserFactory() : UserFactory 初始化中.....");}@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("InitializingBean#afterPropertiesSet() : UserFactory 初始化中.....");}
    }
    
  • BeanInitializationDemo

    package org.xiaoge.thinking.in.spring.bean.definition;import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Lazy;
    import org.xiaoge.thinking.in.spring.bean.factory.DefaultUserFactory;
    import org.xiaoge.thinking.in.spring.bean.factory.UserFactory;/*** @PACKAGE_NAME: org.xiaoge.thinking.in.spring.bean.definition* @Classname BeanInitializationDemo* @Date 2022/11/21 10:26* @Author zhangxiao* @Description Bean 初始化 示例** 实例化:是对象创建的过程。比如使用构造方法new对象,为对象在内存中分配空间。* 初始化:是为对象中的属性赋值的过程。** 1. Lazy 延迟加载* 2. 非延迟加载*/
    @Configuration
    public class BeanInitializationDemo {public static void main(String[] args) {// 1. 创建 ApplicationContext 容器AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();// 2. 注册 Configuration Class 配置类applicationContext.register(BeanInitializationDemo.class);// 3. 开启 spring 应用上下文applicationContext.refresh();// 4. 非延迟初始化在 Spring 应用上下文启动完成后, 被初始化System.out.println("Spring 应用上下文已启动...");// 4. 依赖查找UserFactory userFactory = applicationContext.getBean(UserFactory.class);System.out.println(userFactory);// 5. 关闭 spring 应用上下文applicationContext.close();}@Bean(initMethod  = "initUserFactory")@Lazy // 延迟加载//@Lazy(value = false) // 非延迟加载public UserFactory userFactory(){return new DefaultUserFactory();}}// 延迟加载
    Spring 应用上下文已启动...
    @PostConstruct : UserFactory 初始化中.....
    InitializingBean#afterPropertiesSet : UserFactory 初始化中.....
    自定义初始化方法 initUserFactory() : UserFactory 初始化中.....
    org.xiaoge.thinking.in.spring.bean.factory.DefaultUserFactory@38364841// 非延迟加载
    @PostConstruct : UserFactory 初始化中.....
    InitializingBean#afterPropertiesSet : UserFactory 初始化中.....
    自定义初始化方法 initUserFactory() : UserFactory 初始化中.....
    Spring 应用上下文已启动...
    org.xiaoge.thinking.in.spring.bean.factory.DefaultUserFactory@49070868
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Tj0gVxyP-1669085413219)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20221121120058476.png)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z3G3mslL-1669085413220)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20221121120114370.png)]

    实例化或者说初始化所有剩余非延迟加载的Bean

9. 销毁Spring Bean: 销毁 Bean 的基本操作有那些?

  • Bean销毁(Destroy)

    • @PreDestroy标注方法
    • 实现DisposableBean接口的destroy()方法
    • 自定义销毁方法
      • XML配置:
      • Java注解:@Bean(destroy=“destroy”)
      • Java API: AbstractBeanDefinition#setDestroyMethodName(String)
  • 问题: 假设以上三种方式均在同一 Bean 中定义, 那么这些方法的执行顺序是怎样?

    1. @PreDestroy
    2. DisposableBean接口的destroy()方法
    3. 自定义
  • User.java

    package org.xiaoge.thinking.in.spring.ioc.overview.domain;/*** @Classname User* @Date 2022/10/17 14:57* @Created by ZhangXiao* @Description TODO*/
    public class User {private Long id;private String name;public Long getId() {return id;}public void setId(Long id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "User{" +"id=" + id +", name='" + name + '\'' +'}';}public static User createUser() {User user = new User();user.setId(1L);user.setName("xiaoge");return user;}
    }
    
  • UserFactory.java

    package org.xiaoge.thinking.in.spring.bean.factory;import org.xiaoge.thinking.in.spring.ioc.overview.domain.User;/**** {@link User} 工厂类** @Classname DefaultUserFactory* @Date 2022/11/8 17:19* @Created by ZhangXiao* @Description*/
    public interface UserFactory {default User createUser() {return User.createUser();}}
  • DefaultUserFactory.java

    package org.xiaoge.thinking.in.spring.bean.factory;import org.springframework.beans.factory.DisposableBean;
    import org.springframework.beans.factory.InitializingBean;import javax.annotation.PostConstruct;
    import javax.annotation.PreDestroy;/*** @Classname DefaultUserFactory* @Date 2022/11/8 17:23* @Created by ZhangXiao* @Description TODO*/
    public class DefaultUserFactory implements UserFactory, InitializingBean, DisposableBean {// 基于 @PostConstruct 注解@PostConstructpublic void init() {System.out.println("@PostConstruct : UserFactory 初始化中.....");}public void initUserFactory() {System.out.println("自定义初始化方法 initUserFactory() : UserFactory 初始化中.....");}@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("InitializingBean#afterPropertiesSet() : UserFactory 初始化中.....");}// 基于 @PreDestroy 注解@PreDestroypublic void preDestroy() {System.out.println("@PreDestroy : UserFactory 销毁中.....");}public void doDestroy() {System.out.println("自定义销毁方法 doDestroy() : UserFactory 销毁中.....");}@Overridepublic void destroy() throws Exception {System.out.println("DisposableBean#destroy() : UserFactory 销毁中.....");}
    }
  • BeanInitializationDemo.java

    package org.xiaoge.thinking.in.spring.bean.definition;import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Lazy;
    import org.xiaoge.thinking.in.spring.bean.factory.DefaultUserFactory;
    import org.xiaoge.thinking.in.spring.bean.factory.UserFactory;/*** @PACKAGE_NAME: org.xiaoge.thinking.in.spring.bean.definition* @Classname BeanInitializationDemo* @Date 2022/11/21 10:26* @Author zhangxiao* @Description Bean 初始化 示例** 实例化:是对象创建的过程。比如使用构造方法new对象,为对象在内存中分配空间。* 初始化:是为对象中的属性赋值的过程。** 1. @PreDestroy标注方法* 2. 实现DisposableBean接口的destroy()方法* 3. 自定义方法*/
    @Configuration
    public class BeanInitializationDemo {public static void main(String[] args) {// 1. 创建 ApplicationContext 容器AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();// 2. 注册 Configuration Class 配置类applicationContext.register(BeanInitializationDemo.class);// 3. 开启 spring 应用上下文applicationContext.refresh();// 4. 非延迟初始化在 Spring 应用上下文启动完成后, 被初始化System.out.println("Spring 应用上下文已启动...");// 5. 依赖查找UserFactory userFactory = applicationContext.getBean(UserFactory.class);System.out.println(userFactory);System.out.println("Spring 应用上下文准备关闭...");// 6. 关闭 spring 应用上下文applicationContext.close();System.out.println("Spring 应用上下文已关闭...");}@Bean(initMethod  = "initUserFactory", destroyMethod = "doDestroy")@Lazy(value = false)public UserFactory userFactory(){return new DefaultUserFactory();}}// 运行结果
    @PostConstruct : UserFactory 初始化中.....
    InitializingBean#afterPropertiesSet() : UserFactory 初始化中.....
    自定义初始化方法 initUserFactory() : UserFactory 初始化中.....
    Spring 应用上下文已启动...
    org.xiaoge.thinking.in.spring.bean.factory.DefaultUserFactory@38364841
    Spring 应用上下文准备关闭...
    @PreDestroy : UserFactory 销毁中.....
    DisposableBean#destroy() : UserFactory 销毁中.....
    自定义销毁方法 doDestroy() : UserFactory 销毁中.....
    Spring 应用上下文已关闭...
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vBaNcyBa-1669085413220)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20221121134545326.png)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i764VD1N-1669085413220)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20221121134625003.png)]

    销毁所有的BeanFactory

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YMX5tULm-1669085413220)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20221121134647783.png)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nYta5tot-1669085413221)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20221121134717379.png)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pZ42AeQB-1669085413221)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20221121134807191.png)]

    这里会做一个统计, 统计有多少个Bean实现了destroy方法, 它会足一的去进行销毁

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ALghvDW0-1669085413221)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20221121135007338.png)]

    这里会强制转换成DisposableBean

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xMJlrFSU-1669085413221)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20221121135117318.png)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FzxPISE4-1669085413222)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20221121135132801.png)]

    然后去掉它的destroy方法

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-INT5vlbl-1669085413222)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20221121135223785.png)]

    因为这个方法被DisposableBean接口声明了

    @PreDestroy实现方式

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tDMrBaSS-1669085413222)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20221121135636968.png)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EO1ZyUkg-1669085413223)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20221121135718402.png)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KagJDBOd-1669085413223)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20221121135733015.png)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6ahvGqDR-1669085413223)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20221121135745354.png)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R1HUhcQu-1669085413224)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20221121135824664.png)]

​ 剩下的跟上面一样, @PostConstruct也是这么初始化的

10.回收 Spring Bean: Spring IoC 容器管理的Bean能够被垃圾回收吗?

  • Bean垃圾回收(GC)

    1. 关闭Spring容器〈应用上下文)

    2. 执行GC

    3. Spring Bean覆盖的finalize()方法被回调

    4. 完成以上三步

  • DefaultUserFactory.java

    package org.xiaoge.thinking.in.spring.bean.factory;import org.springframework.beans.factory.DisposableBean;
    import org.springframework.beans.factory.InitializingBean;import javax.annotation.PostConstruct;
    import javax.annotation.PreDestroy;/*** @Classname DefaultUserFactory* @Date 2022/11/8 17:23* @Created by ZhangXiao* @Description TODO*/
    public class DefaultUserFactory implements UserFactory, InitializingBean, DisposableBean {// 基于 @PostConstruct 注解@PostConstructpublic void init() {System.out.println("@PostConstruct : UserFactory 初始化中.....");}public void initUserFactory() {System.out.println("自定义初始化方法 initUserFactory() : UserFactory 初始化中.....");}@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("InitializingBean#afterPropertiesSet() : UserFactory 初始化中.....");}// 基于 @PreDestroy 注解@PreDestroypublic void preDestroy() {System.out.println("@PreDestroy : UserFactory 销毁中.....");}public void doDestroy() {System.out.println("自定义销毁方法 doDestroy() : UserFactory 销毁中.....");}@Overridepublic void destroy() throws Exception {System.out.println("DisposableBean#destroy() : UserFactory 销毁中.....");}@Overridepublic void finalize() throws Throwable {System.out.println("当前 DefaultUserFactory 对象正在被垃圾回收.....");}
    }

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yEtLYMU8-1669085413224)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20221121165623791.png)]

  • BeanGarbageCollectionDemo.java

    package org.xiaoge.thinking.in.spring.bean.definition;import org.springframework.context.annotation.AnnotatedBeanDefinitionReader;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    import org.xiaoge.thinking.in.spring.bean.factory.UserFactory;
    import org.xiaoge.thinking.in.spring.ioc.overview.domain.User;/*** @PACKAGE_NAME: org.xiaoge.thinking.in.spring.bean.definition* @Classname BeanGarbageCollectionDemo* @Date 2022/11/21 16:46* @Author zhangxiao* @Description TODO*/
    public class BeanGarbageCollectionDemo {public static void main(String[] args) throws InterruptedException {// 1. 创建 ApplicationContext 容器AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();// 2. 注册 Configuration Class 配置类applicationContext.register(BeanInitializationDemo.class);// 3. 开启 spring 应用上下文applicationContext.refresh();// 4. 关闭 spring 应用上下文applicationContext.close();System.out.println("Spring 应用上下文已关闭...");Thread.sleep(5000);// 强制执行GC  finalize方法不一定完全被回调, 这个不是必须的, 只是模拟, spring容器中的Bean怎么被回收的System.gc();Thread.sleep(5000);}}// 运行结果
    @PostConstruct : UserFactory 初始化中.....
    InitializingBean#afterPropertiesSet() : UserFactory 初始化中.....
    自定义初始化方法 initUserFactory() : UserFactory 初始化中.....
    @PreDestroy : UserFactory 销毁中.....
    DisposableBean#destroy() : UserFactory 销毁中.....
    自定义销毁方法 doDestroy() : UserFactory 销毁中.....
    Spring 应用上下文已关闭...
    当前 DefaultUserFactory 对象正在被垃圾回收.....
    

11. 面试题精选

  • 如何注册一个 Spring Bean?

    • 答: 通过BeanDefinition和外部单体对象来注册

    • BeanDefinition这里不做例子上面有, 外部单体对象注册如下:

      • SingletonBeanRegistrationDemo.java

        package org.xiaoge.thinking.in.spring.bean.definition;import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
        import org.springframework.context.annotation.AnnotationConfigApplicationContext;
        import org.xiaoge.thinking.in.spring.bean.factory.DefaultUserFactory;
        import org.xiaoge.thinking.in.spring.bean.factory.UserFactory;/*** @PACKAGE_NAME: org.xiaoge.thinking.in.spring.bean.definition* @Classname SingletonBeanRegistrationDemo* @Date 2022/11/21 17:39* @Author zhangxiao* @Description TODO*/
        public class SingletonBeanRegistrationDemo {public static void main(String[] args) {// 创建 ApplicationContext 容器AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();// 创建一个外部 UserFactory 对象UserFactory userFactory = new DefaultUserFactory();ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory();// 注册外部单例 beanbeanFactory.registerSingleton("userFactory", userFactory);// 开启 spring 应用上下文applicationContext.refresh();// 通过依赖查找的方式来获取 UserFactoryUserFactory userFactoryByLookup = beanFactory.getBean("userFactory", UserFactory.class);System.out.println("userFactory == userFactoryByLookup : " + (userFactory == userFactoryByLookup));// 关闭 spring 应用上下文applicationContext.close();}}// 运行结果
        userFactory == userFactoryByLookup : true
        

        [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b2b4pySD-1669085413224)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20221121174819670.png)]

      • SingletonBeanRegistrationDemo.java

        package org.xiaoge.thinking.in.spring.bean.definition;import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
        import org.springframework.beans.factory.config.SingletonBeanRegistry;
        import org.springframework.context.annotation.AnnotationConfigApplicationContext;
        import org.xiaoge.thinking.in.spring.bean.factory.DefaultUserFactory;
        import org.xiaoge.thinking.in.spring.bean.factory.UserFactory;/*** @PACKAGE_NAME: org.xiaoge.thinking.in.spring.bean.definition* @Classname SingletonBeanRegistrationDemo* @Date 2022/11/21 17:39* @Author zhangxiao* @Description TODO*/
        public class SingletonBeanRegistrationDemo {public static void main(String[] args) {// 创建 ApplicationContext 容器AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();// 创建一个外部 UserFactory 对象UserFactory userFactory = new DefaultUserFactory();SingletonBeanRegistry singletonBeanRegistry = applicationContext.getBeanFactory();// 注册外部单例 beansingletonBeanRegistry.registerSingleton("userFactory", userFactory);// 开启 spring 应用上下文applicationContext.refresh();// 通过依赖查找的方式来获取 UserFactoryUserFactory userFactoryByLookup = applicationContext.getBean("userFactory", UserFactory.class);System.out.println("userFactory == userFactoryByLookup : " + (userFactory == userFactoryByLookup));// 关闭 spring 应用上下文applicationContext.close();}}// 运行结果
        userFactory == userFactoryByLookup : true
        

        为什么可以用applicationContext取到? 因为它是委派来实现的它是通过获取BeanFactory来获取bean

        [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kkzM7Z8x-1669085413225)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20221121175252470.png)]

        [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HrIFaSMe-1669085413225)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20221121175317093.png)]

  • 什么是 Spring BeanDefinition?

    • BeanDefinition它实际上是一个关于Bean的定义的元信息, 这个接口可以允许我们存储, 就允许我们getter/setter的方式来来进行这个操作, 我们之前在xml中配置的那些东西, 其实就是是在实现接口中的方法, 例如:

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nRWWTujc-1669085413225)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20221122102642164.png)]

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lfXQF6rh-1669085413225)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20221122102702877.png)]

      等这里就不一一列举了

  • Spring容器是怎样管理注册 Bean

    • 如:loC配置元信息读取和解析、依赖查找和注入以及 Bean生命周期等。

相关内容

热门资讯

喜欢穿一身黑的男生性格(喜欢穿... 今天百科达人给各位分享喜欢穿一身黑的男生性格的知识,其中也会对喜欢穿一身黑衣服的男人人好相处吗进行解...
发春是什么意思(思春和发春是什... 本篇文章极速百科给大家谈谈发春是什么意思,以及思春和发春是什么意思对应的知识点,希望对各位有所帮助,...
网络用语zl是什么意思(zl是... 今天给各位分享网络用语zl是什么意思的知识,其中也会对zl是啥意思是什么网络用语进行解释,如果能碰巧...
为什么酷狗音乐自己唱的歌不能下... 本篇文章极速百科小编给大家谈谈为什么酷狗音乐自己唱的歌不能下载到本地?,以及为什么酷狗下载的歌曲不是...
华为下载未安装的文件去哪找(华... 今天百科达人给各位分享华为下载未安装的文件去哪找的知识,其中也会对华为下载未安装的文件去哪找到进行解...
家里可以做假山养金鱼吗(假山能... 今天百科达人给各位分享家里可以做假山养金鱼吗的知识,其中也会对假山能放鱼缸里吗进行解释,如果能碰巧解...
四分五裂是什么生肖什么动物(四... 本篇文章极速百科小编给大家谈谈四分五裂是什么生肖什么动物,以及四分五裂打一生肖是什么对应的知识点,希...
怎么往应用助手里添加应用(应用... 今天百科达人给各位分享怎么往应用助手里添加应用的知识,其中也会对应用助手怎么添加微信进行解释,如果能...
客厅放八骏马摆件可以吗(家里摆... 今天给各位分享客厅放八骏马摆件可以吗的知识,其中也会对家里摆八骏马摆件好吗进行解释,如果能碰巧解决你...
美团联名卡审核成功待激活(美团... 今天百科达人给各位分享美团联名卡审核成功待激活的知识,其中也会对美团联名卡审核未通过进行解释,如果能...