IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> spring bean管理2 -> 正文阅读

[Java知识库]spring bean管理2

目录

1.将xml配置文件改为配置类

2.@Configuration的proxyBeanMethod属性

3.bean管理方式

4.使用@Import注解注入bean

?注:注解格式导入注解配置bean

5. 编程形式注册bean

?6.导入实现了ImportSelector接口的类??????

?7.导入实现了ImportBeanDefinitionRegistrar接口的类

?8.导入实现了BeanDefinitionRegistryPostProcessor接口的类

总结

bean的加载控制

1.bean的加载控制(编程式)

2. bean的加载控制(注解式)

3. 指定类上根据条件选择性加载


1.将xml配置文件改为配置类

????????如果以前使用xml配置bean,现在想改用注解方式的配置类,如果将xml内的改写太麻烦,而且可能出错,想加载配置类的同时也加载xml配置文件,使用@ImpotResource注解实现。

@ImportResouce("applicationContext.xml") //将applicationContext.xml注入进来,使用一个容器
public class MyConfig {

}

2.@Configuration的proxyBeanMethod属性

????????spring中有两种生成代理对象的方式:jdk动态代理和cglib动态代理

??????? 在配置类上注解@Configuration中有proxyBeanMethod属性,默认值为true,即:

????????@Configuration=@Configuration(proxyBeanMethod=true)

??????? 这样配置类里@Bean生成的对象是使用cglib代理生成的代理对象,这样在其他地方不管调用几次代理对象都是同一个。

??????? 将proxyBeanMethod属性改为false,就不是代理对象了,每次调用生成一个新的对象

????????@Configuration(proxyBeanMethod=true)

3.bean管理方式

?????? 前三种在这。承接上文

参考Spring 加载Bean的方式(8种) - 郝志锋 - 博客园

4.使用@Import注解注入bean

????????使用扫描的方式加载bean是企业级开发中常见的bean的加载方式,但是由于扫描的时候不仅可以加载到你要的东西,还有可能加载到各种各样的乱七八糟的东西,万一没有控制好得不偿失了。

????????所以我们需要一种精准制导的加载方式,使用@Import注解就可以解决你的问题。它可以加载所有的一切,只需要在注解的参数中写上加载的类对应的.class即可。有人就会觉得,还要自己手写,多麻烦,不如扫描好用。对呀,但是他可以指定加载啊,好的命名规范配合@ComponentScan可以解决很多问题,但是@Import注解拥有其重要的应用场景。有没有想过假如你要加载的bean没有使用@Component修饰呢?这下就无解了,而@Import就无需考虑这个问题。

????????被@Import进的bean的名字,是全路径类名。

public class Dog {
}
@Import({Dog.class})
// 被导入的为普通的Class就行,无需使用注解声明为bean
public class SpringConfig4 {

}

测试:

public class App4 {
    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig4.class);
        String[] names = ctx.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }
        System.out.println("----------------------");
    }
}

?

??????? 这样在User类和Book类不需要使用@Component等注解就配置了对应bean,而且创建迅速,当导入的时候就容器中有了对应bean,可直接使用了,也体现了spring的无侵入式编程,降低与spring技术的耦合度。

??????? 也可以导入一个配置类,且配置其中的所有bean。

?注:注解格式导入注解配置bean

????????除了加载bean,还可以使用@Import注解加载配置类。其实本质上是一样的。? 但是注意,这种@Import方式加载进spring的配置Bean的名字(全路径类名)和前面扫描加载进spring的配置bean的名字(类名小写)不同,具体可见方式二中和本方式的测试代码的运行效果。

@Configuration
// 实际山,当被使用@Import 方式导入时, 无论 @Configuration 注解是否有,都可以将 DbConfig和dataSource 加载到Spring容器中
public class DbConfig {
    @Bean
    public DruidDataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        return ds;
    }

}
@Import({Dog.class,DbConfig.class})
public class SpringConfig4 {

}

测试:

public class App3 {
    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig3.class);
        String[] names = ctx.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }

    }
}

?

?

5. 编程形式注册bean

????????前面介绍的加载bean的方式都是在容器启动阶段完成bean的加载,下面这种方式就比较特殊了,可以在容器初始化完成后手动加载bean。通过这种方式可以实现编程式控制bean的加载。这种方式平时应用开发中不常用,但是在框架开发中会使用。

public class Cat {
    public Cat(){
    }

    int age;
    public Cat(int age){
        this.age = age;
    }

    @Override
    public String toString() {
        return "Cat{" +
                "age=" + age +
                '}';
    }
}
public class Mouse {
}

测试:?

public class App5 {
    public static void main(String[] args) {
        // ApplicationContext对象做不了,只能用AnnotationConfigApplicationContext对象
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig4.class);
        //上下文容器对象已经初始化完毕后,手工加载bean
        ctx.registerBean("tom", Cat.class,0);
        ctx.registerBean("tom", Cat.class,1);
        ctx.registerBean("tom", Cat.class,2);
        ctx.register(Mouse.class);
        String[] names = ctx.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }
        System.out.println("----------------------");
        System.out.println(ctx.getBean(Cat.class));
    }
}

?6.导入实现了ImportSelector接口的类??????

??????? bean的加载可以进行编程化的控制,添加if语句就可以实现bean的加载控制了。但是毕竟是在容器初始化后实现bean的加载控制,那是否可以在容器初始化过程中进行控制呢?答案是必须的。实现ImportSelector接口的类可以设置加载的bean的全路径类名,记得一点,只要能编程就能判定,能判定意味着可以控制程序的运行走向,进而控制一切。

????????现在又多了一种控制bean加载的方式,或者说是选择bean的方式。

????????在Spring源码中大量使用,通过导入实现了ImportSelector接口的类,实现对导入源的编程式处理

public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata metadata) {
//        System.out.println("================");
//        System.out.println("提示:"+metadata.getClassName());
//        System.out.println(metadata.hasAnnotation("org.springframework.context.annotation.Configuration"));
//        Map<String, Object> attributes = metadata.getAnnotationAttributes("org.springframework.context.annotation.ComponentScan");
//        System.out.println(attributes);
//        System.out.println("================");

        //各种条件的判定,判定完毕后,决定是否装在指定的bean
        boolean flag = metadata.hasAnnotation("org.springframework.context.annotation.Configuration");
        if(flag){
            return new String[]{"com.hao.bean.Dog"};
        }
        return new String[]{"com.hao.bean.Cat"};
    }
}
@Configuration
//@ComponentScan(basePackages = "com.hao")
@Import(MyImportSelector.class)
public class SpringConfig6 {
}

测试

public class App6 {
    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig6.class);
        String[] names = ctx.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }
        System.out.println("----------------------");
    }
}

?7.导入实现了ImportBeanDefinitionRegistrar接口的类

??????? spring中定义了一个叫做BeanDefinition的东西,它才是控制bean初始化加载的核心。BeanDefinition接口中给出了若干种方法,可以控制bean的相关属性。说个最简单的,创建的对象是单例还是非单例,在BeanDefinition中定义了scope属性就可以控制这个。如果你感觉方式六没有给你开放出足够的对bean的控制操作,那么方式七你值得拥有。我们可以通过定义一个类,然后实现ImportBeanDefinitionRegistrar接口的方式定义bean,并且还可以让你对bean的初始化进行更加细粒度的控制。

????????导入实现了ImportBeanDefinitionRegistrar接口的类,通过BeanDefinition的注册器注册实名bean,实现对 容器中bean的裁定,例如对现有bean的覆盖,进而达成不修改源代码的情况下更换实现的效果

public class MyRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        //1.使用元数据去做判定

        BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(BookServiceImpl2.class).getBeanDefinition();
        registry.registerBeanDefinition("bookService",beanDefinition);
    }
}
@Import(MyRegistrar.class)
public class SpringConfig7 {
}

测试:

public class App7 {
    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig7.class);
        String[] names = ctx.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }
        System.out.println("----------------------");
    }
}

?8.导入实现了BeanDefinitionRegistryPostProcessor接口的类

????????上述七种方式都是在容器初始化过程中进行bean的加载或者声明,但是这里有一个bug。这么多种方式,它们之间如果有冲突怎么办?谁能有最终裁定权?这是个好问题,当某种类型的bean被接二连三的使用各种方式加载后,在你对所有加载方式的加载顺序没有完全理解清晰之前,你还真不知道最后谁说了算。?

????????导入实现了BeanDefinitionRegistryPostProcessor接口的类,通过BeanDefinition的注册器注册实名bean, 可以实现对容器中bean的最终裁定

public interface BookSerivce {
    void check();
}
@Service("bookService")
public class BookServiceImpl1 implements BookSerivce {
    @Override
    public void check() {
        System.out.println("book service 1..");
    }
}
public class BookServiceImpl2 implements BookSerivce {
    @Override
    public void check() {
        System.out.println("book service 2....");
    }
}
public class BookServiceImpl3 implements BookSerivce {
    @Override
    public void check() {
        System.out.println("book service 3......");
    }
}
public class MyRegistrar2 implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        //1.使用元数据去做判定

        BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(BookServiceImpl3.class).getBeanDefinition();
        registry.registerBeanDefinition("bookService",beanDefinition);
    }
}
public class MyPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(BookServiceImpl4.class).getBeanDefinition();
        registry.registerBeanDefinition("bookService",beanDefinition);
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

    }
}
@Import({BookServiceImpl1.class, MyPostProcessor.class, MyRegistrar2.class, MyRegistrar.class})
public class SpringConfig8 {
}

测试:

public class App8 {
    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig8.class);
        BookSerivce bookService = ctx.getBean("bookService", BookSerivce.class);
        bookService.check();
    }
}

?

总结

  1. bean的定义由前期xml配置逐步演化成注解配置,本质是一样的,都是通过反射机制加载类名后创建对象,对象就是spring管控的bean

  2. @Import注解可以指定加载某一个类作为spring管控的bean,如果被加载的类中还具有@Bean相关的定义,会被一同加载

  3. spring开放出了若干种可编程控制的bean的初始化方式,通过分支语句由固定的加载bean转成了可以选择bean是否加载或者选择加载哪一种bean

bean的加载控制

  • AnnotationConfigApplicationContext调用register方法
  • @Import导入ImportSelector接口
  • @Import导入ImportBeanDefinitionRegistrar接口
  • @Import导入BeanDefinitionRegistryPostProcessor接口

1.bean的加载控制(编程式)

以ImportSelector为例

1.根据任意条件确认是否加载bean

2. bean的加载控制(注解式)

需要导入springboot

?使用@Conditional注解的派生注解设置各种组合条件控制bean的加载

格式:@ConditionalOn***

//@Import(MyImportSelector.class)
@Import(Mouse.class)
public class SpringConfig {
 
    @Bean
    //@ConditionalOnClass(Mouse.class)  一般不使用这种方式
    //@ConditionalOnClass(name = "com.huangzx.bean.Mouse") //有Mouse时加载
    //@ConditionalOnMissingClass("com.huangzx.bean.Wolf")  //没有Wolf时加载
 
    @ConditionalOnBean(name = "jerry")  //有Mouse  bean,且Mouse的名字是jerry时加载
    @ConditionalOnMissingClass("com.huangzx.bean.Dog") //有Mouse  bean,且Mouse的名字是jerry时加载,且没有Dog时加载
 
    @ConditionalOnWebApplication //是web程序时加载
    public Cat tom(){
        return new Cat();
    }
}

3. 指定类上根据条件选择性加载

?

后续可以单独配置bean,当有某种环境时,加载某些bean

这里当有mysql数据库连接时,加载Druid数据源

public class SpringConfig {
    @Bean
    @ConditionalOnClass(name = "com.mysql.cj.jdbc.Driver")
    public DruidDataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        return ds;
    }
}

?

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-09-21 00:12:55  更:2022-09-21 00:13:11 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 13:33:12-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码