Spring IoC详解

IoC详解

前面我们提到IoC控制反转,就是将对象的控制权交给Spring的IoC容器,由IoC容器创建及管理对象.

也就是bean的存储.

1)Bean的存储

在之前的入门案例中,要把某个对象交给IoC容器管理,需要下类上添加一个注解: @Component而Spring框架为了更好的服务web应用程序,提供了更丰富的注解.

共有两类注解类型可以实现:

1.类注解: @Controller , @Service , @Repositoty , @Component , @Configuration.

2.方法注解: @Bean

接下来我们分别来看

1.@Controller(控制器存储)

使用@Controller 存储 bean的代码如下所示: 

@Controller // 将对象存储到Spring中
public class UserController {
    public void sayHi() {
        System.out.println("hi, UserController...");
    }
}

如何观察这个对象已经存储在Spring容器当中呢?

接下来我们学习如何从Spring容器中获取对象

@SpringBootApplication
public class DemoApplication {

	public static void main(String[] args) {
		//获取Spring上下文对象
		ConfigurableApplicationContext context = SpringApplication.run(DemoApplication.class, args);
		//从Spring上下文中获取对象
		UserController userController = context.getBean(UserController.class);
		//使用对象
		userController.sayHi();
	}
}

ApplicationContect 翻译过来就是: Spring上下文

因为对象都交给Spring管理了,所以获取对象要从Spring中获取,那么就得先得到Spring的上下文

观察运行结果: 

获取bean对象的其他方式

上述代码是根据类型来查找对象,如果Spring容器中,同一个类型存在多个bean的话,怎么来获取呢?

ApplicationContext也提供了其他获取bean的方式,ApplicationContext获取bean对象的功能,是父类BeanFactory提供的功能.

public interface BeanFactory {
//根据bean名称获取bean
    Object getBean(String name) throws BeansException;
//根据bean名称和类型获取
    <T> T getBean(String name, Class<T> requiredType) throws BeansException;

    Object getBean(String name, Object... args) throws BeansException;
//根据类型获取
    <T> T getBean(Class<T> requiredType) throws BeansException;

    <T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
}

常用的是上述1,2,4种,这三种方式,获取到的bean是一样的

其中1,2种都涉及到根据名称来获取对象.bean的名称是什么?

Spring bean是Spring框架在运行是管理的对象,Spring会给管理的对象起一个名字

比如学校管理学生,会给每个学生分配一个学号,根据学号,就可以找到对应的学生.

Spring也是如此,给每个对象起一个名字,根据Bean的名称就可以获取到对应的对象.

Bean命名约定

程序开发人员不需要为bean指定名称(BeanId),如果没有显示的提供名称(BeanId),然后使用驼峰式大小写

比如

类名: UserController , Bean的名称为: userController 

也有一些特殊情况,当有多个字符并且第一个和第二个字符都是大写时,将保留原始大小写.

比如 

类名: UController, Bean的名称为: UController

根据这个命名规则,我们来获取Bean.

@SpringBootApplication
public class DemoApplication {

	public static void main(String[] args) {
		//获取Spring上下文对象
		ConfigurableApplicationContext context =
		SpringApplication.run(DemoApplication.class, args);
		//从Spring上下文中获取对象
		//根据Bean类型获取
		UserController userController1 = context.getBean(UserController.class);
		//根据bean名称获取
		UserController userController2 = (UserController) context.getBean("userController");
		//根据bean名称 + 类型 获取
		UserController userController3 = context.getBean("userController", UserController.class);
		System.out.println(userController1);
		System.out.println(userController2);
		System.out.println(userController3);
	}

}

地址一样说明对象是一个

获取bean对象,是父类Factory提供的功能

ApplicationContext VS BeanFactory(常见面试题)

  继承关系和功能方面来说: Spring 容器有两个顶级的接口: BeanFactory 和 ApplicationContext .其中 BeanFactory 提供了基础的访问容器的能力,而ApplicationContext 属于 BeanFactory的子类,它除了继承了BeanFactory 的所有功能之外,它还拥有独特的特性, 还添加了对国际化支持,资源访问支持,以及事件传播方面的支持

  从性能方面来说: ApplicationContext 是一次性加载并初始化所有的Bean 对象,而BeanFactory是需要那个才去加载那个, 因此更加轻量.(空间换时间)

2.@Service(服务存储)

使用@Service存储bean的代码如下: 

@Service
public class UserService {
    public void sayHi(String name) {
        System.out.println("Hi"+ name);
    }
}
public static void main(String[] args) {
		//获取Spring上下文对象
		ConfigurableApplicationContext context =
		SpringApplication.run(DemoApplication.class, args);
		//从Spring中获取UserService对象
		UserService userService = context.getBean(UserService.class);
		userService.sayHi("UserService");
}

运行结果: 

3.@Repository(仓库存储)

使用@Repository 存储bean的代码如下所示: 

@Repository
public class UserRepository {
    public void sayHi() {
        System.out.println("Hi,UserRepository");
    }
}
@SpringBootApplication
public class DemoApplication {

	public static void main(String[] args) {
		//获取Spring上下文对象
		ConfigurableApplicationContext context =
		SpringApplication.run(DemoApplication.class, args);
		//从Spring中获取对象
		UserRepository userRepository = context.getBean(UserRepository.class);
		userRepository.sayHi();
  }
}

运行结果: 

4.@Component(组件存储)

使用@Component 存储bean的代码如下所示: 

@Component
public class UserComponent {
    public void sayHi() {
        System.out.println("Hi,UserComponent");
    }
}
public class DemoApplication {

	public static void main(String[] args) {
		//获取Spring上下文对象
		ConfigurableApplicationContext context =
		SpringApplication.run(DemoApplication.class, args);
		//从Spring中获取对象
		UserComponent userComponent = context.getBean(UserComponent.class);
		userComponent.sayHi();
  }
}

运行结果: 

5.@Configuration(配置存储)

使用@Configuration存储bean的代码如下所示: 

@Configuration
public class UserConfiguration {
    public void sayHi() {
        System.out.println("Hi, UserConfiguration");
    }
}
@SpringBootApplication
public class DemoApplication {

	public static void main(String[] args) {
		//获取Spring上下文对象
		ConfigurableApplicationContext context =
		SpringApplication.run(DemoApplication.class, args);
		//从Spring中获取对象
		UserConfiguration userConfiguration = context.getBean(UserConfiguration.class);
		userConfiguration.sayHi();
  }
}

运行结果: 

2)为什么要这么多注解?

这个和应用分成是呼应的.让程序员看到类注解之后,就能直接了解当前类的用途.

  • @Controller: 控制层,接收请求,对请求进行处理,并进行响应.
  • @Service: 业务逻辑层,处理具体的业务逻辑
  • @Repository: 数据访问层,也称为持久层.负责数据访问操作
  • @Configuration: 配置层,处理项目中的一些配置信息

程序的应用分层,调用流程如下: 

类注解之间的关系

其实这些注解里面都有一个注解@Component , 说明它们本身就是属于@Component 的" 子类".

@Component是一个元注解,也就是说可以注解其他类注解,如 @Controller,@Serivce,@Repository等.这些注解被称为@Component的衍生注解.

@Controller,@Service和@Repository用于更具体的用例(分别在控制层,业务逻辑层,持久化层),在开发过程中,如果你要在业务逻辑层使用@Component或@Service,显然@Serivce是更好的选择

3)方法注解@Bean

类注解是添加到某个类上的,但是存在两个问题:

1.使用外部包里的类,没办法添加类注解

2.一个类,需要多个对象,比如多个数据源

这种场景,我们就需要使用方法注解@Bean

我们先来看看方法注解如何使用: 

public class BeanConfig {
    @Bean
    public User user() {
        User user = new User();
        user.setName("zhangsan");
        user.setAge(18);
        return user;
    }
}
	public static void main(String[] args) {
		ConfigurableApplicationContext context = SpringApplication.run(DemoApplication.class, args);

		User user = context.getBean(User.class);
		System.out.println(user);
  }

运行结果:  运行失败

why?

1.方法注解要配合类注解使用

在Spring框架设计中,方法注解@Bean要配合类注解才能将对象正常的存储到Spring容器中,

如下代码所示: 

@Component
public class BeanConfig {
    @Bean
    public User user() {
        User user = new User();
        user.setName("zhangsan");
        user.setAge(18);
        return user;
    }
}

运行结果: 

2.定义多个对象

对于同一个类,如何定义多个对象呢?

我们看下@Bean的使用

@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(19);
        return user;
    }
}

定义了多个对象的话,我们根据类型获取兑现,获取的是哪个对象呢?

	public static void main(String[] args) {
		ApplicationContext context = SpringApplication.run(DemoApplication.class, args);

		User user = context.getBean(User.class);
		System.out.println(user);
}

运行结果:

报错信息显示: 期望只有一个匹配,结果发现了两个,user1,user2

接下来我们根据名称来获取bean对象

	public static void main(String[] args) {
		ApplicationContext context = SpringApplication.run(DemoApplication.class, args);

		User user1 = (User) context.getBean("user1");
		System.out.println(user1);

		User user2 = (User) context.getBean("user2");
		System.out.println(user2);
}

运行结果: 

可以看到,@Bean可以针对同一个类,定义多个对象.

3.重命名Bean

可以通过设置name属性给Bean对象进行重命名操作,如下代码所示: 

   @Bean(name = {"u1,user1"})
        public User user() {
        User user = new User();
        user.setName("zhangsan");
        user.setAge(18);
        return user;
    }

此时我们使用u1就可以获取到User对象了,如下代码所示: 

@SpringBootApplication
public class DemoApplication {

	public static void main(String[] args) {
		ApplicationContext context = SpringApplication.run(DemoApplication.class, args);

		User user = (User) context.getBean("u1");
		System.out.println(user);
  }
}

运行结果: 

name可以省略,只有一个名称时,{}也可以省略.

  • 32
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值