web容器(web服务器)主要有:Apache、IIS、Tomcat、Jetty、JBoss、webLogic等,而Tomcat、Jetty、JBoss、webLogic同时也是servlet容器,或者说他们还包含了servlet容器。没有servlet容器,你也可以用web容器直接访问静态页面,比如安装一个apache等,但是如果要显示jsp/servlet,你就要安装一个包含servlet容器的web容器。大多数servlet容器同时提供了web容器的功能,也就是说大多servelt容器可以独立运行你的web应用。
web容器是管理servlet(通过servlet容器),以及监听器(Listener)和过滤器(Filter)的。这些都是在web容器的掌控范围里
-
DispatchServlet对象里面包含了一个spring容器。这个spring容器一般是存放controller的bean
-
每个spring容器里面都可以设置一个spring父容器。这个容器一般存放的是service和dao层的bean
servlet的3.0版本有了新特性,可以利用ServletContainerInitializer接口实现不用web.xml配置
没有这个特性之前我们需要配置web.xml文件
<web-app>
<!--
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/app-context.xml</param-value>
</context-param>
-->
<servlet>
<servlet-name>app</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value></param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>app</servlet-name>
<url-pattern>/app/*</url-pattern>
</servlet-mapping>
</web-app>
上面配置的作用:
- 加载一个DispatcherServlet,并且给这个类做了一些配置
加载流程
- tomcat启动
- 加载web.xml文件
- 初始化spring容器(此步骤可省略)
- 初始化DispatcherServlet(该类里面会有自己的spring容器)
- ....
3.0特性以后不再需要web.xml配置文件,只需要实现WebApplicationInitializer这个接口
public class MyWebApplicationInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) {
// Load Spring web application configuration
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
//context.register(AppConfig.class);
// Create and register the DispatcherServlet
DispatcherServlet servlet = new DispatcherServlet(context);
ServletRegistration.Dynamic registration = servletContext.addServlet("app", servlet);
registration.setLoadOnStartup(1);
registration.addMapping("/app/*");
}
}
上面代码的作用:
- 加载一个DispatcherServlet,并且给这个类做了一些配置
启动流程(针对外部tomca+war的启动形式,不包括springboot内置tomcat启动)
-
tomcat启动
-
加载SpringServletContainerInitializer这个类
第二个步骤很多小伙伴会有疑问?
为什么会去加载SpringServletContainerInitializer这个类?
这是tomcat实现的,tomcat利用java的spi机制去加载的METAINF\services\javax.servlet.ServletContainerInitializer文件里面的类
org.springframework.web.SpringServletContainerInitializer
以上是spring在web的包里实现的,见下图
我们也可以按照以上的格式自己实现这个类,tomcat也会去调用。但有两点需要注意
- 定义的文件必须是META-INF\services\javax.servlet.ServletContainerInitializer这样
- 把需要加载的类全限定名写在里面
- 这个类必须实现ServletContainerInitializer接口
@HandlesTypes({WebApplicationInitializer.class})
实现ServletContainerInitializer接口的类需要加上这个注解,注解里面的传入的类是一个接口,只要实现这个接口就会被调用
spring传入的是WebApplicationInitializer.class这个类,只要实现这个接口的类就会被调用。这个时候大家会发现上面的代码中的类就实现了该接口,onStartup方法就会被调用。而onStartup方法里面的做的事和web.xml配置文件做的事是一样的,所以才替代了web.xml配置
Springboot内嵌Tomcat
1.3.2. Servlet Context Initialization
Embedded servlet containers do not directly execute the servlet 3.0+ `javax.servlet.ServletContainerInitializer` interface or Spring’s `org.springframework.web.WebApplicationInitializer` interface. This is an intentional design decision intended to reduce the risk that third party libraries designed to run inside a war may break Spring Boot applications.
If you need to perform servlet context initialization in a Spring Boot application, you should register a bean that implements the `org.springframework.boot.web.servlet.ServletContextInitializer` interface. The single `onStartup` method provides access to the `ServletContext` and, if necessary, can easily be used as an adapter to an existing `WebApplicationInitializer`
// 翻译
嵌入式servlet容器不会直接执行servlet 3.0+ javax.servlet.ServletContainerInitializer接口或Spring的org.springframework.web.WebApplicationInitializer接口。这是一个有意的设计决策,目的是为了减少在战争中运行的第三方库可能破坏Spring Boot应用程序的风险。
如果你需要在Spring Boot应用程序中执行servlet上下文初始化,你应该注册一个实现org.springframework.boot.web.servlet.ServletContextInitializer接口的bean。onStartup方法提供了对ServletContext的访问,如果需要的话,可以很容易地用作现有WebApplicationInitializer的适配器。
1.在bean上添加@ServletComponentScan(扫描路径)
一般在启动类上或者配置类上添加这个注解
添加对应的注解
- @WebServlet
- @WebFilter
- @WebListener
@WebServlet(urlPatterns = "/myServlet")
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("-------------------------------");
super.doGet(req, resp);
}
}
// 添加扫描路径
@ServletComponentScan(basePackages = "com.health.manage.controller")
public class ApplicationConfig {
}
@Bean
public ServletRegistrationBean servletRegistrationBean(){
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean();
servletRegistrationBean.setServlet(new MyServlet());
servletRegistrationBean.addUrlMappings("/myServlet");
return servletRegistrationBean;
}
使用其中任意一个都可以实现添加
@Component
public class MyServletContainer implements ServletContextInitializer, ServletContextAware {
// ServletContextInitializer的重写方法
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
servletContext.addServlet("myServlet",new MyServlet()).addMapping("/myServlet");
}
// ServletContextAware的重写方法
@Override
public void setServletContext(ServletContext servletContext) {
servletContext.addServlet("myServlet",new MyServlet()).addMapping("/myServlet");
}
}
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
/**
* 对容器和容器中的beanFactory做一些设置
*
*/
postProcessApplicationContext(context);
/**
* 调用ApplicationContextInitializer的实现类
*/
applyInitializers(context);
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
// Load the sources
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
/**
* 加载启动类到beanDefinition的Map
*/
load(context, sources.toArray(new Object[0]));
listeners.contextLoaded(context);
}
先执行BeanFactoryPostProcessor的子类BeanDefinitionRegistryPostProcessor(顺序:PriorityOrdered ---> Ordered ---> 普通的)
再执行BeanFactoryPostProcessor的实现类(顺序:PriorityOrdered ---> Ordered ---> 普通的)
生成BeanPostProcessor的实现类为bean,再把它加到BeanPostProcessor的列表里,为生成其他的bean调用
生成LoadTimeWeaverAware实现类的bean。
会调用的BeanPostProcessor的实例化前的方法,如果返回bean,则调用实例化后的方法直接返回bean
如果返回为null,反射生成对象,调用实例化后的方法,调用InstantiationAwareBeanPostProcessor的
postProcessProperties的方法,填充属性,进行Aware的回调,进行初始化前方法调用,调用InitializingBean的afterPropertiesSet方法
进行初始化后的方法调用。
对属性资源的扩展,还没弄明白
@Override
protected void initPropertySources(){
}
- SpringApplication实例对象通过addInitializers()方法
对资源的扩展接口接口
在springboot准备容器这个方法中调用这个接口的实现类
ApplicationContextInitializer通过三种方式可以加载
- java的SPI机制
- 配置文件指定全限定名
- SpringApplication实例对象通过addInitializers()方法
springboot通过实现这个接口加载BeanFactoryPostProcessor到AbstractApplicationContext的
List beanFactoryPostProcessors属性中
实现对BeanDefinition的处理,实现的功能
- 注册BeanDefinition
- 移除BeanDefinition
- 是否包含BeanDefinition
- 获取所有的BeanDefinition的名称
- 修改BeanDefinition
主要是对BeanFactory的修改,不能注册BeanDefinition
LoadTimeWeaverAware
在初始化之前先找到LoadTimeWeaverAware的实现类注册为bean
BeanPostProcessor的实现类会被所有的bean调用
实例化之前调用postProcessBeforeInstantiation,如果返回的为不为null,再调用postProcessAfterInstantiation,如果没调用postProcessBeforeInstantiation会直接调用
这是BeanPostProcessor的子类
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println(beanName);
System.out.println("初始化之前");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println(beanName);
System.out.println("初始化之后");
return bean;
}
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
System.out.println(beanName);
System.out.println("实例化之前");
return null;
}
@Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
System.out.println(beanName);
System.out.println("实例化之后");
return false;
}
对单独的bean,在bean的生命周期结束之后调用实现该类的bean的afterSingletonsInstantiated方法
在完成容器刷新的时候调用
在容器刷新完毕了会找实现了该接口的bean,然后调用它的run方法
可以容器刷新完毕之后需要执行某代码可以实现这个接口
和上面的接口作用类似,区别在于调用方法时的参数不一样
- FactoryBean
FactoryBean的接口可以改变真是的Bean对象
@Component
public class MyFactoryBean implements FactoryBean {
@Override
public Object getObject() throws Exception {
return new Object();
}
@Override
public Class<?> getObjectType() {
return Object.class;
}
}
- BeanNameAware
- ApplicationContextAware
- BeanFactoryAware
- BeanClassLoaderAware
package com.bysj.extend;
// 利用这些接口可以在创建bean时获取一些bean的信息
@Component
public class MyBeanTend implements BeanNameAware, ApplicationContextAware, BeanFactoryAware,BeanClassLoaderAware {
// BeanNameAware
@Override
public void setBeanName(String name) {
System.out.println(name);
}
// ApplicationContextAware
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println(applicationContext);
}
// BeanFactoryAware
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println(beanFactory);
}
// BeanClassLoaderAware
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
System.out.println(classLoader);
}
}
- DisposableBean
- InitializingBean
// DisposableBean的方法
@Override
public void destroy() throws Exception {
System.out.println("销毁中-------------");
}
// InitializingBean的方法
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("afterPropertiesSet");
}
@PostConstruct @PreDestroy
@PostConstruct 相当于init-method的方法
@PreDestroy 销毁bean的方法
@Value @Autowired @Resource
@Value注册string的属性
@Autowired 注册bean的属性(这是ByType)
@Resource 注册bean的属性(先是ByName 然后ByType)
在AutowiredxxxBeanFactoryPostPocessor
- 只能通过@Import来引入ImportBeanDefinitionRegistrar的实现类
- 通过注解的方式注册不了ImportBeanDefinitionRegistrar的实现类
- 会自动调用下面个方法
// 回调次方法
default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry,BeanNameGenerator importBeanNameGenerator) {
registerBeanDefinitions(importingClassMetadata, registry);
}
default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {}
ComponentScanAnnotationParser
通过启动类的包名获取扫描路径,把controller和service的bean扫描到容器里面
Set<String> basePackages = new LinkedHashSet<>();
if (basePackages.isEmpty()) {
basePackages.add(ClassUtils.getPackageName(declaringClass));
}
return scanner.doScan(StringUtils.toStringArray(basePackages));
- 获取扫描的包路径
- 引入MapperScannerRegistrar这个类
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({MapperScannerRegistrar.class})
@Repeatable(MapperScans.class)
public @interface MapperScan {
String[] value() default {};
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
Class<? extends Annotation> annotationClass() default Annotation.class;
Class<?> markerInterface() default Class.class;
String sqlSessionTemplateRef() default "";
String sqlSessionFactoryRef() default "";
Class<? extends MapperFactoryBean> factoryBean() default MapperFactoryBean.class;
String lazyInitialization() default "";
}
通过这个类注册MapperScannerConfigurer为BeanDefinition
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
/**
给builder对象添加各种属性的操作,这些代码已经省略
*/
registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
- 保存着扫描Mapper的路径
public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
private String basePackage; //Mapper的路径
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry){
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
scanner.registerFilters();
//ClassPathMapperScanner的方法
scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ",; \t\n"));
}
}
- ClassPathMapperScanner extends ClassPathBeanDefinitionScanner
// 这是ClassPathBeanDefinitionScanner的scan方法
public int scan(String... basePackages) {
int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
this.doScan(basePackages);
if (this.includeAnnotationConfig) {
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
return this.registry.getBeanDefinitionCount() - beanCountAtScanStart;
}
// ClassPathMapperScanner的方法
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
if (beanDefinitions.isEmpty()) {
// 省略......
} else {
// 这个方法会替换成动态Mybatis的FactoryBean
this.processBeanDefinitions(beanDefinitions);
}
return beanDefinitions;
}
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
GenericBeanDefinition definition;
for(Iterator var3 = beanDefinitions.iterator(); var3.hasNext(); definition.setLazyInit(this.lazyInitialization)) {
BeanDefinitionHolder holder = (BeanDefinitionHolder)var3.next();
definition = (GenericBeanDefinition)holder.getBeanDefinition();
String beanClassName = definition.getBeanClassName();
// 设置成为MapperFactoryBean
definition.setBeanClass(this.mapperFactoryBeanClass);
definition.getPropertyValues().add("addToConfig", this.addToConfig);
}
-
yaml配置格式(严格控制字符串)
# name: jiruxin # duixiang student: name: jirui${random.int} age: 3 student2: {name: jirui,age: 3} # shuzu pets: - cat - dog - pig pets1: [cat,dog,pig]
-
多环境配置application.yaml
- ./config/*.yaml
- ./*.yaml
- rec/main/resources/*.yaml
- rec/main/resources/config/*.yaml
编程式事务
在程序中通过编程来开启事务
声明式事务
在需要开启事务的方法中添加注解 @Transactional
@Override
@Transactional
public void laoda() {
laoer();
// 在这假设抛物异常
}
public void laoer() {
// 在这假设抛出异常
}
事务传播行为类型 | 外部不存在事务 | 外部存在事务 | 使用方式 |
---|---|---|---|
REQUIRED | 开启新的事务 | 融合到外部事务中 | 适用增删改查 |
SUPPORTS | 不开启新的事务 | 融合到外部事务中 | 适用查询 |
REQUIRES_NEW | 开启新的事务 | 不用外部事务,创建新的事务 | 适用内部事务和外部事务不存在业务关联的情况如日志 |
NOT_SUPPORTED | 不开启新的事务 | 不用外部事务 | 不常用 |
NEVER | 不开启新的事务 | 抛出异常 | 不常用 |
MANDATORY | 抛出异常 | 融合到外部事物中 | 不常用 |
NESTED | 开启新的事务 | 融合到外部事务中,SavePoint机制,外层影响内层,内层不会影响外层 | 不常用 |
- 调用自身的方法
- 抛出的异常不是RuntimeException和Error
- 用在非public方法上
- 异常被自己try catch
- @Transactiona标记的类或者标记方法所在的类没有被spring管理
- 数据库不支持事务
- 新建一个接口类和该接口的实现类
// 接口类
public interface Play {
void play();
}
// 接口实现类
public class APlayer implements Play{
public void play() {
System.out.println("----------I am Duzhilin---------");
}
}
- 在resource文件下建立META-INF/services这样的目录,在该目录下新建文件,该文件命名方式是接口的全限定名,在文件里面填写实现该类的全限定名。
- 编写测试
public class Main {
public static void main(String[] args) {
ServiceLoader<Play> service = ServiceLoader.load(Play.class);
for (Play player : service) {
player.play();
}
}
}
mvc的配置,是一个接口
WebMvcConfigurer的空实现,已经被弃用
实现了WebMvcConfigurer的一些方法
springboot中对mvc的自动配置,当容器中有WebMvcConfigurationSupport这个类时会失效