• 09_spring注解方式管理bean


    Spring注解方式管理bean(使用)

    欢迎关注公众号“小东方不败”

    0x01_创建项目

    创建一个新的模块,类型选择maven:

    image-20221028180646320

    导入依赖:目前只需要spring-contextjunit两个依赖:

            <dependency>
                <groupId>org.springframeworkgroupId>
                <artifactId>spring-contextartifactId>
                <version>5.3.23version>
            dependency>
    
            <dependency>
                <groupId>junitgroupId>
                <artifactId>junitartifactId>
                <version>4.13.2version>
                <scope>testscope>
            dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    准备实体类和applicationContext.xml

    image-20221028181019059

    其中applicationContext.xml内容如下:

    
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xmlns:p="http://www.springframework.org/schema/p"
            xmlns:c="http://www.springframework.org/schema/c"
            xmlns:util="http://www.springframework.org/schema/util"
           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/util
           http://www.springframework.org/schema/util/spring-util.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context.xsd
    ">
    
    
    
        
    beans>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    里面有一些是之前学习时用到的约束说明,这里就不去掉了。如果想要创建“纯净”的spring的xml配置文件:

    resources目录—》new—>XML Configuration File—>Spring Config

    image-20221028181356691

    0x02_xml注解方式创建对象

    要用xml注解方式创建,必须满足:项目中要有aop依赖,但是我们上面的项目只导入了Context依赖,没关系嘛?

    其实aop依赖已经导入了:

    image-20221028181752436

    下面介绍重点:使用注解方式配置

    @Component 放在类上,用于标记,告诉spring当前类需要由容器实例化bean并放入容器中

    该注解有三个子注解

    • @Controller 用于实例化controller层bean
    • @Service 用于实例化service层bean
    • @Repository 用于实例化持久层bean

    当不确定是哪一层,就用@Component

    这几个注解互相混用其实也可以,但是不推荐

    package com.bones.bean;
    
    import org.springframework.stereotype.Component;
    
    @Component
    //如果要指定id:  @Component("user1")
    //如果不指定id,那就是类名首字母小写
    public class Person {
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    package com.bones.bean;
    
    import org.springframework.stereotype.Component;
    
    @Component
    public class User {
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在配置文件applicationContext.xml中配置包扫描

    
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xmlns:p="http://www.springframework.org/schema/p"
            xmlns:c="http://www.springframework.org/schema/c"
            xmlns:util="http://www.springframework.org/schema/util"
           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/util
           http://www.springframework.org/schema/util/spring-util.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context.xsd
    ">
    
    
        <context:component-scan base-package="com.bones">
    
        context:component-scan>
    
    
        
    beans>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    测试方法:

    package com.bones.test01;
    
    import com.bones.bean.User;
    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class Test1_Component {
        @Test
        public void TestComponent(){
            ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
            User user = applicationContext.getBean("user", User.class);
            System.out.println(user);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    成功获取到对象:

    image-20221028193120555

    0x03_组件扫描配置注解识别

    只扫描指定注解的包:

    applicationContext.xml

    <context:component-scan base-package="com.bones" use-default-filters="false">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    context:component-scan>
    
    • 1
    • 2
    • 3

    注意:use-default-filters默认值是true

    排除某一些注解扫描:

    <context:component-scan base-package="com.bones" use-default-filters="true">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    context:component-scan>
    
    • 1
    • 2
    • 3

    如果没法扫描,但是强制从容器中获取对象的话,会报下面的错:

    org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'person' available
    
    • 1

    0x04_注解方式依赖注入DI

    注解@Autowired和@Qualifier

    准备代码:

    image-20221028201306869

    UserDao

    package com.bones.dao;
    
    import org.springframework.stereotype.Repository;
    
    @Repository
    public interface UserDao {
        int deleteUser();
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    UserDaoImplA

    package com.bones.dao.impl;
    
    import com.bones.dao.UserDao;
    import org.springframework.stereotype.Repository;
    
    @Repository
    public class UserDaoImplA implements UserDao {
        @Override
        public int deleteUser() {
            System.out.println("UserDaoImplA");
            return 0;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    UserDaoImplB

    package com.bones.dao.impl;
    
    import com.bones.dao.UserDao;
    import org.springframework.stereotype.Repository;
    
    //@Repository
    public class UserDaoImplB implements UserDao {
        @Override
        public int deleteUser() {
            System.out.println("UserDaoImplB");
            return 0;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    UserService

    package com.bones.service;
    
    import org.springframework.stereotype.Service;
    
    @Service
    public interface UserService {
        int deleteUser();
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    UserServiceImpl

    package com.bones.service.impl;
    
    import com.bones.dao.UserDao;
    import com.bones.service.UserService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    @Service
    public class UserServiceImpl implements UserService {
    
        @Autowired
        private UserDao userDao;
        @Override
        public int deleteUser() {
            userDao.deleteUser();
            System.out.println("UserServiceImpl");
            return 0;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    applicationContext.xml

    
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xmlns:p="http://www.springframework.org/schema/p"
            xmlns:c="http://www.springframework.org/schema/c"
            xmlns:util="http://www.springframework.org/schema/util"
           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/util
           http://www.springframework.org/schema/util/spring-util.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context.xsd
    ">
    
        <context:component-scan base-package="com.bones" />
    
    
        
    beans>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    测试方法:

    package com.bones.test01;
    
    import com.bones.bean.User;
    import com.bones.service.UserService;
    import com.bones.service.impl.UserServiceImpl;
    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class Test2_Autowired {
        @Test
        public void TestComponent(){
            ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
            UserService userService = applicationContext.getBean("userServiceImpl", UserServiceImpl.class);
            userService.deleteUser();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    image-20221028202812913

    但是注意:

    如果UserDaoImplAUserDaoImplB如果都加上@Repository注解的话,就会报错:

    No qualifying bean of type 'com.bones.dao.UserDao' available: expected single matching bean but found 2: userDaoImplA,userDaoImplB
    
    • 1

    这时候,需要指定到底UserServiceImpl中的属性UserDao到底是实例化哪一个实现类,需要加注解:

    @Qualifier(value="userDaoImplA")
    
    • 1

    可以简写为:

    @Qualifier("userDaoImplA")
    
    • 1

    总结@Autowired和@Qualifier

    @Autowired

    根据类型到容器中去寻找对应的对象,找到后给当前属性赋值

    不需要依赖 set方法

    属性类型可以是接口,会自动匹配对应的实现类对象

    @Qualifier

    根据属性名称注入依赖

    image-20221028203452271

    注解@Resource

    注意:这个注解是JDK的注解,属于javax.annotation.Resource

    在有冲突的时候,也需要加上具体实例化的类:

    @Resource(name = "userDaoImplB")
    
    • 1

    不可以简写。

    用哪一个注解呢?

    前面体验了一下,实际强况下,很少会碰到这样的报错:

    No qualifying bean of type 'com.bones.dao.UserDao' available: expected single matching bean but found 2: userDaoImplA,userDaoImplB
    
    • 1

    那么一般只需要写@Autowired或者@Resource,就可以了。那么到底写哪一个呢?

    下面来详细说说:

    @Autowired的源码

    源码在org.springframework.beans.factory.annotation包下

    
    @Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Autowired {
    
    	boolean required() default true;
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    从源码中可以看出,是加在这些元素上:

    ElementType.CONSTRUCTOR, 
    ElementType.METHOD, 
    ElementType.PARAMETER, 
    ElementType.FIELD, 
    ElementType.ANNOTATION_TYPE
    
    • 1
    • 2
    • 3
    • 4
    • 5

    分别是:

    构造方法

    方法

    方法的参数

    成员变量

    注解

    总结:

    1、@Autowired是Spring自带的注解,通过AutowiredAnnotationBeanPostProcessor 类实现的依赖注入

    2、@Autowired可以作用在CONSTRUCTORMETHODPARAMETERFIELDANNOTATION_TYPE

    3、@Autowired默认是根据类型(byType )进行自动装配的

    4、如果有多个类型一样的Bean候选者,需要指定按照名称(byName )进行装配,则需要配合@Qualifier

    指定名称后,如果Spring IOC容器中没有对应的组件bean抛出NoSuchBeanDefinitionException。也可以将@Autowiredrequired配置为false(其实一般配置为true也不影响运行,比如在Mybatis-plus中),如果配置为false之后,当没有找到相应bean的时候,系统不会抛异常

    @Resource的源码

    javax.annotation包下:

    
    package javax.annotation;
    
    import java.lang.annotation.*;
    import static java.lang.annotation.ElementType.*;
    import static java.lang.annotation.RetentionPolicy.*;
    
    @Target({TYPE, FIELD, METHOD})
    @Retention(RUNTIME)
    public @interface Resource {
        String name() default "";
    
        String lookup() default "";
    
        Class<?> type() default java.lang.Object.class;
    
        enum AuthenticationType {
                CONTAINER,
                APPLICATION
        }
    
        AuthenticationType authenticationType() default AuthenticationType.CONTAINER;
    
        boolean shareable() default true;
    
        String mappedName() default "";
    
        String description() default "";
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    源码中说明了使用的区域:

    TYPE, FIELD, METHOD
    
    • 1

    分别是:

    接口、类、枚举、注解
    成员变量
    方法
    
    • 1
    • 2
    • 3

    总结:

    1、@ResourceJSR250规范的实现,在javax.annotation包下

    2、@Resource可以作用TYPEFIELDMETHOD

    3、@Resource是默认根据属性名称进行自动装配的,如果有多个类型一样的Bean候选者,则可以通过name进行指定进行注入,name 的作用类似 @Qualifier

    @Resource(name = "userDaoImplB")
    
    • 1
    用哪个?

    1、@Autowired是Spring自带的,@Resource是JSR250规范实现的(JDK自带的)

    2、@Autowired如果需要按照名称匹配需要和@Qualifier一起使用,@Resource则通过name进行指定

    Spring官方建议用@Autowired(啥原因都懂),其实这两个一般不会有很大区别,随便用吧,但是估计@Autowired用的会多一点。

    (更深入的以后再分析)

    更深的可以参考链接:https://blog.csdn.net/qq_35634181/article/details/104802906

    注解@Value

        @Value("爱因斯坦")
        private String uname;
        @Value("19")
        private Integer uage;
    
    • 1
    • 2
    • 3
    • 4

    如果想要从properties配置文件中读取,也可以:

        @Value("${uname}")
        private String uname;
        @Value("${uage}")
        private Integer uage;
    
    • 1
    • 2
    • 3
    • 4

    其中配置文件内容:

    uname=爱因斯坦
    uage=18
    
    • 1
    • 2

    同时在容器的配置文件中得读取properties配置文件:

    <context:property-placeholder location="classpath:read.properties"/>
    
    • 1

    注解@ComponentScan(了解)

    如果不希望有配置文件applicationContext.xml(说实话,这么配置也怪麻烦的),可以创建一个config/SpringConfig类,这个类专门用于扫描。

    image-20221028215512575
    package com.bones.config;
    
    import org.springframework.context.annotation.ComponentScan;
    
    @ComponentScan(basePackages = "com.bones")
    public class SpringConfig {
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    测试:

    package com.bones.test01;
    
    import com.bones.config.SpringConfig;
    import com.bones.service.UserService;
    import com.bones.service.impl.UserServiceImpl;
    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class Test2_Config_ComponentScan {
        @Test
        public void TestComponent(){
            ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
            UserService userService = applicationContext.getBean("userServiceImpl", UserServiceImpl.class);
            userService.deleteUser();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    此时容器的创建,要调用的AnnotationConfigApplicationContext这个类,传入有扫描信息的类的字节码文件SpringConfig.class

    但是这种方式也有弊端,实际不怎么用,有一定局限性。

    注解@PropertySource

    想要在@ComponentScan配置的基础上,读取properties文件的话,需要加注解:

    @PropertySource("classpath:XXX.properties")
    
    • 1
  • 相关阅读:
    Hololens开发之实时音视频通讯--01部署开发环境
    oracle重新安装集群软件后挂盘启动数据库
    串口接收不定长数据的几种方法
    基于SSM的二手商品交易系统
    测试流程||接口测试
    技术学习方法分享
    覆盖率检查工具:JaCoCo 食用指南
    【YOLOv5】环境搭建:Win11 + mx450
    在OracleLinux8.6的Zabbix6.0中监控Oracle11gR2
    VsCode搭建Java开发环境 vscode搭建java开发环境 vscode springboot 搭建springboot
  • 原文地址:https://blog.csdn.net/qq_51550750/article/details/127649346