Spring

Spring

依赖

1
2
3
4
5
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>

Main 入口

1
2
3
4
5
6
public static void main(String...args) {
ApplicationContext context =
new AnnotationConfigApplicationContext(Config.class);
Person person = (Person) context.getBean("person");
person.drive();
}

基于 Groovy

1
2
3
4
beans {
xmlns([ctx:'http://www.springframework.org/schema/context'])
ctx.'component-scan'('base-package':'com.zk')
}

把值放入 Spring 中 - @Component 方式

1
2
3
4
5
6
7
8
@Component
public class Truck implements Car {

public void go() {
System.out.println("Truck");
}

}

与此同时,还要开启 @ComponentScan 方式:

1
2
3
4
@Configuration
@ComponentScan
public class Config {
}

把值放入 Spring 中 - @Bean 方式

1
2
3
4
5
6
7
8
9
@Configuration
public class Config {

@Bean
public Car truck() {
return new Truck();
}

}

Spring 中获取值 - @Autowired

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Component
public class Person {

@Autowired
private Car car;

@Autowired
public Person(Car car) {
this.car = car;
}

@Autowired
public void setCar(Car car) {
this.car = car;
}

public void drive() {
car.go();
}

}

当自己 new 一个对象的时候,这个对象就不归 Spring 管理了:

1
2
3
4
5
6
7
@Service
public class Sample {

@AutoWired
Foo foo;

}

然后使用 new 来创建对象,其 foo 字段并不会被自动注解:

1
new Sample();

Spring 的多个可选值中获取值 - @Primary

当有多个合适的候选 @Bean 的时候,使用 @Primary 来指定默认想要获取的值:

1
2
3
4
5
6
7
8
9
10
@Configuration
public class Config {

@Bean
@Primary
public Car bicycle() {
return new Bicycle();
}

}

选择自己喜欢的口味

1
2
3
4
@Component
@Qualifier("cold")
@Qualifier("creamy")
public class IceCream implements Dessert { ... }
1
2
3
4
@Component
@Qualifier("cold")
@Qualifier("fruity")
public class Popsicle implements Dessert { ... }

注入:

1
2
3
4
5
6
@Autowired
@Qualifier("cold")
@Qualifier("creamy")
public void setDessert(Dessert dessert) {
this.dessert = dessert;
}

Bean 的作用域

By default, all beans created in the Spring application context are created as singletons. That is to say, no matter how many times a given bean is injected into other beans, it’s always the same instance that is injected each time.

Spring defines several scopes under which a bean can be created, including the following:

  • Singleton — One instance of the bean is created for the entire application.
  • Prototype — One instance of the bean is created every time the bean is injected into or retrieved from the Spring application context.
  • Session — In a web application, one instance of the bean is created for each session.
  • Request — In a web application, one instance of the bean is created for each request.
1
2
3
4
5
@Component
@Scope(value=WebApplicationContext.SCOPE_SESSION,
proxyMode=ScopedProxyMode.INTERFACES)
public ShoppingCart cart() {
}

As configured, proxyMode is set to ScopedProxyMode.INTERFACES, indicating that the proxy should implement the ShoppingCart interface and delegate to the implementation bean.

This is fine (and the most ideal proxy mode) as long as ShoppingCart is an interface and not a class. But if ShoppingCart is a concrete class, there’s no way Spring can create an interface-based proxy. Instead, it must use CGLib to generate a class-based proxy. So, if the bean type is a concrete class, you must set proxyMode to ScopedProxyMode.TARGET_CLASS to indicate that the proxy should be generated as an extension of the target class.

Bean 的生命周期

使用 initMethoddestroyMethod 方法来声明初始化和销毁的时候调用的方法:

1
2
3
4
5
6
7
8
9
10
@Configuration
public class Config {

@Primary
@Bean(initMethod = "init", destroyMethod = "destroy")
public Car bicycle() {
return new Bicycle();
}

}

与此同时,Bicycle 类中一定要有这两个方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Bicycle implements Car {

public void init() {
System.out.println("===create===");
}

public void go() {
System.out.println("Bicycle go");
}

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

}

调用 close 方法以便我们能够观察到 BicycledestroyMethod 方法的执行:

1
2
3
4
5
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(Config.class);
Person person = (Person) context.getBean("person");
person.drive();
context.close();

激活 Profile

调整环境参数:

1
2
3
4
5
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext();
context.getEnvironment().setActiveProfiles("production");
context.register(Config.class);
context.refresh();

所有涉及到候选 Bean 的地方都得表明 @Profile 的类型:

1
2
3
4
5
6
7
8
9
@Profile("production")
@Component
public class Automobile implements Car {
}

@Component
@Profile("dev")
public class Truck implements Car {
}
1
2
3
4
5
6
7
8
9
10
11
@Configuration
@ComponentScan
public class Config {

@Profile("dev")
@Bean(initMethod = "init", destroyMethod = "destroy")
public Car bicycle() {
return new Bicycle();
}

}

测试

添加依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.3.9.RELEASE</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>

编写测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = Config.class)
@ActiveProfiles("production")
public class CarInjectionTest {

@Autowired
private Car car;

@Test
public void carShouldBeAutomobile() {
Assert.assertTrue(car.toString().contains("Automobile"));
}

}

@ContextConfiguration 简介:

Prior to Spring 3.1, only path-based resource locations (typically XML configuration files) were supported. As of Spring 3.1, context loaders may choose to support either path-based or class-based resources. As of Spring 4.0.4, context loaders may choose to support path-based and class-based resources simultaneously. Consequently @ContextConfiguration can be used to declare either path-based resource locations (via the locations() or value() attribute) or annotated classes (via the classes() attribute).

1
2
@ContextConfiguration(locations = "classpath:spring-beans.xml")
@ContextConfiguration(classes = Config.class)

测试的时候,类上一定要标注上这两行注解,否则会抛出异常:

1
2
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = Config.class)

延迟初始化

1
2
3
4
5
6
7
8
@Bean("apkRedisPool")
@Lazy
public JedisPool urlRedisPool() {
JedisPoolConfig config = new JedisPoolConfig();
return new JedisPool(config,
crawlerConfig.getUrlRedisHost(),
crawlerConfig.getUrlRedisPort());
}

Config 文件中引入其他初始化顺序更早的类

1
2
3
4
5
6
7
8
9
10
11
12
13
@Configuration
@Import({
CrawlerConfig.class
})
@ComponentScan({
"com.appsec.system.interaction.config"
})
public class RootConfig implements AsyncConfigurer {

@Autowired
CrawlerConfig crawlerConfig;

}

构造注入和 Setter 注入相比于字段注入的好处

(1) 字段注入:

1
2
3
4
5
6
public class RedisOperationSingle implements RedisOperation {

@Autowired
RedisPool redisPool;

}

(2) 构造器注入:

1
2
3
4
5
6
7
8
9
10
public class RedisOperationSingle implements RedisOperation {

RedisPool redisPool;

@Autowired
public RedisOperationSingle(@Qualifier("redisPoolImplA") RedisPool redisPool) {
this.redisPool = redisPool;
}

}

(3) Setter 注入:

1
2
3
4
5
6
7
8
9
10
11
public class RedisOperationSingle implements RedisOperation {

RedisPool redisPool;

@Autowired
@Qualifier("redisPoolImplA")
public void setRedisPool(RedisPool redisPool) {
this.redisPool = redisPool;
}

}

构造器注入和 Setter 注入的数据源特别容易替换,参考 Autowire depending upon the subclass

Bean 初始化方法和销毁方法

1
2
3
4
5
6
7
8
9
10
11
12
13
public class CustomerService {

@PostConstruct
public void initIt() throws Exception {
System.out.println("Init method after properties are set : " + message);
}

@PreDestroy
public void cleanUp() throws Exception {
System.out.println("Spring Container is destroy! Customer clean up");
}

}

值得一提的是,将 @PostConstruct@PreDestroy 这两个注解放到父类方法上,对子类依然有效

注意: 这两个注解放到接口上是无效的

推荐文章