Java--Spring之IoC控制反转;基于注解的DI

Posted MinggeQingchun

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java--Spring之IoC控制反转;基于注解的DI相关的知识,希望对你有一定的参考价值。

基于XML配置文件的DI请参考上文

https://blog.csdn.net/MinggeQingchun/article/details/122880488

二、基于注解的DI

DI 使用注解,将不再需要在 Spring 配置文件中声明 bean 实例

Spring 中使用注解,需要在 Spring 配置文件中配置组件扫描器,用于在指定的基本包中扫描注解

1、声明组件扫描器(component-scan),组件就是Java对象

base-package:指定注解在项目中的包名

2、component-scan工作原理:

spring会扫描遍历base-package指定的包, 扫描包和子包中的所有类,找到类中的注解,按照注解创建Java对象,或者给属性赋值

3、加入<context:component-scan>标签,配置文件发生变化

(1)加入了新的约束文件:spring-context.xsd

(2)给这个新的约束文件起个命名空间的名称

xmlns:context="http://www.springframework.org/schema/context" http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.x

 4、指定多个包的3种方式

(1)使用多个 context:component-scan 指定不同的包路径

<context:component-scan base-package="com.mycompany.package1" />
<context:component-scan base-package="com.mycompany.package2" />

(2)指定 base-package 的值使用分隔符

分隔符可以使用逗号(,)分号(;)还可以使用空格,不建议使用空格

<context:component-scan base-package="com.mycompany.package1;com.mycompany.package2" />

(3)base-package 是指定到父包名

但不建议直接使用com,这样会扫描磁盘中所有文件夹,导致效率降低

<context:component-scan base-package="com.mycompany" />

spring配置文件 applicationContext.xml 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 声明组件扫描器(component-scan),组件就是Java对象
         base-package:指定注解在项目中的包名
         component-scan工作原理:spring会扫描遍历base-package指定的包,
            扫描包和子包中的所有类,找到类中的注解,按照注解创建Java对象,或者给属性赋值

         加入<context:component-scan>标签,配置文件发生变化
            1、加入了新的约束文件:spring-context.xsd
            2、给这个新的约束文件起个命名空间的名称
            xmlns:context="http://www.springframework.org/schema/context"
            http://www.springframework.org/schema/context
            https://www.springframework.org/schema/context/spring-context.xsd"
    -->

<!--    <context:component-scan base-package="com.mycompany.p1component" />-->
    <context:component-scan base-package="com.mycompany.p2valuenormal" />


    <!--加载属性配置文件-->
    <context:property-placeholder location="classpath:objectvalue.properties" />

    <!-- 指定多个包有3种方式
        1、使用多次组件扫描器,指定不同的包
        2、使用分隔符(; 或 ,),分隔多个包
        3、指定父包(但不建议直接使用com,这样会扫描磁盘中所有文件夹,导致效率降低)
    -->

<!--    &lt;!&ndash; 1、使用多次组件扫描器,指定不同的包 &ndash;&gt;-->
<!--    <context:component-scan base-package="com.mycompany.p1component" />-->
<!--    <context:component-scan base-package="com.mycompany.p2valuenormal" />-->

<!--    &lt;!&ndash; 2、使用分隔符(; 或 ,),分隔多个包 &ndash;&gt;-->
<!--    <context:component-scan base-package="com.mycompany.p1component;com.mycompany.p2valuenormal"-->

<!--    &lt;!&ndash; 3、指定父包(但不建议直接使用com,这样会扫描磁盘中所有文件夹,导致效率降低) &ndash;&gt;-->
<!--    <context:component-scan base-package="com.mycompany" />-->
</beans>

(一)@Component@Repository@Service@Controller注解

1、@Component :创建对象,等同 <bean> 标签功能

(1)属性:value 对象的名称,就是 <bean>的 id 值 value值唯一,创建对象在整个spring容器中就仅有一个

(2)位置:类上面

(3)语法:

@Component(value = "myUser") 

等同 

<bean id="myUser" class="com.xx.User" />

spring中和@Component功能一致,创建对象的注解还有:

1、@Repository(持久层类的上面):放在dao的实现类上面, 表示创建dao对象,dao对象是能访问数据库的

2、@Service (业务层类的上面):放在service的实现类上面, 创建service对象,service对象是做业务处理,可以有事务等功能的

3、@Controller (控制器的上面):放在控制器(处理器)类的上面,创建控制器对象的, 控制器对象,能够接受用户提交的参数,显示请求的处理结果

以上三个注解的使用语法和@Component一样的,都能创建对象,但是这三个注解还有额外的功能

@Repository ,@Service,@Controller是给项目的对象分层的

@Component有3中用法

(1)使用value属性,指定对象名称 @Component(value = "myUser")

(2)省略value关键字 @Component("myUser")

(3)不指定对象名称,由spring提供默认名称: 类名的首字母小写 @Component

/*
    1、使用value属性,指定对象名称
    @Component(value = "myUser")

    2、省略value关键字
    @Component("myUser")

    3、不指定对象名称,由spring提供默认名称: 类名的首字母小写
    @Component
 */

//使用value属性,指定对象名称
@Component(value = "myUser")
public class User 

测试代码

@Test
    public void testComponent()
        String config = "applicationContext.xml";
        ApplicationContext ac = new ClassPathXmlApplicationContext(config);

        User user = (User) ac.getBean("myUser");

        //不指定对象名称,由spring提供默认名称: 类名的首字母小写 @Component
//        User user = (User) ac.getBean("user");
        System.out.println(user);
    

(二)@Value简单类型赋值

@Value : 简单类型属性赋值

(1)属性: value   

【1】String类型,表示简单类型属性值

【2】也可使用属性配置文件加载,@Value("$属性的key")

在 applicationContext.xml 文件中加载属性配置文件

<!--加载属性配置文件-->
    <context:property-placeholder location="classpath:objectvalue.properties" />

 objectvalue.properties属性配置文件

myName=admin
myAge=18

使用

@Value("$myName")//使用属性配置文件中的数据
    private String userName;
//    @Value("18")
    @Value("$myAge")//使用属性配置文件中的数据
    private int age;

(2)位置: 1、在属性定义上面,无需set方法(建议使用);2、在set方法上面

//使用value属性,指定对象名称
@Component(value = "myUser")
public class User 
    /**
     * @Value : 简单类型属性赋值
     *      属性: value  String类型,表示简单类型属性值
     *            也可使用属性配置文件加载,@Value("$属性的key")
     *      位置: 1、在属性定义上面,无需set方法(建议使用)
     *            2、在set方法上面
     */
//    @Value("admin")
    @Value("$myName")//使用属性配置文件中的数据
    private String userName;
//    @Value("18")
    @Value("$myAge")//使用属性配置文件中的数据
    private int age;

    public User() 
        System.out.println("spring会调用User类的无参构造方法创建对象");
    

//    @Value("admin")
    public void setUserName(String userName) 
        this.userName = userName;
    

//    @Value("18")
    public void setAge(int age) 
        this.age = age;
    

(三)@Autowired

1、byType

@Autowired 默认使用的是 byType自动注入

位置: 1、在属性定义的上面,无需set方法(建议使用) 2、在set方法的上面

//使用value属性,指定对象名称
@Component(value = "myUser")
public class User 

    /**
     * 引用类型 @Autowired(byType)
     * @Autowired :spring框架提供的注解,实现引用类型赋值
     * spring通过注解给引用类型赋值,使用的是自动注入原理,支持 byName,byType
     * @Autowired 默认使用的是 byType自动注入
     *
     * 位置:
     *    1、在属性定义的上面,无需set方法(建议使用)
     *    2、在set方法的上面
     */
    @Autowired
    private Address address;



@Component(value = "myAddress")
public class Address 

2、byName和@Qualifier

@Autowired使用byName方式

在属性上加入 @Autowired;在属性上还要加上@Qualifier(value = "bean的id"):表示使用指定名称的bean完成赋值

注:@Autowired 和 @Qualifier 注解没有先后顺序之分,一般先声明功能,将@Autowired写在上面

//使用value属性,指定对象名称
@Component(value = "myUser")
public class User 

    @Value("admin")
//    @Value("$myName")//使用属性配置文件中的数据
    private String userName;
    @Value("18")
//    @Value("$myAge")//使用属性配置文件中的数据
    private int age;

    /**
     * 引用类型 @Autowired(byType)
     * @Autowired :spring框架提供的注解,实现引用类型赋值
     * spring通过注解给引用类型赋值,使用的是自动注入原理,支持 byName,byType
     * 1、@Autowired 默认使用的是 byType自动注入
     * 位置:
     *  (1)在属性定义的上面,无需set方法(建议使用)
     *  (2)在set方法的上面
     *
     * 2、@Autowired使用byName方式
     *   在属性上加入 @Autowired;
     *   在属性上还要加上@Qualifier(value = "bean的id"):表示使用指定名称的bean完成赋值
     *   注:
     *      @Autowired 和 @Qualifier 注解没有先后顺序之分,一般先声明功能,将@Autowired写在上面
     */
    @Autowired
    @Qualifier("myAddress")
    private Address address;




@Component(value = "myAddress")
public class Address 

3、required属性

required 属性,是一个Boolean类型,默认 true

required = true :表示引用类型赋值失败,程序报错,并终止执行

required = false :表示引用类型赋值失败,程序正常执行,引用类型为 null

一般无特殊请款,建议使用required = true,将问题尽早暴露,防止后续出现空指针异常

required = true :如果spring找不到引用对象bean的id,程序报错如下
       NoSuchBeanDefinitionException: No qualifying bean of type 'com.mycompany.p5referenceautowiredrequired.Address' available:
       expected at least 1 bean which qualifies as autowire candidate. Dependency annotations:
       @org.springframework.beans.factory.annotation.Autowired(required=true),
       @org.springframework.beans.factory.annotation.Qualifier(value=myAddress-1)
//使用value属性,指定对象名称
@Component(value = "myUser")
public class User 

    /**
     * 引用类型 @Autowired(byType)
     *   required 属性,是一个Boolean类型,默认 true
     *      required = true :表示引用类型赋值失败,程序报错,并终止执行
     *      required = false :表示引用类型赋值失败,程序正常执行,引用类型为 null
     *      一般无特殊请款,建议使用required = true,将问题尽早暴露,防止后续出现空指针异常
     *
     * required = true :如果spring找不到引用对象bean的id,程序报错如下
            NoSuchBeanDefinitionException: No qualifying bean of type 'com.mycompany.p5referenceautowiredrequired.Address' available:
            expected at least 1 bean which qualifies as autowire candidate. Dependency annotations:
            @org.springframework.beans.factory.annotation.Autowired(required=true),
            @org.springframework.beans.factory.annotation.Qualifier(value=myAddress-1)
     *
     */
    @Autowired(required = false)
    @Qualifier("myAddress-1")
    private Address address;


@Component(value = "myAddress")
public class Address 

(四)@Resource

@Resource :来自JDK中的注解(包名:javax.annotation.Resource),spring框架提供了对该注解的功能支持,可以使用它给引用类型赋值

@Resource 工作原理:使用自动注入,支持byName, byType

@Resource 注解若不带任何参数,默认是 按名称的方式注入 byName(先使用byName自动注入,如果byName赋值失败,再使用byType)

位置:1、在属性定义的上面,无需set方法(建议使用);2、在set方法上面

1、默认byName,失败则使用byType

//使用value属性,指定对象名称
@Component(value = "myUser")
public class User 

    /**
     * 引用类型 @Resource
     * @Resource :来自JDK中的注解(包名:javax.annotation.Resource),spring框架提供了对该注解的功能支持,可以使用它给引用类型赋值
     *      @Resource 工作原理:使用自动注入,支持byName, byType
     *          @Resource 注解若不带任何参数,默认是 按名称的方式注入 byName(先使用byName自动注入,如果byName赋值失败,再使用byType)
     *      位置:
     *          1、在属性定义的上面,无需set方法(建议使用)
     *          2、在set方法上面
     */
    //默认是byName: 先使用byName自动注入,如果byName赋值失败,再使用byType
    @Resource
    private Address address;

2、使用byName

@Resource 只使用byName方式,需要增加一个属性 name;name的值为 bean 的 id名称

//使用value属性,指定对象名称
@Component(value = "myUser")
public class User 

    /**
     * 引用类型 @Resource
     * @Resource 只使用byName方式,需要增加一个属性 name
     *      name的值为 bean 的 id名称
     */
    //只使用byName
    @Resource(name = "myAddress")
    private Address address;


@Component(value = "myAddress")
public class Address 

以上是关于Java--Spring之IoC控制反转;基于注解的DI的主要内容,如果未能解决你的问题,请参考以下文章

Spring-01 注解实现IOC

谈谈Spring的IoC之注解扫描

Spring_IOC控制反转和DI依赖注入

[转]Java Spring的Ioc控制反转Java反射原理

IOC和AOP注解

IOC 控制反转Android 事件依赖注入 ( 事件三要素 | 修饰注解的注解 | 事件依赖注入步骤 )