Bootstrap

Spring Boot实战

Spring在java EE开发中是实际意义上的标准,但我们在开发Spring的时候可能会遇到以下令人头疼的问题:

1.大量配置文件的定义。
2.与第三方软件整合的技术问题。

Spring每个版本的退出都以减少配置作为自己的主要目标,例如:

1.推出@Component,@Service,@Repository,@Controller注解在类上声明Bean
2.推出@Configuration,@Bean的java配置来替代xml配置。

Spring Boot具有以下特征:

1.遵循“习惯优于配置”的原则,使用Spring Boot只需要很少的配置。大部分可以使用默认配置。
2.项目快速搭建,可无配置整合第三方框架。
3.可完全不使用xml配置,只使用自动配置java config
4.内嵌servlet容器,应用可用jar包运行。
5.运行中应用状态的监控。

虽然Spring Boot给我们带来了类似于脚本语言开发的效率,但Spring Boot里没有使用如何让你意外的技术,完全是一个单纯的基于Spring的应用如,Spring Boot的自动配置是通过Spring 4.x 的@Conditional注解来实现的

第一部分.点睛Spring 4.x

Chapter1.Spring基础

Spring的简史:

1.xml配置

在Spring 1.x时代,使用Spring开发满眼都是xml配置的Bean,随着项目的扩大,我们需要xml配置文件分放到不同的配置文件里,那时候需要频繁地在开发的类和配置文件中切换。

2.注解配置

在Spring 2.x时代,随着JDK 1.5带来的注解支持,Spring提供了声明Bean的注解(如@Component,@Service),大大减少了配置量。这时Spring圈子里存在着一种争论:注解配置和xml配置究竟哪个好?我们最终的选择是应用的基本配置(如数据库配置)用xml,业务配置用注解

3.java配置

Spring3.x到现在,Spring提供了java配置的能力,使用java配置可以让你更理解你配置的Bean。我们目前刚好处于这个时代,Spring 4.x和Spring Boot都推荐使用java配置,所以我们在本书通篇将使用java配置。

Spring使用简单的POJO(plain old java object,即无任何限制的普通java对象)来进行企业化开发,每一个被Spring管理的java对象都称之为Bean。而Spring提供了一个IoC容器用来初始化对象,解决对象间的依赖管理和对象的使用。

Spring的生态:

1.Spring Boot:使用默认开发配置来实现快速开发
2.Spring Data:对主流的关系型和NoSQL数据库的支持
3.Spring Security:通过认证和授权保护应用
4.Spring Web Flow:基于Spring MVC提供基于向导流程式的Web应用开发
5.Spring Web Services:提供了基于协议有限的SOAP/Web服务
6.Spring Session:提供一个API及实现来管理用户会话信息
等等,还有很多。

Spring框架本身有四大原则

1.使用POJO进行轻量级和最小侵入式开发
2.通过依赖注入和基于接口编程实现松耦合
3.通过AOP和默认习惯进行声明式编程
4.通过AOP和模板(template)减少模块化代码

我们经常说的控制翻转(inversion of control-IOC)和依赖注入(dependency injection-DI)在Spring环境下是等同的概念,控制翻转是通过依赖注入实现的。所谓的依赖注入指的是容器负责创建对象和维护对象间的依赖关系,而不是通过对象本身自己的创建和解决自己的依赖。比如典型的:

@Autowired
MyBean bean;

而不是自己new出一个对象

声明Bean的注解:

1.@Component组件,没有明确的角色
2.Service在业务逻辑层(service层)使用
3.@Repository在数据访问层(dao层)使用
4.@Controller在展现层(MVC->Spring MVC)使用

注入Bean的注解:

1.@Autowired,Spring提供的注解(推荐使用)
2.@Inject,JSR-330提供
3.@Resource,JSR-250提供

注入Bean的注解可以注解在set方法上或者属性上,不过最好是在属性上,优点是代码更少,层次更清晰。

@Configuration声明当前类是一个配置类。

使用@ComponentScan,自动扫描包名下所有使用@Service,@Component,@Compository,@Controller的类,并注册为Bean。

java配置是通过@Configuation和@Bean来实现的。

@Configuration声明当前类时一个配置类,相当于一个Spring配置的xml文件。
@Bean注解在方法上,声明当前方法的返回值为一个Bean。

何时使用java配置或者注解配置呢?
我们的原则是:全局配置使用java配置(如数据库相关配置,MVC相关配置),业务Bean的配置使用注解配置

AOP:面向切面编程,相对于OOP面向对象编程

Spring的AOP的存在的目的是为了解耦。AOP可以让一组类共享相同的行为。在OOP中只能通过继承类和实现接口,来使代码的耦合度增强,且类继承只能为单继承,阻碍更多行为添加到一组类上,AOP弥补了OOP的不足。

注解注解本身是没有功能的,就和xml一样。注解和xml都是一种元数据,元数据即解释数据的数据,这就是所谓的配置。

Chapter2.Spring常用配置

Scope描述的是Spring容器如何新建Bean的实例的。有以下几种,通过@Scope注解来实现:

1.Singleton:一个Spring容器中只有一个Bean的实例,此为Spring的默认配置,全容器共享一个实例
2.Prototype:每次调用新建一个Bean的实例。
3.Request:Web项目中,给每一个http request新建一个Bean实例。
4.Session:Web项目中,给每一个http session新建一个Bean实例。

Spring EL-Spring表达式语言,支持在xml和注解中使用表达式,类似于JSP的EL表达式语言。

示例:

//注入普通字符串
@Value("I Love You!") //1
private String normal;
//注入操作系统属性
@Value("#{systemProperties['os.name']}") //2
private String osName;
//注入表达式结果
@Value("#{ T(java.lang.Math).random() * 100.0 }") //3
private double randomNumber;

在实际开发的时候,经常会遇到Bean在使用使用之前或者之后做些必要的操作。在使用java配置和注解配置下提供了如下两种方式:

1.java配置方式:使用@Bean的initMethoddestroyMethod
2.注解方式:利用JSR-250的@PostConstruct@PreDestroy

Spring的事件(Application Event)为Bean与Bean之间的消息通信提供了支持。当一个Bean处理完一个任务之后,希望另外一个Bean知道并能做相应的处理,这时我们就需要让另一个Bean监听当前Bean说发送的事件。

Spring的事件需要遵循如下流程:

1.自定义事件,继承ApplicationEvent
2.定义事件监听器,实现ApplicationListener
3.使用容器发布事件

具体例子看书籍源码即可。

Chapter3.Spring高级话题

Spring的依赖注入的最大亮点就是你所有的Bean对容器Spring容器的存在是没有意识的。即你可以将你的容器替换成别的容器。

但在实际项目中,不可避免要用到Spring容器本身的功能资源,这时你的Bean必须要意识到Spring容器的存在,才能调用Spring说提供的资源,这就是所谓的Spring Aware。其实,Spring Aware本来就是Spring设计用来框架内部使用的,若使用了Spring Aware,你的Bean将会和Spring框架耦合。

Spring Aware的目的是为了让Bean获得Spring容器的服务

多线程

Spring通过任务执行器(TaskExecutor)来实现多线程和并发编程。使用ThreadPoolTaskExecutor可实现一个基于线程池的TaskExecutor。而实际开发中任务一般是非阻碍的,即异步的,所以我们要在配置类中通过@EnableAsync开启对异步任务的支持。并通过在实际执行的Bean方法中使用@Async注解来声明其是一个异步任务。

计划任务

计划任务在Spring中的实现变得异常的简单。首先通过在配置类注解@EnableScheduling来开启对计划任务的支持,然后在要执行任务的方法上注解@Scheduled,声明这是一个计划任务。

Spring通过@Scheduled支持多种类型的计划任务,包含cron,fixDeley,fixedRate等。例如:

//每个5秒执行一次
@Scheduled(fixedRate=5000)
//UNIX或LINUX系统下的定时任务
@Scheduled(cron="0 28 11 ? * *")

组合注解和元注解

注解的大量使用,尤其相同的多个注解用到各个类或方法中,会相当繁琐。这就是所谓的样板代码,是Spring设计原则中要消除的代码。

所谓元注解其实就是可以注解到别的注解上的注解,被注解的注解称之为组合注解

@Enable*注解

通过观察@Enable*注解的源码,我们发现所有的注解都有一个@Import注解,@Import是用来导入配置类的,这也就意味着这些自动开启的实现其实是导入了一些自动配置的Bean。这些导入的配置主要分为三种类型:

1.直接导入配置类
2.依据调价选择配置类
3.动态注册Bean

第二部分.点睛Spring MVC 4.x

Chapter4.Spring MVC基础

分清MVC和三层架构的不同:

MVC:Model+View+Controller(数据模型+视图+控制器)

三层架构:Presentation tier+Application tier+Data tier(展现层+应用层+数据访问层)

实际的MVC只存在三层架构中的展现层,M实际上是数据模型,是包含数据的对象。在Spring MVC中,有一个专门的类叫Model,用来和V之间的数据交互、传值;V指的是视图页面,包含JSP,Thymeleaf等。C当然就是控制器(Spring MVC的注解@Controller类)。

//Spring MVC配置
@Configuration
@EnableWebMvc
@EnableScheduling
@ComponentScan("com.wisely.highlight_springmvc4")
public class MyMvcConfig extends WebMvcConfigurerAdapter {
   

    @Bean
    public InternalResourceViewResolver viewResolver() {
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setPrefix("/WEB-INF/classes/views/");
        viewResolver.setSuffix(".jsp");
        viewResolver.setViewClass(JstlView.class);
        return viewResolver;
    }
}

注意这里对路径前缀的配置为/WEB-INF/classes/views/,不是和我们的开发的目录一样。因为看到的页面效果是运行时而不是开发时的代码,运行时代码会将我们的页面自动编写到/WEB-INF/classes/views/(因为我把JSP文件放在了/resources/views目录下)下。

@Controller
public class HelloController {
   

    @RequestMapping("/index")
    public  String hello(){

        return "index";
    }

}

通过上面的ViewResolver的Bean配置,返回值为index,说明我们的页面放置的路径为/WEB-INF/classes/views/index.jsp。

Spring MVC的常用注解

1.@Controller
2.@RequestMapping
3.@RequestBody
@RequestBody允许request的参数在request体中,而不是在直接链接在地址后面。
4.@ResponseBody
@ResponseBody支持将返回值放在response体内,而不是返回一个页面。
5.@PathVariable
6.@RestController
是一个组合注解,组合了@Controller和@ReponseBody。

示例:
添加jackson即相关依赖,获得对象和json或xml之间的转化。

特别指出:在实际项目中,我们主要支持json数据,没必要同时支持json和xml,因为json比xml更简洁,而且也更推荐。

public class DemoObj {
   
    private Long id;
    private String name;

    //jackson对对象和json做转换时一定需要此空构造
    public DemoObj() {
        super();
    }
    public DemoObj(Long id, String name) {
        super();
        this.id = id;
        this.name = name;
    }
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }



}

演示@Controller控制器:

@Controller 
;