欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

springmvc +mybatis 动态国际化(使用数据库保存国际化配置)

程序员文章站 2022-05-16 10:27:44
...

springMVC的动态国际化,是使用数据库保存国际化对应的value值,系统启动后加载所有国际化配置到缓存(也可以直接读取数据库,效率低,不推荐),修改数据库中对应的国际化value值后刷新。自定义国际化的解析类,从缓存中取得对应的value值

1.定义国际化对应的实体类,以及对应的mybatis mapper类

//entry
public class SysMessageResource implement Serializable {
	
	private static final long serialVersionUID = 1L;
	private String code;		// 键值
	private String lang;		// 国家区域
	private String label;		// 显示的标签
	
	//....
	
}

// mapper
public class SysMessageResourceMapper {

    List<SysMessageResource> findList(SysMessageResource resource);
    // insert
    // update
    // delete
}

2.定义SpringContextHolder:

可以根据class直接获取对应的spring容器管理的对象,(因为我是在Utils的static块中加载的国际化资源,不能使用@autowried注入,当然也可以不用该方法,可以将Utils标为spring对象,继承InitializingBean,在afterPropertiesSet方法中加载国际化资源)

@Service
@Lazy(false)
public class SpringContextHolder implements ApplicationContextAware, DisposableBean {

	private static ApplicationContext applicationContext = null;

	private static Logger logger = LoggerFactory.getLogger(SpringContextHolder.class);

	/**
	 * 取得存储在静态变量中的ApplicationContext.
	 */
	public static ApplicationContext getApplicationContext() {
		assertContextInjected();
		return applicationContext;
	}

	/**
	 * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
	 */
	@SuppressWarnings("unchecked")
	public static <T> T getBean(String name) {
		assertContextInjected();
		return (T) applicationContext.getBean(name);
	}

	/**
	 * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
	 */
	public static <T> T getBean(Class<T> requiredType) {
		assertContextInjected();
		return applicationContext.getBean(requiredType);
	}

	/**
	 * 清除SpringContextHolder中的ApplicationContext为Null.
	 */
	public static void clearHolder() {
		if (logger.isDebugEnabled()){
			logger.debug("清除SpringContextHolder中的ApplicationContext:" + applicationContext);
		}
		applicationContext = null;
	}

	/**
	 * 实现ApplicationContextAware接口, 注入Context到静态变量中.
	 */
	@Override
	public void setApplicationContext(ApplicationContext applicationContext) {
		SpringContextHolder.applicationContext = applicationContext;
	}

	public static  String getStatic(){
		return SpringContextHolder.getApplicationContext().getApplicationName()+ "/static";
	}
	/**
	 * 实现DisposableBean接口, 在Context关闭时清理静态变量.
	 */
	@Override
	public void destroy() throws Exception {
		SpringContextHolder.clearHolder();
	}

	/**
	 * 检查ApplicationContext不为空.
	 */
	private static void assertContextInjected() {
		Validate.validState(applicationContext != null, "applicaitonContext属性未注入, 请在applicationContext.xml中定义SpringContextHolder.");
	}
}

 

3.定义ResourceUtils类,用来加载国际化资源到缓存,或者更新缓存,要在对应的mapper执行插入,更新,删除后执行该utils对应的方法更新缓存,或者reload

public class MessageResourceUtils {
	private static Logger logger = LoggerFactory.getLogger(MessageResourceUtils.class);
//可以使用别的方式,查询数据库
	private static SysMessageResourceMapper sysMessageResourceMapper = SpringContextHolder.getBean(SysMessageResourceMapper.class);
        public static final String MESSAGE_SOURCE_KEY = "message.source.key";
	static {
		reloadCache();
	}
	
	/**
	 * 根据local获取bundle
	 * @param locale
	 * @return
	 */
	public static Properties getResourceProperties(Locale locale){
		return getResourceMap().get(locale);
	}
	
	/**
	 * get resource map of all locale
	 * @return
	 */
	public static Map<Locale, Properties> getResourceMap() {
		Map<Locale, Properties> localeMap = (Map<Locale, Properties>) CacheUtils.get(MESSAGE_SOURCE_KEY);
		if (localeMap == null) {
			localeMap = generateLocaleProperties();
			CacheUtils.put(MESSAGE_SOURCE_KEY, localeMap);
		}
		return localeMap;
	}
	
	/**
	 * reload message resource
	 */
	public static void reloadCache() {
// 可以使用别的缓存方式,可以选择多种方式,在此不提供缓存实现
		CacheUtils.put(MESSAGE_SOURCE_KEY, generateLocaleProperties());
	}
	/**
	 * 清除Message缓存
	 * @param user
	 */
	public static void clearCache(){
		CacheUtils.remove(MESSAGE_SOURCE_KEY);
	}
	
	/**
	 * reload message 
	 * @param locale locale
	 */
	public static void reloadLocaleMessage(Locale locale) {
		if (locale == null) {
			return;
		}
		Map<Locale, Properties> localeMap = getResourceMap();
		SysMessageResource search = new SysMessageResource();
		search.setLang(locale.toString());
		List<SysMessageResource> resourcesList = sysMessageResourceMapper.findList(search);
		Properties prop = new Properties();
		for (SysMessageResource messageResource : resourcesList) {
			if (StringUtils.isBlank(messageResource.getLang()) || StringUtils.isBlank(messageResource.getCode())) {
				continue;
			}
			prop.put(messageResource.getCode(), StringUtils.defaultString(messageResource.getLabel()));
		}
		
		localeMap.put(locale, prop);
		
	}
	
	/**
	 * insert message
	 * @param resource
	 */
	public static void insertMessageResource(SysMessageResource resource) {
		if (StringUtils.isBlank(resource.getLang()) || StringUtils.isBlank(resource.getCode())) {
			return;
		}
		Locale locale = org.springframework.util.StringUtils.parseLocaleString(resource.getLang());
		
		if (locale != null) {
			Properties prop = getResourceProperties(locale);
			if (prop == null) {
				prop = new Properties();
				getResourceMap().put(locale, prop);
			}
			prop.put(resource.getCode(), resource.getLabel());
		}
	}
	
	/**
	 * update message
	 * @param resource
	 */
	public static void updateMessageResource(SysMessageResource resource) {
		insertMessageResource(resource);
	}
	
	/**
	 * delete message
	 * @param resource
	 */
	public static void deleteMessageResource(SysMessageResource resource) {
		if (StringUtils.isBlank(resource.getLang()) || StringUtils.isBlank(resource.getCode())) {
			return;
		}
		Locale locale = org.springframework.util.StringUtils.parseLocaleString(resource.getLang());
		
		if (locale != null) {
			Properties prop = getResourceProperties(locale);
			if (prop != null) {
				prop.remove(resource.getCode());
			}
		}
	}
	/**
	 * load all message
	 * @return
	 */
	private static Map<Locale, Properties> generateLocaleProperties() {
		Map<Locale, Properties> localPropertiesMap = new HashMap<>();
		List<SysMessageResource> resourcesList = Collections.emptyList();
		
		try {
			resourcesList = sysMessageResourceMapper.findList(new SysMessageResource());
		} catch (Exception e) {
			// TODO Auto-generated catch block
			logger.warn("Query messageResource from database error");
		}
		
		for (SysMessageResource messageResource : resourcesList) {
			if (StringUtils.isBlank(messageResource.getLang()) || StringUtils.isBlank(messageResource.getCode())) {
				continue;
			}
			Locale locale = org.springframework.util.StringUtils.parseLocaleString(messageResource.getLang());
			
			if (locale != null) {
				Properties prop = localPropertiesMap.get(locale);
				if (prop == null) {
					prop = new Properties();
					localPropertiesMap.put(locale, prop);
				}
				prop.put(messageResource.getCode(), StringUtils.defaultString(messageResource.getLabel()));
			}
		}
		
		return localPropertiesMap;
	}
}

4.定义message解析类,继承自spring的AbstractMessageSource :

public class ResourceBundleMessageSource extends AbstractMessageSource {


	public ResourceBundleMessageSource() {
		super();

	}

	@Override
	protected MessageFormat resolveCode(String code, Locale locale) {
		
		Properties prop = MessageResourceUtils.getResourceProperties(locale);
		
		if (prop != null) {
			String label = prop.getProperty(code);
			if (label != null) {
				return createMessageFormat(label, locale);
			}
		}
		return null;
	}
}

5. spring-mvc.xml 配置对应的messageSource,以及拦截器

<!-- 拦截器配置,拦截顺序:先执行后定义的,排在第一位的最后执行。-->
	<mvc:interceptors>
		<!-- 国际化操作拦截器 如果采用基于(请求/Session/Cookie)则必需配置 --> 
	    <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor" />  
	</mvc:interceptors>
	
	<!-- 支持Shiro对Controller的方法级AOP安全控制 begin-->
	<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor">
		<property name="proxyTargetClass" value="true" />
	</bean>
	
	<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
		<property name="exceptionMappings">
			<props>
				<prop key="org.apache.shiro.authz.UnauthorizedException">error/403</prop>
				<prop key="java.lang.Throwable">error/500</prop>
			</props>
			</property>
	</bean>
	<!-- 支持Shiro对Controller的方法级AOP安全控制 end -->

	
	
		<!-- 国际化 -->
	<bean id="baseMessageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
	    <!-- 国际化信息所在的文件名 -->                     
<!-- 	    <property name="basename" value="messages" /> -->
	</bean>
	
	<bean id="messageSource" class="com.wenbo.common.bundle.ResourceBundleMessageSource">
	    <!-- 如果在国际化资源文件中找不到对应代码的信息,就用这个代码作为名称  -->  
	    <property name="parentMessageSource" ref="baseMessageSource" />             
		<property name="useCodeAsDefaultMessage" value="true" />           
	</bean>
	
<!-- session 方式 -->
	<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver" >
		<property name="defaultLocale" value="en_US" />
	</bean>

     <!-- cookie 方式 
    <bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver" />
-->

使用方式和使用properties国际化一样,可以参考:

https://blog.csdn.net/D939030515/article/details/64906101

相关标签: 国际化