Spring Boot微服务
http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/
Why 微服务
- 并行开发,快速迭代,独立交付
- 多个WAR包部署在Web容器中,扩展能力受限于Web容器作为一个进程的现状,同时应用间为线程隔离。现在普遍做法是一个Tomcat只部署一个WAR,实现进程的天然隔离. SpringBoot还能以Jar包的形式部署,而不需要另外安装Tomcat(SpringBoot内置tomcat)
Spring IoC
-
两种方式
DI(依赖注入):被动接受其依赖的其他组件被IoC容器注入
DL(依赖查找):主动从某个服务注册地查找其依赖的服务
public class DemoApplication { public static void main(String[] args) { ApplicationContext context = new FileSystemXmlApplicationContext("..."); DemoService service = context.getBean(DemoService.class); service.doSomething(); } }
-
两个阶段
- 收集注册
手动组装
... 或者自动扫描- 分析组装:分析这些已经在IoC容器中的bean,然后根据它们之间的依赖关系先后组装它们.(XML方式或注解方式 @Autowired @Inject)
SpringBoot的工作机制
建立一个Spring Boot 应用
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
@SpringBootApplication
- @SpringBootApplication 等于以下注解的合体(详见@SpringBootApplication接口源码)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
//对于Spring应用,重要的只有下面三个注解
@Configuration
@EnableAutoConfiguration
@ComponentScan
@Configuration
-
任何一个标注了@Configuration的Java类定义都是一个JavaConfig配置类,会被注册进IoC容器(就像@Controller,@Service,@Repository一样),SpringBoot社区推荐使用基于JavaConfig的配置形式
- XML 配置形式
... - 基于JavaConfig的配置形式
@Configuration public class MockConfiguration { //标注了@Bean的方法,其返回值将作为一个bean定义注册到Spring的IoC容器,方法名将默认成为该bean定义的id @Bean public MockService mockService() { return new MockServiceImpl(); } }
-
依赖注入
- XML 配置形式
- JavaConfig 形式
@Configuration public class MockConfiguration { @Bean public MockService mockService() { return new MockServiceImpl(dependencyService()); } /* 如果有多个Bean调用该方法进行依赖注入, 只会new 一个DependencyServiceImpl(). Spring通过拦截@Configuration类的方法调用来避免多次初始化同一类型对象的问题. 一旦拥有拦截逻辑的子类发现当前方法没有对应的类型实例时才会去请求父类的同一方法来初始化对象实例, 否则直接返回之前的对象实例 */ @Bean public DependencyService dependencyService() { return new DependencyServiceImpl(); } }
-
调整自动配置的顺序
@Configuration @AutoConfigureAfter(XXXX.class)//AutoConfigureBefore public class DemoConfiguration {...}
@EnableAutoConfiguration
-
Spring提供各种@EnableXXXX,简单概括:借助@Import的帮助,收集和注册特定场景相关的bean定义:
- @EnableScheduling: 通过@Import将Spring调度框架相关的bean定义都加载IoC容器
- @EnableMBeanExport: 通过@Import将JMX相关的bean定义都加载IoC容器
- @EnableCaching
- @EnableAutoConfiguration借助@Import的帮助,将所有符合自动配置条件的bean定义加载到IoC容器,仅此而已!(即加载Spring框架定义的@configuration bean)
-
EnableAutoConfiguration 是复合Annotation,源码中如下定义
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(EnableAutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration {...}
其中最关键的要属@Import(EnableAutoConfigurationImportSelector.class)
,借助EnableAutoConfigurationImportSelector
类,@EnableAutoConfiguration可以帮助SpringBoot应用将所有符合条件的@Configuration配置都加载到当前SpringBoot创建并使用的IoC容器
* EnableAutoConfigurationImportSelector类中使用了工具类SpringFactoriesLoader帮助进行智能自动配置,源码如下:
public class EnableAutoConfigurationImportSelector
implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
BeanFactoryAware, EnvironmentAware, Ordered {
...
/**
* Return the auto-configuration class names that should be considered. By default
* this method will load candidates using {@link SpringFactoriesLoader} with
* {@link #getSpringFactoriesLoaderFactoryClass()}.
* @param metadata the source metadata
* @param attributes the {@link #getAttributes(AnnotationMetadata) annotation
* attributes}
* @return a list of candidate configurations
*/
protected List getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
List configurations = SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
Assert.notEmpty(configurations,
"No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
}
-
SpringFactoriesLoader的主要功能是从
META-INF/spring.factories(properties文件)
加载配置.在@EnableAutoConfiguration场景中为加载key为org.springframework.boot.autoconfigure.EnableAutoConfiguration
对应的一组@Configuration类//META-INF/spring.factories文件部分内容,注意key,value都是java类型的完整类名 # Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\ org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\ org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\ org.springframework.boot.autoconfigure.MessageSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration,\
总结:
- @EnableAutoConfiguration就是从classpath中搜寻所有META-INF/spring.factories
- 将spring.factories中key为
org.springframework.boot.autoconfigure.EnableAutoConfiguration
对应的value通过反射实例化为对应的标注了@Configuration的JavaConfig形式的IoC容器配置类 - 汇总为一个并加载到IoC容器
@ComponentScan
- 功能:自动扫描并加载符合条件的组件或bean定义,最终将这些bean加载到容器中
- 对应XML配置中的
,用于批量采集标注了@Component,@Repository等bean到Spring的IoC容器中 - Spring默认会从声明@ComponentScan所在类的package进行扫描,可使用basePackages属性进行定制
- 如果当前应用如果没有任何bean定义需要通过@ComponentScan加载,可以除去@ComponentScan
@PropertySource @PropertySources
- 用于从某些地方加载*.properties文件内容,并将其中的属性加载到IoC容器中
// >=Java8
@Configuration
@PropertySource("classpath:1.properties")
@PropertySource("classpath:2.properties")
@PropertySource("...")
public class XConfiguration{
//这种方式不推荐了
@Value("${mongodb.db}")
private String defaultDb;
//推荐这种方式
@Autowired
Environment env;
private void demo(){
String mongodbUrl = env.getProperty("mongodb.url");
String defaultDb = env.getProperty("mongodb.db");
}
//如果properties中有${}占位符,需要加以下这段,才能让spring正确解析出${}中的值
@Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigInDev() {
return new PropertySourcesPlaceholderConfigurer();
}
}
//
@Import @ImportResource
如果要自定义一个SpringApplicationRunListener实现,需要做如下两步 经验证,最新SpringBoot版本没有以下静态方法 在当前SpringBoot应用的classpath下的META-INF/spring.factories文件中进行如下配置 spring-boot-starter在Spring Boot生态中被称为Starter POMs。Starter POMs是一系列轻便的依赖包,是一套一站式的Spring相关技术解决方案。开发者在使用和整合模块时,不必再去搜寻样例代码中的依赖配置来复制使用,只需要引入对应的模块包即可,比如,开发Web应用时,就引入spring-boot-starter-web, 希望应用具备数据库访问能力的时候,那就引入spring-boot-starter-jdbc 为SpringBoot引入应用日志框架,以下选择一种,注意一定不要将这些完成同一目的的spring-boog-starter都引入 logback 配置方式有两种 log4j log4j2 用于使用SpringMVC开发web应用(打包形式为jar) 非Web应用使用spring-boot-starter 引入 项目结构约定 嵌入式Web容器层面约定和定制 再深入定制则需要针对特定的嵌入式Web容器,使用实现对应的Factory并注册到IoC容器 JSP支持(需要打包成war,不能为jar) spring.mvc.view.suffix=.jsp 更多的数据库访问模块:spring-boot-starter-mongodb 默认使用tomcat JDBC连接池,Tomcat7之前,Tomcat本质应用了DBCP连接池技术来实现的JDBC数据源,但在Tomcat7之后,Tomcat提供了新的JDBC连接池方案,作为DBCP的替换或备选方案,解决了许多之前使用DBCP的不利之处,并提高了性能.其它连接池还有c3p0,Druid(淘宝开源) 默认情况,如果没有配置任何DataSource,SpringBoot会自动配置一个基于嵌入式数据库的DataSource, 该行为很适合测试场景 如果只依赖一个数据库,可使用如下配置 除了DataSource会自动配置,SpringBoot还会自动配置相应的JdbcTemplate,DataSourceTransactionManager等关联设施,使用时按如下注入即可 如果依赖多个数据库,使用如下配置在启动时会抛出异常 还有一种方案时排除掉SpringBoot默认提供的DataSource相关自动配置 数据库版本化管理 AOP(Aspect Oriented Programming) 自动配置行为由两部分组成 spring-boot-autoconfigure的org.springframework.boot.autoconfigure.aop.AopAutoConfiguration提供@Configuration配置类和相应配置项,如下 spring-boot-starter-aop 模块自身提供针对spring-aop,aspectjrt和aspectjweaver的依赖 step-step: 定义Aop 手动搭建metrics-spring(未调试成功,略),直接可用方案 Spring Security的Web安全方案基于Java的Servlet API规范构建,因此使用javax.servlet.Filter实现"关卡",制作关卡过程如下: Spring Security 的Web方案也是如上原理,过程如下(源码) HOLD进程SpringBoot启动过程
SpringApplication.run(DemoApplication.class, args);
该语句等同于 new SpringApplication().run()
@SpringApplicationRunListener
public interface SpringApplicationRunListener {
void started();
void environmentPrepared(ConfigurableEnvironment environment);
void contextPrepared(ConfigurableApplicationContext context);
void contextLoaded(ConfigurableApplicationContext context);
void finished(ConfigurableApplicationContext context, Throwable exception);
}
EventPublishingRunListener implements SpringApplicationRunListener
,用于在SpringBoot启动的不同时点发布不同的应用事件类型(ApplicationEvent),如果有哪些ApplicationListener对这些应用事件感兴趣,则可以接收并处理
public class DemoApplicationRunListener implements SpringApplicationRunListener
org.springframework.boot.SpringApplicationRunListener=com.lyf.springboot.demo.DemoApplicationRunListener
@ApplicationListener
SpringApplication.addListeners(...) 或者 SpringApplication.setListeners(...)
org.springframework.context.=com.lyf.springboot.demo.DemoApplicationListener
@ApplicationContextInitializer
public class DemoApplicationContextInitializer implements ApplicationContextInitializer{
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
// do whatever you want with applicationContext,
// e.g. applicationContext.registerShutdownHook();
}
}
CommandLineRunner
public interface CommandLineRunner {
void run(String... args) throws Exception;
}
标注@org.springframework.core.annotation.Order
或
实现org.springframework.core.annotation.Order接口
spring-boot-starter
spring-boot-start-
作为命名前缀
/application.properties
或/config/application.properties
spring-boot-starter-logging
/application.properties
logging.level.root=debug
引入依赖后开箱即用
* 遵循logback约定,在classpath中使用自定义的logback.xml配置文件
* application.properties中使用logging.config配置项指向logback.xml配置文件
logging.config=/{some.path.you.defined}/logback.xml
spring-boot-starter-web
默认为jar包形式打包
传统war包形式
src/main/resources
src/main/webapp
静态资源(css,js等): src/main/resources/static
模版文件(*.vm): src/main/resources/templates
默认为我们自动配置如下SpringMVC必要组件
定制方法
1.必要的ViewResolver, 比如ContentNegotiatingViewResolver,和BeanNameViewResolver
2. 将必要的Converter,GenericConverter和Formatter等bean注册到IoC容器
3. 添加一系列的HttpMessageConverter以便支持对Web请求和相应的类型转换
4.自动配置和注册MesasageCodesResolver
5.其他1.在IoC容器中注册新的同类型的bean定义来替换
2. 直接提供一个基于WebMccConfigurerAdapter类型的bean定义来定制
3.甚至直接提供一个标注了@EnableWebMvc的@Configuration配置类来完全接管所有SpringMVC的相关配置,自己完全重新配置
jar包启动指定端口号可以增加参数 -Dserver.port=8080
//application.properties
server.port = 9000
server.address =
server.ssl.*
server.tomcat.*
@Configuration
public class DemoEmbeddedServletContainerCustomizer implements EmbeddedServletContainerCustomizer {
@Override
public void customize(ConfigurableEmbeddedServletContainer container) {
container.setPort(9999);
container.setContextPath("/demo-project");
//...
}
}
//注意版本,最新的tomcat-embed-jasper版本可能会启动报错
compile("org.apache.tomcat.embed:tomcat-embed-jasper:8.5.8")
compile("javax.servlet:jstl:1.2")
spring.mvc.view.prefix=/WEB-INF/views/
``` * 目录结构,由于tomcat硬编码的原因, 对目录结构有要求,webapp还是需要的
![目录结构](http://upload-images.jianshu.io/upload_images/5748528-832609f7cd383a5e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
打包成可执行war
apply plugin: 'org.springframework.boot'
buildscript {
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:1.4.2.RELEASE")
}
}
buildscript {
repositories {
maven {
name 'Aliyun Maven Repository'
url 'http://maven.aliyun.com/nexus/content/groups/public'
}
}
}
gradle bootRepackage
进行打包java -jar /Users/luyunfei/Documents/dev/workspace/zxd-oa/build/libs/zxd-oa.war
spring-boot-starter-jdbc
//application.properties
spring.datasource.url = jdbc:mysql://{database host}:3306/{databaseName}
spring.datasource.username = {database username}
spring.datasource.password = {database password}
class SomeDao{
JdbcTemplate jdbcTemplate;
public
//异常信息:No qualifying bean of type {javax.sql.DataSource} is defined: expected single matching bean bug found 2
@Bean
//解决方案: 在这里添加@Primary,告诉Spring在出现多个时,优先选择这个
public DataSource dataSource1() throws Throwable{
/*数据库连接池,类似的还有:
*dbcp(有时候会自动断掉,必须重启才能有效)
*C3P0(偶尔断掉,无须重启)
*Druid性能较dbcp,c3p0优越很多(淘宝开源)
*默认的tomcat 连接池
*/
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl(...);
dataSource.setUsername(...);
dataSource.setPassword(...);
return dataSource;
}
@Bean
public DataSource dataSource2() throws Throwable{
...
}
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class})
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration
,org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration
)
传统方式
外部化方式
SQL分布在各项目中
SQL集中到一处管理,有利于DBA进行SQL审查
多个应用使用同一个数据库时,容易相互干扰,且数据库连接过多
统一调度
需要完整的软件交付链路支撑平台
spring-boot-starter-aop
//application.properties
spring.aop.auto = false(关闭自动aop配置)
spring.aop.proxy-target-class=true(启用class而非interface级别的aop代理)
compile("org.springframework.boot:spring-boot-starter-aop:1.4.0.RELEASE")
@Component
@Aspect
public class AutoMetricsAspect {
//切点
@Pointcut(value = "execution(public * *(..))")
public void publicMethods() {
}
//各切面的实现逻辑,函数名称任意
@Before(" publicMethods() && @annotation(countedAnnotation) ")
public void instrumentCounted(JoinPoint jp, Counted countedAnnotation) {...}
@Before(" publicMethods() && @annotation(meteredAnnotation) ")
public void instrumentMetered(JoinPoint jp, Metered meteredAnnotation) {...}
@AfterThrowing(pointcut = " publicMethods() && @annotation(exMeteredAnnotation)", throwing = "ex")
public void instrumentExceptionMetered(JoinPoint jp, Throwable ex, ExceptionMetered exMeteredAnnotation) {...}
@Around(" publicMethods() && @annotation(timedAnnotation) ")
public Object instrumentTimed(ProceedingJoinPoint pjp, Timed timedAnnotation) throws Throwable {...}
}
spring-boot-starter-security
compile("org.springframework.boot:spring-boot-starter-security:1.4.0.RELEASE")
public class SignCheckFilter extends GenericFilterBean {...}
public class DelegatingFilterProxy extends GenericFilterBean{...}
/*
三类:
1.信道与状态管理
2. Web安全防护
3. 认证和授权
*/
FilterComparator() {
int order = 100;
put(ChannelProcessingFilter.class, order);//用于处理http或https之间的切换(信道与状态管理)
order += STEP;
put(ConcurrentSessionFilter.class, order);
order += STEP;
put(WebAsyncManagerIntegrationFilter.class, order);
order += STEP;
put(SecurityContextPersistenceFilter.class, order);//用于重建或者销毁必要的SecurityContext状态(信道与状态管理)
order += STEP;
put(HeaderWriterFilter.class, order);
order += STEP;
put(CorsFilter.class, order);
order += STEP;
put(CsrfFilter.class, order);//Web安全防护
order += STEP;
put(LogoutFilter.class, order);
order += STEP;
put(X509AuthenticationFilter.class, order);
order += STEP;
put(AbstractPreAuthenticatedProcessingFilter.class, order);
order += STEP;
filterToOrder.put("org.springframework.security.cas.web.CasAuthenticationFilter",
order);//认证和授权
order += STEP;
put(UsernamePasswordAuthenticationFilter.class, order);
order += STEP;
put(ConcurrentSessionFilter.class, order);
order += STEP;
filterToOrder.put(
"org.springframework.security.openid.OpenIDAuthenticationFilter", order);
order += STEP;
put(DefaultLoginPageGeneratingFilter.class, order);
order += STEP;
put(ConcurrentSessionFilter.class, order);
order += STEP;
put(DigestAuthenticationFilter.class, order);
order += STEP;
put(BasicAuthenticationFilter.class, order);//认证和授权
order += STEP;
put(RequestCacheAwareFilter.class, order);
order += STEP;
put(SecurityContextHolderAwareRequestFilter.class, order);
order += STEP;
put(JaasApiIntegrationFilter.class, order);
order += STEP;
put(RememberMeAuthenticationFilter.class, order);
order += STEP;
put(AnonymousAuthenticationFilter.class, order);
order += STEP;
put(SessionManagementFilter.class, order);
order += STEP;
//ExceptionTranslationFilter负责接待或送客,如果访客来访,对方没有报上名来,则让访客去找AuthenticationManager登记认证;如果对方报上名却认证失败了,则请重新认证或者走人.它拒绝访客的方式是抛出相应的Exception
put(ExceptionTranslationFilter.class, order);
order += STEP;
put(FilterSecurityInterceptor.class, order);//即前面说到的securedObject(Filter
Invocation)的关卡
order += STEP;
put(SwitchUserFilter.class, order);
}
try {
Thread.currentThread().join();
} catch (InterruptedException e) {
e.printStackTrace();
}