使用纯 JavaConfig 配置 SSM

PPG007 ... 2021-12-26 About 3 min

# 使用纯 JavaConfig 配置 SSM

# 配置 DispatcherServlet 及 Filter

通过继承 AbstractAnnotationConfigDispatcherServletInitializer 类并重写其中方法对应实现 web.xml 的功能:

public class SpringInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    // 此方法返回RootConfig
    // 一些公共的bean,如Services,Repository均在getRootConfigClasses返回的类中配置
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return null;
    }

    // 此方法返回SpringConfig
    // 每个DispatcherServlet的bean如Controller,ViewResolver,HandlerMapping等,均在getServletConfigClasses返回的类中配置
    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[]{Config.class};
    }

    // 配置DispatcherServlet的mappingurl
    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }

    // 配置过滤器,但是尚不知晓如何指定url
    @Override
    protected Filter[] getServletFilters() {
        CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
        characterEncodingFilter.setEncoding("UTF-8");
        characterEncodingFilter.setForceEncoding(true);
        return new Filter[]{characterEncodingFilter};
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

# 配置SpringConfig

通过实现 WebMvcConfigurer 接口,对应实现 mvc 标签的配置,重写的方法均为 mvc 配置:

@Configuration
@EnableWebMvc
@ComponentScan("mapper")
@ComponentScan("service")
@ComponentScan("controller")
public class Config implements WebMvcConfigurer {

    // 配置c3p0数据源
    @Bean
    public ComboPooledDataSource getComboPooledDataSource() throws PropertyVetoException {
        ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
        comboPooledDataSource.setJdbcUrl("jdbc:mysql://localhost:3306/?serverTimezone=UTC");
        comboPooledDataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
        comboPooledDataSource.setUser("root");
        comboPooledDataSource.setPassword("123456");
        return comboPooledDataSource;
    }

    // 配置SqlSessionFactory
    @Bean("sqlSessionFactory")
    public SqlSessionFactoryBean getSqlSessionFactoryBean() throws PropertyVetoException {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        // 设置数据源
        sqlSessionFactoryBean.setDataSource(getComboPooledDataSource());
        // 设置mybatis选项,在xml配置中,可以用configuration location指定mybatis-config.xml,或在Configuration中注入bean
        sqlSessionFactoryBean.setConfiguration(configuration());
        return sqlSessionFactoryBean;
    }

    // 配置Mybatis-config中的setting属性
    @Bean
    public org.apache.ibatis.session.Configuration configuration(){
        org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
        // 设置标准日志输出
        configuration.setLogImpl(StdOutImpl.class);
        return configuration;
    }

    // 开启mapper层扫描,使用mapperScannerConfigurer后,不需要在mapper接口上添加@Repository注解,但是由于不是xml配置,不是实时的,所以IDEA在自动装配时会警告,忽略即可
    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer(){
        MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
        mapperScannerConfigurer.setBasePackage("mapper");
        mapperScannerConfigurer.setSqlSessionFactoryBeanName("sqlSessionFactory");
        return mapperScannerConfigurer;
    }

    // 配置事务管理器
    @Bean
    public DataSourceTransactionManager dataSourceTransactionManager() throws PropertyVetoException {
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
        dataSourceTransactionManager.setDataSource(getComboPooledDataSource());
        return  dataSourceTransactionManager;
    }

    // 配置视图解析器
    @Bean
    public InternalResourceViewResolver internalResourceViewResolver(){
        InternalResourceViewResolver internalResourceViewResolver = new InternalResourceViewResolver();
        internalResourceViewResolver.setPrefix("/WEB-INF/jsp/");
        internalResourceViewResolver.setSuffix(".jsp");
        return internalResourceViewResolver;
    }

    // 配置mvc默认servlet,防止静态资源404
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }

    // 配置消息转换器
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(stringHttpMessageConverter());
    }

    // 配置编码
    @Bean
    public StringHttpMessageConverter stringHttpMessageConverter(){

        return new StringHttpMessageConverter(UTF_8);
    }

    // 配置jackson消息转换器,虽然不配置仅靠stringHttpMessageConverter也没有乱码,但还是配置一下
    @Bean
    public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter(){
        MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
        Jackson2ObjectMapperFactoryBean jackson2ObjectMapperFactoryBean = jackson2ObjectMapperFactoryBean();

        mappingJackson2HttpMessageConverter.setObjectMapper(jackson2ObjectMapperFactoryBean.getObject());
        return  mappingJackson2HttpMessageConverter;
    }

    // 配置jackson的ObjectMapperFactory,其中**setFeaturesToEnable**方法不可省略,否则在MappingJackson2HttpMessageConverter的setObjectMapper方法中会出现空引用
    @Bean
    public Jackson2ObjectMapperFactoryBean jackson2ObjectMapperFactoryBean(){
        Jackson2ObjectMapperFactoryBean jackson2ObjectMapperFactoryBean = new Jackson2ObjectMapperFactoryBean();
        jackson2ObjectMapperFactoryBean.setFailOnEmptyBeans(false);
//        jackson2ObjectMapperFactoryBean.setIndentOutput(true);
//        jackson2ObjectMapperFactoryBean.setDateFormat(new SimpleDateFormat("MM/dd/yyyy"));
//        jackson2ObjectMapperFactoryBean.setFeaturesToDisable(
//                com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        jackson2ObjectMapperFactoryBean.setFeaturesToEnable(
                com.fasterxml.jackson.core.JsonParser.Feature.ALLOW_COMMENTS,
                com.fasterxml.jackson.core.JsonParser.Feature.ALLOW_SINGLE_QUOTES,
                com.fasterxml.jackson.databind.DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
        jackson2ObjectMapperFactoryBean.afterPropertiesSet();
        return jackson2ObjectMapperFactoryBean;
    }

    // 配置文件上传下载,id依然必须为multipartResolver
    @Bean("multipartResolver")
    public CommonsMultipartResolver multipartResolver(){
        CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver();
        commonsMultipartResolver.setDefaultEncoding("UTF-8");
        commonsMultipartResolver.setMaxUploadSize(10485760);

        return commonsMultipartResolver;
    }

    // 在Spring中注册拦截器
    @Bean
    public Interceptor interceptor(){
        return new Interceptor();
    }

    // 添加拦截器到SpringMVC中
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // addPathPatterns添加要拦截的请求,excludePathPatterns添加放行的请求,参数都是String数组
        registry.addInterceptor(interceptor()).addPathPatterns("/**").excludePathPatterns("/css/**");
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134

# 自定义拦截器

简单的登录验证拦截器:

public class Interceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (request.getSession().getAttribute("username")!=null||request.getRequestURI().contains("login")){
            return true;
        }
        request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request,response);
        return false;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# SSM 配置总结

步骤:

  • 配置 DispatcherServlet 和 filter。
  • 配置数据源、SqlSessionFactory、Mapper 层自动扫描及 Mybatis 的属性。
  • 配置事务管理器。
  • 配置视图解析器。
  • 配置默认 servlet 过滤静态资源。
  • 配置文件上传和下载。
  • 配置消息转换器,防止 JSON 乱码。
  • 配置拦截器。

注意事项

  • 要实现 SpringMVC,必须在 SpringConfig 类前添加 @EnableWebMvc 注解。
  • 要使用 AOP 需要在 SpringConfig 类前添加 @EnableAspectJAutoProxy 注解。
  • 注意数据源的各项参数名。
  • 在使用 AOP 时,可能出现直接在 @Before 中指定切点有效,而使用 @PointCut 指定切点并在 @Before 中引用无效、找不到切点问题,此时需要更换更高版本的 AOP 织入包。

关于 @Repository@Mapper 注解:

  • 使用 @mapper 后,不需要在 spring 配置中设置扫描地址,通过 mapper.xml 里面的 namespace 属性对应相关的 mapper 类,spring 将动态的生成 Bean 后注入到 ServiceImpl 中。
  • @repository 则需要在 Spring 中配置扫描包地址,然后生成 dao 层的 bean,之后被注入到 ServiceImpl 中。
Last update: December 26, 2021 13:38
Contributors: PPG007