Spring Boot 之 Spring Java Config

摘要

Spring4 以后,官方推荐我们使用Java Config来代替applicationContext.xml,声明将Bean交给容器管理。
在Spring Boot中,Java Config的使用也已完全替代了applicationContext.xml。实现了xml的零配置。所以无论从Spring的演进,还是学习Spring Boot的需要,都应该深入学习Spring Java Config的使用方法。这篇文章主要从以下几个方面进行介绍:

  • Spring java Config 入门程序
  • bean标签的使用
  • bean的依赖
  • 自动扫描
  • import 和 importResource
  • properties文件的加载及占位
  • profile

Spring Java Config入门介绍及简单程序

回顾以前的applicationContext.xml配置方式,我们会将需要使用bean通过xml的形式来配置,那么Java Config的方式,不需要多思考,就可以判断我们应该将bean配置在一个Java文件中,而且这个Java文件应当被Spring容器所识别。我们看如下这个例子:

创建一个bean类
public class SomeBean {
    public void doWork() {
        System.out.println("do work...");
    }
}

其中,doWork是逻辑方法。

创建一个Config类
@Configuration
public class Config {
    @Bean
    public SomeBean someBean() {
        return new SomeBean();
    }
}

需要注意的是,我们在config类上添加了一个@configuration的注解,见名知意,我们可以理解为Spring中的配置类。在返回值为SomeBeansomeBean方法上我们添加了一个@Bean注解,也不难理解,返回的new SomeBean对象将交由Spring容器进行管理。

测试
public class Test {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
        SomeBean sb = context.getBean(SomeBean.class);
        sb.doWork();
    }
}

这里,我们创建了一个AnnotationConfigApplicationContext对象,传入了Config.class后,得到了SomeBean对象。

do work...

以上就是Spring Java Config 配置bean的最简单的程序,对不喜欢xml配置的开发人员还是比较有优势的。
我们知道,在xml中,一般是这样配置的:

<bean id="someBean" class="com.springboot.javaconfig.SomeBean" initMethod="init" destroyMethod="destroy" ref="otherBean" scope="singlon"

一个完整的bean配置包括了 id,class,initMethod,destroyMethodref,scope

那么,在Java Config如何配置这些属性呢?其实也是很简单的,我们修改第一个例子的代码:

public class SomeBean {

    private void init() {
        System.out.println("init...");
    }

    public void doWork() {
        System.out.println("do work...");
    }

    private void destroy() {
        System.out.println("destroy...");
    }

}

增加了init,destroy方法。

@Configuration
public class Config {

    @Bean(initMethod = "init",destroyMethod = "destroy")
    public SomeBean someBean() {
        return new SomeBean();
    }
}

在bean注解上,属性指向对应的方法名。

public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
        SomeBean sb1 = context.getBean(SomeBean.class);
        System.out.println(sb1);

        SomeBean sb2 = context.getBean(SomeBean.class);
        System.out.println(sb2);
        context.close();
    }
}

输出结果为:

init...
com.spring.SomeBean@16022d9d
com.spring.SomeBean@16022d9d
destroy...

从这个程序我们可以看出如下信息:

  • init,destroy方法都在相应的阶段被调用
  • 两次创建的SomeBean实例指向同一个地址,说明Spring容器给我们创建的SomeBean对象时单例的。
其它

如果希望创建了一个多例的bean,可以这样书写:

@Scope("prototype")
public class SomeBean {
}

如果希望使用id和class共同查找bean,可以这样书写:

SomeBean sb = context.getBean("someBean",SomeBean.class);

需要特别注意的是,Spring默认使用Config中的函数名作为该bean的id。

bean的依赖

在xml中,我们使用value来配置注入简单值,ref来配置注入其他对象,那么在Java Config中如何实现呢?我们来继续编写代码:

新建一个bean类
public class OtherBean {

}
修改SomeBean和Config类
public class SomeBean {

    @Autowired
    private OtherBean otherBean;

    public void sayHello() {
        System.out.println(otherBean);
    }

}
@Configuration
public class Config {

    @Bean
    public SomeBean someBean() {
        return new SomeBean;
    }

    @Bean
    public OtherBean otherBean () {
        return new OtherBean();
    }
}

此时,我们将OtherBean纳入spring容器进行管理,在SomeBean类中对OtherBean的实例进行注入。得到结果:

com.spring.OtherBean@51b279c9

由此,实现了Java Config中的依赖注入,只不过也就是将需要依赖的bean也加入Config类用bean以修饰。
当然,我们也可以不使用@AutoWired标签,而通过set方法注入。


@Configuration
public class Config {

    @Bean
    public SomeBean someBean(OtherBean otherBean) {
        SomeBean someBean = new SomeBean();
        // 1
        someBean.setOtherBean(new OtherBean());
        // 2
        someBean.setOtherBean(otherBean());

        // 3
        someBean.setOtherBean(otherBean);
        return someBean;
    }

    @Bean
    public OtherBean otherBean () {
        return new OtherBean();
    }
}

set注入方法比较多,可以自行创建一个OtherBean对象,因为都在Config类中,也可以调用otherBean()方法,也可以在参数列表中进行传入,参数名要使用OtherBean的方法名。因为OtherBean类型的可能有多个对象。

Java Config 和 注解配置混用

我们知道,spring为我们提供了诸如@component,@controller,@service,@repositroy这类标签,它们增加了类的语义,然后将对应的类加入到了spring容器进行管理,也是避免了在xml中去配置。将Java Config和注解配置混用是我们在日常开发中经常使用的方式,我们来看如下代码,模拟MVC三层架构:

LoginDAO:
@Repository
public class LoginDAO {
    public void login() {
        System.out.println("login...");
    }
}
LoginService
@Service
public class LoginService {

    @Autowired
    private LoginDao loginDao;


    public void login() {
        loginDao.login();
    }
}
LoginController
@RequestMapping("/user")
@RestController
public class LoginController {

    @Autowired
    private LoginService loginService;

    @RequestMapping(path = "/login",method = RequestMethod.POST)
    public void login() {
        loginService.login();
    }
}
Java Config
@ComponentScan(basePackages = "com.spring")
@Configuration
public class Config { 
}
public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);

        LoginController controller = context.getBean("loginController",LoginController.class);

        controller.login();
    }
}

输出结果:

login...

解读:我们并没有将LoginDAO,LoginService,LoginController纳入Config,用@Bean修饰,而是使用了注解配置,在Config类中通过ComponentScan标签,将它们纳入sping容器。通过测试, 我们发现,Java Config 和 注解配置的混合使用时可行的。

Java Config 和 applicationContext.xml混用

有时候,我们会遇到必须使用xml配置的情况,这时候,我们可以这样来操作:

创建applicationContext.xml文件
<bean id="xmlBean" class="com.spring.XMLBean"/>
修改Config
@ImportResource("com/spring/applicationContext.xml")
@ComponentScan(basePackages = "com.spring")
@Configuration
public class Config {
}

运行结果:

com.spring.XMLBean@7bb58ca3

Config类上使用@ImportResource标签,就可以将在xml配置的bean引入到spring容器。

占位符配置

自定义datasource
public class DataSource {
    private String url;
    private String driverClass;
    private String userName;
    private String password;
    // get()
    // set()
    // constructor()
}

在xml中,我们配置datasource时往往将连接信息封装在db.properties中:

driverClass=com.mysql.jdbc.driver
url=jdbc:mysql://localhost:3306/my-database
username=root
password=root

在xml中使用占位的方式来获取:

<context:property-placeholder location="com/spring/db.properties"/>
<bean id="dataSource" class="com.spring.DataSource">
        <property name="url" value="${url}"/>
        <property name="driverClass" value="${driverClass}"/>
        <property name="userName" value="${user}"/>
        <property name="password" value="${password}"/>
</bean>
测试
public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
        DataSource dataSource = context.getBean("dataSource",DataSource.class);
        System.out.println(dataSource);
    }
}

输出结果:

DataSource{url='jdbc:mysql://localhost:3306/test', driverClass='com.mysql.jdbc.driver', userName='root', password='root'}

那么如何在Config中解读占位配置呢?继续编写代码:

以示区分,我们修改一下,db.properties:
driverClass=com.mysql.jdbc.driver
url=jdbc:mysql://localhost:3306/test
user=admin
password=admin
修改Config
//@ImportResource("com/spring/applicationContext.xml")
@PropertySource("com/spring/db.properties")
@ComponentScan(basePackages = "com.spring")
@Configuration
public class Config {

    @Autowired
    private Environment env;

    @Bean
    public DataSource dataSource() {
        return new DataSource(env.getProperty("url"),env.getProperty("driverClass"),
                env.getProperty("user"),env.getProperty("password"));
    }
}

此时,我们先是去掉了@ImportResource标签,因为已经不需要在xml中配置了,又使用了一个新的标签@PropertySource,来加载db,properties文件,然后又注入了一个env对象,Environment类继承了PropertyResolver接口,专门用来解析properties。

输出结果:

DataSource{url='jdbc:mysql://localhost:3306/test', driverClass='com.mysql.jdbc.driver', user='admin', password='admin'}

可以看到,加载properties文件,在Spring Java Config也提供了支持。

Profile

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343

推荐阅读更多精彩内容