Spring注解复盘

发布于 2021-10-27  2.48k 次阅读


一,容器创建注解@Configuration和@Bean

容器的创建和配置有两种中方式

  • XML配置(文件配置):在SSM中经常使用这种方式
  • 注解配置(配置类):在SpringBoot中很常用,并且SpringBoot的自动装配也依赖这种方式

注:文件配置和配置类配置只是操作上不一样,实现的作用是等价的

一,传统XML文件配置bean

① 实体类

public class config_annotation {

    String name;
    int age;

    public String getName() {
        return name; 
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

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

② Spring的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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean  class="com.config.config_annotation" id="config_xml">
            <property name="name" value="WQL"/>
            <property name="age" value="100"/>
     </bean>
</beans>
③ main测试
public static void main(String[] args) {

    //ClassPathXmlApplicationContext这个类通过xml文件获取上下文
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("springconfig.xml");

        //获取bean
    Object bean = context.getBean("config_xml");

    System.out.println(bean);

}

二,注解配置bean

注解配置合xml文件配置不一样,注解作用于类

注解配置bean有两个重要的注解:

  • @Configuration:声明一个配置类(配置类等价于配置文件)
  • @Bean:创建一个bean(和xml配置文件中的<bean>标签是一样的作用)

① 实体类(和上面是一样的)

② 配置类

//配置类注解
@Configuration
public class config_annotation {

    //value为当前bean的名称
    @Bean(value = "get_bean")
    public bean_test  get_bean(){

        return new bean_test("FQ_WQL",100);
}}

③ main测试

public static void main(String[] args) {

    //通过注解配置类方式获取上下文
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(config_annotation.class);

    //获取bean对象
    Object bean = applicationContext.getBean("get_bean");

    System.out.println(bean);
}

二,包扫描注解@ComponentScan

包扫描:只要包中的类标注了@Controller,@Service,@Repository,@Component,都会被在载入到当前Bean中并可以被调用

一,XMl配置包扫描

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

二,注解配置包扫描

一,注解配置使用

① @ComponentScan:单扫描

@ComponentScan(value = "com.bean")
② @ComponentScans:多扫描,里面可以配置多个@ComponentScan
@ComponentScans(value = {@ComponentScan("com.bean"),@ComponentScan("com.bean")})

二,包扫描过滤

一,扫描过滤依赖于两个参数

① excludeFilters:扫描排除,不扫描哪些包或者类

//通过注解类型过滤,包含Service类型的进行过滤
@ComponentScan(value = "com.bean",excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = Service.class)})
② includeFilters:扫描哪些包或者类
//通过注解类型过滤,包含Coltroll类型的进行扫描
@ComponentScan(value = "com.bean",includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = Controller.class)})

二,扫描过滤的方式

通过Filter扫描过滤有五中方式:

  • FilterType.ANNOTATION:指定注解的方式
  • FilterType.ASSIGNABLE_TYPE:指定具体类的方式
  • FilterType.ASPECTJ:使用ASPECTJ表达式
  • FilterType.REGEX:正则表达式
  • FilterType.CUSTOM:自定义规则

注:用的比较多的就是是ANNOTATION和ASSIGNABLE_TYPE两种方式

1,指定注解的方式FilterType.ANNOTATION

可以指定四中注解:Service,Component,Controller,Repository

@ComponentScan(value = "com.bean",includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = Controller.class),
        @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = ComponentScan.class)})

2,指定具体类的方式

@ComponentScan(value = "com.bean",includeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,classes = service_bean.class),
        @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,classes = bean_test.class)})
3,自定义过滤规则

指定义过滤规则必须自定义一个过滤类,这个类必须实现TyprFilter接口

TyprFilter接口:只要一个match的实现方法(返回true表示匹配否则反之),这个方法里面有两个只要参数

  • metadataReader:读取当前正在扫描类的信息
  • MetadataReaderFactory:获取其他任何类的信息

① 自定义过滤类

public class custon_util implements TypeFilter {
    //
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {


        //1,获取当前类注解信息(有获取注解名,注解类型等方法)
        AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();


        //2,获取当前类的类信息(有获取类的名称,实现接口,子类,继承类等方法)
        ClassMetadata classMetadata = metadataReader.getClassMetadata();


        //3,获取当前类资源路径(获取类的文件名称,文件路径,URL等方法)
        Resource resource = metadataReader.getResource();


        //打印类名称
        String classname = classMetadata.getClassName();
        System.out.println(classname);


        //假如类名包含bean就返回true表示匹配
        if(classname.contains("bean")){
            return true;
        }
        return false;
    }
}

② 假如到过滤中

@ComponentScan(value = "com",excludeFilters = {@ComponentScan.Filter(type = FilterType.CUSTOM,classes = custon_util.class)})

三,设置bean的作用域@Scope

Spring默认Bean都是单例的,但Scope可以配置单例和多例

scope两个重要的值:

  • singleton:单例
  • prototype:多例

一,xml配置scope

<bean class="com.bean.bean_test" id="config_xml" scope="singleton" >
        <property name="name" value="WQL"/>
        <property name="age" value="100"/>
</bean>

二,注解配置scope

@Bean(value = "get_bean")
@Scope(value = "prototype")
public bean_test  get_bean(){ 
   return new bean_test("FQ_WQL",100);
}

测试多例:

四,懒加载@Lazy

bean默认在容器启动的时候创建对象

懒加载:容器启动不创建对象,只有在第一次使用(获取)时才创建Bean对象,并初始化

一,xml配置lazy懒加载

<bean class="com.bean.bean_test" id="config_xml" lazy-init="true">
        <property name="name" value="WQL"/>
        <property name="age" value="100"/>
</bean>

二,注解配置lazy懒加载

使用@lazy注解配置懒加载
@Bean(value = "get_lazy_bean")
@Lazy
public lazy_test get_lazy_bean(){
    System.out.println("启动时初始化!");
    return new lazy_test("FQ",10);
}

五,按照条件注册@Conditional

@Bean,@Controller,@Service,@Componter……都是直接在容器中进行注册,而@Conditional按照一定条件进行判断,满足条件才在容器中注册bean

一,@Conditional的实现动态装配

@Conditional格式:

@Conditional(value = {条件判断类})

实现Conditional条件判断,需要有一个条件判断类,这个类必须实现Condition接口

Condition接口只有一个matches方法

方法参数:

  • ConditionContext:上下文,可以获取bean工厂,环境信息,加载器等信息
  • metadata:注释信息

 例:

① 条件判断类 

public class condition_win_util implements Condition {
    /*
    * 匹配成功就返回true,失败就为false
    * ConditionContext:上下文
    * metadata:注释信息
    * */
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {


        //1,获取bean工厂对象
        ConfigurableListableBeanFactory factory = context.getBeanFactory();


        //2,获取类加载器
        ClassLoader classLoader = context.getClassLoader();


        //3,获取当前环境信息
        Environment environment = context.getEnvironment();


        //4,获取到bean的注册类
        BeanDefinitionRegistry registry = context.getRegistry();


        //通过environment获取当前系统的类型
        String os_name = environment.getProperty("os.name");


        //判断当前系统是为linux还是windown
        if(os_name.contains("Windows")){
            System.out.println(os_name);
            return true;
        }
        return false;
    }}

②注解bean

@Conditional(value = condition_win_util.class)//只有条件通过才执行@Bean
@Bean(value = "get_windown")
public condition_test get_win_condition(){


    return new condition_test("windown");
}

六,容器导入组件@Import

Spring往容器中注册组件有多种方式,它们适应于不同对的场景:

  1. 包扫描+组件标注注解(@Service,@Component,@Controller):适用于自己定义的类,无法作用第三方框架
  2. @Configuration+@Bean:适应于导入第三方框架组件并进行配置,一般SpringBoot整合其他框架比如RabbitMQ,Minio等都使用这种方式
  3. @import:适应于快速给容器中导入一个组件
  4. 使用Spring提供的FactoryBean接口,实现组件导入容器

import注解也有三种方式:

  • @Import:快速导入单个或者多个组件
  • ImportSelector:导入选择器类,返回需要导入的组件的全类名数组,即可导入
  • ImportBeanDefinitionRegistrar:手动注册bean到容器中

一,import导入

一,@Import快速导入组件

格式:@import(value={组件全限定类名,……})

@Import(value = {condition_lin_util.class})

二,ImportSelector导入

importselector的实现,需要有实现类,这个类实现ImportSelector接口

ImportSelector接口有一个selectImports方法返回值为string数组,只有一个参数AnnotationMetadata它可以获取所有的注解和类信息

例:

① importselector实现类

//自定义逻辑返回需要导入的组件
public class importselect_util implements ImportSelector {
    /*
    * AnnotationMetadata:获取到当前标注@import注解类的全部注解信息
    * */
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {

        //返回值就是要导入容器中的组件全类名数组
        return new String[]{"com.util.condition_lin_util","com.util.condition_win_util"};
    }
}

②导入(importselect它没有专门的注解,依赖于@import导入)

@Import(value = {importselect_util.class})

三,ImportBeanDefinitionRegistrar手动导入

ImportBeanDefinitionRegistrar手动注册实现依赖ImportBeanDefinitionRegistrar的实现类

这个类也只有一个registerBeanDefinitions方法

两个参数:

  • AnnotationMetadata:当前类的注解信息
  • BeanDefinitionRegistry:BeanDefinition注册类,通过这个类的方法进行手动注册

例:

① ImportBeanDefinitionRegistrar实现类

//手动注册组件
public class ImportBeanDefinitionRegistrar_util implements ImportBeanDefinitionRegistrar {

    /*
    *参数:
    * 1,AnnotationMetadata:当前类的注解信息
    * 2,BeanDefinitionRegistry:BeanDefinition注册类,通过这个类的方法进行手动注册
    *   registry.registerBeanDefinition():手动注册方法
    * */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    }
}

② 导入

@Import(value = {ImportBeanDefinitionRegistrar_util.class})

二,FactoryBean导入

使用Factory实现导入需要定义类并实现Factory接口

这个接口有三个方法:

  • getObject():返回一个对象,并将对象添加到容器中
  • getObjectType():设置返回对象的类型
  • isSingleton():是否为单例,true为单例,反之为多例

例:

① 创建Factory实现类

public class factorybean_util implements FactoryBean {


    //返回一个对象,这个对象会添加到容器中
    @Override
    public Object getObject() throws Exception {
        //返回一个condition_test对象
        return new condition_test("FactoryBean");
    }


    //返回对象的类型
    @Override
    public Class<?> getObjectType() {
        //返回对象condition_test类型
        return condition_lin_util.class;
    }


    //是否为单例,返回true为单例,返回flase为多例
    @Override
    public boolean isSingleton() {
        //为单例
        return true;
    }
}

② 将FactoryBean放入配置类中声明

@Bean
public factorybean_util get_factory_bean(){
    
    //返回的虽然是 factorybean_util对象,实际上是condition_test对象
    return new factorybean_util();
}

③ 获取FactoryBean

获取时FactoryBean有两中情况:

  • 带&开头的:获取FactoryBean本身
  • 不带&:获取FactoryBean创建返回的对象
public static void main(String[] args) 

    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(config_annotation.class);

    Object factoryBean = context.getBean("get_factory_bean");
    Object factoryBean1 = context.getBean("&get_factory_bean");

    System.out.println("FactoryBean生成的对象:"+factoryBean);
    System.out.println("FactoryBean对象本身:"+factoryBean1);
}

七,注解方式配置生命周期

Spring生命周期有两种:

1,正常生命周期:五大过程

  1. 通过有参构造创建Bean对象
  2. 对属性进行DI依赖注入
  3. 调用Bean初始化方法
  4. 获取Bean对象
  5. 销毁bean,并调用bean销毁后需要执行的方法

2,带有前置后置处理器的生命周期:七步过程

  1. 通过有参构造创建Bean对象
  2. 对属性进行DI依赖注入
  3. 将bean传入前置处理器,调用前置处理器方法
  4. 调用Bean初始化方法,对bean进行初始化
  5. 将bean传入后置处理器,调用后置处理器方法
  6. 销毁bean,并调用bean销毁后需要执行的方法

Spring配置生命周期的方式:

  1. 通过initmethod和destroymethod指定初始化和销毁方法
  2. 通过bean实现InitialliazingBean定义初始化逻辑,实现DisposableBean定义销毁逻辑
  3. 使用JSR250实现,@Postconstruct在bean创建完成并属性注入完成执行初始化,@PreDestroy在容器销毁bean之前进行通知的回调方法
  4. 使用BeanPostProcessor定义前置处理器和后置处理器

注:方式1,2,3本质上是差不多的,只是写法配置上和含义上稍微有点

传统的XML配置不进行演示了以前有写过: Spring的IOC和AOP – 晴天 (luoqin.ltd)

一,initmethod和destroymethod实现初始化和销毁

注解initMethod和destroymethod的实现依赖于@Bean种的两个参数:

  • initMethod:指定容器的初始化方法
  • destroyMethod:指定容器销毁后调用的方法

① bean对象

public class init_dostroy_test {

    String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public init_dostroy_test(String name) {
        this.name = name;
        System.out.println("bean有参创建!");
    }

    //初始化方法
    public void init(){
        System.out.println("init_dostroy_test初始化!");
    }

    //销毁方法
    public void destroy(){
        System.out.println("init_dostroy_test销毁!");
    }
}

② 配置类

@Configuration
public class life_annotation {

    //把方法通过initMethod和destroyMethod进行加入
    @Bean(value = "get_init_destroy",initMethod = "init",destroyMethod = "destroy")
    public init_dostroy_test get_init_destroy(){

        return new init_dostroy_test("initMethod和destroyMethod进行初始化和销毁!");
    }

}

③ 测试

public class life_main {


    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(life_annotation.class);

        System.out.println("容器创建完成,获取bean!");
        applicationContext.getBean("get_init_destroy");


        applicationContext.close();
    }
}

二,通过InitialliazingBean和DisposableBean实现初始化和销毁

通过这种发生实现需要实现InitialliazingBean和DisposableBean两个接口,并重写两个接口的两个方法:

  • InitialliazingBean接口中的afterPropertiesSet方法:定义初始化的逻辑
  • DisposableBean接口中的destroy方法:定义销毁逻辑

① bean实例

@Component(value = "InitialliazingBean_test")
//实现InitializingBean和DisposableBean接口
public class InitialliazingBean_test implements InitializingBean, DisposableBean {
    @Override
    public void afterPropertiesSet() throws Exception {

        System.out.println("InitializingBean初始化!");
    }


    @Override
    public void destroy() throws Exception {
        System.out.println("DisposableBean销毁!");
    }


    public InitialliazingBean_test() {

        System.out.println("有参构建bean对象!");
    }
}

② 配置类

//不使用@Bean的方式,直接使用ComponentScan包扫描的方式加入bean对象到容器
@ComponentScan(value = "com.bean")
@Configuration
public class life_annotation {

}

③ 测试

public static void main(String[] args) {
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(life_annotation.class);


    System.out.println("容器创建完成!");
    applicationContext.getBean("InitialliazingBean_test");


    applicationContext.close();
}

三,使用@Postconstruct和@PreDestroy实现初始化和销毁

@Postconstruct和@PreDestroy与上面两种初始化和销毁是有点区别的:

  • @Postconstruct:在容器创建完成之后调用
  • @PreDestroy:在容器销毁之前调用

① bean实例

@Component(value = "postconstruct_test")
public class postconstruct_test {


    public postconstruct_test() {
        System.out.println("无参构造创建对象!");
    }

    @PostConstruct
    public void init(){
        System.out.println("PostConstruct在对象创建完成之后调用!");
    }


    @PreDestroy
    public void destroy(){
        System.out.println("PostConstruct在对象销毁之前调用!");
    }
}

② 配置类

//不使用@Bean的方式,直接使用ComponentScan包扫描的方式加入bean对象到容器
@ComponentScan(value = "com.bean")
@Configuration
public class life_annotation {}

③ 测试

public static void main(String[] args) {
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(life_annotation.class);


    System.out.println("容器创建完成!");
    applicationContext.getBean("postconstruct_test");


    applicationContext.close();
}

四,通过BeanPostProcessor加入前置和后置处理器

实现BeanPostProcessor需要实现该接口,并重写两个方法:

  • postProcessBeforeInitialization:前置处理器
  • postProcessAfterInitialization:后置处理器

① BeanPostProcessor接口实现

@Component
public class BeanPostProcessor_test implements BeanPostProcessor {


    @Override
    /*前置处理器
    * 参数:
    * 1,Object bean:容器中传入的bean实例
    * 2,String beanName:bean名称
    * 处理完成后需要将bean进行返回
    * */
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessBeforeInitialization——>前置处理器:"+beanName);
        return bean;
    }


    @Override
    //后置处理器
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessAfterInitialization——>后置处理器:"+beanName);
        return bean;
    }
}

② 扫描该类加入容器

@ComponentScan(value = "com.bean")
@Configuration
public class life_annotation {}

注:BeanPostProcessor接口实现类加入容器后,默认就会为所有bean对象提供前置和后置

八,注解方式属性赋值

一,@Value属性赋值

属性赋值,String在xml中提供value参数,在注解提供了@value注解

value赋值有三种形式:

  • 基本数值:比如基本类型和String都可以直接赋值
  • SPEL(spring表达式):#{}表示,它支持选择,比较,正则表达式,赋值计算,集合引用等操作
  • 取外部配置文件中的值:通过${},通过propertysource引入,任何在通过${}调用即可

@value注解即可以作用于属性也可以作用于参数,效果是一样的

一,在xml中通过value进行属性赋值

<bean class="com.bean.bean_test" id="config_xml" lazy-init="true">
        <property name="name" value="WQL"/>
        <property name="age" value="100"/>
</bean>

二,注解@Value赋值

① bean实例

@Component
public class value_test {

    @Value("WQL")
    String name;
    @Value("20")
    int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

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

② 加入bean容器

@ComponentScan(value = "com.bean")
@Configuration
public class life_annotation {}

二,@PropertySource加载外部配置文件

一,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">

    <!--引入外部配置文件-->
    <context:property-placeholder location="config.properties"/>


    <bean class="com.bean.bean_test" id="config_xml" lazy-init="true">
    <!--${}引用-->
            <property name="name" value="${name}"/>
            <property name="age" value="${age}"/>
     </bean>


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

二,注解@PropertySource加载外部配置文件

@PropertySource将外部配置文件中的K-V保存到bean运行时的环境中

① bean实例

@Component
public class value_test {

    @Value("${name}")
    String name;
    @Value("${age}")
    int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

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

② 加载bean

@ComponentScan(value = "com.bean")
@PropertySource("classpath:/config.properties")//加载外部配置文件
@Configuration
public class life_annotation {}

九,注解方式自动装配

Spring的自动装配有多种:

  1. Spring提供的@Autowried,它可以结合@Qualifire或者@Primary一起使用
  2. java提供的JSR250标准提供的@Resource和@Inject
  3. 使用spring较为的底层的ApplicationAwarm接口进行装配

一,@Autowried进行装配

@Autowried注入分两种情况:

  1. 默认优先按照类型去容器中寻找组件装配
  2. 如果寻找到多个相同类型时,将属性的名称作为组件的id去容器中查找

@Autowried重要参数:

  • required:为true表示autowried必须装配完成否则会报异常,为flase假如无法进行装配,则会返回空

@Autowried标注的位置:它可以标注在属性,测试,构造器,方法上,实现的功能都是一样的

  • 标注在方法上:方法的参数就会从容器中获取装配,@Bean加+方法参数参数也是从容器中取和不加autowrite是一样的
  • 标注在构造上:构造的参数也从容器中获取,如果注解只有一个有参构造,可以省略autowrite,默认就会从容器中获取
  • 标注在属性上:使用最多的方式,按照类型从容器中获取组件进行装配
  • 标注在参数上:和标注在属性上是一样的

为了应对@Autwried的多个同类型问题,Spring提供两个注解(@Qualifire和@Primary)进行组合使用:

  • @Qualifire:指定需要装配的组件id,不再使用属性命作为id
  • @Primary:标注为首选Bean,在@Autwried装配时同类型时,会优先装配首选的bean

注:假如@Qualifire和@Primary同时存在,会优先@Qualifire注解

例:

① bean实例

public class bean_test {

    String name;
    int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

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

    public bean_test(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

②  bean配置类

@Configuration
public class autowried_config {

    //加载两相同类型的Bean
    @Bean
    public bean_test get_bean(){

        return new bean_test("FQ_LOVE",21);
    }

    @Primary
    @Bean
    public bean_test get_bean1(){

        return new bean_test("WQL_LOVE",21);
    }
}

③ 测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = autowried_config.class)
public class autowried {

    //required = false假如没有装配的bean就返回空,这样不会报错
    @Qualifier(value = "get_bean")
    @Autowired(required = false)
    bean_test get_bean3;

    @Test
    public  void test(){

        System.out.println(get_bean3);

    }
}

二,@Resource和@Inject进行装配

@Resource和@inject都是java提供的规范,它都是单都使用的,并没有依赖配合关系

@Resource的概念:

  • 默认按照组件名称id进行装配
  • 有name参数,可以指定ID进行装配
  • 和@Autowried不一样,它不支持required和@Primary

@Inject的概念:

  • 需要导入javax.Inject第三方包
  • 它的使用和@Autowrited是一样的,优先按照类型进行装配,当它没有required=false属性

一,@Resource装配

① bean实例和上面一样

② 配置bean

@Configuration
public class resource_config {

    @Bean("get_bean_test")
    public bean_test get_bean_test(){

        return new bean_test("EDG",1);
    }
}
③ 测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {resource_config.class})
public class autowried {

    @Resource(name = "get_bean_test")
    bean_test bean;

    @Test
    public  void test1(){
        System.out.println(bean);
    }
}

二,@Inject装配

导入inject的mave依赖:

<dependency>
    <groupId>javax.inject</groupId>
    <artifactId>javax.inject</artifactId>
    <version>1</version>
</dependency>

① bean实例和上面一样

② 配置bean

@Configuration
public class inject_config {


    @Bean
    public bean_test get_bean4(){
        return new bean_test("DK",2);
    }
}

③ 测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {autowried_config.class, resource_config.class, inject_config.class})
public class autowried {

    @Qualifier(value = "get_bean4")
    @Inject
    bean_test bean1;

    @Test
    public  void test2(){
        System.out.println(bean1);
    }
}

三,使用ApplicationAwarm自定义装配

自定义组件可以使用Spring容器底层的一些组件,比如:ApplicationContext,BeanFactory等

实现ApplicationAwarm自定义装配,必须实现ApplicationAwarm接口,这个接口有很多子接口,它们分别有不同的应用场景,通过调用这些接口里面的方法就可以进行注入装配

例:

① 实现接口

@Component
public class RpcServer implements ApplicationContextAware{

    private ApplicationContext context;


        @Override
    public void setApplicationContext(ApplicationContext applicationContext)
            throws BeansException {
        // TODO Auto-generated method stub
        context = applicationContext;       
    }   
     //获得applicationContext
    public static ApplicationContext getApplicationContext() {
        //assertContextInjected();
        return context;
    }    
    public static void clearHolder(){
        context=null;
    }
    //获取Bean
    public static <T> T getBean(Class<T> requiredType){
        //assertContextInjected();
        return (T) getApplicationContext().getBean(requiredType);
    }
    @SuppressWarnings("unchecked")
    public static <T> T getBean(String name){
        assertContextInjected();
        return (T) getApplicationContext().getBean(name);
    }    
    //判断application是否为空
    public static void assertContextInjected(){
        Validate.isTrue(context==null, "application未注入 ,请在springContext.xml中注入SpringHolder!");
    }
}

② 扫描bean

@ComponentScan(value = "com.bean")
@Configuration
public class life_annotation {}

十,@Profile注解

@Profile可以根据环动态对的激活和切换一系列组件的功能,它标注在@Bean上那么假如没有激活profile,bean将不注入

应用场景:假如开发某个模块开发环境,测试环境,生成环境,各不相同,这时就可以使用@Profile写几套程序,在不同的环境下激活

@Profile的参数:

  • value[]:指定组件在那个情况下才能激活被注册到容器中,不指定在任何情况下都能注册(默认default)

@Profile的作用域:

  • 标注在方法上:只对单个方法的bean起效
  • 标注在类上:对整个类都起效

@Profile激活的方式:

1,使用命令行动态参数:在虚拟机参数为加-Dspring.profiles.active=环境标识

2,使用代码的方式:

//1,通过注解配置类方式获取上下文
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();

//2,设置需要激活的环境(比如下面就是激活dev和test环境)
applicationContext.getEnvironment().setActiveProfiles("dev","test");

//3,注册主配置类
applicationContext.register(c3p0_config.class);

//4,启动刷新容器
applicationContext.refresh();
例:使用C3p0做为数据源,定义三份数据源,在不同环境中生效
 
① 外部配置文件(jdbc.properties)
username=root
password=123
drive=com.mysql.jdbc.Driver

② 配置类

@PropertySource("classpath:/jdbc.properties")
@Configuration
public class c3p0_config {

    @Value("${username}")
    String username;
    @Value("${password}")
    String password;
    @Value("${drive}")
    String drive;

    @Profile("test")
    @Bean("testDataSource")
    public DataSource devDataSource() throws PropertyVetoException {
        ComboPooledDataSource comboPooledDataSource =new ComboPooledDataSource();
        comboPooledDataSource.setUser(username);
        comboPooledDataSource.setPassword(password);
        comboPooledDataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
        comboPooledDataSource.setDriverClass(drive);
        return comboPooledDataSource;
    }


    @Profile("dev")
    @Bean("devDataSource")
    public DataSource proDataSource() throws PropertyVetoException {
        ComboPooledDataSource comboPooledDataSource =new ComboPooledDataSource();
        comboPooledDataSource.setUser(username);
        comboPooledDataSource.setPassword(password);
        comboPooledDataSource.setJdbcUrl("jdbc:mysql://localhost:3306/wql");
        comboPooledDataSource.setDriverClass(drive);
        return comboPooledDataSource;
    }


    @Profile("pro")
    @Bean("proDataSource")
    public DataSource dataSource2() throws PropertyVetoException {
        ComboPooledDataSource comboPooledDataSource =new ComboPooledDataSource();
        comboPooledDataSource.setUser(username);
        comboPooledDataSource.setPassword(password);
        comboPooledDataSource.setJdbcUrl("jdbc:mysql://localhost:3306/bookstrom");
        comboPooledDataSource.setDriverClass(drive);
        return comboPooledDataSource;
    }
}

② 环境激活

public static void main(String[] args) {

    //1,通过注解配置类方式获取上下文
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();

    //2,激活dev环境
    applicationContext.getEnvironment().setActiveProfiles("dev");

    //3,注册主配置类
    applicationContext.register(c3p0_config.class);

    //4,启动刷新容器
    applicationContext.refresh();

    //获取所有的bean
    String[] names = applicationContext.getBeanDefinitionNames();

    //打印所有的名称
    for(String name:names){

        System.out.println(name);
    }}

 


路漫漫其修远兮,吾将上下而求索