SpringBoot入门到精通系列
- SpringBoot入门到精通-Spring的注解编程(一)
- SpringBoot入门到精通-SpringBoot入门(二)
- SpringBoot入门到精通-Spring的基本使用(三)
- SpringBoot入门到精通-SpringBoot集成SSM(四)
SpringBoot并不是一个新技术了,但是我发现很多程序员对SpringBoot的掌握并不成体系,或者说并不深入,看到该篇文章的你可能已经使用过SpringBoot了,但是你对SpirngBoot又了解多少呢,只是简单的使用还是有深入的认识。该系列文章将从零开始,一步一步由浅入深带你掌握好SpringBoot,同时又不失深度。
一.SpringBoot基础概念 1.什么是SpringBoot在SpringBoot问世以前的传统项目架构大多都会使用Spring作为业务层技术框架,通常采用XML作为Spring的配置文件,在XML配置文件中我们为了整合持久层框架,WEB层框架以及其他三方框架需要配置一大堆内容,大量的配置内容显得非常的繁杂,降低了程序员的工作效率。
在Spring 3.0增加了注解编程(Java Config)来简化XML配置,并且Spring框架本身也大量使用到注解的方式进行配置,在2013年,Pivotal团队开始研发基于JavaConfig配置的SpringBoot框架,设计目的是用阿里简化新Spring应用的搭建及开发,2014年4月,发布全新开源的轻量级框架的第一个SpringBoot版本。
到底什么是SpringBoot
以下内容摘抄于百度百科
Spring Boot是其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。通过这种方式,Spring Boot致力于在蓬勃发展的快速应用开发领域(rapid application development)成为领导者。
从百度百科的解释中我们可以总结出SpringBoot的设计初衷,首先SpringBoot肯定是基于Spring进行封装的,底层还是Spirng,它的设计目的是用来简化传统Spring应用的搭建和开发过程,有自己的特定配置方式。那么这个特定的配置方式指的是什么呢?就是上面有提到的Java Config。
所以简单理解:SpringBoot是基于Spring的注解进行配置(JavaConfig),设计目的是简化Spring的开发
。
上面我们有提到一个概念Java Config
那么到底什么是Java Config, 我们可以这样理解:Java可以认为是Java类,Config指的是配置,那么Java Config就是使用Java类作为Spring的配置文件,更直白一点就是把以前的XML配置文件中的内容全都搬到Java类进行配置。
同时Java Config也叫Spring的注解编程,因为在Spring的配置类中会大量使用到注解。
二.SpringBoot注解编程 1.Spring的IOC案例什么是IOC? 控制反转,就是把对象的创建,属性设置,初始化,销毁(Bean的生命周期)等工作都交给Spring来管理,实例化好的Bean会注入到Spring容器中,使用的时候直接从容器中获取,从而解放程序员的劳动力。那接下来我们先以XML配置方式来回顾一下Spring的IOC的用法,然后再使用注解编程方式(Java Config 也叫 Spring的注解编程)方式来演示IOC案例。
1.1.基于XML的IOC第一步:首先我们需要创建一个普通的Java项目,然后导入Spring的基础依赖,使用 spring-context就可以了,为了方便测再导入一个 spring-test依赖
junit
junit
4.12
org.springframework
spring-test
4.3.0.RELEASE
org.springframework
spring-context
4.3.0.RELEASE
第二步:接着我们在resource目录创建一个xml作为Spring配置文件,内容如下
第三步:编写实体类 Student
public class Student {
private Long id;
private String username;
public Student() {
}
public Student(Long id, String username) {
this.id = id;
this.username = username;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", username='" + username + '\'' +
'}';
}
}
第四步 : 编写测试类
//XML版本 IOC测试
public class StudentTest {
@Test
public void test() {
//创建IOC容器工厂类 , 加载配置 , 得到容器对象
ClassPathXmlApplicationContext applicationContext =
new ClassPathXmlApplicationContext("applicationContext.xml");
//从容器中获取Bean
Student bean = applicationContext.getBean(Student.class);
//打印
System.out.println(bean);
}
}
这里使用了一个 ClassPathXmlApplicationContext 来加载Spring的配置文件。ClassPathXmlApplicationContext 是IOC容器工厂类,可以默认从classpath加载指定配置。
根据上面案例我们可以总结出IOC的步骤:
- 首先需要有个xml配置文件,配置好Student
- 然后需要有个IOC工厂类去加载xml配置,Spring会自动把xml中配置的Bean注册到容器中
- 最后从容器中直接获取到Bean即可
第一步:创建一个配置类作为Spring的配置文件,用来代替xml配置文件,同时贴上@Configuration标记该类为Spring的配置类
//Spring配置注解,标记该类是Spring的配置类,该类的作用等同于applicationContext.xml
@Configuration
public class JavaConfig {
}
第二步:在配置类中注册Bean
//Spring配置注解,标记该类是Spring的配置类,该类的作用等同于applicationContext.xml
@Configuration
public class JavaConfig {
//注册一个Student到Spring容器中
@Bean
public Student student(){
return new Student();
}
}
@Bean贴在方法上,该方法的返回值会自动注册到Spring容器中。
第三步:编写测试类,使用 AnnotationConfigApplicationContext 来加载配置类
//XML版本 IOC测试
public class StudentTest {
@Test
public void test() {
//创建IOC容器工厂类 , 加载配置 , 得到容器对象
AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext(JavaConfig.class);
//从容器中获取Bean
Student bean = applicationContext.getBean(Student.class);
//打印
System.out.println(bean);
}
}
ClassPathXmlApplicationContext是针对xml的容器工厂,而AnnotationConfigApplicationContext是针对配置类的容器工厂。
总结一下:
- @Configuration : Spring的配置注解,标记某个类成为Spring配置类
- @Bean :用来在配置类中,注册Bean的注解,贴方法上方法的返回实例会被识别为Spring的Bean交给Spring管理
- AnnotationConfigApplicationContext :是针对配置类的容器工厂,SpringBoot中使用的也是这个工厂
下面我们来介绍一下@Bean来做一个全方位的认识,下面是它的结构
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean {
//Bean的名称,如果未指定就使用方法名作为Bean的名称
String[] name() default {};
//自动装配
Autowire autowire() default Autowire.NO;
//Bean的初始方法
String initMethod() default "";
//Bean的销毁方法
String destroyMethod() default "(inferred)";
}
这里稍作解释
- name : Bean的名称,如果未指定就使用方法名作为Bean的名称
- autowire :依赖项是否通过基于约定的自动装配按名称或类型注入
- initMethod : 初始化方法
- destroyMethod : 销毁方法
根据上面的解释我们给之前的代码做些改造
//注册一个Student到Spring容器中
@Bean(name = "student" , //bean的名字
initMethod = "init", //初始化方法,需要给Student提供init方法
destroyMethod = "destory") //销毁方法,需要给Student提供destory方法
public Student student(){
return new Student(1L,"zs");
}
然后给Student提供两个方法 , 将我们再次执行测试,就可以看到init方法被执行。
public class Student {
//省略...
public void init(){
System.out.println("Bean的init被执行...");
}
public void destory(){
System.out.println("Bean的destory被执行...");
}
}
注意:destory方法并不会被执行,因为我们使用的是Junit的测试,如果想要destory正常执行,需要使用Spring-Test。
2.3.destory不执行问题Bean的Destory方法没被执行的原因是Spring容器没被正常关闭,因为我们使用的Junit的测试,测试类是没有交给Spring管理的,当测试方法执行完毕,整个程序也就终止,Spring容器没办法正常关闭,解决方案有两个:一是使用JDK1.7的自动关闭资源的新特性;二是使用Spring的测试,把测试类也交给Spring容器管理。
方式一:使用JDK1.7的自动关闭资源的新特性
//IOC测试
public class StudentTestBF {
@Test
public void test() {
try(
//创建IOC容器工厂类 , 加载配置 , 得到容器对象
AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext(JavaConfig.class)
){
//从容器中获取Bean
Student bean = applicationContext.getBean(Student.class);
//打印
System.out.println(bean);
}
}
}
测试效果如下
方式二:整合Spring的测试,使用@RunWith+@ContextConfiguration注解来完成,要注意的是现在的配置文件是一个配置类,需要通过ContextConfiguration的classes属性来指定。如下:
//IOC测试
@RunWith(SpringJUnit4ClassRunner.class)
//
@ContextConfiguration(classes = JavaConfig.class)
public class StudentTest {
//不再需要创建AnnotationConfigApplicationContext,直接注入即可
//@Autowired
//private ApplicationContext applicationContext;
@Autowired
private Student student;
@Test
public void test() {
//从容器中获取Bean
//Student student = applicationContext.getBean(Student.class);
//打印
System.out.println(student);
}
}
集成Spring的测试之后,我们可以通过注入 ApplicationContext 容器来获取Bean, 甚至直接注入需要的Bean,效果如下
这里我再介绍一种实现Spring的Bean的生命周期的方式,就是 通过@PostConstract+@PreDestory注解来完成,如下
public class Student {
...省略...
//PostConstruct : 该注解用于需要在依赖注入完成后执行任何初始化的方法
@PostConstruct
public void init2(){
}
//PreDestroy : 注释在方法上用作回调通知,以表明实例正在被容器移除
@PreDestroy
public void destory2(){
}
这里解释一下这2个注解
- @PostConstruct :标记了该注解的方法会在:对象的依赖注入完成后执行,可以用作对象的初始化
- @PreDestroy :当Spring容器移除该对象之前会触发标记了该注解的方法的调用,可以用来做一些扫尾工作
这里我介绍了2种生命周期方法,根据你自己的情况使用其中一种就行,当然还有其他的手段,比如通过实现 InitializingBean 和 DisposableBean 来完成,你可以自己去了解一下,这不是我们今天的重点。
2.3.Bean的ScopeSpring中的Bean默认情况下scope为singleton , 在Spring注解编程中提供了@Scope注解来指定,如下
//注册一个Student到Spring容器中
@Bean
@Scope("singleton") //单利,指定为 : prototype 就是多利
public Student student(){
return new Student(1L,"zs");
}
如果没有特殊的需求不建议去修改该属性,默认单利就好。
2.4.Bean的懒初始化在Spring中Bean默认是lazy=false , 在Spring启动的时候,会对 scope为singleton也就是单利的Bean,且没有配置lazy = true的普通Bean进行实例化。也就是说一个普通的Bean在Spring启动的过程中就会为该Bean进行实例化,然后把实例化后的Bean存储到单利Bean的容器中(一个ConcurrentHashMap中)。
在Spring的注解式编程中提供了@Lazy注解来修改是否要懒初始化,如下
@Bean
@Lazy(true) //懒初始化,使用到Bean的时候才会初始化
public Student student(){
return new Student(1L,"zs");
}
当然没有特殊情况此注解也不建议去加,默认就是迫切初始化的。
3.Bean的依赖入驻bean的依赖注入就是DI,我们的Bean交个了Spring去管理,那么Bean和Bean之间的依赖关系也需要Spring为我们去维护,这个就是Spring的属性注入(也叫依赖注入),即DI。在注解式编程中,注册Bean的方式不同,DI的方式也不同。
3.1.准备对象我们先来创建2个类,然后处理好依赖关系
第一步:创建一个Teacer类
public class Teacher {
}
第二步:创建Student类,它依赖了一个Teacher,同时我这里提供了一个构造器方便注入值
public class Student {
private Long id;
private String username;
//依赖teacher
private Teacher teacher;
public Student(Long id, String username, Teacher teacher) {
this.id = id;
this.username = username;
this.teacher = teacher;
}
...省略...
}
3.2.配置Bean的依赖关系
方式一:我们需要定义两个Bean,Teacher和Student ,然后还要为 Student中的Teacher属性注入值(DI)
//Spring配置注解,标记该类是Spring的配置类,该类的作用等同于applicationContext.xml
@Configuration
public class JavaConfig {
//注册一个Student到Spring容器中
@Bean
public Student student(){
//直接new 一个Teacher对象交给Student.
return new Student(1L,"zs",new Teacher());
}
上面我直接new了一个Teacher,交给Student。这种方式确实注入了一个Teacher给Stduent,但是Teacher本身本没有交个Spring管理。
方式二:通过调用Teancher的Bean的定义方法。如果要把Teacher交给Spring管理,那么就必须要定义Teacher的Bean。
//Spring配置注解,标记该类是Spring的配置类,该类的作用等同于applicationContext.xml
@Configuration
public class JavaConfig {
//注册一个Student到Spring容器中
@Bean
public Student student(){
return new Student(1L,"zs",teacher());
}
//注册一个Teacher到Spring容器中
@Bean
public Teacher teacher(){
return new Teacher();
}
}
上面是把Teacher和Student都注册成为Bean,交给Spring管理,然后Student中的Teacher依赖是通过调用teacher()方法来注入,注意:这种调方法的方式也是从容器中拿Bean的。但是这种方式的弊端是两个Bean的定义方法必须在同一个配置类
方式三:通过方法传入依赖对象
//Spring配置注解,标记该类是Spring的配置类,该类的作用等同于applicationContext.xml
@Configuration
public class JavaConfig {
//注册一个Student到Spring容器中
@Bean
public Student student(Teacher teacher){
return new Student(1L,"zs",teacher);
}
//注册一个Teacher到Spring容器中
@Bean
public Teacher teacher(){
return new Teacher();
}
}
上面代码对于Student的定义方法是传入了一个Teacher类注入给Student,这种方式Spring会自动从容器中找到Teacher,然后赋值给student方法的形参teacher,然后完成注入。这种方式的好处是如果依赖的Bean(比如这里的Teacher)不在当前配置类也可以注入。
4.@ComponentScan的使用 4.1.认识ComponentScan在之前的案例中我们是通过@Bean贴方法上
的方式来注册Bean,这本身也没有什么问题,但是当我的Bean特别多的时候,那么我们就需要写很多的方法非常的麻烦。在XML的配置方式中我们也是可以通过指定一个扫描包 的方式来开启IOC组件自动扫描,然后我们只需要在需要交给Spring管理的类上贴:
@Controller ,@Service,@Component,@Repository
等注解即可,当Spring启动的时候,就会自动去扫描指定的base-package包下的标记了如上注解的类,自动注入到Spring容器中。
在注解式编程中Spring也提供了一个注解@ComponentScan
来达到如上所述的相同效果。下面演示一下
第一步:对实体类进行改造,增加一个@Component 注解
//标记为Spring的组件
//@Controller
//@Service
//@Repository
@Component
public class Student {
private Long id;
private String username;
...省略...
}
第二步:配置类增加@ComponentScan
//Spring配置注解,标记该类是Spring的配置类,该类的作用等同于applicationContext.xml
@Configuration
//Spring的IOC自动扫描,可以自动多个扫描包
@ComponentScan(basePackages = {"cn.whale._03_ioc_scan"})
public class JavaConfig {
}
重点说一下 @ComponentScan注解,上面给该注解指定了一个basePackages扫描包路径,那么该注解就会扫描到该表下的贴了@Controller ,@Service,@Component,@Repository
的类,自动完成Bean的注解。如果没有指定basePackages该注解也会默认扫描当前包及其子包中的相关类。
需要注意:如果使用了IOC自动扫描的方式,就可以不使用@Bean的方式注册Bean了,切记。
第三步:执行测试代码,效果同上面案例,这里省略.
4.2.ComponentScan的属性ComponentScan提供了一些属性供我们使用,如下
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
//配置扫描的包路径
@AliasFor("basePackages")
String[] value() default {};
//配置扫描的包路径
@AliasFor("value")
String[] basePackages() default {};
//包含的资源过滤器
Filter[] includeFilters() default {};
//排除的资源过滤器
Filter[] excludeFilters() default {};
//指定是否延迟初始化Bean。默认迫切初始化
boolean lazyInit() default false;
}
4.3.排除资源excludeFilters
该注解中还提供了 lazyInit 来指定是否要懒初始化,还提供了 excludeFilters 来排除某些类不需要交给Spring管理,比如我同一个包下有Student和Teacher类都注解了@Component,但是我想排除Teacher不交给Spring管理,如下:
@Component
public class Student {
}
@Component
public class Teacher {
}
配置类通过excludeFilters来排除,如下
//Spring配置注解,标记该类是Spring的配置类,该类的作用等同于applicationContext.xml
@Configuration
//Spring的IOC自动扫描
@ComponentScan(basePackages = "cn.whale._03_ioc_scan",
//根据类型,排除Teacher这个类不要交给Spring管理
excludeFilters = { @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE , value = Teacher.class)}
)
public class JavaConfig {
}
type = FilterType.ASSIGNABLE_TYPE : 是通过类型来排除,也可以支持通过注解来排除,通过REGEX正则来排除等。
4.4.ComponentScansComponentScans是ComponentScan的复数形式,可以把多个ComponentScan进行组合,如下
//Spring配置注解,标记该类是Spring的配置类,该类的作用等同于applicationContext.xml
@Configuration
@ComponentScans({
@ComponentScan(basePackages = "cn.whale._03_ioc_scan",
//根据类型,排除Teacher这个类不要交给Spring管理
excludeFilters = { @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE , value = Teacher.class)}
),
@ComponentScan(basePackages = "cn.whale._04_ioc_scan",
//根据类型,排除Teacher这个类不要交给Spring管理
excludeFilters = { @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE , value = Teacher.class)}
),
})
public class JavaConfig {
}
这种用法比较复杂,更多的还是使用 @ComponentScan 就能满足我们的需求了。
4.5.Bean的依赖关系前面们在讲DI的时候,是使用定义方法的方式来注册Bean,而现在我们使用组件自动扫描,那么应该如何进行依赖注入的,实际上非常简单,使用@Autowired
注解自动注入即可:
@Component
public class Student {
//自动注入
@Autowired
private Teacher teacher;
...省略...
}
5.@Conditional
@Conditional
注解的作用是标记组件满足某种条件才会被创建,该注解通常贴在定义Bean的方法上,或者配置类上,它提供了一个 Class
第二步:编写配置类,导入xml配置
@Configuration
//读取配置文件
@ImportResource("classpath:applicationContext.xml")
public class JavaConfig {
}
第三步:编写测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = JavaConfig.class)
public class StudentTest {
@Autowired
private Student student;
@Test
public void test() {
//打印
System.out.println(student);
}
}
启动观察控制台,依然能够打赢出Student,说明xml配置中的内容也是生效的。
8.FactoryBeanFactoryBean是创建Bean的一种方式,FactoryBean是一个工厂Bean,可以生成某一个类型Bean实例,它最大的一个作用是:可以让我们自定义Bean的创建过程。我们来演示一个案例,使用FactoryBean的方式来创建Student实例。
8.1.编写工厂类第一步:创建实体类
public class Student {
}
第二步:定义工厂类,实现FactoryBean
public class StudentFactoryBean implements FactoryBean {
//Spring会自动触发 getObject方法拿到Bean的实例,注册到容器中
@Override
public Student getObject() throws Exception {
//这里可以定制创建实例的过程
return new Student();
}
//类型
@Override
public Class getObjectType() {
return Student.class;
}
//是否单利
@Override
public boolean isSingleton() {
return true;
}
}
8.2.编写配置类
第三步:编写配置类 , 注册StudentFactoryBean
@Configuration
public class JavaConfig {
//注册工厂Bean
@Bean
public StudentFactoryBean student(){
return new StudentFactoryBean();
}
}
第四步:编写测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = JavaConfig.class)
public class StudentTest {
@Autowired
private Student student;
@Autowired
private StudentFactoryBean studentFactoryBean;
@Test
public void test() {
//打印
System.out.println(student);
System.out.println(studentFactoryBean);
}
}
打印效果如下
FactoryBean还是很有用的,比如Spring在整合Mybatis的时候,配置的 SqlSessionFactoryBean 就是使用该方式,好处是可以定制复杂的Bean的创建过程。
Spring的注解编程就讲到这里把,这篇文章是为SpirngBoot打下基础,下一篇文章我们正式进入SpringBoot的学习。
喜欢就给个好评吧,喜欢就给个好评吧,喜欢就给个好评吧.