首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >一文讲透Spring的IOC反转控制

一文讲透Spring的IOC反转控制

作者头像
灬沙师弟
发布2023-03-07 09:42:59
发布2023-03-07 09:42:59
2770
举报
文章被收录于专栏:Java面试教程Java面试教程

哈喽,大家好,我是Java小面。

Spring的底层核心是IOC和AOP,本文讲解IOC的两种注入案例,让我们一文掌握全部核心知识!

IOC,英文全称Inversion of Control,意思是反转控制,原先new一对象是手工操作,现在交给spring容器,在大规模项目中减少人工代码,做到解耦、高效的目的!

案例:DI操作(xml驱动)

在之前HelloWorld项目上做个简单扩展,进行DI(Dependency Injection)操作的案例:

这里做了一个最精简的演示,通过演示可以看到,如果有多类适配器(子类),只需要修改配置文件,就完成了装配改装,而没有修改java代码! 这就是spring解耦的魅力!

1、准备demo程序

代码语言:javascript
复制
// 这里准备了一个接口,2个实现类,实现不同语种的欢迎词反馈,用于测试代码的调用时不需要修改一行代码,仅需修改配置文件完成功能的切换!
public interface UserService {
    public String sayHello();
}

public class UserServiceImpl1 implements UserService {
    public String sayHello() {
        return "Nice to meet you!";
    }
}
public class UserServiceImpl2 implements UserService {
    public String sayHello() {
        return "你好啊";
    }
}

2、修改配置文件

代码语言:javascript
复制
<bean class="com.iyyxx.HelloWorld" id="helloWorld">
        <property name="userService" ref="userServiceImpl2"/> <!-- ref这里可以替换为userServiceImpl1或userServiceImpl2查看效果 -->
    </bean>
<bean class="com.iyyxx.service.impl.UserServiceImpl1" id="userServiceImpl1"/>
<bean class="com.iyyxx.service.impl.UserServiceImpl2" id="userServiceImpl2"/>

3、编写测试代码

代码语言:javascript
复制
public class HelloWorld {
    private UserService userService;

    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    public String say2(){
        return userService.sayHello();
    }

    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        HelloWorld helloWorld = (HelloWorld) applicationContext.getBean(HelloWorld.class);
        System.out.println(helloWorld.say2());
    }
}

DI的魅力就在于不修改一行java代码,对配置文件进行简单修改就完成了重新装配!

遍历!基于xml的DI注入方式

1、xml配置(这里演示的是set方法的三种注入方式,还可以通过构造函数,还有复杂类型list、set、map等)

代码语言:javascript
复制
<!-- 1、Bean的定义,核心属性class、id -->
<bean class="com.iyyxx.service.impl.UserServiceImpl1" id="userServiceImpl1"/>

<!-- 2、Bean的手工赋值,在set接口采用value入口 -->
<bean class="com.iyyxx.dao.impl.StudentDaoImpl" id="studentDao">
  <properties name="userName" value="张三" />
</bean>

<!-- 3、Bean的手工赋值,在构造函数接口采用constructor-arg入口 -->
<bean class="com.iyyxx.dao.impl.StudentDaoImpl" id="studentDao">
  <constructor-arg name="userName" value="张三"/>
  <constructor-arg name="userAge" value="15"/>
</bean>

<!-- 4、Bean的引用传递,采用ref入口 -->
<bean class="com.iyyxx.dao.impl.StudentDaoImpl" id="studentDao">
  <properties name="userService" ref="userServiceImpl1" /> <!-- 这里ref指向其他被管理Bean,可以是id或class -->
</bean>


<!-- 5、配置文件读取,常见jdbc连接信息尾部设定、spring引用的方式,组合式操作,用${xx.xx}方式进行set或构造函数注入 -->
<bean class="com.iyyxx.dao.impl.StudentDaoImpl" id="studentDao">
  <properties name="password" ref="${xx.yy.zz}" />
</bean>
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>  <!--读取properties文件-->

<!-- 6、集合的注入方式 -->
<bean class="com.iyyxx.dao.impl.StudentDaoImpl" id="studentDao">
  <properties name="list">
    <list>
      <value>xxx</value>
      <value>yyy</value>
    </list>
  </properties>
  <properties name="set">
    <set>
      <value>xxx</value>
      <value>yyy</value>
    </set>
  </properties>
  <properties name="aryyay">
    <array>
      <value>xxx</value>
      <value>yyy</value>
    </array>
  </properties>
  <properties name="properties">
    <props>
      <prop key="k1">v1</prop>
      <prop key="k2">v2</prop>
    </props>
  </properties>
  <properties name="map">
    <map>
      <entry key="k1" value-ref="phone"/>
      <entry key="k2" value-ref="phone"/>
    </map>
  </properties>
</bean>

遍历!基于注解的DI注入方式

注解驱动的di注入有两个注解可以使用

  • @Autowired,可作用于
    • 成员变量(常用)
    • 构造函数(官方Spring4推荐,但臃肿不易阅读)
    • 方法上,比较诡异
  • @Value,常用于配置文件读取

前置要求:容器配置类已做了包扫描或手工声明,所需bean均已容器化管理,实例如下:

代码语言:javascript
复制
@Configuration
public class SpringConfig {
    @Bean
    public HelloWorld helloWorld(){
        return new HelloWorld();
    }

    @Bean
    public UserService userService(){
        return new UserServiceImpl1();
    }
}

//方案2,比较常用,需要配置器开启@ComponentScan,所有的bean也有Bean声明标签,如@Component、@Service、@Controller
@ComponentScan(basePackages = "com.iyyxx")
public class SpringConfig {
}
@Component
public class HelloWorld {
}
@Service
public class UserServiceImpl1 implements UserService {
}

使用介绍:有了上面的铺垫,使用是很丝滑的,仅仅只是用上@Autowired,除非有多个实现类则需使用==@Qulifier进行特殊指定==

代码语言:javascript
复制
@Component
public class HelloWorld {
    @Autowired
    @Qualifier("userServiceImpl2") //这里有2个实现类,所以用类型的驼峰命名法可以快速指定,当然也可以在bean上面指定别名!
    private UserService userService;

    public String say(){
        return "Hello Spring by Annotation";
    }

    public String say2(){
        return userService.sayHello();
    }

    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
        HelloWorld helloWorld = (HelloWorld) applicationContext.getBean(HelloWorld.class);
        System.out.println(helloWorld.say());
        System.out.println(helloWorld.say2());
    }
}

这里补充下基于构造器、方法的注入方式

代码语言:javascript
复制
//构造器注入方式
@Component
public class HelloWorld {
    private UserService userService;

    @Autowired
    public HelloWorld(@Qualifier("userServiceImpl2") UserService userService) {
        this.userService = userService;
    }
    
    public String say2(){
        return userService.sayHello();
    }

    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
        HelloWorld helloWorld = (HelloWorld) applicationContext.getBean(HelloWorld.class);
        System.out.println(helloWorld.say2());
    }
}

//方法注入方式
@Component
public class HelloWorld {
    private UserService userService;

    @Autowired
    public void setUserService(@Qualifier("userServiceImpl2") UserService userService) {
        this.userService = userService;
    }

    public String say2(){
        return userService.sayHello();
    }

    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
        HelloWorld helloWorld = (HelloWorld) applicationContext.getBean(HelloWorld.class);
        System.out.println(helloWorld.say2());
    }
}

注解驱动方式下获取properties文件属性有两点:

  • 类上面使用@PropertySource并指定文件名(如果有多级路径就加路径)
  • 类中使用成员变量+@Value标签,并使用${xx.xx}方式获取配置信息

实例如下:

代码语言:javascript
复制
@Service
@PropertySource("jdbc.properties")
public class UserServiceImpl2 implements UserService {
    @Value("${jdbc.username}")
    private String userName;
    
    public String sayHello() {
        return "你好啊 "+userName;
    }
}

结语

Spring 学习因三种DI的编码方式让学习起来特别辛苦,虽说目前已面向纯注解的开发方式,但依旧存在一些历史项目、复杂架构需要接收,多学点总是没错的,是不是还不过瘾,后面还有一系列的【一文讲解透】系列分享给大家!

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-11-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Java面试教程 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 案例:DI操作(xml驱动)
    • 遍历!基于xml的DI注入方式
    • 遍历!基于注解的DI注入方式
    • 结语
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档