容器中的组件注入(多种方式)

@Configuration+@Bean

前置

创建User和Pet类

public class User {
    private String name;
    private Integer age;
    public User(){

    }

    public User(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class Pet {
    private String name;

    public Pet(){

    }

    public Pet(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

在以往的Spring的学习中,我们通常将组件注入进容器中的方式是通过配置文件进行注入的。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--在spring中的做法-->
    <bean id="user01" class="com.zjh.boot.bean.User">
        <property name="name" value="zjh"></property>
        <property name="age" value="20"></property>
    </bean>
    <bean id="cat01" class="com.zjh.boot.bean.Pet">
        <property name="name" value="tomcat"></property>
    </bean>
</beans>

在Springboot中,我们注册组件的方式可以通过两个注解配合进行容器的注入@Configuration+@Bean。
我们创建一个类MyConfig,在其中声明两个方法来返回User对象和Pet对象

@Configuration
public class MyConfig {
    @Bean // 会以方法名作为组件的id放入容器里,返回类型就是组件类型,返回的对象就是组件在容器中保存的实例
    public User userConfig(){
        User user = new User("zjh", 200);
        user.setPet(petConfig());
        return user;
    }
    @Bean // bean标签可以自定义名字
    public Pet petConfig(){
        return new Pet("tomcat");
    }
}

@Configuration声明在类上,声明这个类是一个配置类,等同于Spring里的配置文件xml,这个配置类会被扫描进IOC容器里,组件名为类名(若类名的首字母为大写,会自动转为小写),我们可以通过value属性来自定义组件名
@Bean声明在方法上,也会将方法的返回值扫描进容器里,组件名为方法名(若方法名的首字母为大写,会自动转为小写),我们可以通过name属性来自定义组件名。

组件是单实例?

@SpringBootApplication
public class MainApplication {
    public static void main(String[] args) {
        // 1.返回IOC容器
        ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);
        // 2.查看容器里的组件
        String[] names = run.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }
        System.out.println("=================");
        Pet pet01 = run.getBean("petConfig",Pet.class);
        Pet pet02 = run.getBean("petConfig",Pet.class);
        System.out.println("pet01是否等于pet02 ? "+ (pet01 == pet02));
    }
}

在这里插入图片描述
由结果,我们可以得知,IOC容器里的组件是单实例的。

如何设置为多实例?

只需要在方法上声明**@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)**

proxyBeanMethods

@Configuration中的proxyBeanMethods 属性默认是true,那么proxyBeanMethods 属性有什么意义呢?
proxyBeanMethods翻译为 代理bean的方法 。
我们从容器里获取的myConfig组件,其定义了**userConfig()petConfig()两个方法,那么问题来了,如果我获取到myConfig组件,调用其的petConfig()**方法获取到的pet实例是否和直接从容器里获取的实例是否相同?
我们测试一波

@SpringBootApplication
public class MainApplication {
    public static void main(String[] args) {
        // 1.返回IOC容器
        ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);
        // 2.查看容器里的组件
        String[] names = run.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }
        Pet pet01 = run.getBean("petConfig",Pet.class);
        MyConfig myConfig = run.getBean("myConfig", MyConfig.class);
        Pet pet = myConfig.petConfig();
        System.out.println("判断组件取出的pet是否和配置文件里取出的pet是否相同? " +(pet == pet01));
        }
    }

在这里插入图片描述
由结论得知,若proxyBeanMethods=true时,结果为true。proxyBeanMethods=false时,结果为false。
结论:如果configuration为true,则获取的myConfig为代理对象,在调用代理对象的方法时会检查IOC中有没有匹配的组件,如果有就直接拿,如果没有就创建,即它要保持组件单实例
springboot在Configuration的底层配置有两种 full(proxyBeanMethods为true 全配置)lite(proxyBeanMethods为false 轻量级配置)

那么这是解决什么场景呢?

组件依赖
举例,我们user里有一个pet属性

public class User {
    private String name;
    private Integer age;
    private Pet pet;
    public User(){

    }

    public User(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Pet getPet() {
        return pet;
    }

    public void setPet(Pet pet) {
        this.pet = pet;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

@Configuration (proxyBeanMethods = true)
public class MyConfig {

    @Bean // 会以方法名作为组件的id放入容器里,返回类型就是组件类型,返回的对象就是组件在容器中保存的实例
    public User userConfig(){
        User user = new User("zjh", 200);
        user.setPet(petConfig());
        return user;
    }
    @Bean // bean标签可以自定义名字
    public Pet petConfig(){
        return new Pet("tomcat");
    }
}

我们获取user组件,调用其里面的getPet()方法获取到的pet是否和直接从容器里拿的pet组件相同?此时proxyBeanMethods = true

@SpringBootApplication
public class MainApplication {
    public static void main(String[] args) {
        // 1.返回IOC容器
        ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);
        // 2.查看容器里的组件
        String[] names = run.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }
        Pet pet01 = run.getBean("petConfig",Pet.class);
        System.out.println("=================");
        User user = run.getBean("userConfig", User.class);
        Pet user_pet = user.getPet();
        System.out.println("user里的pet和容器里的pet是否相同 "+(user_pet == pet01));

在这里插入图片描述
结论:proxyBeanMethods = true,类及其类中的方法如果获取实例时,会先去IOC中查找是否有符合的实例,如果有就直接拿,如果没有就创建,proxyBeanMethods = false,则直接创建。
proxyBeanMethods为false的优点:springboot不会去容器里检查,运行速度会加快,但是会增加内存开销。
若是只是仅仅注册组件,也不涉及到组件之间的调用,推荐为lite模式,反之。


@Import

@Import({})这是得声明在组件上,我们可以给IOC中导入很多的组件,将指定类型的组件导入IOC中,调用组件的无参构造器创建对象来注入IOC中,默认的名字是全类名。
举例

@Import({User.class, LoggerFactory.class})
@Configuration (proxyBeanMethods = true)
public class MyConfig {

    @Bean // 会以方法名作为组件的id放入容器里,返回类型就是组件类型,返回的对象就是组件在容器中保存的实例
    public User userConfig(){
        User user = new User("zjh", 200);
        user.setPet(petConfig());
        return user;
    }
    @Bean // bean标签可以自定义名字
    public Pet petConfig(){
        return new Pet("tomcat");
    }
}

从中可以得出通过@Improt和@Bean注入两种User组件,我们可以看一下IOC中是否有两个User组件

@SpringBootApplication
public class MainApplication {
    public static void main(String[] args) {
        // 1.返回IOC容器
        ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);
        // 2.查看容器里的组件
        String[] names = run.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }
         System.out.println("==========");
        String[] users = run.getBeanNamesForType(User.class);
        for (String s : users) {
            System.out.println(s);
        }
        System.out.println("========");
        LoggerFactory loggerFactory = run.getBean("org.slf4j.LoggerFactory", LoggerFactory.class);
        System.out.println(loggerFactory);
    }
}

请添加图片描述
由结果得知,导入了两个User组件,且两个组件不相同。


@Conditional

条件装配,满足Conditional指定的条件,则进行组件注入。此注解有许多的派生注解。
在这里插入图片描述
这些派生注解都是对应具备某些条件的情况
例如@ConditionalOnBean代表了具备某个Bean则注入组件,@ConditionalOnMissingBean代表了不具备某个Bean则注入组件等等。
接下来以@ConditionalOnBean举例
我们在Myconfig中声明一个@ConditionalOnBean

@Configuration (proxyBeanMethods = true)
public class MyConfig {
    @ConditionalOnMissingBean(name = "petConfig")
    @Bean
   // 会以方法名作为组件的id放入容器里,返回类型就是组件类型,返回的对象就是组件在容器中保存的实例
    public User userConfig(){
        User user = new User("zjh", 200);
        user.setPet(petConfig());
        return user;
    }
//    @Bean // bean标签可以自定义名字
    public Pet petConfig(){
        return new Pet("tomcat");
    }
}

由上段代码得知,若是组件中没有组件petConfig,则不注入userConfig组件,反之。

@SpringBootApplication
public class MainApplication {
    public static void main(String[] args) {
        // 1.返回IOC容器
        ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);
        // 2.查看容器里的组件
        String[] names = run.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }
        boolean b = run.containsBean("petConfig");
        System.out.println("是否有petConfig组件? "+b);
        boolean b1 = run.containsBean("userConfig");
        System.out.println("是否有userConfig组件? "+b1);
    }
}

请添加图片描述
由结果得知,在petConfig组件不存在的情况下,userConfig也不会被注入进IOC中。
这里有一个注意点,其实@Conditional只是起到一种类似于拦截的作用,真正的注入进IOC并不是依赖此注解,真正的注入还是依赖@Bean进行注入。
实例

@Configuration (proxyBeanMethods = true)
public class MyConfig {
    @ConditionalOnMissingBean(name = "petConfig")
    public User userConfig(){
        User user = new User("zjh", 200);
        user.setPet(petConfig());
        return user;
    }
    @Bean // bean标签可以自定义名字
    public Pet petConfig(){
        return new Pet("tomcat");
    }
}

上段代码中,petConfig已经注入进IOC中,按理说,userConfig应该也进入IOC中。
请添加图片描述
由结果得知,userConfig并没有注入IOC容器中。


@ImportResource

导入配置文件中的组件进入IOC中
举例

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--在spring中的做法-->
    <bean id="hhhhhh" class="com.zjh.boot.bean.User">
        <property name="name" value="zjh"></property>
        <property name="age" value="20"></property>
    </bean>
    <bean id="jjjjjj" class="com.zjh.boot.bean.Pet">
        <property name="name" value="tomcat"></property>
    </bean>
</beans>
@Configuration (proxyBeanMethods = true)
@ImportResource("classpath:beans.xml")
public class MyConfig {
 @Bean
   // 会以方法名作为组件的id放入容器里,返回类型就是组件类型,返回的对象就是组件在容器中保存的实例
    public User userConfig(){
        User user = new User("zjh", 200);
        user.setPet(petConfig());
        return user;
    }
    @Bean // bean标签可以自定义名字
    public Pet petConfig(){
        return new Pet("tomcat");
    }
}
@SpringBootApplication
public class MainApplication {
    public static void main(String[] args) {
        // 1.返回IOC容器
        ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);
        // 2.查看容器里的组件
        String[] names = run.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }
          String[] users = run.getBeanNamesForType(User.class);
        for (String s : users) {
            System.out.println(s);
        }
        String[] pets = run.getBeanNamesForType(Pet.class);
        for (String pet : pets) {
            System.out.println(pet);
        }
    }
}

请添加图片描述
由结果可以得知,配置文件中的组件也被注入进IOC中。


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值