文章目录

  • Spring注解方式管理bean(使用)
  • 0x01_创建项目
  • 0x02_xml注解方式创建对象
  • 0x03_组件扫描配置注解识别
  • 0x04_注解方式依赖注入DI
  • 注解@Autowired和@Qualifier
  • 总结@Autowired和@Qualifier
  • 注解@Resource
  • 用哪一个注解呢?
  • @Autowired的源码
  • @Resource的源码
  • 用哪个?
  • 注解@Value
  • 注解@ComponentScan(了解)
  • 注解@PropertySource


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

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

0x01_创建项目

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

spring 设置默认Bean spring默认管理bean的方式_注解方式

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

<dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.23</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>

准备实体类和applicationContext.xml


spring 设置默认Bean spring默认管理bean的方式_spring_02

其中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: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>

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

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

spring 设置默认Bean spring默认管理bean的方式_注解方式_03

0x02_xml注解方式创建对象

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

其实aop依赖已经导入了:

spring 设置默认Bean spring默认管理bean的方式_java_04

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

@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 {
}
package com.bones.bean;

import org.springframework.stereotype.Component;

@Component
public class User {
}

在配置文件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: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
">

<!--    如果有多个包需要扫描:方法1:指定扫描的包是上一层包com.bones
                          方法2:指定要扫描的包,用逗号隔开
                            比如:com.bones.bean,com.bones.service,com.bones.controller

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

    </context:component-scan>


    
</beans>

测试方法:

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);
    }
}

成功获取到对象:


spring 设置默认Bean spring默认管理bean的方式_java_05

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>

注意: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>

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

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

0x04_注解方式依赖注入DI

注解@Autowired和@Qualifier

准备代码:


spring 设置默认Bean spring默认管理bean的方式_注解方式_06

UserDao

package com.bones.dao;

import org.springframework.stereotype.Repository;

@Repository
public interface UserDao {
    int deleteUser();
}

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;
    }
}

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;
    }
}

UserService

package com.bones.service;

import org.springframework.stereotype.Service;

@Service
public interface UserService {
    int deleteUser();
}

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;
    }
}

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: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>

测试方法:

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();
    }
}


spring 设置默认Bean spring默认管理bean的方式_java_07

但是注意:

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

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

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

@Qualifier(value="userDaoImplA")

可以简写为:

@Qualifier("userDaoImplA")

总结@Autowired和@Qualifier

@Autowired

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

不需要依赖 set方法

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

@Qualifier

根据属性名称注入依赖

spring 设置默认Bean spring默认管理bean的方式_spring_08

注解@Resource

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

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

@Resource(name = "userDaoImplB")

不可以简写。

用哪一个注解呢?

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

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

那么一般只需要写@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;

}

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

ElementType.CONSTRUCTOR, 
ElementType.METHOD, 
ElementType.PARAMETER, 
ElementType.FIELD, 
ElementType.ANNOTATION_TYPE

分别是:

构造方法

方法

方法的参数

成员变量

注解

总结:

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 "";
}

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

TYPE, FIELD, METHOD

分别是:

接口、类、枚举、注解
成员变量
方法

总结:

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

2、@Resource可以作用TYPEFIELDMETHOD

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

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

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

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

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

(更深入的以后再分析)

更深的可以参考链接:

注解@Value

@Value("爱因斯坦")
    private String uname;
    @Value("19")
    private Integer uage;

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

@Value("${uname}")
    private String uname;
    @Value("${uage}")
    private Integer uage;

其中配置文件内容:

uname=爱因斯坦
uage=18

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

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

注解@ComponentScan(了解)

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


spring 设置默认Bean spring默认管理bean的方式_xml_09

package com.bones.config;

import org.springframework.context.annotation.ComponentScan;

@ComponentScan(basePackages = "com.bones")
public class SpringConfig {
}

测试:

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();
    }
}

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

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

注解@PropertySource

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

@PropertySource("classpath:XXX.properties")