Skip to main content
Jkyo Chen Blog

第一部分 Spring 的核心

高级装配 #

环境与 profile #

配置 profile bean #

激活 profile #

使用 profile进行测试 #

条件化的 bean #

public interface Condition {
    boolean matches(ConditionContext ctxt, AnnotatedTypeMetadata metadata);
}

public interface ConditionContext {
    BeanDefinitionRegistry getRegistry();
    ConfigurableListableBeanFactory getBeanFactory();
    Environment getEnvironment();
    ResourceLoader getResourceLoader();
    ClassLoader getClassLoader();
}

public interface AnnotatedTypeMetadata {
    boolean isAnnotated(String annotationType);
    Map getAnnotationAttributes(String annotationType);
    Map getAnnotationAttributes(String annotationType, boolean classValuesAsString);
    MultiValueMap getAllAnnotationAttributes(String annotationTyper);
    MultiValueMap getAllAnnotationAttributes(String annotationTyper, boolean classValuesAsString);
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
@Conditional(ProfileCondition.class) {
    String[] value();
}

处理自动装配的歧义性 #

标示首选的 bean #

@Component
@Primary
public class IceCream implements Dessert {...}

//Java显示配置
@Bean
@Primary
public Dessert iceCream {
    return new IceCream();
}

// XML

限定自动装配的 bean #

@Autowired
@Qualifier("iceCream")
public void setDessert(Dessert dessert) {
    this.dessert = dessert;
}

创建自定义的限定符 #

@Component
@Qualifier("cold")
public class IceCream implements Dessert {...}

@Autowired
@Qualifier("cold")
public void setDessert(Dessert dessert) {
    this.dessert = dessert;
}

// Java配置注解
@Bean
@Qualifier("cold")
public Dessert iceCream() {
    return new IceCream();
}

使用自定义的限定符注解 #

@Target({ElementType.CONSTRUCTOR, ElementType.FIELD, Element.METHOD, ElementType.TYPE)})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Cold { }

@Component
@Cold
@Creamy
public class IceCream implements Dessert {...}

// 注入点
@Autowired
@Cold
@Creamy
public void setDessert(Dessert dessert) {
    this.dessert = dessert;
}

bean 的作用域 #

@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class Notepad {...}

// 或使用@Scope("prototype")
// 但是使用SCOPE_PROTOTYPE常量更加安全并且不易出错

@Bean
@Scope(ConfigurableFactory.SCOPE_PROTOTYPE)
public Notepad notepad() {
    return new Notepad();
}

使用会话和请求作用域 #

在 XML 中声明作用域代理 #


    



// 要求它生成基于接口的代理

运行时值注入 #

注入外部的值 #

@Configuration
@PropertySource("classpatch:/com/soundsystem/app/properties")
public class ExpressiveConfig {
    @Autowired
    Environment env;

    @Bean
    public BlankDisc disc() {
        return new BlankDisc(
            env.getProperty("disc.title"),
            env.getProperty("disc.artist"));
    }
}

深入学习 Spring 的 Environment #

解析属性占位符 #

使用 Spring 表达式语言进行装配 #

表示字面值 #

引用 bean,属性和方法 #

#{artistSelector.selectArtist()?.toUpperCase()}
// 与使用“.”符号不同,"?."可以在访问它右边内容之前,确保它对应的元素不是null
// 如果artistSelector.selectArtist()返回值时是null的话,那么SpEL将不会调用toUpperCase()方法。表达式返回值是null

在表达式中使用类型 #

T(java.lang.Math) // 结果会是一个Class对象
T(java.lang.Math).PI
T(java.lang.Math).random()

SpEL 运算符 #

#{T(java.lang.math).PI * circle.radius ^ 2}
#{disc.title ' by ' + disc.artist}
#{counter.total == 100}
#{scoreboard.score > 1000 ? "Winner!" : "Loser"}

// 三元表达式一个常见的场景就是检查null,并用一个默认值来代替null
#{disc.title ?: 'Rattle and Hum'}
// 这种表达式通常称为Elvis运算符。

计算正则表达式 #

#{admin.email matches '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.com'}
// 返回Boolean值

计算集合 #

#{jukebox.songs[4].title}
#{'This is a test'[3]}

// 查询运算符(.?[]),对集合进行过滤
#{jukebox.songs.?[artist eq 'Aerosmith']}

// .^[], .$[]  分别用来在集合中查询第一个匹配项和最后一个匹配项
#{jukebox.sons.^[artist eq 'Aerosmith']}

// 投影运算符 .![], 会从集合的每个成员中选择特定的属性放到另外一个集合中。
#{jukebox.songs.![title]}

#{jukebox.songs.?[artist eq 'Aerosmith'].![title]}

面向切面的 Spring #

什么是面向切面编程 #

定义 AOP 术语 #

Spring 对 AOP 的支持 #

Spring 通知是 Java 编写的 #

Spring 在运行时通知对象 #

Spring 只支持方法级别的连接点 #

通过切点来选择连接点 #

编写切点 #

execution(* concert.Performance.perform(..))

// execution 在方法执行时触发
// * 返回任意类型
// concert.Performance 方法所属的类
// perform 方法
// .. 指定任意参数,表明切点要选择任意的perform()方法,无论该方法的入参是什么

execution(* concert.Performance.perform(..)) && within(concert.*)
// XML中使用and,not,or

在切点中选择 bean #

execution(* concert.Performance.perform() and bean('woodstock'))

使用注解创建切面 #

定义切面 #

@Aspect
public class Audience {
    @Before("execution(** concert.Performance.perform(..))")
    public void silenceCellPhones() {
        System.out.println("Silence cell phones");
    }

    @Before("execution(** concert.Performance.perform(..))")
    public void takeSeats() {
        System.out.println("Taking seats");
    }

    // 表演之后
    @AfterReturning("execution(** concert.Performance.perform(..))")
    public void applause() {
        System.out.println("CLAP CLAP CLAP!!!");
    }

    // 表演失败之后
    @AfterThrowing("execution(** concert.Performance.perform(..))")
    public void demandRefund() {
        System.out.println("Demanding a refund");
    }
}

创建环绕通知 #