Updated on 2017-06-07

Bean 的条件化注册

MyCondition

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;

public class MyCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Environment environment = context.getEnvironment();
        return environment.getProperty("OS").equals("Windows_NT");     注册条件
    }
}

AppConfig

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {
    @Bean
    @Conditional(MyCondition.class)     指示注册条件
    public String str() {
        return "Hello, Windows";
    }
}

Main

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

        System.out.println(context.containsBean("str"));

        context.destroy();
    }
}

----
输出
true

Bean 的装配歧义

food.Dessert

package food;

import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;

public interface Dessert {
}

@Component
@Primary     首选也可用在 @Bean
@Qualifier("cake")     限定也可用在 @Bean)(显式指定 Bean 的限定符
class Cake implements Dessert {
}

@Component
class IceCream implements Dessert {
}

AppConfig

import food.Dessert;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan("food")
public class AppConfig {
    private Dessert dessert;

    @Autowired
    @Qualifier("cake")     限定也可用在 @Bean
    public void setDessert(Dessert dessert) {
        this.dessert = dessert;
    }
}

Bean 的限定符的缺省值为 Bean 的名称
Bean 的一名称的缺省值为 Bean 的类名@Component或者方法名@Bean)。

Bean 的作用域

  • 单例:每个 容器 创建一个。(Singleton)(默认)
  • 原型:每次 注入 创建一个。(Prototype)
  • 会话:每个 会话 创建一个。(Session)
  • 请求:每个 请求 创建一个。(Request)

AppConfig

import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;

@Configuration
public class AppConfig {
    @Bean
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)    指示每次注入都新创建一个也可用在 @Component
    public String str() {
        return new String("ABC");
    }
}

Main

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

        System.out.println(context.getBean("str") == context.getBean("str"));

        context.destroy();
    }
}

----
输出
false

运行时值注入

123.properties

name=David

AppConfig

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;

@Configuration
@PropertySource("classpath:/123.properties")     指示加载属性文件至 Spring 中的 Environment
public class AppConfig {
    @Bean
    public String str1(Environment environment) {     方式一通过检索 Environment 获取属性
        return environment.getProperty("name", "缺省值");
    }

    @Bean
    public String str2(@Value("${name:缺省值}") String name) {     方式二属性占位符
        return name;
    }

    @Bean
    public String str3(@Value("#{environment['name']?:'缺省值'}") String name) {     方式三SpEL
        return name;
    }
}

Main

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

        System.out.println(context.getBean("str1"));
        System.out.println(context.getBean("str2"));
        System.out.println(context.getBean("str3"));

        context.destroy();
    }
}

----
输出
David
David
David

加载外部资源

Main

import org.springframework.context.support.GenericXmlApplicationContext;
import org.springframework.core.io.Resource;

import java.io.IOException;

public class Main {
    public static void main(String[] args) throws IOException {
        GenericXmlApplicationContext context = new GenericXmlApplicationContext("123.xml");

        Resource resource = context.getResource("classpath:123.xml");
//      Resource resource = context.getResource("file:C:\\123.txt");
//      Resource resource = context.getResource("http://localhost/spring.png");
        System.out.println(resource.getFilename());
        System.out.println(resource.contentLength());

        context.destroy();
    }
}

----
输出
123.xml
190

https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/core/io/ResourceLoader.html#getResource-java.lang.String-

面向切面编程

方法包装

无参

concert
Performance
package concert;

public interface Performance {
    void perform();
}
Concert
package concert;

import org.springframework.stereotype.Component;

@Component
public class Concert implements Performance {
    @Override
    public void perform() {
        System.out.println("音乐会演奏..");
    }
}
Audience
package concert;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Aspect     指示该类为切面
@Component     指示该类为组件Bean
public class Audience {     切面
    @Pointcut("execution(* concert.Performance.perform(..))")     切点
    public void performance() {
    }

    -------------------------------------------------------
    第一种方式

    @Before("performance()")     前置通知
    public void a() {
        System.out.println("观众入场就座");
    }

    @Before("performance()")     前置通知
    public void b() {
        System.out.println("观众关闭手机");
    }

    @AfterReturning("performance()")     后置通知成功返回
    public void c() {
        System.out.println("观众鼓掌喝彩");
    }

    @AfterThrowing("performance()")     后置通知发生异常
    public void d() {
        System.out.println("观众要求退款");
    }

    -------------------------------------------------------
    第二种方式

    @Around("performance()")     环绕通知
    public Object x(ProceedingJoinPoint pjp) {
        Object o = null;
        try {
            System.out.println("观众入场就座");
            System.out.println("观众关闭手机");
            o = pjp.proceed();
            System.out.println("观众鼓掌喝彩");
        } catch (Throwable e) {
            System.out.println("观众要求退款");
        }
        return o;
    }
}

execution 匹配执行方法连接点)。
AppConfig
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@EnableAspectJAutoProxy     启用 AspectJ 自动代理
@ComponentScan("concert")
public class AppConfig {
}
Main
import concert.Performance;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

        Performance performance = context.getBean(Performance.class);
        performance.perform();

        System.out.println(performance.getClass());

        context.destroy();
    }
}

----
输出
观众入场就座
观众关闭手机
音乐会演奏..
观众鼓掌喝彩
class com.sun.proxy.$Proxy16     使用基于接口的代理推荐

有参

log
Log
package log;

import org.springframework.stereotype.Component;

@Component
public class Log {
    public void print(Object o) {
        System.out.println(o);
    }
}
LogInterceptor
package log;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Aspect
@Component     组件Bean
public class LogInterceptor {     切面
    @Pointcut("execution(* log.Log.print(Object)) && args(i)")     切点
    public void logged(Object i) {
    }

    @Before("logged(j)")     前置通知
    public void capture(Object j) {
        System.out.println("捕获:" + j);
    }
}

execution 匹配执行方法连接点)。
args 指示将被通知方法的入参传递给通知方法。(Arguments
AppConfig
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@EnableAspectJAutoProxy     启用 AspectJ 自动代理
@ComponentScan("log")
public class AppConfig {
}
Main
import log.Log;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

        Log log = context.getBean(Log.class);
        log.print(1);
        log.print(2);
        log.print("A");

        System.out.println(log.getClass());

        context.destroy();
    }
}

----
输出
捕获:1
1
捕获:2
2
捕获:A
A
class log.Log$$EnhancerBySpringCGLIB$$72b5a387     由于 Bean 未实现任何接口使用 CGLib 生成基于类的代理不推荐

方法引入

Kotlin 原生支持扩展函数。