1.2spring条件装配

2018-09-26  本文已影响25人  谜00016

通俗解释什么是条件装配

从 Spring Framework 3.1 开始,允许在 Bean 装配时增加前置条件判断。这种在Bean装配时增加判断条件的装配叫做条件装配。

实现方式

spring条件装配实现主要有两种实现方式,一种是配置化条件装配,使用@profile注解来实现。另一种比较灵活,底层实现Condition接口来进行实现。

Coding

  1. @Profile注解方式
    需求:分别使用Java7和Java8方式来实现整数求和。
    定义一个接口CalculateService:
package com.wangming.service;

public interface CalculateService {
   int calculate(Integer ...arcgs);
}

分别使用java7和Java8两种方式实现该功能,注意两个实现类中的@Profile注解:

Java7CalculateServiceImpl

package com.wangming.service.impl;

import com.wangming.service.CalculateService;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;

@Service
@Profile("Java7")
public class Java7CalculateServiceImpl implements CalculateService {

    @Override
    public int calculate(Integer... arcgs) {
        System.out.println("java7实现");
        int sum=0;
        for (int i=0;i<arcgs.length;i++)
        {
            sum+=arcgs[i];
        }
        System.out.println("sum:"+sum);
        return sum;
    }

}

Java8CalculateServiceImpl:

package com.wangming.service.impl;

import com.wangming.service.CalculateService;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;

import java.util.stream.Stream;

@Service
@Profile("Java8")
public class Java8CalculateServiceImpl implements CalculateService {
    
    @Override
    public int calculate(Integer... arcgs) {
        System.out.println("java8实现");

        Integer sum = Stream.of(arcgs).reduce(0, Integer::sum);
        System.out.println("sum:"+sum);

        return sum;
    }

}

引导类,CalculateBootstrap:

package com.wangming.bootstrap;

import com.wangming.service.CalculateService;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication(scanBasePackages = "com.wangming.service")
public class CalculateBootstrap {


    public static void main(String[] args) {

        ConfigurableApplicationContext context =
                new SpringApplicationBuilder(CalculateBootstrap.class)
                .web(WebApplicationType.NONE)
                .profiles("Java8")
                .run(args);

        CalculateService bean = context.getBean(CalculateService.class);
        int calculate = bean.calculate(1, 2, 3);
        // 关闭上下文
        context.close();

    }
}

我们来执行一下看下结果,结果如下: image.png

可以看到程序结果是符合预期的。

  1. Condition接口实现
    在看这个接口之前我们来看下Profile注解方式是如何实现的呢?我们看下源码
    Profile注解源码:
 

package org.springframework.context.annotation;

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;

import org.springframework.core.env.AbstractEnvironment;
import org.springframework.core.env.ConfigurableEnvironment;

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

}

我们发现了这么一个注解@Conditional,见名知意,我们可以大胆推断配置注解的实现必然是在ProfileCondition这个类中进行了筛选,我们看下源码ProfileCondition:

 
package org.springframework.context.annotation;

import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.util.MultiValueMap;

/**
 * {@link Condition} that matches based on the value of a {@link Profile @Profile}
 * annotation.
 *
 * @author Chris Beams
 * @author Phillip Webb
 * @author Juergen Hoeller
 * @since 4.0
 */
class ProfileCondition implements Condition {

    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
        if (attrs != null) {
            for (Object value : attrs.get("value")) {
                if (context.getEnvironment().acceptsProfiles((String[]) value)) {
                    return true;
                }
            }
            return false;
        }
        return true;
    }

}

经过debug,我们发现确实是在此处经过了筛选通过。

好了,我们回到正题,没错,condition接口方式的实现正是如此的原理。我们看一个springframework中一个包org.springframework.boot.autoconfigure.condition image.png

这是该包下的部分注解及其实现类。我们以@ConditionalOnProperty为突破口,来稍微了解一下springboot是如何做到条件装配的。

我们来模拟实现一个自己的@MyConditionalOnProperty:

package com.wangming.condition;

import org.springframework.context.annotation.Conditional;

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
@Conditional({MyOnConditionProperty.class})
public @interface MyConditionOnProperty {

    String prefix() default "";

}

MyOnConditionProperty实现类代码:

package com.wangming.condition;

import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;

import java.util.Map;

public class MyOnConditionProperty implements org.springframework.context.annotation.Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Map<String, Object> annotationAttributes = metadata
                .getAnnotationAttributes(MyConditionOnProperty.class.getName());

        String prefix = String.valueOf(annotationAttributes.get("prefix"));

        return prefix.equals("pass");
    }
}

上述代码我们实现了一个怎样的功能呢?还是用示例来阐述,建一个引导类MyOnConditionPropertyBootstrap:

package com.wangming.bootstrap;

import com.wangming.condition.MyConditionOnProperty;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class MyOnConditionPropertyBootstrap {

    @Bean(name = "hi")
    @MyConditionOnProperty(prefix = "pass")
    public String hello() {
        return "hello";
    }

    public static void main(String[] args) {

        ConfigurableApplicationContext context =
                new SpringApplicationBuilder(MyOnConditionPropertyBootstrap.class)
                        .web(WebApplicationType.NONE)
                        .run(args);

        String bean = context.getBean("hi", String.class);

        // 关闭上下文
        context.close();

    }
}

我们在hello方法上打上我们自己的注解MyConditionOnProperty,并赋值prefix为"pass",这样我们在运行代码的时候,当从容器中获取该Bean的时候,会首先根据条件判断是否要装配这个Bean,此处的条件就是prefix="pass",因为我们在MyOnConditionProperty 实现类中做了筛选。当prefix的值不为"pass"的时候便不会装配该Bean。这种编程的方式实现条件装配,可以根据我们自己的业务需求来进行定制,因此比较灵活。

上一篇 下一篇

猜你喜欢

热点阅读