Spring基础
Spring概述
- Spring是框架是一个轻量级的企业级开发的一站式解决方案。所谓解决方案就是可以基于Spring解决JavaEE开发的所有问题。Spring框架主要提供了IOC容器、AOP、数据访问、Web开发、消息、测试等相关技术的支持。
- Spring使用简单的POJO(无任何限制的普通Java对象)来进行企业级开发。每一个被Spring管理的对象称之为Bean;而Spring提供了一个IOC容器用来初始化对象,解决对象间的依赖管理和对象的使用
Spring基础配置
- Spring本身有四大原则
- 使用POJO进行轻量级和最小侵入式开发
- 通过依赖注入和基于接口编程实现松耦合
- 通过AOP和默认习惯进行声明式编程
- 使用AOP和模板减少模块化代码
依赖注入
- 我们经常说的控制反转(AOP)和依赖注入(DI)在Spring环境下是等同的概念,控制反转是通过依赖注入实现的。所谓的依赖注入指的是容器负责创建对象和维护对象间的依赖关系,而不是通过对象本身负责自己的创建和解决自己的依赖
- SpringIOC容器(ApplicationCoantext)负责创建Bean,并通过容器将功能类bean注入到需要的Bean中。Spring提供使用xml、注解、Java配置、groovy配置实现
- 无论是xml配置、注解配置还是Java配置,都被称为配置元数据。所谓元数据即描述数据的数据。元数据本身不觉被任何可执行的能力,只能通过外界代码来对这些元数据行解析后进行一些有意义操作。Spring容器解析这些配置元数据进行Bean初始化、配置和管理依赖。
- 声明Bean的注解:
- @Component组件,没有明确的角色
- @Service在特务逻辑层使用(Service层)
- @Repository在数据访问层使用(dao层)
- @Controller在表现层使用(Spring MVC)
- 注入Bean的注解:
- @Autowired:Spring提供的注解
- @Inject:JSR-330提供的注解
- @Resource:JSR-250提供的注解
Java配置
- Java配置是通过@Configuration和@Bean来实现的
- @Configuration声明当前类是一个配置类,相当于一个Spring配置的xml文件
- @Bean注解在方法上,声明当前方法的返回值为一个Bean
- 本书通篇使用Java配置和注解混合配置。使用原则:全局配置使用Java配置(如数据库相关配置、MVC相关配置),业务Bean的配置使用注解配置(@Service,@Component,@Repository,@Controller)
AOP
- AOP:面向切面编程,相对于OOP面向对象编程
- Spring的AOP的存在目的是为了解耦。AOP可以让一组类共享相同的行为。在OOP中只能通过集成类和实现接口,来使代码的耦合度增强,且类继承只能为单继承,阻碍更多行为添加到一组类上,AOP弥补了OOP的不足
- Spring支持AspectJ的注解式切面编程
- 使用@Aspect声明一个切面
- 使用@After、@Before、@Around定义建言(advice),可直接建拦截规则(切点)作为参数
- 其中@After、@Before、@Around参数的拦截规则为切点(PointCut),为了使切点复用,可使用@PointCut专门定义拦截规则,然后在@After、@Before、@Around的参数中调用
- 其中符合条件的每一个被拦截处为连接点(JoinPoint)
- 本节示例将演示基于注解拦截和基于方法规则拦截两种方式,演示一种模拟记录操作的日志系统的实现。其中注解式拦截能够很好地控制要拦截的粒度和获得更丰富的信息,Spring本身在事务处理(@Transcational)和数据缓存(@Cacheable)上面都是用这种形式拦截
Spring常用配置
Bean的Scope
- Scope描述的是Spring容器如何创建Bean的实例的。Spring的Scope有以下几种,通过@Scope注解来实现
Singleton:一个Spring容器中只有一个Bean的实例,此为Spring的默认配置,全容器共享一个实例
Prototype:每次调用新建一个Bean的实例
Request:Web项目中,给每一个Http request新建一个Bean实例
Session:Web项目中,给每一个Http Session新建一个Bean实例
GlobalSession:这个只在portal应用中有用,给每一个global http session新建一个Bean实例
另外,在Spring Beatch中还有一个Scope是使用@StepScope
<!-- 添加common-io可简化文件相关操作 -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.3</version>
</dependency>
Srping EL和资源调用
- Spring EL-Spring表达式语言,支持在xml和注解中使用表达式,类似于JSP的EL表达式
- Spring开发中经常涉及调用各种资源的情况,包含普通文件、网址、配置文件、系统环境变量等,我们可以使用Spring的表达式语言实现资源的注入
注入普通字符
注入操作系统属性
注入表达式运算结果
注入其他Bean的属性
注入文件内容
注入网址内容
注入属性文件
Bean的初始化和销毁
- 我们在实际开发的时候,经常会遇到在Bean在使用之前或之后做些必要的操作,Spring对Bean的生命周期的操作提供了支持。在使用Java配置和注解配置下提供如下两种方式:
- Java配置方式:使用@Bean的initMethod和destroyMethod(相当于xml配置的init-method和destory-method)
- 注解方式:利用JSR-250的@PostConstruct和@PreDestory
<!-- 增加JSR250支持 -->
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>jsr250-api</artifactId>
<version>1.0</version>
</dependency>
Profile
- Profile为在不同环境下使用不同的配置提供了支持(开发环境下的配置和生产环境下的配置肯定是不同的,如数据库的配置)
- 通过设定Environment的ActiveProfiles来设定当前context需要使用的配置环境,在开发中使用@Profile注解类或者方法,达到在不同情况下选择实例化不同的Bean
- 通过设置jvm的spring.profile.active参数来设置配置环境
- Web项目设置在Servlet的context parameter中
Servlet2.5及以下
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>spring.profiles.active</param-name>
<param-value>production</param-value>
</init-param>
</servlet>
Servlet3.0及以上
public class WebInit implements WebApplicationInitialzer {
@Override
public void onStartup(ServletContext container) throws ServletException {
container.setInitParameter("spring.profiles.default", "dev");
}
}
事件(Application Event)
- Spring的事件为Bean与Bean之间的消息通信提供了支持。当一个Bean处理完一个任务之后,希望另外一个Bean知道并能做相应的处理,这时我们就需要让另外一个Bean监听当前Bean所发送的事件
- Spring的事件需要遵循如下流程:
- 自定义事件,继承ApplicationEcent
- 定义事件监听器,实现ApplicationListener
- 使用容器发布事件
Spring高级话题
Spring Aware
- Spring的依赖注入的最大亮点就是你所有的Bean对Spring容器的存在是没有意识的。即你可以将你的容器替换成别的容器,如Google Guice,这时Bean之间的耦合度很低
- 但是在实际项目中,你不可避免的要用到Spring容器本身的功能资源,这时你的Bean必须要意识到Spring容器的存在,才能调用Spring所提供的资源,这就是所谓的Spring-Aware。其实Spring-Aware本来就是Spring设计用来框架内部使用的,若使用了Spring-Aware,你的Bean将会和Spring框架耦合
- Spring提供的Aware接口:
- BeanNameAware:获得到容器中Bean的名称
- BeanFactoryAware:获得当前beanFactory,这样可以调用容器的服务
- ApplicationContextAware:当前的applicationContext,这样可以调用容器的服务
- MessageSourceAware:获得messageSource,这样可以获得文本信息
- ApplicationEventPuvlisherAware:应用事件发布器,可以发布事件
- ResourceLoaderAware:获得资源加载器,可以获得外部资源文件
- Spring-Aware的目的是为了让Bean获得Spring容器的服务。因为ApplicationContext接口集成了MessageSource接口、ApplicationEventPublisher接口和ResourceLoader接口,所以Bean继承ApplicationContextAware可以获得Spring容器的所有服务,但原则上我们还是用到什么接口,就实现什么接口。
多线程
- Spring通过任务执行器(TaskExecutor)来实现多线程和并发编程。使用ThreadPoolTaskExecutor可实现一个基于线程池的TackExecutor。而实际开发中任务一般是非阻碍的,即异步的,所以我们要在配置类中通过@EnableAsync开启对一部任务的支持,并通过在实际执行的Bean的方法中使用@Async注解来声明其是一个异步任务
计划任务
- 从Spring3.1开发,计划任务在Spring中的实现变得异常的简单。首先通过在配置类注解@EnableScheduling来开启计划任务的支持,然后在要执行的计划任务的方法上注解@Scheduled,声明这是一个计划任务
- Spring通过@Scheduled支持多种类型的计划任务,包含cron、fixDelay、fixRate等
条件注解@Conditional
- 通过活动的profile,我们可以获得不同的Bean。Spring4提供了一个更通用的基于条件的Bean的创建,即使用@Conditional注解
- @Conditional根据满足某一个特定条件创建一个特定的Bean。比方说当某一个jar包在一个类路径下的时候,自动配置一个或多个Bean;或者只有某个Bean被创建才会创建另外一个Bean。总的来说就是根据特定条件来控制Bean的创建行为,这样我们利通这个特性进行一些自动的配置
组合注解和元注解
- 所谓的元注解其实就是可以注解到别的注解上的注解,被注解的注解被称为组合注解,组合注解具备元注解的功能。Spring的很多注解都可以作为元注解,而且Spring本身已经有很多组合注解,如@Configuration就是一个组合@Component注解,表明这个类其实也是一个Bean
@Enable*注解的工作原理
- 之前已经用过的@Enable*注解
- @EnableAspectJAutoProxy:开启对AspectJ自动代理的支持
- @EnableAsync:开启异步方法的支持
- @EnableScheduling:开启计划任务的支持
- @EnableWebMvc:开启Web MVC的配置支持
- @EnableConfigurationProperties:开启对@ConfigurationProperties注解配置Bean的支持
- @EnableJpaRepositories:开启对Spring Data JPA RePository的支持
- @EnableTransactionManagement:开启注解式事务的支持
- @EnableCacheing:开启注解式的缓存支持
- 通过观察这些@Enable*注解的源码,我们发现所有的注解都有一个@Import注解,@Import是用来导入配置类的,这也就意味着这些自动开启的实现其实是导入了一些自动配置的Bean。这些导入的配置方式主要分为以下三种类型。
第一类:直接导入配置类
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(SchedulingConfiguration.class)
@Documented
public @interface EnableScheduling{
}
- 直接导入配置类SchedulingConfiguration,这个类注解了@Configuration,且注册了一个scheduledAnnotationProcessor的Bean
@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class SchedulingConfiguration(){
@Bean(name = TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public SchedulingAnnotationBeanPostProcessor scheduledAnnotationProcessor(){
return new ScheduledAnnotationBeanPostProcessor();
}
}
}
第二类:依据条件选择配置类
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AsyncConfigurationSelector.class)
public @interface EnableAsync {
Class<? extends Annotation> annotation() default Annotation.class;
AdviceMode mode() default AdviceMode.PROXY;
int order() default Ordered.LOWSER_PROCEDENCE;
}
- AsyncConfigurationSelector通过条件来选择需要导入的配置类,AsyncConfigurationSelector的根接口为ImportSelector,这个接口需重写selectImport方法,在此方法内进行事先条件判断。
public class AsyncConfigurationSelector
extends AdviceModeImportSelector<EnableAsync> {
private static final String ASYNC_EXECUTION_ASPECT_CONFIGURARION_CLASS_NAME
= "org.springframework.scheduling.aspectj.AspectJAsyncConfiguration";
@Override
public String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
case PROXY :
return new String[] {ProxyAsyncConfiguration.class.getName()};
case ASPECTJ :
return new String[] {ASYNC_EXECUTION_ASPECT_CONFIGURARION_CLASS_NAME};
default :
return null;
}
}
}
第三类:动态注册Bean
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
boolean proxyTargetClass() default false;
}
- AspectJAutoProxyRegistrar实现了ImportBeanDefinitionRegistrar接口,ImportBeanDefinitionRegistrar的作用是在运行时自动添加Bean到已有的配置类,通过重写方法
registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry)
- 其中,AnnotationMetadata参数用来获得当前配置类上的注解;BeanDefinitionRegistry参数用来注册Bean
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIdNecessary(registry);
AnnotationArrributes enableAJAutoProxy = AnnotationConfigUtils.
attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
if(enableAJAutoProxy.getBoolean("proxyTargetClass")) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
}
}
测试
- 测试是开发工作中不可缺少的部分。单元测试只针对当前开发的类和方法进行测试,可以简单通过muni依赖来实现,对运行环境没有依赖;但是仅仅进行单元测试是不够的,它只能验证当前类或方法能否正常工作,而我们想要知道系统的各个部分组合在一起是否能正常工作,这就是集成测试存在的意义
- 集成测试一般需要来自不同层的不同对象的交互,如数据库、网络连接、IOC容器等。其实我们也经常通过运行程序,然后通过自己操作来完成类似于集成测试的流程。集成测试为我们提供了一种无序部署或运行程序来完成验证系统各部分是否能正常协同工作的能力
- Spring通过Spring-TestContext-Framework对集成测试提供顶级支持。它不依赖于特定的测试框架,即可使用Junit,也可使用TestNG
- 基于Maven构建的项目结构默认有关于测试的目录:src/test/java(测试代码)、src/test/resource(测试资源)
- Spring提供了一个SpringJUnit4ClassRunner类,它提供了Spring-TestContext-Freamework的功能。通过@ContextConfiguration来配置Application-Context,通过@ActiveProfiles确定活动的profile
<!-- spring test支持 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.3.5.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
</dependency>
Spring MVC基础
Spring MVC概述
- MVC:Model+View+Controller(数据模型+视图+控制器)
- 三层架构:Presentation+Application+Data(展现层+应用层+数据访问层)
- 那MVC和三层架构有什么关系呢?面试经常会问,一般面试者会说:MVC的M就是数据访问层,V就是展现层,C就是应用层。但实际上MVC只存在三层架构的展现层,M实际上是数据模型,是包含数据的对象。在Spring-MVC里,有一个专门的类叫Model,用来和V之间的数据交互、传值;V值的是视图页面,包含JSP、freeMarker、Velocity、Thymeleaf、Tile等;C当然就是控制器
Spring MVC项目快速搭建
- Spring-MVC提供了一个DispatcherServlet来开发Web应用。在Servlet2.5及以下的时候只要在web.xml下配置元素即可。本节将使用Servlet3.0+无Web.xml的配置方式,在Spring-MVC里实现WebApplicationInitializer接口便可实现等同于web.xml的配置
// Spring MVC配置
@Configuration
@EnableWebMvc
@ComponentScan("com.kings.springmvc")
public class MyMvcConfig {
@Bean
public InternalResourceViewResolver viewResolver(){
InternalResourceViewResolver viewResolver =
new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/classes/views/");
viewResolver.setSuffix(".jsp");
viewResolver.setViewClass(JstlView.class);
return viewResolver;
}
}
// Web配置
public class WebInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext context =
new AnnotationConfigWebApplicationContext();
context.register(MyMvcConfig.class);
context.setServletContext(servletContext);
Dynamic servlet = servletContext.addServlet("dispatcher", new DispatcherServlet(context));
servlet.addMapping("/");
servlet.setLoadOnStartup(1);
}
}
Spring MVC的常用注解
- Spring-MVC常用以下几个注解:
- @Controller
- @Controller注解在类上,表明这个类是Spring-MVC里的Controller,将其声明为Spring的一个Bean,DispatcherServlet会自动扫描注解了此注解的类,并将Web强求映射到注解了@RequestMapping的方法上。这里指出:在声明普通Bean的时候,使用@Component、@Service、@Repository和@Contriller是等同的,因为@Service、@Repository、@Controller都组合了Component元注解;但在Spring MVC声明控制器Bean的时候,只能使用@Controller
- @RequestMapping
- @RequestMapping注解是用来映射Web请求、处理类和方法的。@RequestMapping可注解在类或方法上。注解在方法上的@ReqiestMapping路径会继承注解在类上的路径,@RequestMapping支持Servlet的request和response作为参数,也支持对request和response的媒体类型进行配置
- @ResponseBody
- @ResponseBody支持将返回值放在response体内,而不是返回一个页面。我们在很多基于Ajax的程序的时候,可以以此注解返回数据而不是页面;此注解可防止在返回值前或方法上
- @RequestBody
- @RequestBody允许request的参数在request体中,而不是在直接链接在地址后面。此注解放置在参数前
- @PathVariable
- @PathVariable用来接受路径参数,如/news/001,可接受001作为参数,此注解放置在参数前
- @RestController
- @RestController是一个组合注解,组合了@Controller和@ResponseBody,这就意味着当你只开发一个和页面家户数据的控制的是偶,需要使用此注解。若没有此注解,要想实现上述功能,则需要使用两个注解
Spring MVC基本配置
- Spring MVC的定制配置需要类继承一个WebMvcConfigurerAdapter类,并在此类使用@EnableWebMvc注解,来开启对Spring-MVC的配置支持,这样我们就可以重写这个类的方法,完成我们的常用配置。
静态资源映射
- 程序的静态文件(js,css,图片)等需要直接访问,这时我们可以在配置里重写addResourceHandlers方法来实现
拦截器配置
- 拦截器(Intercepter)实现对每一个请求处理前后进行相关的业务处理,类似于Servlet的Filter。
- 可让普通的Bean实现HanlderIntercepter接口或者继承HandlerInterceptorAdapter类来实现自定义拦截器
@ControllerAdvice
- 通过@ControllerAdvice,我们可以将对于控制器的全局配置放置在同一位置,注解了@Controller的类的方法可使用@ExceptionHandler、@InitBinder、@ModelAttribute注解到方法上,这对所有注解了@RequestMapping的控制器内的方法有效
- @ExceptionHandler:用于全局处理控制器里的异常
- @InitBinder:用来设置WebDataBinder,WebDataBinder用来自动绑定前台请求参数到Model中
- @ModelAttribute:本来的作用是绑定键值对到Model里,此处是让全局的@RequestMapping都能获得在此处设置的键值对
- @ExceptionHandler:处理全局异常,更人性化的将异常输出给用户
其他配置
- 快捷的ViewController
// 在配置类中重写addViewControllers方法
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/index").setViewName("/index");
}
- 路径匹配参数设置
- 在Spring-MVC中,路径参数如果带".“的话,”.“后面的值将被忽略,可以通过重写configurePathMatch方法可不忽略”."后面的参数
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.setUseSuffixPatternMatch(false);
}
Spring MVC的高级配置
文件上传配置
- 文件上传是一个项目中经常要用的功能,Spring-MVC通过配置一个multipartResolver来上传文件
- 在Spring的控制器中,通过MultipartFile-file来接受文件,通过MultipartFile[]-files接受多个文件上传
自定义HttpMessageConverter
- HttpMessageConverter是用来处理request和response里的数据的。Spring为我们内置了大量的HttpMessageConverter,例如MappingJachson2HttpMessageConverter、SpringHttpMessageConverter等。
- 在配置类中配置自定义的HttpMessageConverter的Bean,在Spring-MVC里注册HttpMessageConverter有两个方法:
- configureMessageConverters:重载会覆盖掉Spring-MVC默认注册的多个HttpMessageConverter
- extendMessageConverters:仅添加一个自定义的HttpMessageConverter,不覆盖默认注册的HttpMessageConverter
服务器端推送技术
- 服务器端推送技术在我们日常开发中比较常用,可能早期很多人的解决方案是使用Ajax向服务器轮询消息,是浏览器尽可能在第一时间获得服务端的信息,因为这种方式的轮询频率不好控制,所以大大增加了服务端的压力
- 本节的服务器端推送的方案都是基于:当客户端向服务端发送请求,服务端会抓住这个请求不放,等有数据更新的时候才返回给客户端,当客户端接收到消息后,再向服务端发送请求,周而复始。这种方式减少了服务器的请求数量,大大减少了服务器的压力
- 除了服务器端推送技术以外,还有一个另外的双向通信技术-WebSocket
- 将提供基于SSE(Server-Send-Event服务端发送事件)的服务器端推送和基于Servlet3.0+的异步方法特性,其中第一种方式需要形式浏览器的支持,第二种方式是跨浏览器的
Servlet3.0异步方法处理
- 在Web配置中开启异步方法支持
Dynamic servlet = servletContext.addServlet("dispatcher",
new DispatcherServlet(context));
servlet.addMapping("/");
servlet.setLoadOnStartup(1);
servlet.setAsyncSupported(true); // 开启异步方法支持
- 在Spring-MVC中开启计划任务的支持
@Configuration
@EnableWebMvc // 开启SpringMVC支持
@EnableScheduling // 开启计划任务的支持
@ComponentScan("com.kings.springmvc")
Spring MVC的测试
- 测试是保证软件质量的关键。为了测试Web项目通常不需要启动项目,我们需要一些Servlet相关的模拟对象,比如:MockMVC,MockHttpServletRequest,MockHttpServletResponse,MockHttpSession等
- 在Spring里,我们使用WebAppConfiguration指定加载的ApplicationContext是一个WebApplocationContext
- 其实在现实开发中,我们是先有需求,也就是说先知道我们想要的是什么样的,然后按照我们想要的样子去开发。在这里我也要引入一个概念叫测试驱动开发(Test-Driven-Development,TDD),设计人员按照需求先写一个自己预期结果的测试用例,这个测试用例刚开始肯定是失败的测试,随着不断的编码和重构,最终让测试用例通过测试,这样才能保证软件的质量和可控性
<!-- 添加测试依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
Spring Boot基础
Spring Boot概述
什么是Spring Boot
- 随着动态语言的流行(Ruby,Groovy,Scala,Node.js),Java的开发显得格外的笨重:繁多的配置、低下的开发效率、复杂的部署流程以及第三发技术的集成难度大
- Spring-Boot使用“习惯优先配置”(项目中存在大量的配置,此外还内置一个习惯性的配置,让你无序手动进行配置)的理念让你的项目快速运行起来。使用Spring-Boot很容易创建一个独立运行(运行jar,内嵌Servlet容器)、准生产级别的基于Spring框架的项目,使用Spring-Boot你可以不用或者只需要很少的Spring配置
Spring Boot核心功能
- 1)独立运行的Spring项目
- Spring-Boot可以以jar包的形式独立运行,运行Spring-Boot项目只需如下命令
- Java -jar xxx.jar
- 2)内嵌Servlet容器
- Spring-Boot可选择内嵌Tomcat、Jetty或者Undertow,这样我们无需以war包形式的部署项目
- 3)提供starter简化Maven配置
- Spring提供了一系列的starter-pom来简化Maven的依赖加载
- 4)自动配置Spring
- Spring-Boot会根据在类路径中的jar包、类,为jar包里的类自动配置Bean,这样会极大地减少我们要使用的配置。当然Spring-Boot只是考虑了大多数的开发场景,并不是所有的场景,若在实际开发中我们需要自动配置Bean,而Spring-Boot没有提供支持,则可以自定义自动配置
- 5)准生产的应用监控
- Spring-Boot提供基于Http、ssh、teInet对运行时的项目进行监控
- 6)无代码生成和xml配置
- Spring-Boot的神奇的不是借助于代码生成来实现的,而是通过条件注解来实现的。这时Spring4.x提供的新特性
Spring Boot的优缺点
优点:
1、快速构建项目
2、对主流开发框架的无配置集成
3、项目可独立运行,无须外部依赖Servlet容器
4、提供运行时的应用监控
5、极大地提供了开发、部署效率
6、与云计算的天然集成
缺点:
1、书籍文档较少且不够深入
Spring Boot快速搭建
- 我们在此以Maven作为项目构建方式,Spring-Boot还支持以Gradle作为项目构建工具
- 部署形式以jar形式,当然也可以用传统的war形式
- 一般的IDE都会有Spring-Boot的快速创建方式,但是如果不使用快速创建,该如何创建Spring-Boot项目
<!-- 新创建空的Maven项目并修改其pom文件 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.6.RELEASE</version>
<relativePath />
</parent>
<!-- 添加WEB的依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 添加Spring Boot的编译插件 -->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
Spring Boot核心
基本配置
入口类和@SpringBootApplication
- Spring-Boot通常有一个名为*Applicaiton的入口类,入口类有一个main方法,这个main方法其实就是一个标准的Java应用的入口方法。在main方法中使用SpringApplication.run(*Application.class, args),启动Spring-Boot项目
- @SpringApplication是Spring-Boot的核心注解,它是一个组合注解
@Target(ElementType.TYPE)
@Retention(RetentionPoliy.RUNTIME)
@Documented
@Inherited
@Configuration
@CompontScan
public @interface SpringBootApplication {
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
- @SpringBootApplication注解主要组合了@Configuration、@EnableAutoConfiguration、@ComponentScan;若不使用@SpringBootApplication注解,则可以在入口类上直接使用以上几个注解
- 其中,@EnableAutoConfiguration让Spring-Boot根据类路径中的jar包依赖为当前项目进行自动配置
关闭特定的自动配置
- 通过和上面的@SpringBootApplication的源码我们可以看出,关闭特定的自动配置应该使用@SpringBootApplication注解的Exclude参数
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
定制Banner
- 修改Banner
- 在Spring-Boot启动的时候会有一个默认启动图案
- 在src/resources下新建一个banner.txt
- 通过http://patorjk.com/software/taag网站生成字符,将生成的字符复制到banner,txt中,此时再启动程序,图案将会有所改变
- 关闭banner
- 修改main方法里面的内容为
SpringApplication app = new SpringApplication(xxxApplication.class);
app.setShowBanner(false);
app.run(args);
* 或使用fluent API修改为
new SpringApplicationBuilder(xxxApplication.class)
.showBanner(false)
.run(args);
Spring Boot的配置文件
- Spring-Boot使用一个全局的配置文件application.properties或application.yml,放置在src/main/resources目录或者类路径的/config下
- Spring-Boot不仅支持常规的properties配置文件,还支持yaml语言的配置文件。yaml是以数据为中心的语言,在配置数据的时候具有面向对象的特征
- Spring-Boot的全局配置文件的作用是对一些默认配置的配置值进行修改
- 简单示例
- 将Tomcat的默认端口号修改为9090,并将默认的访问路径"/“修改为”/hello"。
- 在application.properties中添加
server.port=9090
server.context-path=/hello
* 或者application.yml中添加
server:
port: 9090
contextPath: /hello
starter pom
- Spring-Boot为我们提供了简化企业级开发绝大多数场景的starter-pom,只要使用了应用场景所需要的starter-pom,相关的技术配置将会消除,就可以得到Spring-Boot为我们提供的自动配置的Bean
使用xml配置
- Spring-Boot提倡零配置,即无xml配置,但是在实际项目中,可能有一些特殊要求你必须使用xml配置,这时我们可以通过spring提供的@ImportResource来加载xml配置
@ImportResource({"classpath:some.xml", "classpath:another.xml"})
外部配置
- Spring-Boot允许使用properties文件、yaml文件或者命令行参数作为外部配置
命令行参数配置
- Spring-Boot可以基于jar包运行的,打成jar包的程序可以直接通过下面命令运行
java -jar xxx.jar
- 可以通过以下名利修改Tomcat端口号
java -jar xxx.jar --server.port=9090
常规属性配置
- 在常规Spring环境下,注入properties文件里的值的方式,通过@PropertySource指明properties文件的位置,然后通过@Value注入值。在Spring-Boot里,我们只需在application.properties定义属性,直接使用@Value注入即可
类型安全的配置(基于properties)
- Spring-Boot还提供了基于类型安全的配置方式,通过@ConfigurationProperties将properties属性和一个Bean及其属性关联,从而实现类型安全的配置
日志配置
- Spring-Boot支持Java-Util-Logging、Log4j、Log4J2和Logback作为日志框架,无论使用哪种日志框架,Spring-Boot已为当前使用日志框架的控制台输出及文件输出做好了配置
- 默认情况下,Spring-Boot使用Logback作为日志框架
- 配置日志文件
- logging.file=D:/mylog/log.log
- 配置日志级别
- logging.level.org.springframework.web=DEBUG
Profile配置
- Profile是Spring用来针对不同环境对不同的配置提供支持的,全局Profile配置使用application-{profile}.properties
- 在application.properties中设置spring.profiles.active=prod来指定活动的profile
Spring Boot运行原理
- Spring-Boot可以通过下面三种方式查看当前项目中已启动和未启动的自动配置的报告
- 运行jar时增加 --debug 参数
- java -jar xxx.jar --debug
- 在application.properties中设置属性
- debug=true
- 在STS中设置启动参数
- -Ddebug
- 运行jar时增加 --debug 参数
启动原理
- 下面是@EnableAutoConfiguration的源码
@Target(ElementType.TYPE)
@Retention(RetentionPoliy.RUNTIME)
@Documented
@Inherited
@Import({
EnableAutoConfigurationImportSelector.class,
AutoConfigurationPackages.Registrar.class
})
public @interface EnableAutoConfiguration {
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
- 这里的关键功能是@Import注解导入的配置功能,EnableAutoConfigurationImportSelector使用SpringFactoriesLoder.loadFactoryName方法来扫描具有META-INFO/spring.factories文件的jar包,而我们的spring-boot-autoconfigure-1.3.0.x.jar里就有一个spring.factories文件,此文件中声明了很多的自动配置
核心注解
- 打开上面任意一个AutoConfiguration文件,一般都会有下面的条件注解
- @ConditionalOnBean:当容器里有指定的Bean条件下
- @ConditionalOnClass:当类路径下有指定的类的条件下
- @ConditionalOnExpression:基于SpEL表达式作为判断条件
- @ConditionalOnJava:基于JVM版本作为判断条件
- @ConditionalOnJndi:在JNDI存在的条件下查找指定的位置
- @ConditionalOnMissingBean:当容器里没有指定Bean的情况下
- @ConditionalOnMissingClass:当类路径下没有指定类的情况下
- @ConditionalOnNotWebApplication:当前项目不是Web项目的条件下
- @ConditionalOnProperty:指定的属性是否有指定的值
- @ConditionalOnResource:类路径是否有指定的值
- @ConditionalOnSingleCandidate:当指定的Bean在容器中只有一个,或者虽然有多个但是指定首选的Bean
- @ConditionalOnWebApplication:当前项目是Web项目的条件下
- 从isWebApplication方法可以看出,判断条件是:
- GenericWebApplicationContext是否在类路径中
- 容器里是否有名为session的scope
- 当前容器的Enviroment是否为StandardServletEnvironment
- 当前的ResourceLoader是否为WebApplicationContext
- ResourceLoader是ApplicationContext的顶级接口之一
- 我们需要构造CondititonOutcome类的对象来帮助我们,最终通过ConditionOutcome.isMatch方法返回布尔值来确定条件
实例分析
- 在了解了Spring-Boot的运作原理和主要的条件注解后,现在来分析一个简单的Spring-Boot内置的自动配置功能:http的编码配置
- 在常规web项目中配置http编码的时候是在web.xml里配置一个filter
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
- 可得知自动配置要满足两个条件
- 能配置CharacterEncodingFilter这个Bean
- 能配置encoding和forceEncoding这两个参数
- 配置参数
- 在之前讲述的类型安全的配置,Spring-Boot的自动配置也是基于这一点实现的,这里的配置类可以在application.properties中直接设置
@ConfigurationProperties(prefix = "spring.http.encoding")
public class HttpEncodingProperties {
public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
private Charset charset = DEFAULT_CHARSET;
private boolean force = true;
...settings/gettings...
}
- 配置Bean
- 通过调用上述配置,并根据条件配置CharacterEncodingFilter的Bean
@Configuration
@EnableConfigurationProperties(HttpEncodingProperties.class)
@ConditionalOnClass(CharacterEncodingFilter.class)
@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled",
matchIfMissing = true)// 如果没有这时则默认为true
public class HttpEncodingAutoConfiguration {
@Autowired
private HttpEncodingProperties httpEncodingProperties;
@Bean
@ConditionalOnMissingBean(CharacterEncodingFilter.class)
public CharacterEncodingFilter characterEncodingFilter(){
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
filter.setEncoding(this.httpEncodingProperties.getCharset().name());
filter.setForceEncoding(this.httpEncodingProperties.isForce());
return filter;
}
}
Spring Boot的Web开发
Spring Boot的Web开发支持
- Spring-Boot提供了spirng-boot-starter-web为Web开发给予支持。spring-boot-starter-web为我们提供嵌入的Tomcat以及Spring-MVC的依赖。而Web相关的自动配置存储在spring-boot-autoconfigure.jar的org.springframework.boot.autoconfigure.web下
- 从文件名可以看出:
- ServerPropertiesAutoConfiguration和ServerProperties:自动配置内嵌Servlet容器
- HttpEncodingAutoConfiguration和HttpEncodingProperties:自动配置http的编码
- MultipartAutoConfiguration和MultipartProperties:自动配置上传文件的属性
- JasksonHttpMessageConverters:自动配置mappingJackson2HttpMessageConverter和mapppingJackson2XmlHttpMessageConverter
- WebMvcAutoConfiguration和WebMvcProperties:配置Spring-MVC
Thymeleaf模板支持
- Spring-Boot提供了大量的模板引擎,包括FreeMarker,Groovy,Thymeleaf,Velocity和Mustache,Spring-Boot中推荐使用Thymeleaf作为模板引擎,因为Thymeleaf提供了完美的Spring-MVC支持
Thymeleaf基础知识
- Thymeleaf是一个Java类库,他是一个xml/xhtml/html5的模板引擎,可以作为MVC的Web应用的View层
- Thymeleaf还提供了额外的模板与Spring-MVC集成,所以我们可以使用Thymeleaf完全替代JSP
1、引入Thymeleaf
- 下面的代码是一个基本的Thymeleaf模板页面,在这里我们引入了Bootstrap(作为样式控制)和jQuery(DOM操作),当然他们不是必须的
<!-- 通过该命名空间,将静态页面转换为动态的视图。
需要进行动态处理的元素将使用"th:"为前缀 -->
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta content="text/html;charset=UTF-8" />
<!-- 通过"@{}"引入Web静态资源,这在JSP下是极易出错的 -->
<link th:src="@{bootstrap/css/bootstrap.min.css}" rel="stylesheet" />
<link th:src="@{bootstrap/css/bootstrap-theme.min.css}" rel="stylesheet" />
<script th:src="@{jquery-1.10.2.min.js}" type="text/javascript"></script>
<script th:src="@{bootstrap/js/bootstrap.min.js}" type="text/javascript">
</script>
</head>
<body>
</body>
</html>
2、访问model中的数据
- 通过" " 访 问 m o d e l 中 的 属 性 ( t h : t e x t = " {}"访问model中的属性(th:text=" "访问model中的属性(th:text="{singlePerson.name}")
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">访问Model</h3>
</div>
<div class="panel-body">
<span th:text="${singlePerson.name}"></span>
</div>
</div>
3、model中的数据迭代
- 使用th:each来做循环迭代(th:each=“person:${people}”)
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">列表</h3>
</div>
<div class="panel-body">
<ul class="list-group">
<li class="list-group-item" th:each="person:${people}">
<span th:text="${person.name}"></span>
<span th:text="${person.age}"></span>
</li>
</ul>
</div>
</div>
4、数据判断
- 通过${not #lists.isEmpty(people)}表达式判断people是否为空。Thymeleaf支持>,<,>=,<=,==,!=作为比较条件,同时也支持将SpringEL表达式语言用于条件中
<div th:if="${not #lists.isEmpty(people)}">
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">列表</h3>
</div>
<div class="panel-body">
<ul class="list-group">
<li class="list-group-item" th:each="person:${people}">
<span th:text="${person.name}"></span>
<span th:text="${person.age}"></span>
</li>
</ul>
</div>
</div>
</div>
5、在JavaScript中访问model
- 在项目中,我们经常需要在JavaScript访问model中的值,在Thymeleaf实现代码如下:
<!-- 通过th:inline="javascript"添加到script标签 -->
<script th:inline="javascript">
<!-- 通过"[[${}]]"格式获得实际的值 -->
var single = [[${singlePerson}]];
console.log(single.name + "/" + single.age)
</script>
- 还有一种是需要在html的代码里访问model中的属性,例如:我们需要在列表后单击每一行后面的按钮获取model中的值,可做如下处理
<li class="list-group-item" th:each="person:${people}">
<span th:text="${person.name}"></span>
<span th:text="${person.age}"></span>
<button class="btn" th:onclick="'getName(\' + ${person.name} + '\');'">
获得名字</button>
</li>
与Spring MVC集成
- 在Spring-MVC中,若我们需要集成一个模板引擎的话,需要定义ViewResolver,而ViewResolver需要定义一个View
@Bean
public InternalResourceViewResolver viewResolver(){
InternalResourceViewResolver viewResolver =
new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/classes/views");
viewResolver.setSuffix(".jsp");
viewResolver.setViewClass(JstlView.class);
return viewResolver;
}
- 通过上面的代码可以看出,使用JsltView定义了一个InternalResourceViewResolver,因而使用Thymeleaf作为我们的模板引擎也应该做类似的定义。
- Thymeleaf还提供了一个SpringTemplateEngine类,用来驱动在Spring-MVC下使用Thymeleaf模板引擎,另外还提供了一个TemplateResolver用来设置通用的模板引擎(包括前缀,后缀等),这使我们在Spring-MVC中集成Thymeleaf引擎变得十分简单
@Bean
public TemplateResolver templateResolver(){
TemplateResolver templateResolver = new ServletContextTemplateResolver();
templateResolver.setPerfix("/WEB-INF/templates");
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode("HTML5");
return templateResolver;
}
@Bean
public SpringTemplateEngine templateEngine(){
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver());
return templateEngine;
}
@Bean
public ThymeleafViewResolver thymeleafViewResolver(){
ThymeleafViewResolver thymeleafViewResolver = new ThymeleafViewResolver();
thymeleafViewResolver.setTemplateEngine(templateEngine());
// thymeleafViewResolver.setViewClass(ThymeleafView.class);
return thymeleafViewResolver;
}
Spring Boot的Thymeleaf支持
- Spring-Boot通过org.springframework.boot.autoconfigure.thymeleaf包对Thymeleaf进行了自动配置。通过ThymeleafConfiguration类对集成所需要的Bean进行自动配置,包括templateResolver、templateEngine和thymeleafViewResolver的配置
- 通过ThymeleafProperties来配置Thymeleaf,在application.properties中,以spring.thymeleaf开头来配置,通过查看ThymeleafProperties的主要源码,我们看出如何设置属性以及默认配置
@ConfigurationProperties("spring.thymeleaf")
public class ThymeleafProperties {
public static final String DEFAULT_PREFIX = "classpath:/templates/";
public static final String DEFAULT_SUFFIX = ".html";
// 前缀设置,Spring Boot默认模板,放置在classpath:/templates/目录下
private String prefix = DEFAULT_PREFIX;
// 后缀设置,默认为html
private String suffix = DEFAULT_SUFFIX;
// 模板模式设置,默认为HTML5
private String mode = "HTML5";
// 模板的编码设置,默认为UTF-8
private String encoding = "UTF-8";
// 模板的媒体类型设置,默认为text/html
private String contentType = "text/html";
// 是否开启模板缓存,默认是开启,开发时请关闭
private boolean cache = true;
}
Web相关配置
Spring Boot提供的自动配置
- 通过查看WebMvcAutoConfiguration和WebMvcProperties的源码,可以发现Spring-Boot为我们提供了如下的自动配置
1、自动配置的ViewResolver
- ContentNegotiatingViewResolver
- 这是Spring-MVC提供的一个特殊的ViewResolver,ContentNegotiatingViewResolver不是自己处理View,而是代理给不同的ViewResolver来处理不同的View,所以它有最高的优先级
- BeanNameViewResolver
- 在控制器中的一个方法的返回值的字符串会根据BeanNameViewResolver去查找Bean的名称为返回字符串的View来渲染视图
// 定义BeanNameViewResolver的Bean
@Bean
public BeanNameViewResolver beanNameViewResolver() {
BeanNameViewResoler resolver = new BeanNameViewResolver();
return resolver;
}
// 定义一个View的Bean,名称为jsonView
@Bean
public MappingJackson2JsonView jsonView(){
MappingJackson2JsonView jsonView = new MappingJackson2JsonView();
return jsonView;
}
- 在控制器中返回值为字符串jsonView,他会找Bean的名称为jsonView的视图来渲染
@RequestMapping(value = "/json", produces={MediaType/APPLICATION_JSON_VALUE})
public String json(Model model){
Person single = new Person("aa", 11);
model.addAttribute("single", single);
return "jsonView";
}
- InternalResourceViewResolver
- 这是一个极为常用的ViewResolver,主要通过设置前缀、后缀,以及控制器中方法来返回视图名的字符串,以得到实际页面
@Bean
@ConditionalOnMissingBean(InternalResourceViewResolver.class)
public InternalResourceViewResolver defaultViewResolver(){
InternalResourceViewResolver resolver =
new InternalResourceViewResolver();
resolver.setPrefix(this.prefix);
resolver.setSuffix(this.suffix);
return resolver;
}
2、自动配置的静态资源
在自动配置类的addResourceHandlers方法中定义了一下静态资源的自动配置
- 类路径文件
- 把类路径下的/static,/public,/resources和/META-INF/resources文件夹下
- 的静态文件直接映射为/**,可以通过http://localhost:8080/**来访问
- webjar
- webjar就是将我们常用的脚本框架封装在jar包中的jar包
3、自动配置的Formatter和Converter
- 关于自动配置Formatter和Converter,可以先看一下WebMvcAutoConfiguration中
@Override
public void addFormatter(FormatterRegistry registry){
for(Converter<?, ?> converter : getBeanOfType(Converter.class)){
registry.addConverter(converter);
}
for(GenericConverter converter : getBeanOfType(GenericConverter.class)){
registry.addConverter(converter);
}
for(Formatter<?> formatter : getBeanOfType(Formatter.class)){
registry.addFormatter(formatter);
}
}
private <T> Collection<T> getBeanOfType(Class<T> type){
return this.beanFactory.getBeanOfType(type).values();
}
- 可以看出,只要我们定义了Converter、GenericConverter和Formatter接口的实现类的Bean,这些Bean就会自动注册到Spring-MVC中
4、自动配置的HttpMessageConverters
- 在WebMvcAutoConfiguration中,我们注册了messageConverters
@Autowired
private HttpMessageConverters messageConverters;
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters){
converters.addAll(this.messageConverters.getConverters());
}
- 在Spring-Boot中,如果要新增自定义的HttpMessageConverter,则需要定义一个你自己的HttpMessgaeConverters的Bean,然后在此Bean中注册自定义HttpMessageConverter即可
@Bean
public HttpMessageConverters customConverters(){
HttpMessageConverter<?> customConverter1 = new CustomConverter1();
HttpMessageConverter<?> customConverter2 = new CustomConverter2();
return new HttpMessageConverter(customConverter1, customConverter2);
}
5、静态首页的支持
- 把静态index.html文件放置在如下目录
- classpath:/META-INF/resources/index.html
- classpath:/resources/index.html
- classpath:/static/index.html
- classpath:/public/index.html
- 当我们访问应用根目录http://localhost:8080/时,会直接映射