目录
承接上文:详解Spring IoC&DI (一)
1.IoC详解
1.1方法注解@Bean
1.2方法注解要配合类注解使用
1.3定义多个对象
1.4重命名Bean
1.5扫描路径
2.DI详解
2.1DI与IoC的关系
2.2属性注入
2.3构造方法注入
2.4Setter注入
2.5 三种注入优缺点分析
2.6Autowired存在问题
2.6.1Primary
2.6.2Qualifier
2.6.3Resource
承接上文:详解Spring IoC&DI (一)
1.IoC详解
1.1方法注解@Bean
类注解是添加到某个类上的 , 但是存在两个问题:
1.使⽤外部包⾥的类, 没办法添加类注解
2.⼀个类, 需要多个对象, 比如多个数据源
这种场景, 我们就需要使⽤⽅法注解@Bean
代码示例:
public class BeanConfig {
@Bean
public User user(){
User user = new User();
user.setName("zhangsan");
user.setAge(18);
return user;
}
}
当我们写完以上代码 ,尝试获取 bean 对象中的 user 时却发现 ,根本获取不到:
@SpringBootApplication
public class TestSpringTwoApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(TestSpringTwoApplication.class,args);
//从Spring上下⽂中获取对象
User user =context.getBean(User.class);
//使⽤对象
System.out.println(user);
}
}
执行结果如下:
1.2方法注解要配合类注解使用
在 Spring 框架的设计中 ,⽅法注解 @Bean 要配合类注解才能将对象正常的存储到Spring 容器中
如下代码所示:
@Component
public class BeanConfig {
@Bean
public User user(){
User user = new User();
user.setName("zhangsan");
user.setAge(18);
return user;
}
}
再次执⾏以上代码 ,运⾏结果如下:
1.3定义多个对象
多数据源的场景, 类是同⼀个, 但是配置不同, 指向不同的数据源.
@Component
public class BeanConfig {
@Bean
public User user1(){
User user = new User();
user.setName("zhangsan");
user.setAge(18);
return user;
}
@Bean
public User user2(){
User user = new User();
user.setName("lisi");
user.setAge(28);
return user;
}
}
获取对象:
@SpringBootApplication
public class TestSpringTwoApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(TestSpringTwoApplication.class, args);
//从Spring上下⽂中获取对象
User user1 = (User) context.getBean("user1");
User user2 = (User) context.getBean("user2");
//使⽤对象
System.out.println(user1);
System.out.println(user2);
}
}
运行结果:
1.4重命名Bean
可以通过设置 name 属性给 Bean 对象进⾏重命名操作 ,如下代码所示:
@Bean(name = {"u1","user1"})
public User user1(){
User user = new User();
user.setName("zhangsan");
user.setAge(18);
return user;
}
此时我们使用u1就可以获取到User对象:
User u1 = (User) context.getBean("u1");
name={} 可以省略 ,如下代码所⽰:
@Bean({"u1","user1"})
public User user1(){
User user = new User();
user.setName("zhangsan");
user.setAge(18);
return user;
}
只有⼀个名称时, {}也可以省略, 如:
@Bean("u1")
public User user1(){
User user = new User();
user.setName("zhangsan");
user.setAge(18);
return user;
}
1.5扫描路径
使⽤前⾯学习的四个注解声明的bean想要⽣效 ,还需要被Spring扫描
通过修改项目工程的目录结构进行测试:
运行结果:
解释: 没有bean的名称为u1,
使⽤五⼤注解声明的bean ,要想⽣效, 还需要配置扫描路径, 让Spring扫描到这些注解也就是通过
@ComponentScan来配置扫描路径.
@ComponentScan({"com.example.demo"})
@SpringBootApplication
public class TestSpringTwoApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(TestSpringTwoApplication.class,args);
//从Spring上下⽂中获取对象
User user =context.getBean(User.class);
//使⽤对象
System.out.println(user);
}
}
@ComponentScan注解虽然没有显式配置,但是实际已经包含在了启动类声明注解
@SpringBootApplication中了,默认扫描的范围是启动类所在包及其子包
2.DI详解
2.1DI与IoC的关系
DI(Dependency Injection,依赖注入)和 IoC(Inversion of Control,控制反转)有着密切的关系。 可以说 DI 是实现 IoC 的一种具体方式。 IoC 是一种设计思想,强调将对象之间的控制权进行反转,不再由对象自己去创建或获取其依赖,而是由外部(如框架)来控制和管理依赖关系的创建和注入。 而 DI 则侧重于具体的将依赖对象注入到目标对象的操作,它是实现 IoC 理念的重要手段之一。通过 DI,依赖对象在合适的时候被准确地注入到需要它们的对象中,从而体现了 IoC 的原则。 简单来说,IoC 是更宏观的概念,DI 是实现 IoC 的具体策略和机制。
关于依赖注⼊, Spring也给我们提供了三种⽅式:
属性注⼊(Field Injection)、构造⽅法注⼊(Constructor Injection)、Setter 注⼊(Setter Injection)
2.2属性注入
@SpringBootApplication
public class SpringIocDemoApplication {
public static void main(String[] args) {
//获取Spring上下⽂对象
ApplicationContext context = SpringApplication.run(SpringIocDemoApplicatio
//从Spring上下⽂中获取对象
UserController userController = (UserController) context.getBean("userCont
//使⽤对象
userController.sayHi(); 11 }
}
属性注⼊是使用@Autowired实现的 ,将 Service 类注⼊到 Controller 类中.
Service类的实现代码如下:
import org.springframework.stereotype.Service;
@Service
public class UserService {
public void sayHi() {
System.out.println("Hi,UserService");
}
}
Controller 类的实现代码如下:
@Controller
public class UserController {
//注⼊⽅法1: 属性注⼊
@Autowired
private UserService userService;
public void sayHi(){
System.out.println("hi,UserController...");
userService.sayHi();
}
}
获取Controller中的sayHi方法:
@SpringBootApplication
public class SpringIocDemoApplication {
public static void main(String[] args) {
//获取Spring上下⽂对象
ApplicationContext context = SpringApplication.run(TestSpringTwoApplication.class,args);
//从Spring上下⽂中获取对象
UserController userController = context.getBean(UserController.class);
//使⽤对象
userController.sayHi();
}
}
运行结果为:
2.3构造方法注入
构造方法注入是在类的构造方法中实现注入,如下代码所示:
@Controller
public class UserController2 {
//注⼊⽅法2: 构造⽅法
private UserService userService;
@Autowired
public UserController2(UserService userService) {
this.userService = userService;
}
public void sayHi(){
System.out.println("hi,UserController2...");
userService.sayHi();
}
}
注意事项:如果类只有⼀个构造⽅法 ,那么@Autowired 注解可以省略;如果类中有多个构造⽅法, 那么需要添加上@Autowired 来明确指定到底使⽤哪个构造⽅法。
2.4Setter注入
Setter 注⼊和属性的 Setter 方法实现类似 ,只不过在设置 set 方法的时候需要加上@Autowired 注解 ,如下代码所示:
@Controller
public class UserController3 {
//注⼊⽅法3: Setter⽅法注⼊
private UserService userService;
@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
public void sayHi(){
System.out.println("hi,UserController3...");
userService.sayHi();
}
}
2.5 三种注入优缺点分析
属性注⼊
优点: 简洁 ,使⽤⽅便;
缺点:只能⽤于 IoC 容器 ,并且只有在使⽤的时候才会出现 NPE( 空指针异常)、 不能注入⼀个Final修饰的属性
构造函数注⼊(Spring 4.X推荐)
优点:可以注⼊final修饰的属性、注⼊的对象不会被修改、依赖对象在使⽤前⼀定会被完全初始化 ,因为依赖是在类的构造⽅法中执⾏的 ,⽽构造⽅法是在类加载阶段就会执⾏的⽅法、 通⽤性好, 构造⽅法是JDK⽀持的, 所以更换任何框架,他都是适⽤的
缺点: 注入多个对象时, 代码会比较繁琐
Setter注⼊(Spring 3.X推荐)
优点: ⽅便在类实例之后, 重新对该对象进⾏配置或者注⼊
缺点:不能注⼊⼀个Final修饰的属性、注⼊对象可能会被改变, 因为setter⽅法可能会被多次调⽤ , 就有被修改的风险.
2.6Autowired存在问题
当同⼀类型存在多个bean时, 使⽤@Autowired会存在问题
@Component
public class BeanConfig {
@Bean("u1")
public User user1(){
User user = new User();
user.setName("zhangsan");
user.setAge(18);
return user;
}
@Bean
public User user2() {
User user = new User();
user.setName("lisi");
user.setAge(19);
return user;
}
}
@Controller
public class UserController {
//注⼊user
@Autowired
private User user;
public void sayHi(){
System.out.println("hi,UserController...");
userService.sayHi();
System.out.println(user);
}
}
运行结果:
报错的原因是 ,⾮唯—的 Bean 对象。
Spring提供了以下几种解决⽅案:
2.6.1Primary
@Primary注解: 当存在多个相同类型的Bean注⼊时 ,加上@Primary注解 ,来确定默认的实现.
@Component
public class BeanConfig {
@Primary //指定该bean为默认bean的实现
@Bean("u1")
public User user1(){
User user = new User();
user.setName("zhangsan");
user.setAge(18);
return user;
}
@Bean
public User user2() {
User user = new User();
user.setName("lisi");
user.setAge(19);
return user;
}
}
2.6.2Qualifier
指定当前要注⼊的bean对象。 在@Qualifier的value属性中 ,指定注⼊的bean 的名称。
@Qualifier注解不能单独使⽤ ,必须配合@Autowired使用
@Controller
public class UserController {
@Qualifier("user2") //指定bean名称
@Autowired
private User user;
public void sayHi(){
System.out.println("hi,UserController...");
System.out.println(user);
}
}
2.6.3Resource
使⽤@Resource注解:是按照bean的名称进⾏注⼊。通过name属性指定要注⼊的bean的名称。
@Controller
public class UserController {
@Resource(name = "user2")
private User user;
public void sayHi(){
System.out.println("hi,UserController...");
System.out.println(user);
}
}
@Autowird 与 @Resource的区别
• @Autowired 是spring框架提供的注解 ,而@Resource是JDK提供的注解
• @Autowired 默认是按照类型注⼊ ,⽽@Resource是按照名称注⼊ . 相⽐于 @Autowired 来说, @Resource ⽀持更多的参数设置 ,例如 name 设置 ,根据名称获取 Bean。