当前位置 : 主页 > 编程语言 > 其它开发 >

SSM框架整合过程解析

来源:互联网 收集:自由互联 发布时间:2022-05-30
添加Sring+SpringMVC相关依赖 因为使用的maven,web容器使用的tomcat,所以先在pom.xml中加上: packagingwar/packaging 这样项目就会自动将依赖放进WEB-INF下面的lib中,否则会出现一系列NoClassFound等
添加Sring+SpringMVC相关依赖

因为使用的maven,web容器使用的tomcat,所以先在pom.xml中加上:

<packaging>war</packaging>

这样项目就会自动将依赖放进WEB-INF下面的lib中,否则会出现一系列NoClassFound等错误。
添加spring-webmvc依赖:

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-webmvc</artifactId>
  <version>${spring.version}</version>
</dependency>
<!-- 方便查看源码 -->
<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>javax.servlet-api</artifactId>
  <version>4.0.1</version>
</dependency>

只添加一个spring-webmvc就够了
image.png
可以看到已经将相关依赖全部加载进去了。
这里有一个问题,@Contoller和@GetMapping等注解是在spring-web中,然而只有这两个注解,不去配置DispatcherServlet是不会处理请求的,也就是说如果只导入spring-web依赖,而没有spring-webmvc,即使有@Controller,也只是个普通的注解。

配置spring

在web.xml中配置

<listener>
  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 采用注解方式配置 -->
<context-param>
  <param-name>contextClass</param-name>
  <param-value>
    <!-- 使用注解实现类 -->
    org.springframework.web.context.support.AnnotationConfigWebApplicationContext
  </param-value>
</context-param>
<context-param>
  <param-name>contextConfigLocation</param-name>
  <!-- 配置类 -->
  <param-value>com.sk.day31.config.ApplicationConfig</param-value>
</context-param>

ContextLoaderListener实现了ServletContextListener接口,ServletContextListener在文档中的解释是

该接口的实现可以接收关于它们所处的Web应用程序的Servlet上下文变化的通知。为了接收通知事件,必须在Web应用程序的部署描述符中配置该实现类。

ServletContextListener 是 Java EE 标准接口之一,类似 Jetty,Tomcat,JBoss 等 java 容器启动时便会触发该接口的 contextInitialized。

spring加载流程
  1. 在Web容器启动后,触发ContextLoaderListener,contextInitialized方法被调用,接着调用initWebApplicationContext方法,并将ServletContext当作参数传入
@Override
public void contextInitialized(ServletContextEvent event) {
    initWebApplicationContext(event.getServletContext());
}
  1. 进入initWebApplicationContext方法后,调用createWebApplicationContext
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
        ...
		try {
			// Store context in local instance variable, to guarantee that
			// it is available on ServletContext shutdown.
			if (this.context == null) {
				this.context = createWebApplicationContext(servletContext);
			}
        ...
	}
  1. 进入createWebApplicationContext方法, 源码如下:
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
		Class<?> contextClass = determineContextClass(sc);
		if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
			throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
					"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
		}
		return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
	}

determineContextClass用来返回一个实现了WebApplicationContext接口的类:

protected Class<?> determineContextClass(ServletContext servletContext) {
		String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
		if (contextClassName != null) {
			try {
				return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
			}
			catch (ClassNotFoundException ex) {
				throw new ApplicationContextException(
						"Failed to load custom context class [" + contextClassName + "]", ex);
			}
		}
		else {
			contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
			try {
				return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
			}
			catch (ClassNotFoundException ex) {
				throw new ApplicationContextException(
						"Failed to load default context class [" + contextClassName + "]", ex);
			}
		}
	}

determineContextClass根据web.xml中定义的contextClass,来决定返回的实现类,如果没有配置,则返回默认的XmlWebApplicationContext,最后通过BeanUtils.instantiateClass(contextClass)创建对象。
那么就出现一个问题,XmlWebApplicationContext是什么?
首先要先讲一下BeanFactory,根据官方文档,BeanFactory接口提供了一个高级配置机制,能够管理任何类型的对象。简单来说就是Spring的容器,用来管理各种Bean。

import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
public class MainApp {
   public static void main(String[] args) {
      XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("Beans.xml"));
      HelloWorld obj = (HelloWorld) factory.getBean("helloWorld");
      obj.getMessage();
   }
}

上述例子就是用了BeanFactory的其中一个实现类XmlBeanFactory来创建Bean。
而ApplicationContext是BeanFactory的扩展,提供了更多的功能,例如:

  • 用于访问应用程序组件的Bean工厂方法。从ListableBeanFactory继承而来。
  • 以通用方式加载文件资源的能力。从ResourceLoader接口继承而来。
  • 向注册的听众发布事件的能力。从ApplicationEventPublisher接口继承而来。
  • 解决消息的能力,支持国际化。继承自MessageSource接口。

ApplicationContext常用的实现类有ClassPathXmlApplicationContext,以及上面提到的WebApplicationContext,这些实现类相当于特化,比如ClassPathXmlApplicationContext就是从类路径中获取配置文件:

public class TestSpring {
    @Test
    public void test(){
        ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");         
        Hello hello=(Hello)context.getBean("helloWorld");
        hello.say();
    }
}

WebApplicationContext是专门为web应用准备的,它允许从相对于Web根目录的路径中装载资源配置文件完成初始化工作。而WebApplicationContext最常用的实现类也就是上面的XmlWebApplicationContext了,此实现类会默认从WEB-INF的目录下读取applicationContext.xml来创建容器,当然这个路径也可以自定义。

  1. 再回到前面,determineContextClass返回了XmlWebApplicationContext类,并实例化,将其强转为ConfigurableWebApplicationContext,并执行configureAndRefreshWebApplicationContext
if (this.context instanceof ConfigurableWebApplicationContext) {
				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
				if (!cwac.isActive()) {
                    ...
					configureAndRefreshWebApplicationContext(cwac, servletContext);
				}
			}
  1. 进入configureAndRefreshWebApplicationContext方法,通过ServletContext获取web.xml中的contextConfigLocation值,设置到XmlWebApplicationContext中。
String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
		if (configLocationParam != null) {
			wac.setConfigLocation(configLocationParam);
		}
    ...
    wac.refresh();
  1. 执行refresh方法
public void refresh() throws BeansException, IllegalStateException {
    Object var1 = this.startupShutdownMonitor;
    synchronized(this.startupShutdownMonitor) {
       //容器预先准备,记录容器启动时间和标记
      prepareRefresh();
      //创建bean工厂,里面实现了BeanDefinition的装载
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
      //配置bean工厂的上下文信息,如类装载器等
      prepareBeanFactory(beanFactory);
      try {
        //在BeanDefinition被装载后,提供一个修改BeanFactory的入口
        postProcessBeanFactory(beanFactory);
        //在bean初始化之前,提供对BeanDefinition修改入口,PropertyPlaceholderConfigurer在这里被调用
        invokeBeanFactoryPostProcessors(beanFactory);
        //注册各种BeanPostProcessors,用于在bean被初始化时进行拦截,进行额外初始化操作
        registerBeanPostProcessors(beanFactory);
        //初始化MessageSource
        initMessageSource();
        //初始化上下文事件广播
        initApplicationEventMulticaster();
        //模板方法
        onRefresh();
        //注册监听器
        registerListeners();
        //初始化所有未初始化的非懒加载的单例Bean
        finishBeanFactoryInitialization(beanFactory);
        //发布事件通知
        finishRefresh();
        } catch (BeansException var5) {
            if(this.logger.isWarnEnabled()) {
                this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var5);
            }
            this.destroyBeans();
            this.cancelRefresh(var5);
            throw var5;
        }
    }
}

完成bean的加载。

JavaConfig方式配置ApplicationConfig
@Configuration
@ComponentScan(basePackages = "com.sk.day31", excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Controller.class)})
public class ApplicationConfig {
    
}

效果类似

<context:component-scan base-package="com.sk.day31">
  <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>-->
</context:component-scan>
配置spring-webmvc

在web.xml中配置:

<servlet>
  <servlet-name>dispatcherServlet</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <init-param>
    <param-name>contextClass</param-name>
    <param-value>
      <!-- 使用注解实现类 -->
      org.springframework.web.context.support.AnnotationConfigWebApplicationContext
    </param-value>
  </init-param>
  <init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>com.sk.day31.config.SpringMvcConfig</param-value>
  </init-param>
  <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
  <servlet-name>dispatcherServlet</servlet-name>
  <url-pattern>/</url-pattern>
</servlet-mapping>
@Configuration
@ComponentScan(basePackages = "com.sk.day31.controller")
@EnableWebMvc
public class SpringMvcConfig implements WebMvcConfigurer {
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/resources/**")
                .addResourceLocations("classpath:/static/")
                .setCacheControl(CacheControl.maxAge(Duration.ofDays(365)));
    }

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
}

@EnableWebMvc效果等于mvc:annotation-driven/。
configureDefaultServletHandling方法作用等于mvc:default-servlet-handler/

配置mybatis 添加依赖
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-jdbc</artifactId>
  <version>${spring.version}</version>
</dependency>
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>8.0.28</version>
</dependency>
<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>druid</artifactId>
  <version>1.1.22</version>
</dependency>
<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis-spring</artifactId>
  <version>2.0.7</version>
</dependency>
<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis</artifactId>
  <version>3.5.9</version>
</dependency>

spring-jdbc必须添加,不然会报错java.lang.NoClassDefFoundError: org/springframework/dao/support/DaoSupport。

配置类
@Configuration
@PropertySource("classpath:druid.properties")
@MapperScan(basePackages = "com.sk.day31.mapper")
public class MybatisConfig {
    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;

    @Bean
    public DruidDataSource dataSource(){
        DruidDataSource dataSource=new DruidDataSource();
        dataSource.setDriverClassName(driver);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        return dataSource;
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setTypeAliasesPackage("com.sk.day31.entity");
        factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
        factoryBean.setDataSource(dataSource());
        return factoryBean.getObject();
    }
}

到此就算是整合完毕了

上一篇:LevelDB 学习笔记3:合并策略
下一篇:没有了
网友评论