1 概述
IOC:控制反转
网上关于IOC有大量解释,但是大都趋于理论,或多或少有些难以理解,
最近我看了官方文档的解释以及一切大神的解说,根据自己的总结实现
了一个能说明Spring中IOC思想的简答例子,一来梳理知识,二来希望
能帮助到被IOC困惑的其他人。
首先,我们回顾下我们平时传统上不进行IOC时如果需要创建调用对象应该怎么实现,以下我们有4个类,分别是Dog,Animals,Cat,SayHello
public class interface Animals{
public void sayHello();
}
public class Dog implements Animals{
@Override
public void sayHello(){
System.out.println("wang wang");
}
}
public class Cat implements Animals{
@Override
public void sayHello(){
System.out.println("miao miao");
}
}
public class SayHello{
private Animals animals;
public SayHello(){
this.animals = new Dog();
}
}
上面就是我们日常使用时调用Animals类的方法,通过SayHello类来实现Animals的创建。当我们创建SayHello类时,Animals类也同时被创建,当然Animals类是通过Dog类来创建的。这样做有一个缺陷,那就是我们的目的是创建SayHello的同时,Animals类可以同时被创建,上述方法我们只能认定其为Dog。这其中的关系是SayHello类控制着Animals类的对象创建,前人有了一种思考,我们能不能将这种创建交付给第三方而不是通过SayHello创建呢?这就是IOC的初衷。
因此,我们调整下SayHello
public class SayHello{
private Animals animals;
public SayHello(){
}
public void setAnimals(Animals animals){
this.animals = animals;
}
public Animals getAnimals(){
return this.animals;
}
}
IOC容器创建SayHello对象,然后通过setter方法将Animals对象“注入”到SayHello中,IOC正对SayHello对象进行“依赖注入”,这里的依赖关系指的是对象之间的关系依赖(SayHello 与Animals)
IoC容器将作为管理器,同时创建SayHello和Animals对象,这就是IOC,上面是简要的概述,如果你还不是很清楚那我们接下来根据一个具体的项目来更深一步地理解它!
2 项目实现
2.1 创建项目
此为项目的工程目录结构
2.2 代码实现
首先我们建立一个通用接口 Animals.java
package com.caigentan.spring.ioc;
public interface Animals {
public String getHello();
public String getBye();
}
接下来是分别对Animals接口的具体实现 Dog.java,Cat.java,Duck.java
Dog.java
package com.caigentan.spring.impl;
import com.caigentan.spring.ioc.Animals;
public class Dog implements Animals {
@Override
public String getHello() {
return "wang wang Hello";
}
@Override
public String getBye() {
return "wang wang Bye";
}
}
Cat.java
package com.caigentan.spring.impl;
import com.caigentan.spring.ioc.Animals;
public class Cat implements Animals {
@Override
public String getHello() {
return "miao miao Hello";
}
@Override
public String getBye() {
return "miao miao Bye";
}
}
Duck.java
package com.caigentan.spring.impl;
import com.caigentan.spring.ioc.Animals;
public class Duck implements Animals {
@Override
public String getHello() {
return "ga ga Hello";
}
@Override
public String getBye() {
return "ga ga Bye";
}
}
构建Spring bean
HelloService.java
package com.caigentan.spring.bean;
import com.caigentan.spring.ioc.Animals;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import src.main.java.com.caigentan.spring.Language;
// @Service注解,用于注解一个类,以通知Spring该类是一个Spring Bean
@Service
public class HelloService {
// @Autowired注解用于在一个字段上注释,以通知Spring我们向字段注入值
@Autowired
private Animals animals;
public HelloService(){
}
public void sayHello(){
String hello = animals.getHello();
System.out.println("Hello:" + hello);
}
}
MyRepository.java
package com.caigentan.spring.bean;
import org.springframework.stereotype.Repository;
import java.util.Date;
@Repository
public class MyRepository {
public String getAppName(){
return "Hello caigentan";
}
public Date getSystemDate(){
return new Date();
}
}
MyComponent.java
package com.caigentan.spring.bean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* 此类在此处我主要是用于解释@Component注解的作用,方便后面理解 bean 的概念
*/
@Component
public class MyComponent {
@Autowired
private MyRepository repository;
public void showAppInfo(){
System.out.println(repository.getAppName());
System.out.println("Now : " + repository.getSystemDate());
}
}
2.3 测试结果
TestProgram.java
package com.caigentan.spring;
import com.caigentan.spring.bean.HelloService;
import com.caigentan.spring.bean.MyComponent;
import com.caigentan.spring.bean.MyRepository;
import com.caigentan.spring.config.AppConfiguration; i
mport com.caigentan.spring.ioc.Animals;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class TestProgram {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfiguration.class);
System.out.println("****************************");
Animals animals = (Animals)context.getBean("animals");
System.out.println("beans is " + animals);
System.out.println("animal beans Bye: " + animals.getBye());
System.out.println("****************************");
HelloService service = (HelloService) context.getBean("helloService");
service.sayHello();
System.out.println("****************************");
MyComponent component = (MyComponent)context.getBean("myComponent");
component.showAppInfo();
System.out.println("****************************");
MyRepository repository = (MyRepository)context.getBean("myRepository");
System.out.println(repository.getAppName());
System.out.println(repository.getSystemDate());
}
}
先看结果,再分析:
解释:
那么,这个过程中IOC究竟干了什么呢?他们的这些注解又是如何工作的?别急,听我慢慢道来
首先我们应该了解,在运行过程中,我们已经将对象间的依赖交给了IOC,无需我们自己构造(各种注解实现)。在此时IOC中应该存在这些玩意儿:
是不是清楚了许多?从上我们可以知道关于IOC的以下几点:
Q1、IOC里存放什么?
A1、IoC容器中包含应用程序中使用的所有Spring bean的容器。
Q2、IOC具体指什么?
A2、如果一个对象是传统上从一个类创建的,那么它的字段将在类内部分配值。相反,对于Spring,它的对象和字段的值是由名为IOC的对象从外部注入的。
到此,我想你应该对Spring 的IOC机制有了一个比较清晰的认识。另外对于博文中的一些对于初学者可能比较晦涩难懂的概念我在此也作一些解释
-
Q:@Repository,@Component,@Service都是为了向IOC中注入Spring Bean那么这3中注解有什么区别呢,我应该改如何在合适的场景使用?
A:根据我的一些实践,我可以说,这3种注解在功能实现上是没区别的,无论使用哪一个都可以使用预期的功能(不信你项目中这3类注解相互替换试试),但是应该根据自己的项目合理选择注解以方便使用(可以根据他们的字面意思来使用)。 -
Q: @AutoWired与@Bean的区别与联系是什么?
A:@AutoWired与@Bean做的是不同的事,
@Bean 告诉Spring’这是此类的一个实例,请保留该类,并在我询问时将其还给我’。
@Autowired说“请给我一个该类的实例,例如,我@Bean之前用注释创建的一个实例”。本项目中HelloService类中animals获得AppConfiguration类中的@Bean(name = “animals”),此@Bean()与getter方法获得一个new Dog(),都是一一对应的。
在Spring中创建的类作为Bean时,自动将其首字母小写作为约束