社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/
两种方式
DI(依赖注入):被动接受其依赖的其他组件被IoC容器注入
DL(依赖查找):主动从某个服务注册地查找其依赖的服务
public class DemoApplication {
public static void main(String[] args) {
ApplicationContext context = new FileSystemXmlApplicationContext("...");
DemoService service = context.getBean(DemoService.class);
service.doSomething();
}
}
两个阶段
手动组装
<bean id="mockService" class="..MockServiceImpl">
...
</bean>
或者自动扫描
<context:component-scan base-package="com.lyf">
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
//对于Spring应用,重要的只有下面三个注解
@Configuration
@EnableAutoConfiguration
@ComponentScan
任何一个标注了@Configuration的Java类定义都是一个JavaConfig配置类,<font color=red>会被注册进IoC容器</font>(就像@Controller,@Service,@Repository一样),SpringBoot社区推荐使用基于JavaConfig的配置形式
<bean id="mockService" class="..MockServiceImpl">
...
</bean>
@Configuration
public class MockConfiguration {
//标注了@Bean的方法,其返回值将作为一个bean定义注册到Spring的IoC容器,方法名将默认成为该bean定义的id
@Bean
public MockService mockService() {
return new MockServiceImpl();
}
}
依赖注入
<bean id="mockService" class="..MockServiceImpl">
<property name="dependencyService" ref="dependencyService" />
</bean>
@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 {...}
Spring提供各种@EnableXXXX,简单概括:借助@Import的帮助,收集和注册特定场景相关的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<String> getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
List<String> 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,
总结:
org.springframework.boot.autoconfigure.EnableAutoConfiguration
对应的value通过反射实例化为对应的标注了@Configuration的JavaConfig形式的IoC容器配置类<context:component-scan>
,用于批量采集标注了@Component,@Repository等bean到Spring的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();
}
}
//<Java8
@PropertySources({
@PropertySource("classpath:1.properties"),
@PropertySource("classpath:2.properties"),
...
})
public class XConfiguration{...}
<import resource="XXX.xml/>将多个分开的容器配置合到一个配置中
@Configuration
//@Import负责引入JavaConfig形式的配置
@Import(MockConfiguration.class)
//@ImportResource负责引入XML形式的配置
@ImportResource("classpath:spring-dubbo.xml")
public class XConfiguration{...}
SpringApplication.run(DemoApplication.class, args);
该语句等同于 new SpringApplication().run()
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对这些应用事件感兴趣,则可以接收并处理如果要自定义一个SpringApplicationRunListener实现,需要做如下两步
public class DemoApplicationRunListener implements SpringApplicationRunListener
org.springframework.boot.SpringApplicationRunListener=com.lyf.springboot.demo.DemoApplicationRunListener
经验证,最新SpringBoot版本没有以下静态方法
SpringApplication.addListeners(...) 或者 SpringApplication.setListeners(...)
在当前SpringBoot应用的classpath下的META-INF/spring.factories文件中进行如下配置
org.springframework.context.=com.lyf.springboot.demo.DemoApplicationListener
public class DemoApplicationContextInitializer implements ApplicationContextInitializer{
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
// do whatever you want with applicationContext,
// e.g. applicationContext.registerShutdownHook();
}
}
public interface CommandLineRunner {
void run(String... args) throws Exception;
}
标注@org.springframework.core.annotation.Order
或
实现org.springframework.core.annotation.Order接口
spring-boot-starter在Spring Boot生态中被称为Starter POMs。Starter POMs是一系列轻便的依赖包,是一套一站式的Spring相关技术解决方案。开发者在使用和整合模块时,不必再去搜寻样例代码中的依赖配置来复制使用,只需要引入对应的模块包即可,比如,开发Web应用时,就引入spring-boot-starter-web, 希望应用具备数据库访问能力的时候,那就引入spring-boot-starter-jdbc
spring-boot-start-
作为命名前缀/application.properties
或/config/application.properties
为SpringBoot引入应用日志框架,以下选择一种,注意一定不要将这些完成同一目的的spring-boog-starter都引入
/application.properties
logging.level.root=debug
logback
引入依赖后开箱即用
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
配置方式有两种
* 遵循logback约定,在classpath中使用自定义的logback.xml配置文件
* application.properties中使用logging.config配置项指向logback.xml配置文件logging.config=/{some.path.you.defined}/logback.xml
log4j
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j</artifactId>
</dependency>
log4j2
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
用于使用SpringMVC开发web应用(打包形式为jar)
非Web应用使用spring-boot-starter
引入
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
项目结构约定
默认为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的相关配置,自己完全重新配置 |
嵌入式Web容器层面约定和定制
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");
//...
}
}
再深入定制则需要针对特定的嵌入式Web容器,使用实现对应的Factory并注册到IoC容器
JSP支持(需要打包成war,不能为jar)
//注意版本,最新的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/
spring.mvc.view.suffix=.jsp
```
* 目录结构,由于tomcat硬编码的原因, 对目录结构有要求,webapp还是需要的
![目录结构](http://upload-images.jianshu.io/upload_images/5748528-832609f7cd383a5e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
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-mongodb
默认使用tomcat JDBC连接池,Tomcat7之前,Tomcat本质应用了DBCP连接池技术来实现的JDBC数据源,但在Tomcat7之后,Tomcat提供了新的JDBC连接池方案,作为DBCP的替换或备选方案,解决了许多之前使用DBCP的不利之处,并提高了性能.其它连接池还有c3p0,Druid(淘宝开源)
默认情况,如果没有配置任何DataSource,SpringBoot会自动配置一个基于嵌入式数据库的DataSource, 该行为很适合测试场景
如果只依赖一个数据库,可使用如下配置
//application.properties
spring.datasource.url = jdbc:mysql://{database host}:3306/{databaseName}
spring.datasource.username = {database username}
spring.datasource.password = {database password}
除了DataSource会自动配置,SpringBoot还会自动配置相应的JdbcTemplate,DataSourceTransactionManager等关联设施,使用时按如下注入即可
class SomeDao{
JdbcTemplate jdbcTemplate;
public <T> List<T> queryForList(String sql){
//...
}
}
如果依赖多个数据库,使用如下配置在启动时会抛出异常
//异常信息: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{
...
}
还有一种方案时排除掉SpringBoot默认提供的DataSource相关自动配置
@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审查 |
多个应用使用同一个数据库时,容易相互干扰,且数据库连接过多 | 统一调度 |
需要完整的软件交付链路支撑平台 |
AOP(Aspect Oriented Programming)
自动配置行为由两部分组成
spring-boot-autoconfigure的org.springframework.boot.autoconfigure.aop.AopAutoConfiguration提供@Configuration配置类和相应配置项,如下
//application.properties
spring.aop.auto = false(关闭自动aop配置)
spring.aop.proxy-target-class=true(启用class而非interface级别的aop代理)
spring-boot-starter-aop 模块自身提供针对spring-aop,aspectjrt和aspectjweaver的依赖
step-step: 定义Aop 手动搭建metrics-spring(未调试成功,略),直接可用方案
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 {...}
}
compile("org.springframework.boot:spring-boot-starter-security:1.4.0.RELEASE")
Spring Security的Web安全方案基于Java的Servlet API规范构建,因此使用javax.servlet.Filter实现"关卡",制作关卡过程如下:
public class SignCheckFilter extends GenericFilterBean {...}
<filter>
<filter-name>signCheckFilter</filter-name>
<filter-class>com.lyf.demo.filter.SignCheckFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>signCheckFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Spring Security 的Web方案也是如上原理,过程如下(源码)
public class DelegatingFilterProxy extends GenericFilterBean{...}
<filter>
<filter-name>springSecurityFilterChain(默认名称)</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
/*
三类:
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);
}
HOLD进程
try {
Thread.currentThread().join();
} catch (InterruptedException e) {
e.printStackTrace();
}
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!