前言

一 Spring配置可选方案

Spring提供了三种主要的装配机制:

1)在XML中进行显示配置。

2)在Java中进行显示配置。

3)隐式的bean发现机制和自动装配。

这三种方式怎么选择,其实没有严格的要求,他们是可以搭配使用的,大部分取决于个人喜好和项目实际情况。

但是建议使用自动配置机制会让你在维护上面省心,代码简洁。显示配置越少越好,如果代码非得需要显示配置时,

比如要用某些第三方组件时,我们可以使用javaConfig配置,因为比XML功能更强大。最后,如果想要使用XML命名空间时,

并且在javaConfig中没有同样的实现时,才应该使用XML。这里主要看下自动化装配。

二 自动化装配Bean

Spring从两个角度来完成自动化装配:

1)组件扫描(component scanning):spring会自动发现应用上下文中所创建的Bean。

2)自动装配(autowiring):spring自动满足bean之间的依赖关系。

通过组件扫描和自动装配组合在一起能发挥出无限的威力,能够把显示配置降低到最少,谁用谁知道,谁用谁说好!

1 创建可被发现的Bean

创建一个接口

package com.jpeony.spring.hello;

/**
 * @author yihonglei
 */
public interface HelloService {
    void sayHello(String name);
}

创建一个实现类

package com.jpeony.spring.hello;

import org.springframework.stereotype.Component;

/**
 * 注解@Component表明该类会作为组件类,并告知spring要为这个类创建bean。
 *
 * @author yihonglei
 */
@Component
public class HelloServiceImpl implements HelloService{
    @Override
    public void sayHello(String name) {
        System.out.println("Hello:" + name);
    }
}

注解@Component表明该类会作为组件类,并告知spring要为这个类创建bean。

启用组件扫描

javaConfig配置类ApplicationConfig:

package com.jpeony.spring.hello;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

/**
 * 1. 注解@ComponentScan开启组件扫描,spring默认是关闭的;
 *    注解@ComponentScan扫描与Application类同级包以及子包下包含@Component的类,
 *    并在spring上下文中创建为一个bean;
 *
 * 2. 如果不想使用@ComponentScan默认扫描包,可以通过basePackages显示指定,
 *    比如: @ComponentScan(basePackages = {"com.jpeony.spring"})
 *    同时basePackages默认参数是数组,如果想指定多个扫描包,用逗号隔开就行。
 *    注解@ComponentScan还有另外一种方式就是通过basePackageClasses显示指定,
 *    比如: @ComponentScan(basePackageClasses = {HelloServiceImpl.class})
 *    含义就是扫描HelloServiceImpl类所在的包,也即是通过类反推出扫描包。
 *
 * @author yihonglei
 */
@Configuration
@ComponentScan
public class ApplicationConfig {

}

Spring组件扫描默认是不启用的,通过注解@ComponentScan启用组件扫描。如果没有其他配置的话,

@ComponentScan默认扫描与配置类相同的包。Spring会扫描与ApplicationConfig类所在包以及这个包下的所有子包,

查找带有@Component的注解类,并且在spring中自动创建为一个bean。

测试一下组件扫描能否发现HelloServiceImpl

package com.jpeony.spring;

import com.jpeony.spring.hello.ApplicationConfig;
import com.jpeony.spring.hello.HelloService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/**
 * 1. SpringJUnit4ClassRunner测试启动时,自动创建spring应用上下文;
 * 2. 注解@ContextConfiguration会告诉它需要在ApplicationConfig加载配置;
 * 3. 而在ApplicationConfig类中包含@Configuration,@ComponentScan,
 * 注解@ComponentScan开启组件扫描,spring默认是关闭的;
 * 注解@ComponentScan默认扫描与ApplicationConfig类同级包以
 * 及子包下包含@Component的类,spring上下文创建为一个bean;
 * 4. 如果不想使用@ComponentScan默认扫描包,可以通过basePackages显示指定,
 * 比如: @ComponentScan(basePackages = {"com.jpeony.spring"})
 * 同时basePackages默认参数是数组,如果想指定多个扫描包,用逗号隔开就行。
 * 注解@ComponentScan还有另外一种方式就是通过basePackageClasses显示指定,
 * 比如: @ComponentScan(basePackageClasses = {HelloServiceImpl.class})
 * 含义就是扫描HelloServiceImpl类所在的包,也即是通过类反推出扫描包。
 * 5. 默认创建bean的ID为注解@Component对应类首字母小写,比如:
 *
 * @Component public class HelloServiceImpl implements HelloService{
 * ......
 * }
 * 创建的bean的ID为helloServiceImpl,如果我们想要替换,可以显示指定,比如:
 * @Component("helloService") public class HelloServiceImpl implements HelloService{
 * ......
 * }
 * 创建出来的bean的ID就为helloService。
 * 6. spring除了@Component指定创建bean外,也可以用@Named替代,大部分情况下两者
 * 是可以互换的,个人偏好使用@Component。
 *
 * @author yihonglei
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = ApplicationConfig.class)
public class HelloTest {
    @Autowired
    private HelloService helloService;

    @Test
    public void testHello() {
        helloService.sayHello("Tom");
    }
}

2 为组件扫描的bean重命名

Spring应用上下文中每一个bean都会有一个ID,默认创建bean的ID为注解@Component对应类首字母小写,比如:

@Component
 public class HelloServiceImpl implements HelloService{
   ......
 }

创建的bean的ID为helloServiceImpl,如果我们想要替换,可以显示指定,比如:

@Component("helloService")
 public class HelloServiceImpl implements HelloService{
   ......
 }

创建出来的bean的ID就为helloService。Spring除了@Component指定创建bean外,还有另外一种bean的命名方式,

也可以用@Named替代,大部分情况下两者是可以互换的,个人偏好使用@Component。

3 设置组件扫描的基础包

注解@ComponentScan默认扫描与ApplicationConfig类同级包以及子包下包含@Component的类,

Spring上下文创建为一个bean;但是有些时候,我们想要扫描不同的包,我们可以配置@ComponentScan实现。

可以通过basePackages显示指定,比如:

@ComponentScan(basePackages = {"com.jpeony.spring"})

同时basePackages默认参数是数组,如果想指定多个扫描包,用逗号隔开就行。

注解@ComponentScan还有另外一种方式就是通过basePackageClasses显示指定,比如:

@ComponentScan(basePackageClasses = {HelloServiceImpl.class})

含义就是扫描HelloServiceImpl类所在的包,也即是通过类反推出扫描包。

4 通过为bean添加注解实现自动装配

自动装配就是让Spring自动满足bean依赖的一种方法,在满足依赖的过程中,

会在Spring应用上下文中寻找匹配某个bean需求的其他bean。为了声明要进行自动装配,

我们可以使用@Autowired注解,完成自动化装配,该注解可以用于构造器,以及setter方法上。

创建一个HappyNewYear类

package com.jpeony.spring.hello;

import org.springframework.stereotype.Component;

@Component
public class HappyNewYear {
    public void sayHappyNewYear() {
        System.out.println("Happy New Year!");
    }
}

修改HelloServiceImpl类,引入HappyNewYear,并调用HappyNewYear方法:

HelloService再加个void testAutowired();方法:

package com.jpeony.spring.hello;

/**
 * @author yihonglei
 */
public interface HelloService {
    void sayHello(String name);
    void testAutowired();
}
package com.jpeony.spring.hello;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * 注解@Component表明该类会作为组件类,并告知spring要为这个类创建bean。
 *
 * @author yihonglei
 */
@Component
public class HelloServiceImpl implements HelloService {
    /**
     * 测试自动装配
     */
    @Autowired
    private HappyNewYear happyNewYear;

    /*@Autowired
    public HelloServiceImpl(HappyNewYear happyNewYear) {
        this.happyNewYear = happyNewYear;
    }*/

    @Override
    public void testAutowired() {
        happyNewYear.sayHappyNewYear();
    }

    @Override
    public void sayHello(String name) {
        System.out.println("Hello:" + name);
    }
}

新建一个测试类,测试注解@Autowired是否自动装配bean:

package com.jpeony.spring;

import com.jpeony.spring.hello.ApplicationConfig;
import com.jpeony.spring.hello.HappyNewYear;
import com.jpeony.spring.hello.HelloService;
import com.jpeony.spring.hello.HelloServiceImpl;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/**
 * @author yihonglei
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = ApplicationConfig.class)
public class AutowiredTest {
    @Autowired
    private HappyNewYear happyNewYear;
    @Autowired
    private HelloService helloService;

    /**
     * 通过断言测试happyNewYear是否被注入到spring容器中。
     * 如果不为空,说明自动装配成功。
     *
     * @author yihonglei
     */
    @Test
    public void testHappyNewYear() {
        Assert.assertNotNull(happyNewYear);
    }

    /**
     * 调用HelloServiceImpl类中的testAutowired,
     * 而testAutowired方法中通过HappyNewYear类调用
     * 其sayHappyNewYear方法。
     *
     * @author yihonglei
     */
    @Test
    public void testAutowired() {
        helloService.testAutowired();
    }
}