spring注解驱动开发

参考:https://www.bilibili.com/video/BV1Qz4y197SF?p=23&vd_source=aa4e16557c3a6622877c08d8d7a0a57f

 

 

一:AnnotationConfigApplicationContext

import com.hdx.hkafka.config.BeanConfigRation;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringTest {


    public static void main(String[] args) {
        // 方式1
        ApplicationContext ac1 = new ClassPathXmlApplicationContext("ApplicationContext.xml");


        // 方式2 @Configuration
        ApplicationContext ac2 = new AnnotationConfigApplicationContext(BeanConfigRation.class);

        // 方式3 @@SpringBootApplication注解的启动类
        ApplicationContext ac3 = new AnnotationConfigApplicationContext(HkafkaApplication.class);


        System.out.println(ac3.getBean("testController"));
    }
}

 

 二:组件添加,组件注册

2.1:@ComponentScan

jdk1.8开始支持可以在一个类似写多个:@ComponentScan

import com.hdx.hkafka.bean.Car;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

@Configuration
@ComponentScan(value = "com.hdx.hkafka", excludeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Component.class, Service.class})})
@ComponentScan(value = "com.hdx.hkafka", includeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Component.class, Service.class})}
//配置includeFilters生效
,useDefaultFilters = false)

// @ComponentScan value:指定要扫描的包
// excludeFilters=Filter[]:指定扫描的时候按照什么规则排除那些组件 type:指定过滤规则根据注解还是其它方式,classes:指定过滤的注解
// includeFilters=Filter[]:指定扫描的时候只需要包含哪些组件 type:指定过滤规则根据注解还是其它方式,classes:指定过滤的注解
public class BeanConfigRation {

@Bean(initMethod="init", destroyMethod="detory")
public Car getCar(){
return new Car();
}

}

 

jdk1.8之前可以使用:@ComponentScans


import com.hdx.hkafka.bean.Car;
import org.springframework.context.annotation.*;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

@ComponentScans({ @ComponentScan(value
= "com.hdx.hkafka", excludeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Component.class, Service.class})}), @ComponentScan(value = "com.hdx.hkafka", includeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Component.class, Service.class})} //配置includeFilters生效 ,useDefaultFilters = false) }) // @ComponentScan value:指定要扫描的包 // excludeFilters=Filter[]:指定扫描的时候按照什么规则排除那些组件 type:指定过滤规则根据注解还是其它方式,classes:指定过滤的注解 // includeFilters=Filter[]:指定扫描的时候只需要包含哪些组件 type:指定过滤规则根据注解还是其它方式,classes:指定过滤的注解 public class BeanConfigRation { @Bean(initMethod="init", destroyMethod="detory") public Car getCar(){ return new Car(); } }

ComponentScan可以结合Configuration或者

@Configuration
@ComponentScan

或者

@ComponentScan
@SpringBootApplication

 

@ComponentScan.Filter

@ComponentScan(value = "com.hdx.hkafka", includeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Component.class, Service.class}),
        @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,classes = {ConsumerFastStart.class}),
        @ComponentScan.Filter(type = FilterType.CUSTOM,classes = {MyTypeFilter.class})
}

FilterType.ANNOTATION 按照注解

FilterType.ASSIGNABLE_TYPE 按照给定的类型

FilterType.CUSTOM 自定义

1、需要自定义一个类实现TypeFilter接口,编写自己想要的规则

import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;

import java.io.IOException;

public class MyTypeFilter implements TypeFilter {


    /**
     *
     * @param metadataReader 读取到的当前正在扫描的类的信息
     * @param metadataReaderFactory 可以获取到其他任何类信息的
     * @return boolean
     * @throws IOException
     */
    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {

        // 获取当前类注解的信息
        AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();

        // 获取当前正在扫描类的信息
        ClassMetadata classMetadata = metadataReader.getClassMetadata();

        // 获取当前资源(类的路径)
        Resource resource = metadataReader.getResource();

        // 获取类名
        String className = classMetadata.getClassName();

        // 满足条件则匹配
        if(className.contains("service")) {
            return true;
        }

        return false;
    }
}

 

 2.2:@Scope

import com.hdx.hkafka.bean.Car;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;

/*
* 构造(对象创建)
    单实例:在容器启动的时候创建对象
    多实例:在每次获取的时候创建对象
* */
@Configuration
public class BeanConfigRation {

    /**
     * ConfigurableBeanFactory.SCOPE_PROTOTYPE,
     * ConfigurableBeanFactory.SCOPE_SINGLETON,
     * org.springframework.web.context.WebApplicationContext.SCOPE_REQUEST,
     * org.springframework.web.context.WebApplicationContext.SCOPE_SESSION
     * @return
     *
     * prototype:多实例的:ioc容器启动并不会去调用方法创建对象放在容器中。每次获取的时候才会调用方法创建对象
     * singleton:单实例的(默认值):ioc容器启动会调用方法创建对象放到ioc容器中。以后每次获取就是直接从容器(map.get())中拿,
     * request:同一次请求创建一个实例
     * session:同一个session创建一个实例
     */
    @Scope("prototype")
    @Bean("car")
    public Car getCar(){
        return new Car();
    }

 

 2.3:@Lazy

package com.hdx.hkafka.config;

import com.hdx.hkafka.bean.Car;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;

@Configuration
public class TestBeanConfigRation {

    /**
     * ConfigurableBeanFactory.SCOPE_PROTOTYPE,
     * ConfigurableBeanFactory.SCOPE_SINGLETON,
     * org.springframework.web.context.WebApplicationContext.SCOPE_REQUEST,
     * org.springframework.web.context.WebApplicationContext.SCOPE_SESSION
     * @return
     *
     * prototype:多实例的
     * singleton:单实例的(默认值)
     * request:同一次请求创建一个实例
     * session:同一个session创建一个实例
     *
     * 懒加载:
     *      单实例bean:默认在容器启动的时候创建对象;
     *      懒加载:容器启动不创建对象。第一次使用(获取)Bean创建对象,并初始化;
     */
    @Lazy
    @Bean("car")
    public Car getCar(){
        return new Car();
    }

}

 2.4:@Conditional系类

2.4.1:@Conditional可以作用在方法上,也可以作用在类上。

import com.hdx.hkafka.test.MyCondition;
import org.springframework.context.annotation.Conditional;
import org.springframework.stereotype.Component;

/**
 * 满足当前条件,这个类中配置的所有bean注册才能生效
 */
@Conditional({MyCondition.class})
@Component
public class ConditionalTest {

}

:2.4.2:@ConditionalOnProperty可以作用在方法上,也可以作用在类上。

import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;

/**
 * 满足当前条件,这个类中配置的所有bean注册才能生效
 */
@ConditionalOnProperty(
        // 属性key
        prefix = "spring.bean.is.open",
        // 属性值
        value = "on",
        // 如果用户不配置默认是否有效
        matchIfMissing = true
)
@Component
public class ConditionalTest {

}

2.4.3:@ConditionalOnBean可以作用在方法上,也可以作用在类上。

import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.stereotype.Component;

/**
 * DataSource在spring容器中存在则有效
 */
@ConditionalOnBean(javax.sql.DataSource.class)
@Component
public class ConditionalTest {

}

2.4.4:@ConditionalOnMissingBean可以作用在方法上,也可以作用在类上。

import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.stereotype.Component;

/**
 * 在spring容器中不存在DataSource类型的数据则有效
 */
@ConditionalOnMissingBean(javax.sql.DataSource.class)
@Component
public class ConditionalTest {

}

 

@ConditionalOnBean:该注解的参数对应的类必须存在,否则不解析该注解修饰的配置类;
@ConditionalOnMissingBean:该注解表示,如果存在它修饰的类的bean,则不需要再创建这个bean;可以给该注解传入参数例如

2.4.5:@ConditionalOnClass

@ConditionalOnClass注解判断配置的类是否存在的方式是通过Class.forName的方式

import com.hdx.hkafka.bean.Car;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.stereotype.Component;

/**
 * 在spring容器中存在com.hdx.hkafka.bean.Car类型的数据则有效
 */
@ConditionalOnClass(value = {Car.class})
@Component
public class ConditionalTest {

}

 

2.4.6:@ConditionalOnMissingClass可以作用在方法上,也可以作用在类上。

import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.stereotype.Component;

/**
 * 在spring容器中不存在com.hdx.hkafka.bean.Car类型的数据则有效
 */
@ConditionalOnMissingClass("com.hdx.hkafka.bean.Car")
@Component
public class ConditionalTest {

}

 

@ConditionalOnClass // 当给定的类名在类路径上存在,则实例化当前Bean

@ConditionalOnMissingClass // 当给定的类名在类路径上不存在,则实例化当前Bean

 2.4.7:@ConditionalOnMissingFilterBean可以作用在方法上,也可以作用在类上。

 2.5:@Import

给容器中注册组件;
1)、包扫描+组件标注注解(@Controller/@Service/@Repository/@Component)[自己写的类]
2)、@Bean[导入的第三方包里面的组件]
3)、@Import[快速给容器中导入一个组件]
    1)、@Import(要导入到容器中的组件);容器中就会自动注册这个组件,id默认是全类名
    2)、ImportSelector:返回需要导入的组件的全类名数组;
    3)、ImportBeanDefinitionRegistrar:手动注册bean到容器中
4)、使用Spring提供的FactoryBean(工厂Bean)
    1)、默认获取到的是工厂bean调用getobject创建的对象
    2)、要获取工厂Bean本身,我们需要给id前面加一个&
    &colorFactoryBean

 

2.5.1:@Import

package com.hdx.hkafka.config;

import com.hdx.hkafka.bean.Car;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

// @Import导入组件,id默认是组件的全类名,value是一个数组Class<?>[] value()
@Import(Car.class)
@Configuration
public class ImportTest {

}

2.5.2:ImportSelector

import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;

//自定义逻辑返回需要导入的组件
public class MyImportSelector implements ImportSelector {
    //返回值,就是到导入到容器中的组件全类名
    //AnnotationMetadata:当前标注@Tmport注解的类的所有注解信息
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        // TODO Auto-generated method stub
        //importingClassMetadata
        //方法不要返回nu11值
        return new String[]{"com.hdx.hkafka.bean.Car"};
    }
}
import com.hdx.hkafka.test.MyImportSelector;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

// 也可以定义实现ImportSelector接口的类返回一组需要加入容器的bean
@Import({MyImportSelector.class})
@Configuration
public class ImportTest {
}

 2.5.3:MyImportBeanDefinitionRegistrar

import com.hdx.hkafka.bean.Car;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    /**
     * AnnotationMetadata:当前类的注解信息
     * BeanDefinitionRegistry:BeanDefinition注册类;
     * 把所有需要添加到容器中的bean;调用
     *     BeanDefinitionRegistry.registerBeanDefinition手工注册进来
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        // 判断当前容器中是否存在此类型的bena
        boolean definition = registry.containsBeanDefinition("com.hdx.hkafka.chapter1.ProducerDemo");
        boolean definition2 = registry.containsBeanDefinition("com.hdx.hkafka.chapter1.ConsumerFastStart");
        if(definition && definition2){
            //(Bean的类型,Bean。//指定Bean定义信息;。)
            RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Car.class);
            //注册一个Bean,指定bean名
            registry.registerBeanDefinition("car", rootBeanDefinition);
        }

    }
}
import com.hdx.hkafka.test.MyImportBeanDefinitionRegistrar;
import com.hdx.hkafka.test.MyImportSelector;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

// 也可以定义实现ImportSelector接口的类返回一组需要加入容器的bean
@Import({MyImportSelector.class,MyImportBeanDefinitionRegistrar.class})
@Configuration
public class ImportTest {
}

 2.5.4:FactoryBean

import com.hdx.hkafka.bean.Car;
import org.springframework.beans.factory.FactoryBean;

//创建一个Spring定义的FactoryBean
public class MyFactoryBean implements FactoryBean {
    //返回一个Car对象,这个对象会添加到容器中
    @Override
    public Car getObject() throws Exception {
        return new Car();
    }

    @Override
    public Class<?> getObjectType() {
        return Car.class;
    }

    //是单例?
    // true:这个bean是单实例,在容器中保存一份
    // false:多实例,每次获取都会创建一个新的bean;
    @Override
    public boolean isSingleton() {
        return false;
    }
}
import com.hdx.hkafka.test.MyFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ImportTest {

/**
* 4)、使用Spring提供的FactoryBean(工厂Bean)
* 1)、默认获取到的是工厂bean调用getobject创建的对象
* 2)、要获取工厂Bean本身,我们需要给id前面加一个&
* &colorFactoryBean
*/
@Bean
public MyFactoryBean myFactoryBean(){
return new MyFactoryBean();
}
}

 

三:bean生命周期

 

构造(对象创建)单实例:
在容器启动的时候创建对象多实例:
在每次获取的时候创建对象

BeanPostProcessor.postProcessBeforeInitialization
初始化:
对象创建完成,并赋值好,调用初始化方法。。。BeanPostProcessor.postProcessAfterInitialization
销毁:
单实例:容器关闭的时候多实例:
容器不会管理这个bean;容器不会调用销毁方法;

 

初始化bean操作:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
遍历得到容器中所有的BeanPostProcessor;挨个执行befforeInitialization
一但返回null,跳出for循环,不会执行后面的BeanPostProcessor.postProcessBeforeInitialization

BeanPostProcessor原理
populateBean(beanName, mbd, instanceWrapper);|给bean属性赋值
initializeBean{
  applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
  invokeInitMethods(beanName, wrappedBean, mbd);
  applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}

1)、指定初始化和销毁方法;通过@Bean指定init-method和destroy-method;
2)、通过让Bean实现InitializingBean(定义初始化逻辑),DisposableBean(定义销毁逻辑);
3)、可以使用JSR250;@PostConstruct:在bean创建完成并且属性赋值完成;来执行初始化方法@PreDestroy:在容器销毁bean之前通知我们进行清理工作
4)、BeanPostProcessor【interface】:bean的后置处理器;在bean初始化前后进行一些处理工作;
  postProcessBeforeInitialization:在初始化之前工作
  postProcessAfterInitialization:在初始化之后工作

Spring底层对BeanPostProcessor的使用;
  bean赋值,注入其他组件,@Autowired,生命周期注解功能,@Async,xxx BeanPostProcessor;

 

3.1:执行初始化和销毁的方式

3.1-1:使用bean注解执行类初始化和销毁执行的方法

前提:必读加入spring容器且是单例情况下

构造(对象创建)
单实例:在容器启动的时候创建对象
多实例:在每次获取的时候创建对象,spring不会管理bean,所以此时指定销毁和初始化方法无效
@Configuration
public class BeanConfigRation {

    @Bean(initMethod="init", destroyMethod="detory")
    public Car getCar(){
        return new Car();
    }

}
public class Car {

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

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

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

3.1-2:实现InitializingBean, DisposableBean

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;

@Component
public class People implements InitializingBean, DisposableBean {

    /*类的属性赋值完成后*/
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("afterPropertiesSet");
    }

    /*在bean被销毁时*/
    @Override
    public void destroy() throws Exception {
        System.out.println("destroy");
    }
}

3.1-3:使用@PostConstruct和@PreDestroy

实现类:org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor

还有:

postProcessBeforeInitialization调用初始化方法
postProcessBeforeDestruction调用销毁方法
/**
     * Specify the init annotation to check for, indicating initialization
     * methods to call after configuration of a bean.
     * <p>Any custom annotation can be used, since there are no required
     * annotation attributes. There is no default, although a typical choice
     * is the JSR-250 {@link javax.annotation.PostConstruct} annotation.
     */
    public void setInitAnnotationType(Class<? extends Annotation> initAnnotationType) {
        this.initAnnotationType = initAnnotationType;
    }

    /**
     * Specify the destroy annotation to check for, indicating destruction
     * methods to call when the context is shutting down.
     * <p>Any custom annotation can be used, since there are no required
     * annotation attributes. There is no default, although a typical choice
     * is the JSR-250 {@link javax.annotation.PreDestroy} annotation.
     */
    public void setDestroyAnnotationType(Class<? extends Annotation> destroyAnnotationType) {
        this.destroyAnnotationType = destroyAnnotationType;
    }
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
try {
metadata.invokeInitMethods(bean, beanName);
}
catch (InvocationTargetException ex) {
throw new BeanCreationException(beanName, "Invocation of init method failed", ex.getTargetException());
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Failed to invoke init method", ex);
}
return bean;
}

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}

@Override
public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
try {
metadata.invokeDestroyMethods(bean, beanName);
}
catch (InvocationTargetException ex) {
String msg = "Destroy method on bean with name '" + beanName + "' threw an exception";
if (logger.isDebugEnabled()) {
logger.warn(msg, ex.getTargetException());
}
else {
logger.warn(msg + ": " + ex.getTargetException());
}
}
catch (Throwable ex) {
logger.warn("Failed to invoke destroy method on bean with name '" + beanName + "'", ex);
}
}
 

 

import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

@Component
public class People {

    /*类的属性赋值完成后*/
    @PostConstruct
    public void init() throws Exception {
        System.out.println("init");
    }

    /*在bean被销毁时*/
    @PreDestroy
    public void destroy() throws Exception {
        System.out.println("destroy");
    }
}

 3.1-4:BeanPostProcessor原理

初始化bean操作:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
遍历得到容器中所有的BeanPostProcessor;挨个执行befforeInitialization
一但返回null,跳出for循环,不会执行后面的BeanPostProcessor.postProcessBeforeInitialization

BeanPostProcessor原理
populateBean(beanName, mbd, instanceWrapper);|给bean属性赋值
initializeBean{
  applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
  invokeInitMethods(beanName, wrappedBean, mbd);
  applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}

 

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;

/**
* 后置处理器:初始化前后进行处理工作
*/
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {

/**
* 初始化前调用
* @param bean
* @param beanName
* @return
* @throws BeansException
*/
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}

/**
* 初始化后调用
* @param bean
* @param beanName
* @return
* @throws BeansException
*/
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}

 

3.1-5:ApplicationContextAware应用

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;

/**
 * 原理类:org.springframework.context.support.ApplicationContextAwareProcessor
 */
@Component
public class SpringUtils implements ApplicationContextAware {
 
    private static ApplicationContext applicationContext;

    // 原理类:org.springframework.context.support.ApplicationContextAwareProcessor
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    /**
     * 当前spring中配置的参数对象
     * @return
     */
    public static Environment getEnvironment() {
        return applicationContext.getEnvironment();
    }
}

 四:属性赋值

4.1:@Value

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

@Component
public class People {

// 直接赋值
@Value("张三")
private String name;

// 使用spel
@Value("#{32-14}")
private String age;

// 读取properties配置文件赋值
@Value("${test.desc}")
private String desc;
}

 

4.2:@PropertySource(value={})

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

// 使用@PropertySource读取外部配置文件中的k/v保存到运行的环境变量中;加款完外部的配置文件以后使用${}取出配置文件的值
@PropertySource(value={"classpath:/application.properties"})
@Configuration
public class TestBeanConfigRation {

}
// 获取对应属性配置值
ConfigurableEnvironment environment = ApplicationContext.getEnvironment();
String property = environment.getProperty("person.nickName");
ApplicationContext.close();

五:自动装配

5.1:@Autowired

@Autowired:构造器,参数,方法,属性:

// @Qualifier("bookDao"):使用@Qualifier指定需要装配的组件的id,而不是使用属性名
    @Qualifier("people")
    // 1、先使用People.class类型在容器中查找
    // 2、再使用people1作为bean名称在容器中查找
    // 3、required = false设置如果没有不抛异常
    @Autowired(required = false)
    private People people1;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;

// Primary作用在类和方法上标准当前类的实例在容器中级别中默认等级最高
@Primary
@Component
public class People {

}

 在set方法上

import com.hdx.hkafka.bean.People;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class OrderController {

    // 标注在方法,Spring容器创建当前对象,就会调用方法,完成赋值;
    // 方法使用的参数,自定义类型的值从ioc容器中获取
    @Autowired
    public void setPeople2(People people2) {
        this.people2 = people2;
    }


    private People people2;
}

 在构造方法

import com.hdx.hkafka.bean.People;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RestController;

//默认加在ioc容器中的组件,容器启动会调用无参构造器创建对象,再进行初始化赋值等操作
@RestController
public class OrderController {

    // 构造器要用的组件,都是从容器中获取
    @Autowired
    public OrderController(People people) {
        this.people = people;
    }

    private People people;
}

在有参数构造器

import com.hdx.hkafka.bean.People;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class OrderController {

    public OrderController(@Autowired People people) {
        this.people = people;
    }

    private People people;
}

 

 

@Bean+方法参数:参数从容器中获取;默认不写@Autowired效果是一样的:都能自动装配

 

// @Bean标注的方法创建对象的时候,方法参数的值从容器中获取
    @Bean
    public People getCar(People people){
        System.out.println(people);
        return new People();
    }

 

5.2:Spring还支持使用@Resource(JSR250)和@Inject(JSR330)[java规范的注解]

@Resource
import com.hdx.hkafka.bean.People;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
public class OrderController {

    // 可以和@Autowired一样实现自动装配功能:默认是按照组件名称进行装配的
    // 没有能支持@Primary功能没有支持@Autowired(reqiured=false)
    @Resource(name = "people2")
    private People people2;
}
@Inject
需要导入javax.inject的包,和Autowired的功能一样。没有required=false的功能

<!-- https://mvnrepository.com/artifact/javax.inject/javax.inject -->
        <dependency>
            <groupId>javax.inject</groupId>
            <artifactId>javax.inject</artifactId>
            <version>1</version>
        </dependency>
import com.hdx.hkafka.bean.People;
import org.springframework.web.bind.annotation.RestController;

import javax.inject.Inject;

@RestController
public class OrderController {

    @Inject
    private People people2;
}

 5.3:Aware接口

完成路径:org.springframework.beans.factory.Aware

自定义组件想要使用Spring容器底层的一些组件(ApplicationContext,BeanFactory,xxx);
  自定义组件实现xxxAware;在创建对象的时候,会调用接口规定的方法注入相关组件;Aware;
  把Spring底层一些组件注入到自定义的Bean中;
  xxxAware:功能使用xxxProcessor;
    ApplicationContextAware==>ApplicationContextAwareProcessor;

 示例

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import org.springframework.util.StringValueResolver;

/**
 * 原理类:org.springframework.context.support.ApplicationContextAwareProcessor
 */
@Component
public class SpringUtils implements ApplicationContextAware, EmbeddedValueResolverAware, BeanNameAware {
 
    private static ApplicationContext applicationContext;

    // 原理类:org.springframework.context.support.ApplicationContextAwareProcessor
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    /**
     * 当前spring中配置的参数对象
     * @return
     */
    public static Environment getEnvironment() {
        return applicationContext.getEnvironment();
    }
// EmbeddedValueResolverAware // 设置用于解析嵌入定义值的StringValueResolver。 @Override public void setEmbeddedValueResolver(StringValueResolver resolver) { String val = resolver.resolveStringValue("你好${os.name}我是#{20*18}"); System.out.println("解析的字符串:"+ val); } // BeanNameAware @Override public void setBeanName(String name) { System.out.println("当前bean的名字:"+ name); } }

 

5.3:Profile注解

Profile:
  Spring为我们提供的可以根据当前环境,动态的激活和切换一系列bean的功能
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({ProfileCondition.class})
public @interface Profile {
    String[] value();
}

 

验证类:

import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.context.annotation.PropertySource;
import org.springframework.util.StringValueResolver;

import javax.sql.DataSource;


/**
 *
 * 开发环境、测试环境、生产环境;
 * 数据源:(/A)(/B)(/C);
 *
 * @Profile:指定组件在哪个环境的情况下才能被注册到容器中;不指定任何环境都可以
 *
 *  1)、加了环境标识的bean,只有这个环境被激活的时候才能注册到容器中。默认是default环境
 *  2)、写在配置类上,只有是指定的环境的时候,整个配置类里面的所有配置才能开始生效
 *  3)、没有标注环境标识的bean在,任何环境下都是加载的;
 */
@PropertySource("classpath:/dbconfig.properties")
@Configuration
public class MainConfigOfProfile implements EmbeddedValueResolverAware {

    @Value("${db.user}")
    private String user;

    private StringValueResolver valueResolver;

    @Profile("dev")
    @Bean("dataSourcelDev")
    public DataSource dataSourcelDev(@Value("${db.password}")String pwd) throws Exception{
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser(user);
        dataSource.setPassword(pwd);
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
        String dirverClass = valueResolver.resolveStringValue("${db.driverClass}");
        dataSource.setDriverClass("com.mysql.jdbc.Driver");
        return dataSource;
    }

    @Profile("sit")
    @Bean("dataSourcelSit")
    public DataSource dataSourcelSit(@Value("${db.password}")String pwd) throws Exception{
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser(user);
        dataSource.setPassword(pwd);
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");

        String dirverClass = valueResolver.resolveStringValue("${db.driverClass}");
        dataSource.setDriverClass(dirverClass);
        return dataSource;
    }

    // 默认生效
    @Profile("default")
    @Bean("dataSourcelDefault")
    public DataSource dataSourcelDefault(@Value("${db.password}")String pwd) throws Exception{
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser(user);
        dataSource.setPassword(pwd);
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");

        String dirverClass = valueResolver.resolveStringValue("${db.driverClass}");
        dataSource.setDriverClass(dirverClass);
        return dataSource;
    }

    @Override
    public void setEmbeddedValueResolver(StringValueResolver resolver) {
        this.valueResolver  = resolver;
    }
}

 

配置文件:

db.user=root
db.password=root
db.driverClass=com.mysql.jdbc.Driver

 

测试类:

 

import com.hdx.hkafka.test.MainConfigOfProfile;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import javax.sql.DataSource;

public class MainConfigOfProfileTest {

private void test() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfProfile.class);

String[] names = applicationContext.getBeanNamesForType(DataSource.class);
for (int i = 0; i < names.length; i++) {
System.out.println(names[i]);
}

applicationContext.close();
}

// 1、使用命令行动态参数:在虚拟机参数位置加载-Dspring.profiles.active=test
@Test
public void test1() {
// 验证执行
test();
}

/**
* 2、代码的方式激活某种环境;
* 2.1、创建一个applicationContext
* 2.2、设置需要激活的环境applicationContext.getEnvironment().setActiveProfiles("test","dev");
* 2.3、注册主配置类applicationContext.register(MainConfigOfProfile.class);
* 2.4、启动刷新容器applicationContext.refresh();
*/
@Test
public void test2() {
AnnotationConfigApplicationContext applicationContext =new AnnotationConfigApplicationContext();
applicationContext.getEnvironment().setActiveProfiles("test","dev");
applicationContext.register(MainConfigOfProfile.class);
applicationContext.refresh();

// 验证执行
test();
}
}

 

posted @ 2022-12-31 21:09  银河系的极光  阅读(96)  评论(0)    收藏  举报