【SpringMVC】SPI、父子容器、xml与注解式配置解读

UE5Could not be compiled. Try rebuilding from source manually. 阅读详情

一、回顾Servlet

@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
	
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		//super.doGet(req, resp);
		System.out.println(Thread.currentThread()+" start...");
		try {
			sayHello();
		} catch (Exception e) {
			e.printStackTrace();
		}
		resp.getWriter().write("hello...");
		System.out.println(Thread.currentThread()+" end...");
	}
	
	public void sayHello() throws Exception{
		System.out.println(Thread.currentThread()+" processing...");
		Thread.sleep(3000);
	}
}
web三大组件

Servlet、Filter、Listener

二、SPI机制

SPI机制我们或多或少都了解过,Java中有SPI,SprngBoot中有SPI,SpringMVC中也有对应的SPI机制。

SpringMVC根据Servlet3.0的新规范:

你的项目里面如果有某些类或者方法,需要在启动的时候被tomcat(容器)调用的话

首先在下面的目录META-INF/services/javax.servlet.ServletContainerInitializer 建立一个文件,他里面写了ServletContainerInitializer实现类,那么,当web容器启动时就会运行这个初始化器做一些组件内的初始化工作,该初始化工作包括:

  • 调用onStartup()方法
  • 注入感兴趣的了

tomcat是servlet容器

SPI机制官方也叫作Shared libraries(共享库) / runtimes pluggability(运行时插件能力)

那么我们看一下我们引入的web依赖spring-web包下有什么:

META-INF/services/javax.servlet.ServletContainerInitializer该文件就是Servlet3.0的SPI用到的文件

文件内容为:

org.springframework.web.SpringServletContainerInitializer

而SpringServletContainerInitializer实现了接口ServletContainerInitializer

我们首先说一下这个类

三、SpringServletContainerInitializer

该类的结构为:

@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer 
    implements ServletContainerInitializer {

注解@HandlesTypes的意思是MVC会找到容器中所有该类的子类,然后传到onStartup()方法参数中,下我们就看看这个方法

下面这个方法的意义就是找到感兴趣的类(非抽象类和接口),然后也是依次调用他们的onStartup()方法。这2个onStartup()方法不要搞混了,处理时机不同,参数也不同

@Override
public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses,// 感兴趣的类,WebApplicationInitializer的实现类
                      ServletContext servletContext) {//要被初始化的servlet context

    List<WebApplicationInitializer> initializers = new LinkedList<>();

    // 遍历感兴趣的类
    for (Class<?> waiClass : webAppInitializerClasses) {
        if (!waiClass.isInterface() && // 不要接口
            !Modifier.isAbstract(waiClass.getModifiers()) && // 不要抽象类
            WebApplicationInitializer.class.isAssignableFrom(waiClass)) {

            // 往initializers里加 new 感兴趣的类,而感兴趣的是WebApplicationInitializer
            initializers.add((WebApplicationInitializer)
                             ReflectionUtils.accessibleConstructor(waiClass).newInstance());
        }
    }

    // 排序可以看AnnotationAwareOrderComparator
    AnnotationAwareOrderComparator.sort(initializers);
    // 依次调用他们的onStartup()方法
    for (WebApplicationInitializer initializer : initializers) {
        initializer.onStartup(servletContext);// 传入了容器,如tomcat
    }
}

那么我们有哪些感兴趣的类呢?不妨看看他的实现类

下面我们看看这几个实现类的作用

四、WebApplicationInitializer

我们先不管具体的实现类,我们先看看抽象类是什么作用,这样子类继承这些抽象类的时候就可以继承这些抽象类的功能,从而简化开发。

如图,主要有下面三个抽象类,它们是父子继承关系

红色字是下面方法的onStartup()方法体,最下面的AbstractAnnotationConfigDispatcherServletInitializer类没有重写该方法

有了这个思路我们就开始往下看吧

public abstract class AbstractContextLoaderInitializer
    implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        registerContextLoaderListener(servletContext);
    }

    protected void registerContextLoaderListener(ServletContext servletContext) {
        // 该方法在AbstractAnnotationConfigDispatcherServletInitializer中被实现
        WebApplicationContext rootAppContext = createRootApplicationContext();

        // 包装为监听器
        ContextLoaderListener listener = new ContextLoaderListener(rootAppContext);
        listener.setContextInitializers(getRootApplicationContextInitializers());
        // 放到tomcat容器中
        servletContext.addListener(listener);
    }

在AbstractAnnotationConfigDispatcherServletInitializer中的createRootApplicationContext();实现为:

protected WebApplicationContext createRootApplicationContext() {
    Class<?>[] configClasses = getRootConfigClasses();
    if (!ObjectUtils.isEmpty(configClasses)) {
        // 基于注解的web容器
        AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
        context.register(configClasses);
        return context;
    }
    else {
        return null;
    }
}

那么registerDispatcherServlet(servletContext);是做什么的呢?

2)AbstractDispatcherServletInitializer

首先了解一下父子容器的结构

img
// 注册DispatcherServlet到tomcat中
protected void registerDispatcherServlet(ServletContext servletContext) {// 参数为要注册的容器
    // DispatcherServlet的名字
    String servletName = getServletName();

    // 创建一个web的ioc容器
    WebApplicationContext servletAppContext = createServletApplicationContext();

    // 创建servlet并注册到web容器
    FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);

    dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers());

    // 注册dispatcherServlet到tomcat
    ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);

    // 设置dispatcherServlet初始化时机、映射url、异步
    registration.setLoadOnStartup(1);
    registration.addMapping(getServletMappings());
    registration.setAsyncSupported(isAsyncSupported());

    // 遍历注册过滤器
    Filter[] filters = getServletFilters();
    for (Filter filter : filters) {
        registerServletFilter(servletContext, filter);
    }

    // 扩展接口
    customizeRegistration(registration);
}
3)AbstractAnnotationConfigDispatcherServletInitializer

3)、AbstractAnnotationConfigDispatcherServletInitializer:注解方式配置的DispatcherServlet初始化器

  • 创建根容器:createRootApplicationContext()
    getRootConfigClasses();传入一个配置类
  • 创建web的ioc容器: createServletApplicationContext();
    获取配置类;getServletConfigClasses();
public abstract class AbstractAnnotationConfigDispatcherServletInitializer
    extends AbstractDispatcherServletInitializer {

    @Override
    @Nullable
    protected WebApplicationContext createRootApplicationContext() {
        Class<?>[] configClasses = getRootConfigClasses();
        if (!ObjectUtils.isEmpty(configClasses)) {
            AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
            context.register(configClasses);
            return context;
        }
        else {
            return null;
        }
    }
    
    @Override
    protected WebApplicationContext createServletApplicationContext() {
        AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
        Class<?>[] configClasses = getServletConfigClasses();
        if (!ObjectUtils.isEmpty(configClasses)) {
            context.register(configClasses);
        }
        return context;
    }

总结:

1)、AbstractContextLoaderInitializer:创建根容器;createRootApplicationContext();
2)、AbstractDispatcherServletInitializer:
			创建一个web的ioc容器;createServletApplicationContext();
			创建了DispatcherServlet;createDispatcherServlet();
			将创建的DispatcherServlet添加到ServletContext中; // servlet上下文
				getServletMappings();
3)、AbstractAnnotationConfigDispatcherServletInitializer:注解方式配置的DispatcherServlet初始化器
			创建根容器:createRootApplicationContext()
					getRootConfigClasses();传入一个配置类
			创建web的ioc容器: createServletApplicationContext();
					获取配置类;getServletConfigClasses();
总结
1、web容器在启动的时候,会扫描每个jar包下的META-INF/services/javax.servlet.ServletContainerInitializer
2、加载这个文件指定的类SpringServletContainerInitializer
3、spring的应用一启动会加载感兴趣的WebApplicationInitializer接口的下的所有组件;
4、并且为WebApplicationInitializer组件创建对象(组件不是接口,不是抽象类)
	1)、AbstractContextLoaderInitializer:创建根容器;createRootApplicationContext();
	2)、AbstractDispatcherServletInitializer:
			创建一个web的ioc容器;createServletApplicationContext();
			创建了DispatcherServlet;createDispatcherServlet();
			将创建的DispatcherServlet添加到ServletContext中;
				getServletMappings();
	3)、AbstractAnnotationConfigDispatcherServletInitializer:注解方式配置的DispatcherServlet初始化器
			创建根容器:createRootApplicationContext()
					getRootConfigClasses();传入一个配置类
			创建web的ioc容器: createServletApplicationContext();
					获取配置类;getServletConfigClasses();
	
总结:
	以注解方式来启动SpringMVC;继承AbstractAnnotationConfigDispatcherServletInitializer;
实现抽象方法指定DispatcherServlet的配置信息;

===========================
定制SpringMVC;
1)、@EnableWebMvc:开启SpringMVC定制配置功能;
	<mvc:annotation-driven/>;

2)、配置组件(视图解析器、视图映射、静态资源映射、拦截器。。。)
	extends WebMvcConfigurerAdapter
为了不用写接口的实现才用适配器类

五、父子容器

在这里插入图片描述

上图中显示了2个WebApplicationContext实例,为了进行区分,分别称之为:Servlet WebApplicationContext、Root WebApplicationContext。 其中:

  • Servlet WebApplicationContext:这是对J2EE三层架构中的web层进行配置,如控制器(controller)、视图解析器(view resolvers)等相关的bean。通过spring mvc中提供的DispatchServlet来加载配置,通常情况下,配置文件的名称为spring-servlet.xml。
  • Root WebApplicationContext:这是对J2EE三层架构中的service层、dao层进行配置,如业务bean,数据源(DataSource)等。通常情况下,配置文件的名称为applicationContext.xml。在web应用中,其一般通过ContextLoaderListener来加载。

我们在配置过程中,通常是将两个容器进行bean的隔离。所谓bean的隔离,其实就是分别存储不同的bean类型对象。也就是在bean的声明时,有目的的将不同的bean配置在两个容器的xml文件中,防止所有的bean都在一个配置文件中,也就是一个容器里。

作用

父子容器的作用主要是划分框架边界。

​ 在J2EE三层架构中,在service层我们一般使用spring框架, 而在web层则有多种选择,如spring mvc、struts等。因此,通常对于web层我们会使用单独的配置文件。例如在上面的案例中,一开始我们使用spring-servlet.xml来配置web层,使用applicationContext.xml来配置service、dao层。如果现在我们想把web层从spring mvc替换成struts,那么只需要将spring-servlet.xml替换成Struts的配置文件struts.xml即可,而applicationContext.xml不需要改变。

​ 事实上,如果你的项目确定了只使用spring和spring mvc的话,你甚至可以将service 、dao、web层的bean都放到spring-servlet.xml中进行配置,并不是一定要将service、dao层的配置单独放到applicationContext.xml中,然后使用ContextLoaderListener来加载。在这种情况下,就没有了Root WebApplicationContext,只有Servlet WebApplicationContext。

  • Tomcat在启动时给每个Web应用创建一个全局的上下文环境,这个上下文就是ServletContext,其为后面的Spring容器提供宿主环境。
  • Tomcat在启动过程中触发容器初始化事件,Spring的ContextLoaderListener会监听到这个事件,它的contextInitialized()方法会被调用,在这个方法中,Spring会初始化全局的Spring根容器,这个就是Spring的IoC容器,IoC容器初始化完毕后,Spring将其存储到ServletContext中,便于以后来获取。
  • Tomcat在启动过程中还会扫描Servlet,一个Web应用中的Servlet可以有多个,以SpringMVC中的DispatcherServlet为例,这个Servlet实际上是一个标准的前端控制器,用以转发、匹配、处理每个Servlet请求。

Servlet一般会延迟加载,当第一个请求达到时,Tomcat&Jetty发现DispatcherServlet还没有被实例化,就调用DispatcherServlet的init方法,DispatcherServlet在初始化的时候会建立自己的容器,叫做SpringMVC 容器,用来持有Spring MVC相关的Bean。同时,Spring MVC还会通过ServletContext拿到Spring根容器,并将Spring根容器设为SpringMVC容器的父容器,请注意,Spring MVC容器可以访问父容器中的Bean,但是父容器不能访问子容器的Bean, 也就是说Spring根容器不能访问SpringMVC容器里的Bean。说的通俗点就是,在Controller里可以访问Service对象,但是在Service里不可以访问Controller对象。

å¨è¿éæå¥å¾çæè¿°
web.xml解读

上面配置了Root容器和Web容器,但是还有一项非常重要的工作没有做,那就是要去加载这两个容器的配置文件。那么如何让springmvc框架去为我们加载配置文件呢,这就用到了springmvc框架的基础配置文件web.xml,这个配置文件是spring在启动时最先加载的配置文件,如果我们在这个配置文件中,由引入了咱们自己写的Root容器和Web容器的配置文件,那么springmvc框架在加载自己的web.xml时,会顺带着把我们定义的两个容器的配置文件也一起加载了,达到我们的目的。

在web.xml中我们配置了监听器

    <!--spring监听器-->
    <listener>  
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>  
    </listener>  

ContextLoaderListener会被优先初始化时,其会根据<context-param>元素中contextConfigLocation参数指定的配置文件路径,在这里就是"/WEB-INF/”,来创建WebApplicationContext实例。 并调用ServletContext的setAttribute方法,将其设置到ServletContext中,属性的key为”org.springframework.web.context.WebApplicationContext.ROOT”,最后的”ROOT"字样表明这是一个 Root WebApplicationContext。

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:spring-root.xml</param-value>
</context-param>

监听器和过滤器几个标签的顺序,是固定的,不可以随便的更换。

最后,我们配置了DispatcherServlet,这是Springmvc容器的必备过滤器,

<!--注册DispatcherServlet-->
    <servlet>  
        <servlet-name>dispatcher</servlet-name>  
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
        <init-param>  
            <param-name>contextConfigLocation</param-name>  
            <param-value>/WEB-INF/spring/spring-mvc.xml</param-value>  
        </init-param>  
        <load-on-startup>1</load-on-startup>  
    </servlet>  
    <servlet-mapping>  
        <servlet-name>dispatcher</servlet-name>  
        <url-pattern>/*</url-pattern>  
    </servlet-mapping>  

这里我们在<servlet-class>类方法中使用了<init-param>标签,也就是给指定的方法传入初始化参数,也就是我们自定义的参数,那么这里,我们传入的参数名称是contextConfigLocation,值是classpath:spring-mvc.xml,瞄向的正是我们刚才自己配置的Web容器,所以当springmvc框架加载到这几行代码时,就会去读取我们指定的Web容器配置文件,将Web容器中的内容进行加载。这里必须强调,要使用<init-param>标签将web容器的路径进行指定,否则web容器将无法加载,那我们的配置文件就等于没写。最终会创建Servlet WebApplicationContext。同时,其会调用ServletContext的getAttribute方法来判断是否存在Root WebApplicationContext。如果存在,则将其设置为自己的parent。这就是父子上下文(父子容器)的概念。

父子容器的作用在于,当我们尝试从child context(即:Servlet WebApplicationContext)中获取一个bean时,如果找不到,则会委派给parent context (即Root WebApplicationContext)来查找。

如果我们没有通过ContextLoaderListener来创建Root WebApplicationContext,那么Servlet WebApplicationContext的parent就是null,也就是没有parent context

六、重读xml配置

1)web.xml

之前我们用web.xml方式配置时,一般配置为:

<?xml version="1.0" encoding="UTF-8"?>  
  
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"  
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaeehttp://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">  
    
    <context-param>  
        <param-name>contextConfigLocation</param-name>  
        <param-value>/WEB-INF/spring.xml</param-value>  
    </context-param>  
  
    <!--spring监听器-->
    <listener>  
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>  
    </listener>  
    
    <!--注册DispatcherServlet-->
    <servlet>  
        <servlet-name>dispatcher</servlet-name>  
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
        <init-param>  
            <param-name>contextConfigLocation</param-name>  
            <param-value>/WEB-INF/spring/spring-mvc.xml</param-value>  
        </init-param>  
        <load-on-startup>1</load-on-startup>  
    </servlet>  
    <servlet-mapping>  
        <servlet-name>dispatcher</servlet-name>  
        <url-pattern>/*</url-pattern>  
    </servlet-mapping>  
  
</web-app>

上面的意思是,分别通过监听器和dispatcher来加载spring根容器和mvc子容器

一般web.xml中还有post请求编解码过滤器、rest请求过滤器,这些配置在前面。

2)spring.xml

Servlet容器(tomcat、jetty)启动时,使用ContextLoaderListener读取 web.xml中的contextConfigLocation全局参数,初始化spring容器,如果没有这 个参数,那么ContextLoaderListener会加载/WEBINF/applicationContext.xml文件; spring容器主要用于整合struts1、Struts2;

<?xml version="1.0" encoding="UTF-8"?>
<!--标准的spring的配置文件-->
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
       http://www.springframework.org/schema/tx
   http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
       http://www.springframework.org/schema/context
   http://www.springframework.org/schema/context/spring-context-4.3.xsd
       http://www.springframework.org/schema/aop
   http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
        ">

    <context:component-scan base-package="com.golden">
        <!-- 扫描@Service和@Repository注解 -->
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>

        <!-- 排除@Controller、@RestController、@ControllerAdvice【用于全局异常处理】 -->
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
        <context:exclude-filter type="annotation" expression="org.springframework.web.bind.annotation.RestController" />
        <context:exclude-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice" />
    </context:component-scan>

    <!-- 1. 数据源 -->
    <!-- 驱动名称、链接地址、用户名、密码 -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://127.0.0.1:3306/test?serverTimezone=GMT" />
        <property name="username" value="root" />
        <property name="password" value="root" />
    </bean>

    <!-- 2. SqlSessionFactoryBean -->
    <!-- 属性: Bean别名、数据源、mapper位置、插件 【config.xml】-->
    <!-- typeAliasesPackage\dataSource\mapperLocations\plugins -->
    <bean name="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="mapperLocations" value="classpath:mappers/*.xml" />
        <property name="typeAliasesPackage" value="com.golden.bean" />
        <property name="plugins">
            <array>
                <!--5.0版本前是PageHelper-->
                <bean class="com.github.pagehelper.PageInterceptor">
                    <!-- 调用setProperties()方法 -->
                    <property name="properties">
                        <props>
                            <prop key="helperDialect">mysql</prop>
                            <prop key="resonable">true</prop>
                        </props>
                    </property>
                </bean>
            </array>
        </property>
    </bean>

    <!-- 3. MapperScannerConfigurer: 扫描mapper接口,创建代理类,并将代理类加载到spring容器中 -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.golden.mapper" />
        <!--如果有多个FactoryBean的话,可以使用这种方式指定一下-->
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
    </bean>

    <!-- aspectj方式配置声明式事务 -->
    <!-- 事务管理器 -->
    <bean id="tx" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>

    <!-- 事务的通知 -->
    <tx:advice id="txAdvice" transaction-manager="tx">
        <tx:attributes>
            <tx:method name="add*" propagation="REQUIRED" isolation="DEFAULT" rollback-for="Throwable"/>
            <tx:method name="insert*" propagation="REQUIRED" isolation="DEFAULT" rollback-for="Throwable"/>
            <tx:method name="update*" propagation="REQUIRED" isolation="DEFAULT" rollback-for="Throwable"/>
            <tx:method name="delete*" propagation="REQUIRED" isolation="DEFAULT" rollback-for="Throwable"/>
            <tx:method name="del*" propagation="REQUIRED" isolation="DEFAULT" rollback-for="Throwable"/>
        </tx:attributes>
    </tx:advice>

    <aop:config>
        <!-- 切入点 -->
        <aop:pointcut id="tx-point" expression="execution(* com.golden.service..*.*(..))" />
        <!-- 将事务通知与切入点关联起来 -->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="tx-point" />
    </aop:config>

    <!--注解版事务-->
    <tx:annotation-driven transaction-manager="tx" />

</beans>

以上Root容器配置文件,里面包含了所有需要扫描的Bean,限定类型为:Service和Repository。像@Controller、@RestController、@ControllerAdvice注解,统统拦截住不去扫描,因为这些注解是接下来Web容器需要扫描的。由此可以看出,这些基础的Bean类型,在Root容器中扫描过,就需要在Web容器中剔除,属于有你没我的状态,以此来实现完全隔离。

初次之外,还配置了持久层的相关内容,例如:spring整合Mybatis的相关配置和事务管理器的配置。这些都不属于web相关内容,而是持久层的内容,所以配置在root容器中。

3)springmvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<!--标准的spring的配置文件-->
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
                           http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
                           http://www.springframework.org/schema/mvc
                           http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
                           http://www.springframework.org/schema/context
                           http://www.springframework.org/schema/context/spring-context-4.3.xsd">

    <!-- 容器隔离 -->
    <context:component-scan base-package="com.golden">
        <!--指定 spring mvc 扫描的注解-->
        <!--@Controller @RestController @ControllerAdvice-->
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
        <context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.RestController" />
        <context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice" />

        <!--指定不扫描的注解-->
        <!--@Service  @Respository-->
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service" />
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository" />
    </context:component-scan>

    <mvc:annotation-driven />

    <mvc:default-servlet-handler />

    <mvc:resources mapping="/pics/**" location="file:d:/upload/" />
    <mvc:resources mapping="/**" location="/static/" />

    <!--整合Thymeleaf-->
    <!--SpringResourceTemplateResovler-->
    <bean id="templateResolver" class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
        <!--5个属性-->
        <property name="prefix" value="/templates/" />
        <property name="suffix" value=".html" />
        <property name="templateMode" value="HTML" />
        <property name="characterEncoding" value="UTF-8" />
        <!-- 在开发环境中,将cacheable设置为false,进行热加载 -->
        <!--正式环境中,是true-->
        <property name="cacheable" value="false" />
    </bean>

    <bean id="templateEngine" class="org.thymeleaf.spring5.SpringTemplateEngine">
        <property name="templateResolver" ref="templateResolver"/>
    </bean>

    <bean class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
        <property name="templateEngine" ref="templateEngine"/>
        <!--如果不写这个,返回到页面的中文将乱码-->
        <property name="characterEncoding" value="UTF-8" />
    </bean>

    <!-- 文件上传解析器 -->
    <!-- id的值是multipartResolver -->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!-- 允许上传的文件大小:2MB -->
        <property name="maxUploadSize" value="2097152" />
    </bean>


    <!-- 配置拦截器 -->
    <!-- 配置登录拦截器 -->
    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <mvc:exclude-mapping path="/js/**" />
            <mvc:exclude-mapping path="/user/toLogin" />
            <mvc:exclude-mapping path="/user/login" />
            <bean class="com.golden.interceptor.LoginInterceptor" />
        </mvc:interceptor>
    </mvc:interceptors>

</beans>

注解式代替xml

下面我们也说了mvc的SPI机制,其实他是Servlet 3.0被设计用来支持编码配置servlet容器,他通过使用spring的SPI规范代替传统的web.xml配置方式。

他利用了是spring的SPI规范注入了一个ServletContainerInitializer类。

操作机制:如果spring-web JAR包被放到类路径下,先会实例化SpringServletContainerInitializer类,在实例化SpringServletContainerInitializer后,会调用onStartup方法,而该方法会让感兴趣的类(WebApplicationInitializer)被加载了

加载机制是利用了ServiceLoader#load(Class)方法在spring-web JAR包下,检测到META-INF/services/javax.servlet.ServletContainerInitializer这个文件,可以看 https://download.oracle.com/javase/6/docs/technotes/guides/jar/jar.html#Service%20Provider

spring的WebApplicationInitializer的关系:spring的SPI包含了一个方法WebApplicationInitializer#onStartup(ServletContext),该方法类似于ServletContainerInitializer#onStartup(Set, ServletContext)

SpringServletContainerInitializer负责实例化和派发ServletContext给用户定义的WebApplicationInitializer实现。具体工作是由WebApplicationInitializer负责的,具体工作包括初始化ServletContext

该类应该被视为面向用户SPI的supporting infrastructure,如果没有WebApplicationInitializer实现类,该从起将不起作用

容器并没有绑定到mvc上,而是spring-web JAR包中关联着。可以被视为方便注解开发ServletContext。也就是说任何三大组件
servlet, listener, or filter可以注册到WebApplicationInitializer,而不仅仅是MVC的组件。

该类不支持继承,他应该被视为一个中间类

1)初始化器
//web容器启动的时候创建对象;调用方法来初始化容器以前前端控制器
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

	//获取根容器的配置类;(Spring的配置文件)   父容器;
	@Override
	protected Class<?>[] getRootConfigClasses() {
		// TODO Auto-generated method stub
		return new Class<?>[]{RootConfig.class};
	}

	//获取web容器的配置类(SpringMVC配置文件)  子容器;
	@Override
	protected Class<?>[] getServletConfigClasses() {
		// TODO Auto-generated method stub
		return new Class<?>[]{AppConfig.class};// WebMvcConfigurerAdapter
	}

	//获取DispatcherServlet的映射信息
	//  /:拦截所有请求(包括静态资源(xx.js,xx.png)),但是不包括*.jsp;
	//  /*:拦截所有请求;连*.jsp页面都拦截;jsp页面是tomcat的jsp引擎解析的;
	@Override
	protected String[] getServletMappings() {
		// TODO Auto-generated method stub
		return new String[]{"/"};
	}
}
2)MVC配置

AppConfig

//SpringMVC只扫描Controller;子容器
//useDefaultFilters=false 禁用默认的过滤规则;
@ComponentScan(value="com.atguigu",includeFilters={
		@Filter(type=FilterType.ANNOTATION,classes={Controller.class})
},useDefaultFilters=false)//禁用默认的过滤规则
@EnableWebMvc
public class AppConfig  extends WebMvcConfigurerAdapter  {

	//定制
	
	//视图解析器
	@Override
	public void configureViewResolvers(ViewResolverRegistry registry) {
		//默认所有的页面都从 /WEB-INF/ xxx .jsp
		//registry.jsp();
		registry.jsp("/WEB-INF/views/", ".jsp");
	}
	
	//静态资源访问
	@Override
	public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
		configurer.enable();
	}
	
	//拦截器
	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		//super.addInterceptors(registry);
		registry.addInterceptor(new MyFirstInterceptor()).addPathPatterns("/**");
	}

}
3)spring配置

RootConfig

package com.atguigu.config;


//Spring不扫描@Controller
@ComponentScan(value="com.atguigu",excludeFilters={
		@Filter(type=FilterType.ANNOTATION,classes={Controller.class})
})
public class RootConfig {

}

七、静态资源处理

  • Web容器中注册的Bean都是直接与web请求密切关联的,由最开始的@Controller @RestController @ControllerAdvice注解扫描器的配置;
  • 使用<mvc:annotation-driven />,Spring会默认帮我们注册处理请求,参数和返回值的类。主要是实现了以下两个接口:HandlerMapping与HandlerAdapter。
  • 使用<mvc:default-servlet-handler />,会在Spring MVC上下文中定义一个DefaultServletHttpRequestHandler,它会像一个检查员,对进入DispatcherServlet的URL进行筛查,
    • 如果发现是静态资源的请求,就将该请求转由Web应用服务器默认的Servlet处理,
    • 如果不是静态资源的请求,才由DispatcherServlet继续处理。
    • 一般Web应用服务器默认的Servlet名称是"default",因此DefaultServletHttpRequestHandler可以找到它。如果你所有的Web应用服务器的默认Servlet名称不是"default",则需要通过default-servlet-name属性显示指定:<mvc:default-servlet-handler default-servlet-name="所使用的Web服务器默认使用的Servlet名称" />
  • 使用<mvc:resources mapping="" location="">:
    • <mvc:default-servlet-handler />将静态资源的处理经由Spring MVC框架交回Web应用服务器处理。而<mvc:resources />更进一步,由Spring MVC框架自己处理静态资源,并添加一些有用的附加值功能。
    • 首先,<mvc:resources />允许静态资源放在任何地方,如WEB-INF目录下、类路径下等,你甚至可以将JavaScript等静态文件打到JAR包中。通过location属性指定静态资源的位置,由于location属性是Resources类型,因此可以使用诸如"classpath:"等的资源前缀指定资源位置。传统Web容器的静态资源只能放在Web容器的根路径下,<mvc:resources />完全打破了这个限制。
    • 其次,<mvc:resources />依据当前著名的Page Speed、YSlow等浏览器优化原则对静态资源提供优化。你可以通过cacheSeconds属性指定静态资源在浏览器端的缓存时间,一般可将该时间设置为一年,以充分利用浏览器端的缓存。在输出静态资源时,会根据配置设置好响应报文头的Expires 和 Cache-Control值。
    • 在接收到静态资源的获取请求时,会检查请求头的Last-Modified值,如果静态资源没有发生变化,则直接返回303相应状态码,提示客户端使用浏览器缓存的数据,而非将静态资源的内容输出到客户端,以充分节省带宽,提高程序性能。
  • 整合Thymeleaf,包括:SpringResourceTemplateResolver、SpringTemplateEngine、ThymeleafViewResolver
  • 文件上传解析器,用于前端文件上传时使用。
  • 各种拦截器。例如:登录请求拦截器,用来检测用户是否登录成功,如果没有登录则无权访问其他页面。需要提前将拦截器写好,在这里注册

八、注册web组件

有了上面的经验,我们用它往容器中注册普通servlet、过滤器、监听器

/**
	 * 1)、使用ServletContext注册Web组件(Servlet、Filter、Listener)
	 以前加注册的方式我们自己写代码的时候可以加上@HandlesTypes,但是第三方包如果是原来的web.xml可以配置在其中,但现在没有了xml,我们可以利用ServletContext给他注册进来
	 * 2)、使用编码的方式,在项目启动的时候给ServletContext里面添加组件;
	 * 		必须在项目启动的时候来添加;
	 * 		1)、ServletContainerInitializer得到的ServletContext;
	 * 		2)、ServletContextListener得到的ServletContext;
	 */
@HandlesTypes(value={HelloService.class})//传入感兴趣的类型;
public class MyServletContainerInitializer 
    implements ServletContainerInitializer {


    @Override//应用启动的时候,会运行onStartup方法;
    public void onStartup(Set<Class<?>> arg0, //感兴趣的类型的所有子类
                          ServletContext sc) {//代表当前Web应用的ServletContext;一个Web应用一个ServletContext;

        //注册组件  ServletRegistration  
        ServletRegistration.Dynamic servlet = sc.addServlet("userServlet", new UserServlet());
        //配置servlet的映射信息 // 处理/user请求
        servlet.addMapping("/user");

        //注册Listener
        sc.addListener(UserListener.class);
        
        //注册Filter  FilterRegistration
        FilterRegistration.Dynamic filter = sc.addFilter("userFilter", UserFilter.class);
        //配置Filter的映射信息
        filter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST),//过滤类型 
                                        true, 
                                        "/*");//要拦截的路径,所有请求
自定义web监听器
/**
 * 监听项目的启动和停止
 */
public class UserListener implements ServletContextListener {

	//监听ServletContext销毁
	@Override
	public void contextDestroyed(ServletContextEvent arg0) {
		System.out.println("UserListener...contextDestroyed...");
	}

	//监听ServletContext启动初始化
	@Override
	public void contextInitialized(ServletContextEvent arg0) {
		ServletContext servletContext = arg0.getServletContext();
		System.out.println("UserListener...contextInitialized...");
	}
}
自定义web过滤器
public class UserFilter implements Filter {

	@Override
	public void destroy() {}

	@Override
	public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2)
			throws IOException, ServletException {
		System.out.println("UserFilter...doFilter...");
		arg2.doFilter(arg0, arg1);
	}

	@Override
	public void init(FilterConfig arg0) throws ServletException {}
}

附录

定制SpringMVC;
1)、@EnableWebMvc:开启SpringMVC定制配置功能;
	相当于<mvc:annotation-driven/>;

2)、配置组件(视图解析器、视图映射、静态资源映射、拦截器。。。)
	通过继承extends WebMvcConfigurerAdapter

DispatcherServlet

img

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HttpServletRequest processedRequest = request;
		HandlerExecutionChain mappedHandler = null;
		boolean multipartRequestParsed = false;

		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

		try {
			ModelAndView mv = null;
			Exception dispatchException = null;

			try {
				processedRequest = checkMultipart(request);
				multipartRequestParsed = (processedRequest != request);

				// Determine handler for the current request.
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

				// Determine handler adapter for the current request.
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

				// Process last-modified header, if supported by the handler.
				String method = request.getMethod();
				boolean isGet = "GET".equals(method);
				if (isGet || "HEAD".equals(method)) {
					long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
					if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
						return;
					}
				}

				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}

				// Actually invoke the handler.
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}

				applyDefaultViewName(processedRequest, mv);
				mappedHandler.applyPostHandle(processedRequest, response, mv);
			}
			catch (Exception ex) {
				dispatchException = ex;
			}
			catch (Throwable err) {
				// As of 4.3, we're processing Errors thrown from handler methods as well,
				// making them available for @ExceptionHandler methods and other scenarios.
				dispatchException = new NestedServletException("Handler dispatch failed", err);
			}
			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
		}
		catch (Exception ex) {
			triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
		}
		catch (Throwable err) {
			triggerAfterCompletion(processedRequest, response, mappedHandler,
					new NestedServletException("Handler processing failed", err));
		}
		finally {
			if (asyncManager.isConcurrentHandlingStarted()) {
				// Instead of postHandle and afterCompletion
				if (mappedHandler != null) {
					mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
				}
			}
			else {
				// Clean up any resources used by a multipart request.
				if (multipartRequestParsed) {
					cleanupMultipart(processedRequest);
				}
			}
		}
	}
pom

没有xml的时候,在pom.xml里写打包方式是war时会报错,设置好maven-war-plugin解决

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.atguigu</groupId>
    <artifactId>springmvc-annotation</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>4.3.11.RELEASE</version>
        </dependency>
        
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>3.0-alpha-1</version>
            <!--Tomcat里也有这个包,所以打包的时候不要他-->
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.4</version>
                <configuration>
                    <!--没有web.xml时不要报错-->
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>6</source>
                    <target>6</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

实战

// 自己启动一个tomcat
main(){
    Tomcat tomcat = new Tomcat();
    tomcat.setPort(80);
    tomcat.addWebapp("/","C:/tomcat");
    tomcat.start();
    tomcat.getServer.await();
}

// 把下面这个类写到ServletContainerInitializer文件中
// main启动,然后SPI加载到容器,容器加载到WebApplicationInitializer,调用它的方法
@ComponentScan()
public class MyWebApplicationinitializer implements WebApplicationInitializer{
    @Override
    public void onStartup(ServletContext servletContext){// 前面传过来的参数

        // 创建WebApplication
        AnnotationConfigWebApplication webAC = new AnnotationConfigWebApplication();
        webAC.register(MyWebApplicationinitializer.class);//开启包扫描
        webAC.refresh();
        
        DispatcherServlet dispatcherServlet = new DispatcherServlet(webAC );
        // 往容器里加servlet
        ServletRegistration.Dynamic dynamic=  servletContext.addServlet("myDis",dispatcherServlet);
        dynamic.setLoadOnstartup(1);
        dynamic.addMapping("/");
    }
}

public class MyWebApplicationInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext servletContext) {

        // Load Spring web application configuration
        AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
        context.register(AppConfig.class);

        DispatcherServlet servlet = new DispatcherServlet(context);
        // 他有一个返回值
        ServletRegistration.Dynamic registration = servletContext.addServlet("app", servlet);
        registration.setLoadOnStartup(1);
        registration.addMapping("/app/*");
    }
}
相当于
<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>
LangGraph教程8:LangGraph流式处理 流式处理可显著提升基于LLM应用的响应性。LangGraph支持两种流式模式:values模式返回每一步的完整结果,适合需要完整上下文处理的场景;updates模式仅返回增量变化,适用于实时更新需求。示例代码展示了如何用create_react_agent实现这两种模式,其中values模式完整记录处理流程,updates模式逐步输出结果。通过异步迭代输出流,开发者可以灵活选择适合应用场景的流式处理方式。 阅读详情

相关推荐

SAP批量程序(一)-静态批量EX,FX和期间批量MB,WB和TB

SAP批量程序(一)-静态批量和区间批量

weixin_38974136的博客 5476

spring5源码篇(13)——spring mvc无xml整合tomcat与父子容器的启动

spring5源码篇(13)——spring mvc无xml整合tomcat与父子容器的启动

笔记罢了 1146

关于Vscode配置Unity环境时的一些报错问题(持续更新)

3.找到设置文件,添加如下代码,用于告诉VSCode)在处理 C# 项目时优先使用 C# 扩展来加载和管理项目,而不是使用 C# Dev Kit 或其他扩展。1.很可能是unity的插件版本问题,打开window > package manager将。2.很可能是项目文件的问题,建议unity打开edit > perfercenes。3.手动重装其他版本的.Net框架(去官网找,多下几个无所谓的)解决:(由于造成问题不一样,所以建议都尝试一次)下载net请求超时(一般都会超时很正常的)

bug制造机 1886

深度解析dubbo扩展技术dubbo spi(实现二)

1.从配置文件加载扩展点实现 接着上篇文章从getExtensionClasses()方法开始,来看看这个获取获取扩展点实现类class这个方法 private Map<String, Class<?>> getExtensionClasses() { Map<String, Class<?>> classes = cachedClasses.get(); // 之前没有加载过 if (classes == nu

猿上生活 3497

别再手动算频谱了!用这个在线FFT工具,5分钟搞定JSON/CSV数据分析

本文介绍了一款高效的在线FFT工具,帮助用户快速分析JSON/CSV格式的时域数据,5分钟内完成频谱分析。通过傅里叶变换(FFT)算法,大幅提升计算效率,解决传统手动计算的痛点,适用于工业振动分析、设备故障诊断等场景。

weixin_29172201的博客 258

如何创建属于自己的大语言模型:从零开始的指南

随着人工智能的快速发展,大语言模型(LLM)在各种场景中表现出了卓越的能力,例如文本生成、对话交互和内容总结等。本文将带你了解创建 LLM 的核心步骤,从理论到实践,让你拥有一个自己的智能助手。

赵大仁的博客 3279

SpringMvc 源码分析 (SPI 机制) (一)

SpringMvc基于Spi机制来启动web容器

959 1192

Microsoft Bitlocker企业级管理部署方案

BitLocker是Windows 10以及更高版本中内置的磁盘加密工具,是一种可靠的数据保护机制,可以保护计算机上的敏感数据不被未经授权的人访问。BitLocker的工作原理是将整个系统卷或其他数据卷加密的同时,向某个可信软件或硬件模块请求密钥解密数据。BitLocker最大的好处在于其内置于Windows系统,所以用户无需安装其他软件或购买额外的许可证或硬件设备。本文将介绍Microsoft BitLocker企业级管理部署方案,包括:BitLocker部署前的准备工作。

xueyunshengling的博客 2073

关于Spring框架中多个xml的调用方法

关于Spring框架中多个xml的调用方法 注意如果每个包都调用一个,第一个xml文件应命名为Beans1,第二个命名为Beans2。 而不是第一个命名为Beans,第二个命名为Beans2。 若有违反,依旧冲突报错。 1.如图所示 2.项目结构 3.总结 ...

existent 420

OmniBox2.0.X版本 一站式影视订阅与多端播放平台文档

OmniBox 是一站式影视订阅与多端播放平台:你可以用 JS 或 Python 按 OmniBox 规范自建爬虫源,在后台把多个爬虫源自由组合成订阅,然后在网页、手机、电视等全平台的客户端里使用。只要客户端支持 TVBox、猫源、UZ、Emby 等协议,即可无缝对接。同时支持电视直播源、六大平台直播间和网盘源视频在线观看,通过 Docker 一键部署。

欢迎来到鸾姝淡月博客,专为程序员和编程爱好者打造的技术博客。我们分享编程语言趋势、开发工具、编程哲学、职业发展等,助你掌握新技术,优化技能,激发编程热情。 1893

Visual Studio Code + MCP Server + Claude Code 三件套进行 ABAP 开发

本文介绍了如何在Visual Studio Code中利用Claude Code和SAP官方提供的MCP Server进行ABAP开发。作者分享了19年ABAP开发经验,从手动编码到如今通过自然语言生成高质量代码的技术演进。详细说明了配置步骤:启用ADT MCP Server、安装Claude Code扩展、配置.claude.json文件,并验证MCP Server的14个功能。通过实例演示了如何用自然语言指令让Claude Code创建包含冒泡排序实现和单元测试的ABAP类,展示了MCP Server在

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 1295

spring的SPEL表达式

SPEL表达式

qq_40419080的博客 2196

【LangChain编程:从入门到实践】实现多模态代理

【LangChain编程:从入门到实践】实现多模态代理 关键词:LangChain, 多模态代理, 自然语言处理, 机器学习, 知识图谱, 对话系统, 代码生成, AI代理 1. 背景介绍 随着人工智

欢迎来到我的优快云空间!这里聚焦AI大模型应用实战,分享前沿技术、实战案例与开发经验。 578

Spring 中配置文件里SPEL表达式的使用

在配置中使用SpEL表达式 #{表达式} <bean id="str" class="java.lang.String" c:original="hello"/> <bean id="bean2" class="com.example.spring.di.spel.Bean2"> <!-- SpEL表达式,是在Spring配置文件中原生支持的。 --> <property name="dbConfig" value=

weixin_47371330的博客 3806

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

Spring MVC配置

<br />----使用SpringMVc开发时<br />----使用SpringMVc开发时<br /><br /> <?xml version="1.0" encoding="UTF-8"?><br /> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jee="http://www.springframewo

masuwen博客 1173

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

tomcat启动创建spring父子容器

一、小知识课堂 1.关键性接口ServletContextListener 监听器ServletContextListener,监听ServletContext的生命周期。换句话说,监听Web容器的生命周期。所以说,如果tomcat启动时就加载spring容器,必然与该监听器有关联。 public interface ServletContextListener extends EventListener { // tomcat启动时,触发该方法 void contextInitialized(S

weixin_43843164的博客 457

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

Tomcat部署及优化

文章目录引言一、Tomcat服务器简介1、什么是 servlet?2、什么是 JSP?3、tomcat核心组件3.1 Tomcat 功能组件结构3.2 Container 结构分析3.3 Tomcat 请求过程3.4 tomcat核心和功能组件作用二、Tomcat数据流向1、Tomcat数据流向2、Nginx-tomcat数据流三、Tomcat服务部署安装1、将所需的安装包拖入/opt目录下准备安装2、关闭防火墙3、安装JDK4、设置JDK环境变量5、编写一个java文件验证6、安装启动Tomcat7、优化

qq_45088125的博客 1593

Spring源码学习-SPI机制与Tomcat结合SpringMVC原理剖析

属于Java的Servlet(servlet是接口,其余是抽象类)Servlet定义了最初始重要的五个方法留给子类去实现定义了一个简单版本的servlet但是无法使用,允许你继承实现这个接口去扩展(init方式是空的)没有提供init初始化方法的实现,把service处理进一步细化,分为get,post等等属于Spring的Servlet重写了init方法,并且继续给子类留下了模板方法由子类去任意发挥重写了上面留下的模板方法initServletBean,在这个里面调用了。

一个有信仰的技术人 710

再学一点Spring MVC

MVC模式,全称是Model-View-Controller(模型-视图-控制器)模式,它是一种软件架构模式,目标是将软件的用户界面和业务逻辑隔离,使代码具有更高的可扩展性,可复用行,可维护性以及灵活性. MVC模式将应用程序划分成模型、视图、控制器等三层,如下图所示: 概念解释: view controller MVC模式的优点:完整的DispatcherServlet初始化流程,详见下图: 这里我们以springboot为例进行springmvc初始化流程的分析,大家需要注意了,如果

zk1z23456789的博客 661

Tomcat中容器是什么以及容器与容器之间的数量关系。

Tomcat容器到底是什么 学java有一小段时间了,一直使用Tomcat,也知道Tomcat是一个大的Servlet容器,里面还有许多子容器,容器之间都是相互嵌套的。也看过一下接收Tomcat的文章,都是一幅图,图中有好多个框框相互嵌套,表示容器与容器之间的相互嵌套,也没什么说明,把我看得云里雾里的。所以要想了解的更多必须自己探索一下。   现在把自己学到的一点点知识记下来,以免忘...

dieyuanjiao9299的博客 382

xml tomcat不加载_一次搞懂Spring Web零xml配置原理以及父子容器关系

前言在使用Spring和SpringMVC的老版本进行开发时,我们需要配置很多的xml文件,非常的繁琐,总是让用户自行选择配置也是非常不好的。基于约定大于配置的规定,Spring提供了很多注解帮助我们简化了大量的xml配置;但是在使用SpringMVC时,我们还会使用到WEB-INF/web.xml,但实际上我们是完全可以使用Java类来取代xml配置的,这也是后来SpringBoott的实现原理...

weixin_39751453的博客 211

【SpringMVC(一)】父子容器 web应用 容器启动 原理

之前介绍了spring ioc容器的基本原理,这里介绍下spring ioc容器在web场景下的原理。主要从以下几个方面: 1.web应用容器概述 2.web应用父容器启动过程; 3.web应用父容器与servletContext的关系; 4.dispatcherServlet中子容器启动过程; 5.dispatcherServlet的子容器与父容器的关系; 6.两个容器的配置文件; ...

u010900754的专栏 1178

Spring与SpringMVC父子容器的关系与初始化

Spring和SpringMVC的容器具有父子关系,Spring容器为父容器,SpringMVC为子容器,子容器可以引用父容器中的Bean,而父容器不可以引用子容器中的Bean。了解了Spring与SpringMVC父子容器的关系,接下来让我们看看Spring与SpringMVC容器的初始化过程。以下讲解使用的web.xml文件如下: &lt;context-param&gt; ...

菜到怀疑人生的博客 1万+

Spring父子容器关系---解决ssm框架启动tomcat异常无法创建bean

今天在整合ssm和redis的时候,将redis的连接池bean声明文件放在了springmvc的配置文件之下,却发生了tomcat启动创建bean异常,异常如下: 声明的bean如下: 实验了几次后,发现只要将这个bean的声明放在别的spring配置文件中,就可以生效,非常的不理解,于是心想这个mvc和别的配置文件所对应的容器是不是作用域和范围不一样,上网查询资料终于得到了解决。 原来在spring的容器中是区分父容器和子容器的。他们之间的关系是:子容器可以读取父容器中的内容,但是父容

qq_39949910的博客 688

WEB服务器,容器及tomcat之间的关系

学习web项目的时候,一直对各种容器和服务器的关系搞不清,觉得很绕,就在网上找了一些博客看了一下后稍微对这些有了一些了解.首先一个博客讲的已经挺详细了https://blog.youkuaiyun.com/tjiyu/article/details/531481741、什么是WEB服务器Web服务器是指能够为发出请求的浏览器提供文档的程序。服务器是一种被动程序,只有浏览器发出请求的时候才会响应。应用层使用的是...

bai大伯伯的博客 8017

Spring学习笔记--父子容器详解

此文转至 http://www.tianshouzhi.com/api/tutorials/spring 容器加载过程 Root WebApplicationContext创建过程源码分析 Servlet WebApplicationContext创建过程源码分析 容器加载过程 Servlet WebApplicationContext:这是对J2EE三层架构中的...

makeliwei1的博客 2063
上一篇: 【SpringMVC】源码阅读
下一篇: 【java多线程】对象头、synchronized锁的升级、monitor
hancoder
博客等级 码龄9年 4135粉丝 167原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值