4. 对象装配(对象注入)@Autowired

目录

1. 属性注入

2. Setter 注入

3. 构造方法注入   

4. 三种注入优缺点分析

5. @Resource

6. @Autowired 和 @Resource 的区别

7. 根据日志定位问题

ClassCastException 类型匹配异常

NoSuchMethodException 没有找到构造方法


获取 bean 对象也叫做对象装配,是把对象取出来放到某个类中,有时候也叫对象注入。 对象装配(对象注入)的实现方法以下 3 种:

  1. 属性注入
  2. Setter 注入
  3. 构造方法注入

以上三种注入方式,均需要使用 @Autowired 注解并搭配五大类注解,注解一般不能放在启动类中,一般注解放在 web 项目中。

接下来,我们具体来看以上三种实现方法是如何具体实现的。

1. 属性注入

在 UserController 类中注入 userService,直接使用 @Autowired:

@Controller
public class UserController {
    @Autowired
    private UserService userService;
    public void sayHi(){
        userService.sayHi();
        System.out.println("hi,Controller...");
    }
}
@Service
public class UserService {
    public void sayHi(){
        System.out.println("Hi,Service...");
    }
}
public class App {
    public static void main(String[] args) {
        // 得到 Spring 上下文
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        // 获取 Bean 对象
        UserController userController= (UserController) context.getBean("userController");
        // 使用 Bean
        userController.sayHi();
    }
}

是实际开发中使用最多的,但是不被 Spring 官方推荐。

@Autowired 是根据类型进行匹配的,当我们改变对象名称时,依然不影响结果:

那么,当我们类型相同的对象有多个时,如何进行匹配呢?

@Configuration
public class BeanConfig {
    @Bean
    public Integer age(){
        return 15;
    }
    @Bean
    public Users user(Integer age){
        Users user = new Users();
        user.setName("小明");
        user.setAge(age);
        return user;
    }
    public Users user2(){
        Users user = new Users();
        user.setName("小蓝");
        user.setAge(19);
        return user;
    }
}

此时,Users 对象有两个,分别是 user 和 user2,当我们根据同样的方法进行属性注入时,拿到的是哪个对象呢?

@Controller
public class UserController {
    @Autowired
    private UserService us;

    @Autowired
    private Users user;

    public void sayHi(){
        us.sayHi();
        System.out.println(user.getName());
        System.out.println("hi,Controller...");
    }
}

 可以看到我们获取到的是根据名称进行匹配的。

因此我们可以得出结论,属性注入 @AutoWired 和 @Bean 的匹配机制是一样的:当只存在一个对象时,根据对象的类型进行匹配;当存在多个对象时,根据对象的名称进行匹配。

当注入多个属性时,每个属性都需要加上 @Autowired。 

2. Setter 注入

/**
 * Setter 方法注入
 */
@Controller
public class UserController2 {
    private UserService us;

    public void setUs(UserService us) {
        this.us = us;
    }

    public void sayHi(){
        us.sayHi();
        System.out.println("hi,Controller...");
    }
}
public class App2 {
    public static void main(String[] args) {
        // 得到 Spring 上下文
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        /**
         * setter 注入
         */
        UserController userController= (UserController) context.getBean("userController2");
        userController.sayHi();
    }
}

直接运行以上代码,可以看到报错:

Exception in thread "main" java.lang.ClassCastException: SpringCoreDemo.springcore.UserController2 cannot be cast to SpringCoreDemo.springcore.UserController   

原因是,此时我们使用的是 UserController2 这个类,但是在强制类型转换时使用的是  UserController,因此修改代码:

UserController2 userController2= (UserController2) context.getBean("userController2");
userController2.sayHi();

但是,当我们运行以上代码后,依然有报错:

因为 setUs 方法并未被调用,因此在使用 us.sayHi() 时,出现空指针异常。因此需要将 setUs 方法交给 Spring 去管理。

修改后完整代码如下:

@Controller
public class UserController2 {
    private UserService us;

    @Autowired
    public void setUs(UserService us) {
        this.us = us;
    }

    public void sayHi(){
        us.sayHi();
        System.out.println("hi,Controller...");
    }
}
public class App2 {
    public static void main(String[] args) {
        // 得到 Spring 上下文
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        /**
         * setter 注入
         */
        UserController2 userController2= (UserController2) context.getBean("userController2");
        userController2.sayHi();
    }
}

3. 构造方法注入   

直接加入构造方法:

@Controller
public class UserController3 {
    private UserService us;

    public UserController3(UserService us) {
        this.us = us;
    }

    public void sayHi(){
        us.sayHi();
        System.out.println("hi,Controller...");
    }
}
public class App3 {
    public static void main(String[] args) {
        // 得到 Spring 上下文
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        /**
         * 构造方法注入
         */
        UserController3 userController3= (UserController3) context.getBean("userController3");
        userController3.sayHi();
    }
}

可以看到直接运行是可以成功注入的。   那么。如果我们需要注入多个呢?

@Controller
public class UserController3 {
    private UserService us;
    private UserConfiguartion userConfiguartion;

    public UserController3(UserService us) {
        this.us = us;
    }

    public UserController3(UserService us, UserConfiguartion userConfiguartion) {
        this.us = us;
        this.userConfiguartion = userConfiguartion;
    }

    public void sayHi(){
        us.sayHi();
        System.out.println("hi,Controller...");
    }
}

 运行以上代码,出现报错:

Caused by: java.lang.NoSuchMethodException: SpringCoreDemo.springcore.UserController3.<init>()  

在我们没有写构造函数时,会默认生成一个无参的构造函数,当我们写了有参构造函数时,就不再生成默认的无参构造函数。

此时,有两个有参的构造函数,创建对象需要调用构造函数,可能是我们自己的程序调用也可能是 Spring 通过反射(根据类名可以获取到内部的属性和方法)调用。因此,当出现两个构造函数时,程序不知道该调用哪个构造函数,所以会报错。所以,我们需要通过 @Autowired 注解来告诉 Spring 去调用哪个构造函数。

接下来,我们修改代码如下:

给第一个构造函数添加注解:

@Controller
public class UserController3 {
    private UserService us;
    private UserConfiguartion userConfiguartion;

    @Autowired
    public UserController3(UserService us) {
        System.out.println("第一个构造函数...");
        this.us = us;
    }

    public UserController3(UserService us, UserConfiguartion userConfiguartion) {
        System.out.println("第二个构造函数...");
        this.us = us;
        this.userConfiguartion = userConfiguartion;
    }

    public void sayHi(){
        us.sayHi();
        System.out.println("hi,Controller...");
    }
}

 给第二个构造函数添加注解:

@Controller
public class UserController3 {
    private UserService us;
    private UserConfiguartion userConfiguartion;


    public UserController3(UserService us) {
        System.out.println("第一个构造函数...");
        this.us = us;
    }
    @Autowired
    public UserController3(UserService us, UserConfiguartion userConfiguartion) {
        System.out.println("第二个构造函数...");
        this.us = us;
        this.userConfiguartion = userConfiguartion;
    }

    public void sayHi(){
        us.sayHi();
        System.out.println("hi,Controller...");
    }
}

如果只有一个构造函数,可以省略 @Autowired 注解。

如果有多个构造函数,需要添加 @Autowired 注解来决定使用哪个构造函数。

不能同时对两个构造函数都添加 @Autowired 注解。

4. 三种注入优缺点分析

注入方法优点缺点
属性注入简洁,使用方便

1. 只能用于 IoC 容器,只有在使用时才出现 NPE (空指针异常)

2. 不能注入一个 Final 修饰的属性

Setter 方法注入方便在类实例之后重新对该对象进行配置或注入

1. 不能注入一个 Final 修饰的属性

2. 注入对象可能会被改变,因为 Setter方法可能会被多次调用,有被修改的风险

构造函数

1. 可以注入 Final 属性

2. 注入的对象不会被修改

3. 依赖对象在使用前一定会被完全初始化,因为依赖实在类的构造方法中执行的,而构造方法是在类加载阶段就会执行的方法

4. 通用性好,构造方法是 JDK 支持的,所以更换任何框架都适用。

注入多个对象时,代码比较繁琐

Final 修饰的属性需要具备两个条件之一:

1. 初始化赋值

2. 构造函数赋值

当我们使用 @Autowired 注解时,就是已经交给 Spring 去管理了,并不需要再在创建对象时进行赋值。

构造方法注入是 Spring 推荐的方式,但是使用较多的是属性注入。

5. @Resource

@Controller
public class UserController4 {
    @Resource
    private UserService userService;

    public void sayHi(){
        userService.sayHi();
        System.out.println("hi,Controller...");
    }
}
public class App4 {
    public static void main(String[] args) {
        // 得到 Spring 上下文
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        UserController4 userController4= (UserController4) context.getBean("userController4");
        userController4.sayHi();
    }
}

可以看到同样能够运行成功。那么 @Autowired 和 @Resource 有什么区别呢?

6. @Autowired 和 @Resource 的区别

  1. 出身不同:@Autowired 来自于 Spring,@Resource 来自于 JDK 的注解;
  2. 使用时设置的参数不同:相比于 @Autowired 来说,@Resource 支持更多的参数设置,例如 name 设置,根据名称获取 Bean。
  3. @Autowired 可用于 Setter 注入、构造函数注入和属性注入,而 @Resource 只能用于 Setter 注入和属性注入,不能用于构造函数注入

通过 @Resource 设置 name: 

@Controller
public class UserController4 {
    @Resource
    private UserService userService;
    @Resource(name = "user")
    private Users user1;

    public void sayHi(){
        userService.sayHi();
        System.out.println(user1.getName());
        System.out.println("hi,Controller...");
    }
}
public class App4 {
    public static void main(String[] args) {
        // 得到 Spring 上下文
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        UserController4 userController4= (UserController4) context.getBean("userController4");
        userController4.sayHi();
    }
}

@Aurowired 可以搭配 @Qualifier 使用设置 name,等同于 @Resouce 设置 name。

7. 根据日志定位问题

ClassCastException 类型匹配异常

Exception in thread "main" java.lang.ClassCastException: SpringCoreDemo.springcore.UserController2 cannot be cast to SpringCoreDemo.springcore.UserController            

即类型转换出现错误。

NoSuchMethodException 没有找到构造方法

 Caused by: java.lang.NoSuchMethodException: SpringCoreDemo.springcore.UserController3.<init>()

 

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
1. 什么是 spring? 2. 使用 Spring 框架的好处是什么? 3. Spring 由哪些模块组成? 4. 核心容器(应用上下文) 模块 5. BeanFactory – BeanFactory 实现举例 6. XMLBeanFactory 7. 解释 AOP 模块 8. 解释 JDBC 抽象和 DAO 模块 9. 解释对象/关系映射集成模块 10. 解释 WEB 模块 12. Spring 配置文件 13. 什么是 Spring IOC 容器? 14. IOC 的优点是什么? 15. ApplicationContext 通常的实现是什么? 16. Bean 工厂和 Application contexts 有什么区别? 17. 一个 Spring 的应用看起来象什么? 18. 什么是 Spring 的依赖注入? 19. 有哪些不同类型的 IOC(依赖注入)方式? 20. 哪种依赖注入方式你建议使用,构造器注入,还是 Setter 方法注入? 21.什么是 Spring beans? 22. 一个 Spring Bean 定义 包含什么? 23. 如何给 Spring 容器提供配置元数据? 24. 你怎样定义类的作用域? 25. 解释 Spring 支持的几种 bean 的作用域 26. Spring 框架中的单例 bean 是线程安全的吗? 27. 解释 Spring 框架中 bean 的生命周期 28. 哪些是重要的 bean 生命周期方法? 你能重载它们吗? 29. 什么是 Spring 的内部 bean? 30. 在 Spring 中如何注入一个 java 集合? 31. 什么是 bean 装配? 32. 什么是 bean 的自动装配? 33. 解释不同方式的自动装配 34.自动装配有哪些局限性? 35. 你可以在 Spring 中注入一个 null 和一个空字符串吗? 36. 什么是基于 Java 的 Spring 注解配置? 给一些注解的例子 37. 什么是基于注解的容器配置? 38. 怎样开启注解装配? 39. @Required 注解 40. @Autowired 注解 41. @Qualifier 注解 42.在 Spring 框架中如何更有效地使用 JDBC? 43. JdbcTemplate 44. Spring 对 DAO 的支持 45. 使用 Spring 通过什么方式访问 Hibernate? 46. Spring 支持的 ORM 47.如何通过 HibernateDaoSupport 将 Spring 和 Hibernate 结合起来? 48. Spring 支持的事务管理类型 49. Spring 框架的事务管理有哪些优点? 50. 你更倾向用那种事务管理类型? 51. 解释 AOP 52. Aspect 切面 53. 在 Spring AOP 中,关注点和横切关注的区别是什么? 54. 连接点 55. 通知 56. 切点 57. 什么是引入? 58. 什么是目标对象? 59. 什么是代理? 60. 有几种不同类型的自动代理? 61. 什么是织入。什么是织入应用的不同点? 62. 解释基于 XML Schema 方式的切面实现 63. 解释基于注解的切面实现 64. 什么是 Spring 的 MVC 框架? 65. DispatcherServlet 66. WebApplicationContext 67. 什么是 Spring MVC 框架的控制器? 68. @Controller 注解 69. @RequestMapping 注解

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值