Spring注解驱动-属性赋值和自动装配部分

本文是尚硅谷雷丰阳老师《尚硅谷Spring注解驱动教程》课程的笔记

系列文章目录
1.Spring注解驱动-组件注册部分
2.Spring注解驱动-属性赋值和自动装配部分



Spring注解驱动开发

属性赋值-@Value

使用@Value赋值
1.基本数值
2.可以写SpEL; #{}
3.可以写${};取出配置文件中的值(在运行环境变量里面的值),取出配置文件【properties】中的值(在运行环境变量里面的值)


新建com/atguigu/config/MainConfigOfPropertyValues.java

@Configuration
public class MainConfigOfPropertyValues {

    @Bean
    public Person person(){
        return new Person();
    }
}

新建测试类com/atguigu/test/IOCTest_PropertyValue.java

public class IOCTest_PropertyValue {
    // 1.创建ioc容器
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfigOfPropertyValues.class);


    @Test
    @Test
    public void test01() {
        System.out.println("容器创建完成...");
        printBeans(context);
        System.out.println("===========");
        Person person = (Person) context.getBean("person");
        System.out.println(person);
        // 关闭容器
        context.close();
    }

    // 抽取公共方法输出容器里面所有组件
    private void printBeans(AnnotationConfigApplicationContext context){
        String[] beanDefinitionNames = context.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            System.out.println(beanDefinitionName);
        }
    }
}

结果

mainConfigOfPropertyValues
person
===========
Person{name='null', age=null}
113, 2023 4:07:46 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext doClose
信息: Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@7bc1a03d: startup date [Fri Jan 13 16:07:45 CST 2023]; root of context hierarchy

Process finished with exit code 0

我们发现,该person类并没有配置属性,如果要配置属性的话

传统xml配置文件

    <bean id = "person" class="com.atguigu.bean.Person" scope="prototype">
        <property name="age" value="18"></property>
        <property name="name" value="zhansan"></property>
    </bean>

注解方式

public class Person {

    @Value("张三")
    private String name;
    @Value( "#{20-2}")
    private Integer age;
}

结果

可发现对应的值都赋值上去了

mainConfigOfPropertyValues
person
===========
Person{name='张三', age=18}

属性赋值-@PropertySource加载外部配置文件

修改Person文件

public class Person {

    @Value("张三")
    private String name;
    @Value( "#{20-2}")
    private Integer age;
	
    private String nickName;

	...
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", nickName='" + nickName + '\'' +
                '}';
    }
	...
}

新建配置文件src/main/resources/person.properties

person.nickName=小张三

传统xml做法

    <context:property-placeholder location="classpath:person.properties" />
    <context:component-scan base-package="com.atguigu" use-default-filters="false"></context:component-scan>
    <bean id = "person" class="com.atguigu.bean.Person" scope="prototype">
        <property name="age" value="18"></property>
        <property name="name" value="zhansan"></property>
        <property name="nickName" value="${person.nickName}"></property>
    </bean>

注解方法

修改MainConfigOfPropertyValues (com/atguigu/config/MainConfigOfPropertyValues.java)
使用@PropertySource读取外部配置文件中的K/V保存到运行的环境变量中;加载完外部的配置文件以后使用${}取出配置文件的值

@PropertySource(value = {"classpath:/person.properties"})
@Configuration
public class MainConfigOfPropertyValues {

    @Bean
    public Person person(){
        return new Person();
    }
}

查看@PropertySource()注解源码

指定一个String数组,用于指定配置文件的路径classpath
由类路径开始或文件路径开始

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(PropertySources.class)
public @interface PropertySource {
 name() default "";

	String[] value();

	boolean ignoreResourceNotFound() default false;

	String encoding() default "";

	Class<? extends PropertySourceFactory> factory() default PropertySourceFactory.class;

}

修改person

    @Value("${person.nickName}")
    private String nickName;

结果发现乱码,修改@PropertySource

@PropertySource(value = {"classpath:/person.properties"}, encoding = "utf-8")

结果

org.springframework.context.event.internalEventListenerFactory
mainConfigOfPropertyValues
person
===========
Person{name='张三', age=18, nickName='小张三'}

小测试

查看上下文中的配置

        ConfigurableEnvironment environment = context.getEnvironment();
        String property = environment.getProperty("person.nickName");
        System.out.println(property);

结果

mainConfigOfPropertyValues
person
===========
Person{name='张三', age=18, nickName='小张三'}
小张三

自动装配-@Autowired&@Qualifier&@Primary

自动装配:Spring依赖注入(DI),完成对IOC容器中各个组件的依赖关系赋值;
1)@Autowired,自动注入

  • 默认优先按照类型去容器中找对应的组件:applicationContext.gerBean(BookDao.class)
  • 如果找到多个相同类型的组件,再将属性的名称作为组件的id去容器中查找
  • @Qualifier("bookDao"):使用@Qualifier指定需要装配的组件的id,(优先级更高)而不是使用属性名
  • 自动装配默认一定要将属性赋值好,没有找到就会报错;
  • 可以使用@Autowired(required = false),实现工厂中有就装配,没有就不进行装配
  • @Primary:让Spring进行自动装配的时候,默认使用首选的bean,也可以继续使用@Qualifier("bookDao")指定需要装配的bean的名字
// 名字默认是首字母小写
@Service
public class BookService {
  @Autowired
  private BookDao bookDao;
}

完善BookServiceBookController

@Service
public class BookService {
    @Autowired
    private BookDao bookDao;
    
    public void print() {
        System.out.println(bookDao);
    }
}

@Controller
public class BookController {
    @Autowired
    private BookService bookService;
}

创建MainConfigOfAutowired (com/atguigu/config/MainConfigOfAutowired.java)

@Configuration
@ComponentScan({"com.atguigu.service", "com.atguigu.dao", "com.atguigu.controller"})
public class MainConfigOfAutowired {
}

创建IOCTest_Autowiredcom/atguigu/test/IOCTest_Autowired.java

    @Test
    public void test01() {
        // 1.创建ioc容器
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfigOfAutowired.class);
        System.out.println("容器创建完成...");
        // 关闭容器
        System.out.println(context.getBean(BookService.class));
        context.close();
    }

结果,可以发现BookService

容器创建完成...
com.atguigu.service.BookService@5456afaa

测试一下是否是容器中的DAO

    public void test01() {
        // 1.创建ioc容器
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfigOfAutowired.class);
        System.out.println("容器创建完成...");
        // 关闭容器
        System.out.println(context.getBean(BookService.class));
        System.out.println(context.getBean(BookDao.class));
        context.close();
    }

结果可以看出,‘BookService’中注入的BookDao确实是容器中的Dao

容器创建完成...
BookService{bookDao=com.atguigu.dao.BookDao@5456afaa}
com.atguigu.dao.BookDao@5456afaa

通过@Bean向容器注入另一个BookDao

@Configuration
@ComponentScan({"com.atguigu.service", "com.atguigu.dao", "com.atguigu.controller"})
public class MainConfigOfAutowired {
    @Bean("bookDao2")
    public BookDao bookDao(){
        BookDao bookDao = new BookDao();
        bookDao.setLable("2");
        return bookDao;
    }
}

改造BookDao

// 名字默认是类首名首字母小写
@Repository
public class BookDao {

    private String lable = "1";


    public String getLable() {
        return lable;
    }

    public void setLable(String lable) {
        this.lable = lable;
    }
    
    @Override
    public String toString() {
        return "BookDao{" +
                "lable='" + lable + '\'' +
                '}';
    }
}

结果报错

容器创建完成...
BookService{bookDao=BookDao{lable='1'}}

...
	at com.atguigu.test.IOCTest_Autowired.test01(IOCTest_Autowired.java:23)

解决方法一:修改属性名称,改用一样的bookDao2
修改BookService

@Service
public class BookService {

    @Autowired
    private BookDao bookDao2;

    public void print() {
        System.out.println(bookDao2);
    }

    @Override
    public String toString() {
        return "BookService{" +
                "bookDao=" + bookDao2 +
                '}';
    }
}

结果发现装载的是BookDao2

容器创建完成...
BookService{bookDao=BookDao{lable='2'}}

解决方法二:使用@Qualifier()
修改BookService

    @Qualifier("bookDao")
    @Autowired
    private BookDao bookDao2;

    public void print() {
        System.out.println(bookDao2);
    }

结果

容器创建完成...
BookService{bookDao=BookDao{lable='1'}}

需要分析,工厂中有就装配,没有就不进行装配

查看@Autowired注解

public @interface Autowired {

	/**
	 * Declares whether the annotated dependency is required.
	 * <p>Defaults to {@code true}.
	 */
	boolean required() default true;
}

通过设置required即可满足该需求

    @Qualifier("bookDao")
    @Autowired(required = false)
    private BookDao bookDao2;

结果,没有的情况下该属性为null

容器创建完成...
BookService{bookDao=null}

使用@Primary

作用是工厂中有多个bean,但是按设置的首选进行装配

  • 步骤一:设置@Primary
    @Primary
    @Bean("bookDao2")
    public BookDao bookDao(){
        BookDao bookDao = new BookDao();
        bookDao.setLable("2");
        return bookDao;
    }
  • 步骤二:去除@Qualifier
@Service
public class BookService {

//    @Qualifier("bookDao")
    @Autowired(required = false)
    private BookDao bookDao;

    public void print() {
        System.out.println(bookDao);
    }

    @Override
    public String toString() {
        return "BookService{" +
                "bookDao=" + bookDao +
                '}';
    }
}
  • 测试结果
容器创建完成...
BookService{bookDao=BookDao{lable='2'}}
BookDao{lable='2'}

注意,@Qualifier优先级比@Primary高,在没有指定@Qualifier时,用@Primary指定的,用了@Qualifier则用@Qualifier指定的

自动装配-@Resource&@Inject

Spring支持使用@Resource(JSR250)和@Inject(JSR330)【java规范的注解】

  • @Resource:可以和@Autowired一样实现自动装配功能,默认是按照组件名称进行装配的;没有能支持@Primary功能以及没有支持@Autowired(required = false)
  • @Inject:需要导入javax.inject的包,和Autowired的功能一样,没有required = false的功能;
    @Autowired:Spring定义的;而@Resource@Inject都是Java规范
    AutowiredAnnotationBeanPostProcessor:解析完成自动装配功能;
@Resource
  • 改造BookService
    @Resource
    private BookDao bookDao;
  • 结果
容器创建完成...
BookService{bookDao=BookDao{lable='1'}}

可以看出装配的是label1的bean,这是因为默认是按照组件名称进行装配的


  • 使用name指定装配的bean
    @Resource(name = "bookDao2")
    private BookDao bookDao;
  • 结果
容器创建完成...
BookService{bookDao=BookDao{lable='2'}}
@Inject

使用这个注解需要先在maven中导入相应的依赖

        <dependency>
            <groupId>javax.inject</groupId>
            <artifactId>javax.inject</artifactId>
            <version>1</version>
        </dependency>
  • 使用@Inject
    @Inject
    private BookDao bookDao;
  • 结果

首先,该注解和@Resource的功能一样;同时还具有@Primary特性,默认装配;自动装配的特性也是可以调用的。

120, 2023 3:52:35 下午 org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor <init>
信息: JSR-330 'javax.inject.Inject' annotation found and supported for autowiring
容器创建完成...
BookService{bookDao=BookDao{lable='2'}}

对比@Autowired@Inject,可以看出@Autowired有一个属性而@Inject没有

public @interface Autowired {
	boolean required() default true;
}

public @interface Inject {
}

分析:为什么这些注解拥有自动注入的功能
我们可以看到上面的结果信息,看到AutowiredAnnotationBeanPostProcessor,解析完成自动装配功能


自动装配-方法、构造器位置的自动装配

回顾:@Component默认加在ioc容器中的组件,容器启动会调用无参构造器创建对象,再进行初始化赋值操作。这个无参构造器是java默认给出的,但是当指定了有参构造器后,java就不会给出默认的无参构造器。
@Autowired:构造器,参数,方法,属性;都是从容器中获取参数组件的值

  • 【标注在方法位置】:@Bean+方法参数,参数从容器中获取。因此默认不写@Autowired效果是一样的,都能自动装配
  • 【标在构造器上】:在单例模式的情况下,如果组件只有一个有参构造器,这个有参构造器参数的Autowired可以省略,参数位置的组件还是可以自动从容器中获取
  • 【放在参数位置】
  • 新建Boss类
src/main/java/com/atguigu/bean/Boss.java
@Component
public class Boss {
    
    private Car car;

    @Override
    public String toString() {
        return "Boss{" +
                "car=" + car +
                '}';
    }

    public Car getCar() {
        return car;
    }

    public void setCar(Car car) {
        this.car = car;
    }
}
  • 修改MainConfigOfAutowired,对其扫描路径加上对应的类
@Configuration
@ComponentScan({"com.atguigu.service", "com.atguigu.dao", "com.atguigu.controller", "com.atguigu.bean"})
public class MainConfigOfAutowired {

    @Primary
    @Bean("bookDao2")
    public BookDao bookDao(){
        BookDao bookDao = new BookDao();
        bookDao.setLable("2");
        return bookDao;
    }
}
  • Car加上@Component
@Component
public class Car {

    public Car() {
        System.out.println("car constructor...");
    }

    public void init() {
        System.out.println("car...init...");
    }

    public void destroy() { System.out.println("car...destroy..."); }
}

@Autowired标在方法上

标注在方法上,Spring容器创建当前对象,就会调用方法,完成赋值。方法使用的参数,自定义类型的值从ioc容器中获取

    @Autowired
    public void setCar(Car car) {
        this.car = car;
    }

测试查看容器中的Boss的Car,可看出是同一个car

容器创建完成...
com.atguigu.bean.Car@5456afaa
Boss{car=com.atguigu.bean.Car@5456afaa}

@Autowired标在构造器上、参数位置上
  • 修改Boss添加上有参构造器
    方法一:
    @Autowired
    public Boss(Car car) {
        this.car = car;
        System.out.println("Boss...有参构造器");
    }

方法二:

    public Boss(@Autowired Car car) {
        this.car = car;
        System.out.println("Boss...有参构造器");
    }

方法三:在方法二的基础上省略@Autowired

    public Boss( Car car) {
        this.car = car;
        System.out.println("Boss...有参构造器");
    }

构造器要用的组件,都是从容器中获取


方法三例子

  • 修改Color
public class Color {

    private Car car;

    public Car getCar() {
        return car;
    }

    public void setCar(Car car) {
        this.car = car;
    }
}
  • 修改MainConfigOfAutowired
public class MainConfigOfAutowired {

    @Primary
    @Bean("bookDao2")
    public BookDao bookDao(){
        BookDao bookDao = new BookDao();
        bookDao.setLable("2");
        return bookDao;
    }

    @Bean
    public Color color(@Autowired Car car){
        Color color = new Color();
        color.setCar(car);
        return color;
    }
}

注意,这里的public Color color(Car car)可以加@Autowired亦可不加
@Bean标注的方法创建对象的时候,方法参数的值从容器中获取

  • 结果,可看出确实装载进去了
car constructor...
postProcessBeforeInitialization...car=>com.atguigu.bean.Car@4c012563
postProcessAfterInitialization...car=>com.atguigu.bean.Car@4c012563
Boss...有参构造器
postProcessBeforeInitialization...boss=>Boss{car=com.atguigu.bean.Car@4c012563}
postProcessAfterInitialization...boss=>Boss{car=com.atguigu.bean.Car@4c012563}
Boss{car=com.atguigu.bean.Car@4c012563}

自动装配-Aware注入Spring底层组件&原理

自定义组件想要使用Spring容器底层的一些组件(ApplicationContextBeanFactory,…)
自定义组件实现xxxAware即可;在创建对象的时候,会调用接口规定的方法注入相关组件;Aware
把Spring底层一些组件注入到自定义的Bean中;
xxxAware:功能使用xxxProcessor;比如ApplicationContextAware=====》ApplicationContextAwareProcessor
实际上这个功能是由xxxAwareProcessor实现的, 而它则是实现BeanProcessor接口,后置处理器机制

例子:

  • 修改red
@Component
public class Red implements ApplicationContextAware, BeanNameAware, EmbeddedValueResolverAware {
    
    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("传入的ioc:" + applicationContext);
        this.applicationContext = applicationContext;
    }

    @Override
    public void setBeanName(String name) {
        System.out.println("当前bean的名字:" + name);
    }

    // 值解析器
    @Override
    public void setEmbeddedValueResolver(StringValueResolver resolver) {
        String s = resolver.resolveStringValue("你好${os.name} 我是#{20*18}");
        System.out.println("解析的字符串是:" + s);
    }
}

结果

当前bean的名字:red
解析的字符串是:你好Windows 10 我是360
传入的ioc:org.springframework.context.annotation.AnnotationConfigApplicationContext@7ff2a664: startup date [Fri Jan 20 16:54:59 CST 2023]; root of context hierarchy
postProcessBeforeInitialization...red=>com.atguigu.bean.Red@42530531
postProcessAfterInitialization...red=>com.atguigu.bean.Red@42530531
postProcessBeforeInitialization...color=>com.atguigu.bean.Color@35645047
postProcessAfterInitialization...color=>com.atguigu.bean.Color@35645047
容器创建完成...

Debug相应的源码
在这里插入图片描述

  • 首先查看后置处理器ApplicationContextAwareProcessor

作为后置处理器,实际上,postProcessBeforeInitializationbeanRed,然后判断是Red是否实现了EnvironmentAware ApplicationContextAware等接口。然后检查是否能调用方法AccessController.doPrivileged,接着调用invokeAwareInterfaces(bean)

class ApplicationContextAwareProcessor implements BeanPostProcessor {
...

	@Override
	public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
		AccessControlContext acc = null;

		if (System.getSecurityManager() != null &&
				(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
						bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
						bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)) {
			acc = this.applicationContext.getBeanFactory().getAccessControlContext();
		}

		if (acc != null) {
			AccessController.doPrivileged(new PrivilegedAction<Object>() {
				@Override
				public Object run() {
					invokeAwareInterfaces(bean);
					return null;
				}
			}, acc);
		}
		else {
			invokeAwareInterfaces(bean);
		}

		return bean;
	}

}

...

接着查看invokeAwareInterfaces(bean),此时的bean仍然是Red对象。然后判断Red是否是Aware接口旗下的,一个个判断,若是实现了EnvironmentAware接口则将ioc容器中获取对应的对象并调用set方法赋值进去。
同时这个ApplicationContextAwareProcessor已经把众多的xxxAware接口实现了

实现接口
实现方法
实现接口
实现方法
实现接口
实现方法
Red
ApplicationContextAware
setApplicationContext
BeanNameAware
setBeanName
EmbeddedValueResolverAware
setEmbeddedValueResolver
	private void invokeAwareInterfaces(Object bean) {
		if (bean instanceof Aware) {
			if (bean instanceof EnvironmentAware) {
				((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
			}
			if (bean instanceof EmbeddedValueResolverAware) {
				((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
			}
			if (bean instanceof ResourceLoaderAware) {
				((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
			}
			if (bean instanceof ApplicationEventPublisherAware) {
				((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
			}
			if (bean instanceof MessageSourceAware) {
				((MessageSourceAware) bean).setMessageSource(this.applicationContext);
			}
			if (bean instanceof ApplicationContextAware) {
				((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
			}
		}
	}

自动装配-@Profile环境搭建

Profile:Spring为我们提供的可以根据当前环境,动态激活和切换一系列组件的功能
开发环境、测试环境、生成环境
数据源:(/A)(/A)(/A);
@Profile

  • 使用Mavan引用数据源
<!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->
<dependency>
    <groupId>com.mchange</groupId>
    <artifactId>c3p0</artifactId>
    <version>0.9.2</version>
</dependency>

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.44</version>
</dependency>
  • 新建配置类MainConfigOfProfile

代码分析:
首先设置了扫描的配置路径@PropertySource("classpath:/dbconfig.properties")
其次设置三种不同的方法用于获取配置文件中的值

  • 方法一:直接使用@Value("${db.user}")
  • 方法二:在方法参数中调用获取 public DataSource dataSourceTest(@Value("${db.password}") String pwd) throws PropertyVetoException
  • 方法三:使用值解析器。先实现EmbeddedValueResolverAware接口。重写setEmbeddedValueResolver方法解析对应的参数this.resolveStringValue = valueResolver.resolveStringValue("${db.driverClass}");
//src/main/java/com/atguigu/config/MainConfigOfProfile.java
@PropertySource("classpath:/dbconfig.properties")
public class MainConfigOfProfile implements EmbeddedValueResolverAware {
    
    @Value("${db.user}")
    private String user;
    
    private StringValueResolver valueResolver;
    
    private String resolveStringValue;

    @Override
    public void setEmbeddedValueResolver(StringValueResolver resolver) {
        this.valueResolver = resolver;
        this.resolveStringValue = valueResolver.resolveStringValue("${db.driverClass}");
    }

    @Bean("testDataSource")
    public DataSource dataSourceTest(@Value("${db.password}") String pwd) throws PropertyVetoException {
        ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
        comboPooledDataSource.setUser(user);
        comboPooledDataSource.setPassword(pwd);
        comboPooledDataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
        comboPooledDataSource.setDriverClass(resolveStringValue);
        return null;
    }

    @Bean("testDevSource")
    public DataSource dataSourceDev(@Value("${db.password}") String pwd) throws PropertyVetoException {
        ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
        comboPooledDataSource.setUser(user);
        comboPooledDataSource.setPassword(pwd);
        comboPooledDataSource.setJdbcUrl("jdbc:mysql://localhost:3306/yygh_hosp");
        comboPooledDataSource.setDriverClass(resolveStringValue);
        return null;
    }

    @Bean("testProdSource")
    public DataSource dataSourceProd(@Value("${db.password}") String pwd) throws PropertyVetoException {
        ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
        comboPooledDataSource.setUser(user);
        comboPooledDataSource.setPassword(pwd);
        comboPooledDataSource.setJdbcUrl("jdbc:mysql://localhost:3306/school");
        comboPooledDataSource.setDriverClass(resolveStringValue);
        return null;
    }
}

  • 创建配置文件
# src/main/resources/dbconfig.properties
db.user = root
db.password = 123456
db.driverClass = com.mysql.jdbc.Driver
  • 新建测试方法
// src/test/java/com/atguigu/test/IOCTest_Profile.java
public class IOCTest_Profile {
    @Test
    public void test01() {
        // 1.创建ioc容器
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfigOfProfile.class);
        System.out.println("容器创建完成...");

        String[] beanNamesForType = context.getBeanNamesForType(DataSource.class);
        for (String s : beanNamesForType) {
            System.out.println(s);
        }
        context.close();
    }
}
  • 结果
容器创建完成...
testDataSource
testDevSource
testProdSource

自动装配-@Profile根据环境注册bean

@Profile:指定组件在哪个环境的情况下次才能被注册到容器中,不指定,任何环境下都能注册这个组件

  • 加入环境标识的bean,只有这个环境被激活时才能注册到容器中,默认是default环境
  • 写在配置类上,只有是指定的环境的时候,整个配置类里面的所有配置才能开始生效
  • 没有标注环境标识的bean下,任何环境下都是加载的。
  • 查看@Profile注解,该注解中有唯一的一个属性,这个属性用于标识环境
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(ProfileCondition.class)
public @interface Profile {

	/**
	 * The set of profiles for which the annotated component should be registered.
	 */
	String[] value();

}
  • 修改MainConfigOfProfile
    @Profile("test")
    @Bean("testDataSource")
    public DataSource dataSourceTest(@Value("${db.password}") String pwd) throws PropertyVetoException {
...
}

    @Profile("dev")
    @Bean("testDevSource")
    public DataSource dataSourceDev(@Value("${db.password}") String pwd) throws PropertyVetoException {
 ...
}
   
    @Profile("prod")
    @Bean("testProdSource")
    public DataSource dataSourceProd(@Value("${db.password}") String pwd) throws PropertyVetoException {
    ...
}

此时运行会发现容器中一个bean都没了,这是因为每个环境都加了标识,若改成@Profile("default)则变成默认的环境


那么怎么才能按照对应的环境进行切换装载呢?

方法一:使用命令行动态参数

在虚拟机参数位置加载 -Dspring.profiles.active=test

  • 修改vm变量
    在这里插入图片描述
-ea -Dspring.profiles.active=test
  • 结果:
容器创建完成...
testDataSource
方法二:代码的方式激活某种环境

首先查看AnnotationConfigApplicationContext的构造方法

	public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
		this();
		register(annotatedClasses);
		refresh();
	}

我们使用的是有参构造器,一启动就会进行加载对应的类并注入到Ioc容器中,这是不行的。因此需要使用无参构造器


对test01进行修改

  1. 创建一个ApplicationContext对象
  2. 设置需要激活的环境
  3. 注册主配置类
  4. 启动刷新容器
public class IOCTest_Profile {
    @Test
    public void test01() {
        // 1.创建ioc容器
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
//        1. 创建一个ApplicationContext对象
//        2. 设置需要激活的环境
        context.getEnvironment().setActiveProfiles("test", "dev");
//        3. 注册主配置类
        context.register(MainConfigOfProfile.class);
//        4. 启动刷新容器
        context.refresh();
        System.out.println("容器创建完成...");

        String[] beanNamesForType = context.getBeanNamesForType(DataSource.class);
        for (String s : beanNamesForType) {
            System.out.println(s);
        }
        context.close();
    }
}

结果:(需要先将方法一的-Dspring.profiles.active=test删除)

容器创建完成...
testDataSource
testDevSource

写在配置类上

@Profile("prod")
@PropertySource("classpath:/dbconfig.properties")
public class MainConfigOfProfile implements EmbeddedValueResolverAware {
...
}

结果:

容器创建完成...

分析,此时结果是什么都没有创建,这是因为标注了@Profile("prod"),此时前面仅配置了testdev环境,并非prod环境,因此导致MainConfigOfProfile整个配置类都没被加载。


没有标识@Profile

  • 修改MainConfigOfProfile
@PropertySource("classpath:/dbconfig.properties")
public class MainConfigOfProfile implements EmbeddedValueResolverAware {

    ...
    
    @Bean
    public Yellow yellow(){
        return new Yellow();
    }

    @Profile("test")
    @Bean("testDataSource")
    public DataSource dataSourceTest(@Value("${db.password}") String pwd) throws PropertyVetoException { ... }
	
	...

}

此时添加了一个@Bean的Yellow组件,同时删除了类上的@Profile(“prod”)。此时Yellow怎么都会被创建,而下面的配置类则按情况装载配置类

测试:

public class IOCTest_Profile {
    @Test
    public void test01() {
        // 1.创建ioc容器
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
//        1. 创建一个ApplicationContext对象
//        2. 设置需要激活的环境
        context.getEnvironment().setActiveProfiles("test", "dev");
//        3. 注册主配置类
        context.register(MainConfigOfProfile.class);
//        4. 启动刷新容器
        context.refresh();
        System.out.println("容器创建完成...");

        String[] beanNamesForType = context.getBeanNamesForType(DataSource.class);
        for (String s : beanNamesForType) {
            System.out.println(s);
        }
        String[] beanNamesForType1 = context.getBeanNamesForType(Yellow.class);
        for (String s : beanNamesForType1) {
            System.out.println(s);
        }
        context.close();
    }
}

结果: 如分析一致

容器创建完成...
testDataSource
testDevSource
yellow
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据提供的引用内容,spring-boot-starter-data-redis是Spring Boot中用于自动装配Redis的starter包。它包含了自动装配所需的类和注解等。当我们在项目的pom.xml文件中引入spring-boot-starter-data-redis包时,Spring Boot会自动根据配置文件中的相关配置信息来完成Redis的自动装配。 具体来说,spring-boot-starter-data-redis使用了RedisAutoConfiguration类来实现自动装配。该类通过读取配置文件中的相关配置信息,例如主机名、端口号、密码等,来创建Redis连接工厂和RedisTemplate等实例。这些实例可以在应用程序中直接使用,而无需手动配置和初始化。 下面是一个示例代码,展示了如何使用spring-boot-starter-data-redis进行自动装配: ```java import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.data.redis.core.RedisTemplate; @SpringBootApplication public class RedisApplication { private final RedisTemplate<String, String> redisTemplate; public RedisApplication(RedisTemplate<String, String> redisTemplate) { this.redisTemplate = redisTemplate; } public static void main(String[] args) { SpringApplication.run(RedisApplication.class, args); } // 在需要使用Redis的地方,可以直接注入RedisTemplate实例,并进行操作 // 例如: // redisTemplate.opsForValue().set("key", "value"); // String value = redisTemplate.opsForValue().get("key"); } ``` 通过上述代码,我们可以看到,在Spring Boot应用程序中,我们只需要在需要使用Redis的地方注入RedisTemplate实例,就可以直接使用Redis的相关操作方法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值