xml用着烦,ssm项目改零配置,顺便内嵌tomcat

如何一步步消灭spring,mybatis以及springmvc的xml,这篇文章会慢慢告诉你,同时也会给出保持xml的方法,只需要消灭你想消灭的部分就行了,顺便学springboot内嵌tomcat。

xml用着是这真的烦,顺便最近看了些springboot源码,然而有些项目还是ssm的,不好换springboot。因此就就尝试把旧的ssm项目改造,这篇文章注重于应用,会尽量告诉你这东西是干嘛的。而不会讲源码,如果有兴趣,等下次有机会再拉出来讲吧,让我们开始吧。

 

目录

maven依赖

消灭Spring.xml

写一个扫描包的Component

读取这个特殊的Component

消灭mybatis.xml

扫描mapper接口

搞定mapper.xml文件

配置数据库连接池

所有mybatis配置

消灭*Mapper.xml

消灭SpringMvc.xml

消灭web.xml

内嵌Tomcat

读取Properties文件


maven依赖



    4.0.0

    com.ssm
    ssm-test
    1.0-SNAPSHOT
    

    
        
        
            javax.servlet
            javax.servlet-api
            3.1.0
            provided
        
        
        
            org.springframework
            spring-context-support
            5.2.8.RELEASE
        
        
            org.springframework
            spring-aop
            5.2.8.RELEASE
        
        
            org.slf4j
            slf4j-nop
            1.7.2
        
        
        
            org.springframework
            spring-jdbc
            5.2.7.RELEASE
        
        
            com.zaxxer
            HikariCP
            3.4.5
        
        
            org.springframework
            spring-aspects
            5.2.2.RELEASE
        
        
            mysql
            mysql-connector-java
            8.0.20
        
        
        
            org.mybatis
            mybatis-spring
            2.0.4
        
        
            org.mybatis
            mybatis
            3.5.4
        
        
        
            org.springframework
            spring-webmvc
            5.2.8.RELEASE
        
        
        
            org.apache.tomcat.embed
            tomcat-embed-core
            9.0.37
        
        
            org.apache.tomcat.embed
            tomcat-embed-el
            9.0.33
        
        
        
            org.apache.tomcat.embed
            tomcat-embed-jasper
            9.0.20
        

    

    
        UTF-8
        1.8
        1.8
    

    

        
            
                src/main/java
                
                    **/*.properties
                    **/*.xml
                
                
                false
            
            
                src/main/resources
                
            
        


        
            
                org.apache.maven.plugins
                maven-compiler-plugin
                3.8.0
                
                    1.8
                    1.8
                    utf8
                
            
        
    

 

消灭Spring.xml

写一个扫描包的Component

  首先,注册bean,这个bean的特殊之处在于,它会扫描其他组件,spring提供了@ComponentScan注解进行扫描。这里由于后面需要配置springmvc,因此需要排除所有带有@Controller的注解

@Configuration
// 排除Controller注解的类
@ComponentScan(basePackages = "com.ssm.*",
        excludeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION,
                classes = Controller.class)})
public class SpringConfig{}

 

读取这个特殊的Component

  那么问题来了,不用xml要怎么加载这个特殊的bean呢。答案是spring不止有xml的ClassPathXmlApplicationContext还有AnnotationConfigApplicationContext的上下文容器。

xml用着烦,ssm项目改零配置,顺便内嵌tomcat_第1张图片

 public class MyApplicationContext {

	public static void main(String[] args) {
        // 此构造方法会自动调用refresh方法启动容器
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class);
        }
 }

  你可能发现了,如果此方法写到main方法里,那不就需要在此方法上才能运行容器么?那tomcat是怎么运行的?想知道原因,请看后面。此类只是做个过渡,实际上后面会用其他方法加载,而不是在main函数运行,不然就不能部署在tomcat上了

 

消灭mybatis.xml

由于mybatis的配置牵扯的东西较多,下面分个小节阐述

扫描mapper接口

首先消灭mybatis的包的mapper接口的扫描,使用@MapperScan,这个不用我多说吧,springboot也经常用。在刚才的spring的类上加上,顺便开启spring的事务。

@Configuration
@MapperScan(basePackages = "com.ssm.dao")
@ComponentScan(basePackages = "com.ssm.*",
        excludeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION,
                classes = Controller.class)})
@EnableTransactionManagement
public class SpringConfig {}

搞定mapper.xml文件

如果是mybatis.xml要读取*mapper.xml文件,mybatis提供了以下四种方法



  
  
  



  
  
  



  
  
  



  

首先,mapper.xml留着好还是不留好仁者见仁,就算是springboot,也是很多有些mapper.xml的。不过后面也会提出使用注解代替mapper.xml的方法。

另外,关于上面的xml代码。上面三种方式都还行,一个个写,但是官方并没有提供基于包的扫描方法。只提供了一个参数为Resource数组的扫描方法。因此,本人在这里使用了spring提供的方法了。补充下知识,ResourceLoader是一个顶级接口,所有的ApplicationContext都实现了这个接口,用于读取多种不同类型的Resoutce,xml自然也能读取。之后将此数组传给SqlSessionFactoryBean就行了。这个类后面会解释,到时给出完整代码

        /* 读取所有mapper文件 */
        ResourceLoader resourceLoader = new DefaultResourceLoader();
        ResourcePatternResolver resolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
        String scanPath = "classpath:xml\\*.xml";
        Resource[] resources = resolver.getResources(scanPath);

配置数据库连接池

这里用Hikari为例子,这里先写死,后面再使用properties

    @Bean
    DataSource dataSource(){
        HikariDataSource hikariDataSource = new HikariDataSource();
        hikariDataSource.setUsername("root");
        hikariDataSource.setPassword("MySQL/80/root:pwd");
        hikariDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        hikariDataSource.setJdbcUrl("jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC&&characterEncoding=UTF-8");
        return hikariDataSource;
    }

所有mybatis配置

mybatis需要实际需要配置的地方挺多的,因此上面导入了spring-mybatis包,此包提供一个你估计听说过的类,SqlSessionFactoryBean,几乎所有的mybatis配置都可以交给这个类配置。

这里给不熟spring的人提示一下,FactoryBean接口实际上注入的是Factory,而不是FactoryBean对象。也就是最后返回的是SqlSessionFactory,使用SqlSessionFactoryBean是因为配置太多,mybatis会默认帮我们配置一些属性。几乎所有在mybatis中配置的属性都可以在其中配置。配置项挺多的,这里配置几个以举例

    @Bean
    SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) throws IOException {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        /* 设置数据库连接池*/
        sqlSessionFactoryBean.setDataSource(dataSource);
//        // 读取mybatis.xml的配置信息, 此类不能跟setMapperLocations一起使用
//        ClassPathResource resource = new ClassPathResource("mybatis.xml");
//        sqlSessionFactoryBean.setConfigLocation(resource);
        /* 配置默认数据库提供者*/
        VendorDatabaseIdProvider provider = new VendorDatabaseIdProvider();
        Properties properties = new Properties();
        properties.setProperty("MySQL", "mysql");
        properties.setProperty("Oracle", "oracle");
        provider.setProperties(properties);
        sqlSessionFactoryBean.setDatabaseIdProvider(provider);

        /* 其他设置*/
        org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
        configuration.setCacheEnabled(true);
        configuration.setDefaultStatementTimeout(5);
        // 驼峰命名法
        configuration.setMapUnderscoreToCamelCase(true);
        sqlSessionFactoryBean.setConfiguration(configuration);

        /* 读取所有mapper文件 */
        ResourceLoader resourceLoader = new DefaultResourceLoader();
        ResourcePatternResolver resolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
        String scanPath = "classpath:xml\\*.xml";
        Resource[] resources = resolver.getResources(scanPath);
        sqlSessionFactoryBean.setMapperLocations(resources);
        return sqlSessionFactoryBean;
    }

到此为止,我们消灭了mybatis.xml

消灭*Mapper.xml

mybatis其实提供了注解来注入mapper接口,可以不使用mapper.xml。不过遇到复杂sql,还是建议用xml,注解写起来更麻烦

@Select("select * from user where id = #{id}")
User findById(@Param("id") long id);

@Select("select * from user where name = #{name}")
User findByName(@Param("name") String name);

@Select("select * from user where email = #{email}")
User findByEmail(@Param("email") String email);

 

消灭SpringMvc.xml

这个配置要消灭很简单,跟spring差不多,就多了个@EnableMvc的注解以及多一个WebMvcConfigurer接口,这个接口可以做大部分的SpringMvc配置。比如注册拦截器等。

@Configuration
@ComponentScan(basePackages = "com.ssm.controller",
        includeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION,
                classes = Controller.class)
})
@EnableWebMvc
public class SpringMVCConfig implements WebMvcConfigurer{

    // 注册视图解析器
    @Bean
    InternalResourceViewResolver InternalResourceViewResolver(){
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setPrefix("/WEB-INF/classes/views/");
        viewResolver.setSuffix(".jsp");
        return viewResolver;
    }
}

消灭web.xml

要消灭这个,需要用到servlet3.0的标准。具体我就不介绍了,需要扯一些源码。

package com.ssm;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.request.RequestContextListener;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.DispatcherServlet;

import javax.servlet.DispatcherType;
import javax.servlet.FilterRegistration;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import javax.xml.ws.Dispatch;
import java.util.EnumSet;

// 此接口可以代替web.xnl
public class WebConfig implements WebApplicationInitializer {

    public void onStartup(javax.servlet.ServletContext servletContext) throws ServletException {
        System.out.println("准备加载web应用");
        // 创建一个web容器
        AnnotationConfigWebApplicationContext webContext = new AnnotationConfigWebApplicationContext();
        webContext.register(SpringMVCConfig.class);
        webContext.register(SpringConfig.class);
        webContext.setServletContext(servletContext);
        // 设置dispatchServlet
        DispatcherServlet dispatcherServlet = new DispatcherServlet(webContext);
        ServletRegistration.Dynamic addServlet = servletContext.addServlet("dispatchServlet", dispatcherServlet);
        addServlet.addMapping("/");
        //设置servlet的启动优先级
        addServlet.setLoadOnStartup(1);
        // 设置请求的监听器
        servletContext.addListener(new RequestContextListener());
        // 设置字符编码过滤器
        FilterRegistration.Dynamic filter = servletContext.addFilter("charsetFilter", new CharacterEncodingFilter());
        EnumSet dispatcherTypeEnumSet = EnumSet.allOf(DispatcherType.class);
        dispatcherTypeEnumSet.add(DispatcherType.REQUEST);
        dispatcherTypeEnumSet.add(DispatcherType.FORWARD);
        // 设置过滤请求的范围
        filter.addMappingForUrlPatterns(dispatcherTypeEnumSet, true,"/");

        webContext.refresh();
    }
}

上面的选项中,基本上跟web.xml一一对应,配置dispatchServlet,配置监听器,配置字符集过滤器等等。另外可以看到上面的代码中,我们把spring的bean配置跟springmvc的bean配置都读取进去了。且只有一个容器,当然你也可以创建两个容器,然后把spring容器设为springmvc容器得到父容器。

不过,上面的代码还是比较原始,SpringMvc提供了一个更好的类给我们使用,那就是AbstractAnnotationConfigDispatcherServletInitializer

// 此类有几乎能配置用户用的所有东西,用 各种get配置
public class AppWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
    //加载model
    @Override
    protected Class[] getRootConfigClasses() {
        return new Class[]{SpringConfig.class};
    }
    //官方建议在此方法中加载View,Controller
    @Override
    protected Class[] getServletConfigClasses() {
        return new Class[]{SpringMVCConfig.class};
    }
    //定义请求映射
    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }

    // 重写父类的此方法进行注册过滤器
    @Override
    protected Filter[] getServletFilters() {
        return new Filter[] {
                new HiddenHttpMethodFilter(), new CharacterEncodingFilter() };
    }

//    @Override
//    public void onStartup(ServletContext servletContext) throws ServletException {
//        FilterRegistration.Dynamic filter = servletContext.addFilter("charsetFilter", new CharacterEncodingFilter());
//        EnumSet dispatcherTypeEnumSet = EnumSet.allOf(DispatcherType.class);
//        dispatcherTypeEnumSet.add(DispatcherType.REQUEST);
//        dispatcherTypeEnumSet.add(DispatcherType.FORWARD);
//        // 设置过滤请求的范围
//        filter.addMappingForUrlPatterns(dispatcherTypeEnumSet, true,"/");
//        super.onStartup(servletContext);
//    }
}

想放在spring容器加载的类就放在RootConfig中。到此为止,消灭了web.xml。可以直接部署在tomcat上运行。如果没成功,可能是servlet版本不对,我也因为版本不对玩了两个小时。。。后来解决方法是新建一个项目,把类都拷过去,没话说。

 

内嵌Tomcat

内嵌tomcat其实没那么牛逼,记得前面导入的几个jar包么,里面就有tomcat对象。。。不过要吐槽的是,我也因为tomcat的版本玩了一下午,求求你别再用版本问题搞我了

public class Start {

    public static void run() {
        Tomcat tomcat = new Tomcat();
        tomcat.addWebapp("/",
                "D:\\Users\\Downloads\\forum-master\\src\\main\\webapp");
        Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
        connector.setPort(8080);
        tomcat.setConnector(connector);

        try {
            tomcat.start();
        } catch (LifecycleException e) {
            e.printStackTrace();
        }
        tomcat.getServer().await();
    }
}

 

读取Properties文件

都知道springboot有注解可以直接读取properties文件,然而spring没有提供,只能自己注入咯。这里需要用到BeanFactoryPostProcessor。这东西是spring最牛逼的接口之一,时间原因不多讲了。后面取值用@Value注解就可以了

@Component
public class PropertiesProcesor implements BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        PropertySourcesPlaceholderConfigurer cfg = new PropertySourcesPlaceholderConfigurer();
        // 这里是读取类路径中的配置文件
        cfg.setLocation(new ClassPathResource("my.properties"));
        cfg.postProcessBeanFactory(configurableListableBeanFactory);
    }
}

或者,如果你不用注入,也可以在容器运行前这样写

 public class MyApplicationContext {
	public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();

        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        PropertySourcesPlaceholderConfigurer cfg = new PropertySourcesPlaceholderConfigurer();
        cfg.setLocation(new ClassPathResource("df.properties"));
        cfg.postProcessBeanFactory(factory);
        context.addBeanFactoryPostProcessor(cfg);

        context.register(ApplicationConfig.class);
        context.refresh();
    }

 

ssm零配置到此为止。有机会再分享各个部分的原理。

你可能感兴趣的:(springboot)