工作了半年,spring每天都在用,但对于它的了解只停留在表面。会使用,懂基础,但不够深入,所以想重新好好学一遍,并记录下学习的过程。记录的内容也会由浅入深,学习的方式主要围绕spring实战一书以及网上的相关博客。言归正传,接下来开始spring学习之旅。
一、spring核心
spring有两个核心特性,(dependency injection,DI)依赖注入和(Aspect-oriented programming , AOP )面向切面编程,它们的目的都是为了简化java的开发。
为了降低java开发的复杂性,Spring采取了四种关键策略(摘自spring实战)
1、基于POJO的轻量级和最小侵入性编程
2、通过依赖注入和面向接口实现松耦合
3、基于切面和惯例进行声明式编程
4、通过切面和模板减少样板式代码
二、bean的装配
在传统代码中,我们需要自己new一个对象,每个对象管理与自己相互协作的对象的引用,这样的代码将高度耦合与难以测试。通过DI,对象的依赖关系将由系统中负责协调各对象的第三方组件在创建对象时进行设定,对象就无需自行创建和管理它们的依赖关系。
创建对象之间依赖关系的行为通常称之为装配,spring有三种装配bean的方式。
1、在xml中进行显式配置
<bean id="CDPlayer" class="com.shiqy.dao.CDPlayer"></bean>
2、在java中进行显式配置
@Configuration
public class CDPlayerConfig {
@Bean
public CDPlayer cdPlayer(){
return new CDPlayer();
}
}
3、隐式的bean发现机制和自动装配
组件扫描:spring会自动发现应用上下文中所创建的bean
自动装配:spring自动满足bean之间的依赖
在xml中进行配置
<context:component-scan base-package="com.shiqy.dao"/>
java中显式配置
@Configuration
@ComponentScan
public class CDPlayerConfig {、
}
@ComponentScan默认会扫描与配置类相同的包,还可以为其设置属性value,@ComponentScan(”xxxx”),它会扫描这个基础包下的组件。如果有多个基础包,可以设置为@ComponentScan(basePackages={“xxxx”.”xxxx”}),除了这些还可以指定为包中所包含的类或接口,@ComponentScan(basePackageClasses={xxx.class,xxx.class})
bean配置完之后就是注入,注入有两种方式,分别是构造器注入和设值注入(setter注入)。
构造器注入
@Component
public class CDPlayer {
private SgtPeppers sgtPeppers;
@Autowired
public CDPlayer(SgtPeppers sgtPeppers){
this.sgtPeppers = sgtPeppers;
}
public void play() {
sgtPeppers.paly();
}
}
设值注入
@Component
public class CDPlayer {
private SgtPeppers sgtPeppers;
@Autowired
public void setSgtPeppers(SgtPeppers sgtPeppers){
this.sgtPeppers = sgtPeppers;
}
public void play() {
sgtPeppers.paly();
}
}
@Autowired注解将查找被标注的方法的入参类型的 Bean,并调用方法自动注入这些 Bean,不需要再去xml文件中配置 <constructor-arg>
或<property>
。
三、装配扩展
1、条件化的bean:Spring4引入了一个新的@Conditional
注解,被它注解的类只有在给定的条件为true时,才会创建这个bean。
首先我们定义一个接口
package com.shiqy.server;
/**
* Created by shiqy on 2018/3/20.
*/
public interface WhatToDo {
public void doSomething();
}
接下来编写一个类来实现它
package com.shiqy.server;
import org.springframework.context.annotation.Conditional;
import org.springframework.stereotype.Service;
/**
* Created by shiqy on 2018/3/20.
*/
@Service
@Conditional(PlayConditional.class)
public class PlayOutside implements WhatToDo {
@Override
public void doSomething() {
System.out.println("play outside!!!");
}
}
我们还需要一个配置类来装配bean
package com.shiqy.server;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
* Created by shiqy on 2018/3/20.
*/
@Configuration
@ComponentScan
public class DoConfig {
}
因为使用了@Conditional(PlayConditional.class)
注解,我们还需要一个PlayConditional.class
package com.shiqy.server;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
/**
* Created by shiqy on 2018/3/20.
*/
public class PlayConditional implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
return true;
}
}
这个类实现了Condition
接口,这是一个很简单的接口,只需要实现matches()
方法即可,方法中的逻辑由自己编写,当返回值为true时,被@Conditional
注解注释的bean才会被创建。
编写测试类
package com.shiqy.server;
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;
/**
* Created by shiqy on 2018/3/20.
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = DoConfig.class)
public class PlayTest {
@Autowired
private PlayOutside playOutside;
@Test
public void test(){
playOutside.doSomething();
}
}
测试结果为:play outside!!!