用户工具

站点工具


code:java:spring-framework

Spring 框架介绍

Spring 框架:资源的创建管理不再由使用资源的人处理,而是交给 spring 容器,对资源的依赖,都由容器注入。最大的好处是:解耦。

基于 Java 的容器配置

核心概念是两个注解: @Bean 和 @Configuration

@Configuration 注解的类代表其包含了 Bean 的定义,就和 XML 配置文件里的 <beans/> 类似, @Bean 注解的方法就和 XML 配置文件里的 <bean/> 类似。

初始化 基于 Java 的容器配置

最直接的方式是通过 AnnotationConfigApplicationContext 这个 Spring 3 后新增的应用上下文实现类,示例代码如下:

@Configuration
public class SpringExamplesApplication {
 
    public class Foo {
    }
 
    @Bean
    public Foo foo() {
        return new Foo();
    }
 
 
    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringExamplesApplication.class);
        System.out.println(ctx.getBean(Foo.class)); //org.isouth.examples.SpringExamplesApplication$Foo@453da22c
    }
}

当然,其实也可以把添加了 @Configuration 注解的 Class 本身写到传统的 XML 配置文件里,当作一个普通的 Bean 进行实例化,这样就可以通过传统的 XML 配置文件来加载基于 Java 的 Spring 配置了(确保启用了 context 命名空间下的 <context:annotation-config/>)。

更加常用的方式是在 XML 配置文件里,引入 context 命名空间,然后使用 component-scan 指定让 Spring 扫描包路径下的注解类: <context:component-scan base-package=“org.isouth.examples”/>

@Bean 注解

@Bean 应用于方法上,通常被 @Bean 注解的方法一般要位于使用了 @Configuration 注解的类里(也可以位于 @Component 一类注解的类里,但是 Spring 对其的处理能力上要弱一些)。

在 @Configuration 注解的类里添加 @Bean 注解的方法,Spring 会执行对应的方法,并将方法的返回值作为一个 Bean 的实例,默认情况下使用方法名作为 Bean 的名称。比如:

@Configuration
public class SpringExamplesApplication {
 
    public class Foo {
 
    }
 
    @Bean
    public Foo foo() {
        return new Foo();
    }
 
}

也可以和 XML 配置类似,通过注解的属性指定 Bean 的名称,初始化方法,销毁方法等:

@Configuration
public class SpringExamplesApplication {
    public class Bar {
        public void init() {
        }
 
        public void destroy() {
        }
    }
 
    @Bean(name = "bar", initMethod = "init", destroyMethod = "destroy")
    public Bar bar() {
        return new Bar();
    }
}

初始化销毁方法也可以在类的对应方法上添加 @PostConstruct 或 @PreDestroy 注解(还可以实现 Spring 全系列的 *Aware 接口):

@Configuration
public class SpringExamplesApplication {
    public class Bar {
        @PostConstruct
        public void init() {
            System.out.println("init");
        }
 
        @PreDestroy
        public void destroy() {
            System.out.println("destroy");
        }
    }
 
    @Bean
    public Bar bar() {
        return new Bar();
    }
 
 
    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringExamplesApplication.class);
        Bar bar = ctx.getBean(Bar.class);
        System.out.println(bar);
    }
}

如果要声明的 Bean 还对其他 Bean 有依赖,可以在 Bean 的属性里直接通过 @Autowired 让 Spring 自动根据类型来进行查找注入依赖:

@Configuration
public class SpringExamplesApplication {
 
    public class Foo {
 
    }
 
    @Bean
    public Foo foo() {
        return new Foo();
    }
 
    public class Bar {
        @Autowired
        private Foo foo;
 
        public void init() {
        }
 
        public void destroy() {
        }
    }
 
    @Bean(name = "bar", initMethod = "init", destroyMethod = "destroy")
    public Bar bar() {
        return new Bar();
    }
 
 
    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringExamplesApplication.class);
        Bar bar = ctx.getBean(Bar.class);
        System.out.println(bar.foo); //org.isouth.examples.SpringExamplesApplication$Foo@6591f517
    }
}

也可以在添加了 @Bean 注解的方法上添加参数,Spring 会自动查找依赖,然后将依赖作为参数传入, 此时更便于通过代码逻辑来处理注入,尤其是注入一些配置属性时,可以方便地使用程序来调整默认值等:

@Configuration
public class SpringExamplesApplication {
 
    public class Foo {
 
    }
 
    @Bean
    public Foo foo() {
        return new Foo();
    }
 
    public class Bar {
        private Foo foo;
 
        public void init() {
        }
 
        public void destroy() {
        }
    }
 
    @Bean(name = "bar", initMethod = "init", destroyMethod = "destroy")
    public Bar bar(Foo foo) {
        Bar bar = new Bar();
        bar.foo = foo;
        return bar;
    }
 
 
    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringExamplesApplication.class);
        Bar bar = ctx.getBean(Bar.class);
        System.out.println(bar.foo); //org.isouth.examples.SpringExamplesApplication$Foo@7ff2a664
    }
}

假如依赖的资源本身是同一个 @Configuration 类里的其他 @Bean, 甚至可以在方法里直接调用其它 @Bean 方法来获取引用, Spring 会确保被依赖的 @Bean 方法只会被调用一次:

@Configuration
public class SpringExamplesApplication {
 
    public class Foo {
 
    }
 
    @Bean
    public Foo foo() {
        return new Foo();
    }
 
    public class Bar {
        private Foo foo;
 
        public void init() {
        }
 
        public void destroy() {
        }
    }
 
    @Bean(name = "bar", initMethod = "init", destroyMethod = "destroy")
    public Bar bar() {
        Bar bar = new Bar();
        bar.foo = foo();
        return bar;
    }
 
 
    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringExamplesApplication.class);
        Bar bar = ctx.getBean(Bar.class);
        System.out.println(bar.foo == ctx.getBean(Foo.class)); // true
    }
}

@Configuration 注解

@Configuration 注解应用在类上,使用了 @Configuration 的注解类为其内部包含的 @Bean 方法提供了各种强大的支持。@Configuration 注解类本身也包含一些其他的能力,比如:

把一个应用里所有的模块的 Bean 都声明都一个 Java 类里显然会显得比较杂乱和庞大,此时可以用 @Import 属性来组织多个 @Configuration 类

@Configuration
public class ConfigA {
  public @Bean A a() { return new A(); }
}
 
@Configuration
@Import(ConfigA.class)
public class ConfigB {
  public @Bean B b() { return new B(); }
}

此时 @Autowired 等注解一切工作正常,但是如果还想和前面一样通过直接调用依赖 Bean 的方法来获取 Bean 就必须先通过 @Autowired 注入其他 Bean 所在的 @Configuration 类了(@Configuration 类本身也是一个 Bean)

@Configuration
@Import(FooConfig.class)
public class SpringExamplesApplication {
 
    public class Foo {
 
    }
 
    @Bean
    public Foo foo() {
        return new Foo();
    }
 
 
    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringExamplesApplication.class);
        FooConfig.Bar bar = ctx.getBean(FooConfig.Bar.class);
        System.out.println(bar.foo == ctx.getBean(Foo.class)); //true
    }
}
 
@Configuration
class FooConfig {
 
    @Autowired
    private SpringExamplesApplication app;
 
    public class Bar {
        public SpringExamplesApplication.Foo foo;
    }
 
    @Bean
    public Bar bar() {
        Bar bar = new Bar();
        bar.foo = app.foo();
        return bar;
    }
}

除了导入其他 @Configuration 类外,还可以通过 @ImportResource 注解来导入其他的 XML 配置文件

@Configuration
@ImportResource("classpath:/META-INF/spring/xx.service.xml")
public class AppConfig {

@Configuration 本身还支持 Spring 的 @Profile 注解,可以方便地使用 @Profile 来区分生产、开发环境配置, 也可以使用 @PropertySource 注解来引入配置,引入的配置会被加载到 容器上下文的 Environment

参考

code/java/spring-framework.txt · 最后更改: 2018/12/31 18:41 (外部编辑)