【Spring IoC&DI】(Ioc详解 方法注解@Bean使用@Bean传递参数 扫描路径 DI详解 属性注入 构造方法注入 Setter注入 三种注入优缺点分析@Autowired存在问题)


Ioc 详解

五大注解只能加在类上,并且只能加在自己的代码上,如果引入了一个第三方Jar包,也希望交给Spring管理,是没有办法加五大类注解的,此时我们可以使用@Bean

@SpringBootApplication
public class  DemoApplication {
    public static void main(String[] args) {
        //Spring 上下文
       ApplicationContext context = SpringApplication.run(DemoApplication.class, args);
       // 从context中获取bean
        UserConfiguration userConfiguration = context.getBean(UserConfiguration.class);
        userConfiguration.doConfiguration();
        System.out.println("userConfiguration :" + userConfiguration);

       UserConfiguration userConfiguration2 = context.getBean(UserConfiguration.class);
        userConfiguration2.doConfiguration();
        System.out.println("userConfigurationw2 :" + userConfiguration2);
        System.out.println("======================");
        System.out.println("userConfiguration == userConfigurationw2" + (userConfiguration == userConfiguration2));
    }
}

在这里插入图片描述
他们两个的地址相同,也就是说它俩指向同一个对象。我们使用五大注解的方式,从容器中取多少次取到的都是同一个对象。

所以还有这一种场景:对于一个类,定义多个对象时,比如数据库操作,定义多个数据源,我们需要多个对象,就可以使用@Bean

方法注解 @Bean

在这里插入图片描述

@Data
public class UserInfo {
    private Integer id;
    private String name;
    private Integer age;
}
public class BeanConfig {
    @Bean
    public UserInfo userInfo(){
        UserInfo userInfo = new UserInfo();
        userInfo.setId(1);
        userInfo.setName("zhangsan");
        userInfo.setAge(18);
        return userInfo;
    }
    @Bean
    public UserInfo userInfo2(){
        UserInfo userInfo = new UserInfo();
        userInfo.setId(2);
        userInfo.setName("lisi");
        userInfo.setAge(19);
        return userInfo;
    }
}

使用
@SpringBootApplication
public class  DemoApplication {
    public static void main(String[] args) {
        //Spring 上下文
       ApplicationContext context = SpringApplication.run(DemoApplication.class, args);
       // 从context中获取bean
        //使用@Bean
        UserInfo userInfo = context.getBean(UserInfo.class);
        System.out.println(userInfo);
    }
}

此代码发现报错,

在这里插入图片描述
bean没有定义,但是我们不是加了注解吗?
Spring通过扫描项目来确定是否加注解, 但是把整个项目去扫描是不合理的,如果我们需要Spring帮我们做一些事情的时候,给他个提醒,然后让他去做,这样不就更高效吗?
我们告诉Spring,BeanConfig这个类里面有需要它帮我们管理的对象,告诉的方式就是通过五大类注解,加了只后,Spring才会去扫描下面的方法。

在这里插入图片描述
改完后运行,我们发现又报错了!!!

Exception in thread “main” org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type ‘com.example.demo.ioc.config.UserInfo’ available: expected single matching bean but found 2: userInfo,userInfo2

期望是一个bean名称,结果有两个bean名称。

使用@Bean注解时,bean的名称是方法名。
在这里插入图片描述
当前我们使用类型去获取bean,但是当前有两个:
在这里插入图片描述

我们先使用bean的名称来获取:

//使用@Bean
UserInfo userInfo = (UserInfo) context.getBean("userInfo");
System.out.println(userInfo);

如果想获取userInfo2,名称改一下就可以了:

//使用@Bean
UserInfo userInfo = (UserInfo) context.getBean("userInfo2");
System.out.println(userInfo);

在这里插入图片描述
这个方式也可以,就不用强制类型转换了:

UserInfo userInfo2 = context.getBean("userInfo",UserInfo.class);
System.out.println(userInfo2);
@Bean传递参数

在这里插入图片描述

定义了一个叫name2的String类型的对象,定义了一个叫name的String类型的对象。先根据类型拿,如果类型有多个,就根据名称去拿。

在这里插入图片描述
在这里插入图片描述
现在把名为name的注掉
在这里插入图片描述

它此时拿的就是name2了
在这里插入图片描述

结论:如果需要的Bean的类型,对应的对象只有一个时,就直接赋值,如果有多个时,通过名称去匹配

扫描路径

SpringBoot特点:约定大于配置

我们将DemoApplication移动到controller路径下。
Spring默认扫描package com.example.demo.ioc.controller;路径下的内容,所以运行DemoApplication会报错。
在这里插入图片描述
在这里插入图片描述

解决方法就是通过注解:

指定扫描的路径
在这里插入图片描述

@ComponentScan("com.example.demo")

在这里插入图片描述
此时就运行成功了
在这里插入图片描述
一开始我们并没有使用@ComponentScan("com.example.demo")这个注解,为什么就默认扫描的com.example.demo,原因在于这个注解:@SpringBootApplication它里面有一个@ComponentScan,如下:

在这里插入图片描述
它就表示扫描当前类的目录以及它的子目录

总结:

DI详解

依赖注⼊(DI)是⼀个过程,是指IoC容器在创建Bean时, 去提供运⾏时所依赖的资源,⽽资源指的就是对象. 就是把对象取出来放到某个类的属性中.

依赖注⼊ Spring提供了三种⽅式:

  1. 属性注⼊(Field Injection)
  2. 构造⽅法注⼊(Constructor Injection)
  3. Setter 注⼊(Setter Injection)

属性注入

Service 类的实现代码

Controller 类的实现代码:

@Controller
public class UserController {
    @Autowired
    private UserService userService;
    public void doController(){
        userService.doService();
        System.out.println("do Controller...");
    }
}

获取 Controller 中的 doController⽅法:

@SpringBootApplication
public class  DemoApplication {
    public static void main(String[] args) {
        //Spring 上下文
       ApplicationContext context = SpringApplication.run(DemoApplication.class, args);
       // 从context中获取bean
       UserController bean = context.getBean(UserController.class);
       bean.doController();
    }
}

结果:
在这里插入图片描述
属性注入以类型进行匹配,与注入的属性名称无关。但是如果一个类型存在多个对象时,优先名称匹配,如果名称都匹配不上,那就报错。

构造方法注入

@Controller
public class UserController {
    //构造方法注入
    private UserService userService;
    private UserInfo userInfo;

    public UserController(UserService userService, UserInfo userInfo) {
        this.userService = userService;
        this.userInfo = userInfo;
    }
    @Autowired
    public UserController(UserService userService) {
        this.userService = userService;
    }

    public void doController(){
        userService.doService();
        System.out.println("do Controller...");
    }
}

如果存在多个构造函数,需要加上@AutoWired注明使用哪个构造函数,如果只有一个构造函数@AutoWired可以省略掉。

Setter注入

Setter 注⼊和属性的 Setter ⽅法实现类似,只不过在设置 set ⽅法的时候需要加上@Autowired 注解

@Controller
public class UserController {
    // Setter 方法注入
    private UserService userService;
    @Autowired
    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    public void doController(){
        userService.doService();
        System.out.println("do Controller...");
    }
}

在这里插入图片描述

三种注入优缺点分析

属性注入:

  • 优点:
    简洁,使用方便。
  • 缺点:
    1.只能用于IoC容器,如果是⾮ IoC 容器不可⽤,并且只有在使⽤的时候才会出现 NPE(空指针异常)
    2.不能注⼊⼀个Final修饰的属性。

构造函数注入:

  • 优点:
    1.可以注入final修饰的属性
    2.注⼊的对象不会被修改
    3.依赖对象在使用前⼀定会被完全初始化,因为依赖是在类的构造方法中执行的,而构造方法是在类加载阶段就会执行的方法.
    4.通用性好, 构造⽅法是JDK支持的, 所以更换任何框架,他都是适用的
  • 缺点
    注入多个对象时, 代码会⽐较繁琐。

Setter注入:

  • 优点:
  • 方便在类实例之后, 重新对该对象进行配置或者注入。
  • 缺点:
    1.不能注入⼀个Final修饰的属性。
    2.注入对象可能会被改变, 因为setter⽅法可能会被多次调⽤, 就有被修改的⻛险。

@Autowired存在问题

当同⼀类型存在多个bean时, 使用@Autowired会存在问题

@Configuration
public class BeanConfig {
    @Bean
    public String name(){
        return "zhangsan";
    }
//    @Bean
//    public String name2(){
//        return "wangwu";
//    }
    @Bean
    public UserInfo userInfo(String name){
        UserInfo userInfo = new UserInfo();
        userInfo.setId(1);
        userInfo.setName(name);
        userInfo.setAge(18);
        return userInfo;
    }
    @Bean
    public UserInfo userInfo2(){
        UserInfo userInfo = new UserInfo();
        userInfo.setId(2);
        userInfo.setName("lisi");
        userInfo.setAge(19);
        return userInfo;
    }
}
@Controller
public class UserController {
    //属性注入
    @Autowired
    private UserService userService;
    @Autowired
    private UserInfo user;

    public void doController(){
        userService.doService();
        System.out.println(user);
        System.out.println("do Controller...");
    }
}

在这里插入图片描述
报错的原因是,⾮唯⼀的 Bean 对象

解决方法:

  1. 属性名和需要使用的对象名保持一致。
  2. 使用@Primary注解标识默认的对象。

在这里插入图片描述

  1. 使用@Qualifier注解

在这里插入图片描述

  1. 使用@Resource

在这里插入图片描述

在这里插入图片描述

@Autowird@Resource的区别

  1. @Autowired 是spring框架提供的注解,而@Resource是JDK提供的注解

在这里插入图片描述

在这里插入图片描述

  1. @Autowired 默认是按照类型注入,而@Resource是按照名称注入. 相比于@Autowired 来说,@Resource 支持更多的参数设置,例如name设置,根据名称获取 Bean

@Resource ⽀持更多的参数设置下面源码就体现了:name lookup type等等

Target({TYPE, FIELD, METHOD})
@Retention(RUNTIME)
public @interface Resource {
    /**
     * The JNDI name of the resource.  For field annotations,
     * the default is the field name.  For method annotations,
     * the default is the JavaBeans property name corresponding
     * to the method.  For class annotations, there is no default
     * and this must be specified.
     */
    String name() default "";

    /**
     * The name of the resource that the reference points to. It can
     * link to any compatible resource using the global JNDI names.
     *
     * @since Common Annotations 1.1
     */

    String lookup() default "";

    /**
     * The Java type of the resource.  For field annotations,
     * the default is the type of the field.  For method annotations,
     * the default is the type of the JavaBeans property.
     * For class annotations, there is no default and this must be
     * specified.
     */
    Class<?> type() default java.lang.Object.class;

    /**
     * The two possible authentication types for a resource.
     */
    enum AuthenticationType {
            CONTAINER,
            APPLICATION
    }

    /**
     * The authentication type to use for this resource.
     * This may be specified for resources representing a
     * connection factory of any supported type, and must
     * not be specified for resources of other types.
     */
    AuthenticationType authenticationType() default AuthenticationType.CONTAINER;

    /**
     * Indicates whether this resource can be shared between
     * this component and other components.
     * This may be specified for resources representing a
     * connection factory of any supported type, and must
     * not be specified for resources of other types.
     */
    boolean shareable() default true;

    /**
     * A product specific name that this resource should be mapped to.
     * The name of this resource, as defined by the <code>name</code>
     * element or defaulted, is a name that is local to the application
     * component using the resource.  (It's a name in the JNDI
     * <code>java:comp/env</code> namespace.)  Many application servers
     * provide a way to map these local names to names of resources
     * known to the application server.  This mapped name is often a
     * <i>global</i> JNDI name, but may be a name of any form. <p>
     *
     * Application servers are not required to support any particular
     * form or type of mapped name, nor the ability to use mapped names.
     * The mapped name is product-dependent and often installation-dependent.
     * No use of a mapped name is portable.
     */
    String mappedName() default "";

    /**
     * Description of this resource.  The description is expected
     * to be in the default language of the system on which the
     * application is deployed.  The description can be presented
     * to the Deployer to help in choosing the correct resource.
     */
    String description() default "";
}

@AutoWired默认是按照类型注入的,如果同一个类型存在多个对象,按名称匹配,如果名称匹配不上就会报错。

  • 16
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

马尔科686

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值