Spring实现数据库读写分离/spring事务配置解释(Annotation/Spring AOP/Reflection)

本文主要是介绍Spring实现数据库读写分离/spring事务配置解释(Annotation/Spring AOP/Reflection),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

项目开发中读写的频率差距很大,所以实现读写分离:主库(master)中非实时读取的查询交给负载均衡的从库(slave),查询cpu的消耗和写入的io延时,保证DB系统的健壮性。

最终问题,分布式事务的线索。最后附源码[springmvc+spring+mybatis+MySQL]:

注:注释和部分代码省略。
1:AbstractRoutingDataSource这个数据源路由类是spring2.0以后增加的,AbstractRoutingDataSource的定义:/* @since 2.0.1*/
public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean {……}
通过实现determineCurrentLookupKey()抽象方法可以实现读写数据源的切换。
2:AbstractRoutingDataSource继承了AbstractDataSource,而AbstractDataSource是DataSource的子类。DataSource是javax.sql的数据源接口,定义:
public interface DataSource  extends CommonDataSource,Wrapper {Connection getConnection() throws SQLException;Connection getConnection(String username, String password) throws SQLException;
}
DataSource数据源接口定义了2个方法,都是获取数据库连接的。
3:AbstractRoutingDataSource这个数据源路由类对获取连接的实现:
public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean {private Map<Object, DataSource> resolvedDataSources;private Object defaultTargetDataSource;private Map<Object, Object> targetDataSources;@Overridepublic Connection getConnection() throws SQLException {return determineTargetDataSource().getConnection();}@Overridepublic Connection getConnection(String username, String password) throws SQLException {return determineTargetDataSource().getConnection(username, password);}protected abstract Object determineCurrentLookupKey();
}
通过自己定义的determineTargetDataSource()方法获取到数据连接connection。
4:determineTargetDataSource的定义:protected DataSource determineTargetDataSource() {Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");Object lookupKey = determineCurrentLookupKey();DataSource dataSource = this.resolvedDataSources.get(lookupKey);if (dataSource == null && (this.lenientFallback || lookupKey == null)) {dataSource = this.resolvedDefaultDataSource;}if (dataSource == null) {throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");}return dataSource;}
determineCurrentLookupKey方法返回lookupKey,resolvedDataSources方法就是根据lookupKey从Map中获得数据源。从3看出resolvedDataSources集合属性和determineCurrentLookupKey()抽象方法定义。
5:我们自己定义类继承路由类AbstractRoutingDataSource去实现determineCurrentLookupKey()方法,然后自己来切换字符串告诉this.resolvedDataSources.get(lookupKey);方法这个lookupKey是谁?(也就是这个字符所对应的数据源的key(请看mybatis-context.xml配置40行注入defaultTargetDataSource和targetDataSources的值,通过‘lookupKey’字符串切换,spring注入的数据源对象.
1:通过注解在dao方法上注解,此方法开启那个数据源:

package com.common.readwriteseparate;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DataSource {String value();
}
2:自己定义类继承路由类去实现determineCurrentLookupKey()方法:

package com.common.readwriteseparate;import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;public class DynamicDataSource extends AbstractRoutingDataSource{@Overrideprotected Object determineCurrentLookupKey() {String dataSouceKey = DynamicDataSourceHolder.getDataSouce();return dataSouceKey;}}
多线程辅助类:

package com.common.readwriteseparate;public class DynamicDataSourceHolder {public static final ThreadLocal<String> holder = new ThreadLocal<String>();public static void putDataSource(String name) {holder.set(name);}public static String getDataSouce() {return holder.get();}
}

在DynamicDataSource的定义知道,返回的是DynamicDataSourceHolder.getDataSouce()值,我们需要在程序运行时调用DynamicDataSourceHolder.putDataSource()方法,对dataSouceKey数据源key赋值。

3:通过Spring AOP/Reflection获取执行获取注解的value然后DynamicDataSourceHolder.putDataSource(value)告诉要使用那个数据源:

package com.common.readwriteseparate;import java.lang.reflect.Method;import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class DataSourceAspect {//log private static final Logger LOG = LoggerFactory.getLogger(DataSourceAspect.class);public void before(JoinPoint point) {Object target = point.getTarget();String method = point.getSignature().getName();Class<?>[] classz = target.getClass().getInterfaces();Class<?>[] parameterTypes = ((MethodSignature) point.getSignature()).getMethod().getParameterTypes();try {Method methodRefRes = classz[0].getMethod(method, parameterTypes);if (methodRefRes != null && methodRefRes.isAnnotationPresent(DataSource.class)) {DataSource data = methodRefRes.getAnnotation(DataSource.class);DynamicDataSourceHolder.putDataSource(data.value());LOG.info("\n************************************************\n" + "\t~~~DB:: " + data.value() + "\n************************************************");}} catch (Exception e) {LOG.error("数据源失败切面获取异常:" + e.getMessage(), e);}}
}
5:先看看配置:

①spring的配置(spring-context.xml):

<?xml version="1.0" encoding="UTF-8"?>  
<beans xmlns="http://www.springframework.org/schema/beans"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"  xmlns:context="http://www.springframework.org/schema/context"  xmlns:aop="http://www.springframework.org/schema/aop"xmlns:mvc="http://www.springframework.org/schema/mvc"  xsi:schemaLocation="http://www.springframework.org/schema/beans    http://www.springframework.org/schema/beans/spring-beans-3.2.xsd    http://www.springframework.org/schema/context    http://www.springframework.org/schema/context/spring-context-3.2.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd    http://www.springframework.org/schema/mvc    http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">  <!-- 使用annotation 自动注册bean,并检查@Required,@Autowired的属性已被注入 --><context:component-scan base-package="com.user.service,com.user.dao,com.user.bean,com.user.xml" /><!-- 引入属性配置文件 --><bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="location" value="classpath:database.properties" />        </bean>  <!--或 <context:property-placeholder location="classpath*:*.properties" /> -->
</beans> 
②:mybatis-context.xml的配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:tx="http://www.springframework.org/schema/tx"xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsdhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsdhttp://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd" default-lazy-init="true"><description>MyBatis的数据库持久层配置/配置主-从数据源</description><bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"><property name="dataSource" ref="dataSource" /><!-- 自动扫描entity目录, 省掉Configuration.xml里的手工配置 --><property name="mapperLocations" value="classpath*:com/user/xml/*.xml" /><property name="configLocation" value="classpath:mybatis-config.xml"></property></bean><!-- 扫描dao --><bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"><property name="basePackage" value="com.user.dao" /><property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" /></bean><!-- 配置数据库注解aop --><aop:aspectj-autoproxy /><bean id="dataSourceAspect" class="com.common.readwriteseparate.DataSourceAspect" /><aop:config><aop:aspect id="c" ref="dataSourceAspect"><aop:pointcut id="tx" expression="execution(* com.user.dao.*.*(..))" /><aop:before pointcut-ref="tx" method="before" /></aop:aspect></aop:config><!-- 主-从数据源路由 --><bean id="dataSource" class="com.common.readwriteseparate.DynamicDataSource"><property name="targetDataSources">  <map key-type="java.lang.String">  <!-- write --><entry key="master" value-ref="masterDataSource"/>  <!-- read --><entry key="slave" value-ref="slaveDataSource"/>  </map>  </property>  <property name="defaultTargetDataSource" ref="masterDataSource"/>  </bean>
</beans>
③:mybatis-config.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">  
<configuration>  <typeAliases><package name="com.user.bean"/></typeAliases>
</configuration> 
④:database.properties:

#mysql-Used to verify the effectiveness of the database connection 
validationQuery=SELECT 1
jdbc.initialSize=5
jdbc.maxActive=20
jdbc.maxWait=60000
jdbc.poolPreparedStatements=false
jdbc.poolMaximumIdleConnections=0
jdbc.driverClassName=org.gjt.mm.mysql.Driver
#1.tms business.  2.The db level optimization,data concurrency,desirable.
master.jdbc.url=jdbc:mysql://your ip:3306/master?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull
slave.jdbc.url=jdbc:mysql://your ip:3306/slave?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull
jdbc.username=username
jdbc.password=password
⑤:数据源配置:datasource-context.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:tx="http://www.springframework.org/schema/tx"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsdhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsdhttp://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd" default-lazy-init="true"><description>配置主-从数据源</description><!-- 配置数据源-Master --><bean name="masterDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"><property name="driverClassName" value="${jdbc.driverClassName}" /><property name="url" value="${master.jdbc.url}" /><property name="username" value="${jdbc.username}" /><property name="password" value="${jdbc.password}" /><!-- 初始化连接大小 --><property name="initialSize" value="0" /><!-- 连接池最大使用连接数量 --><property name="maxActive" value="20" /><!-- 连接池最大空闲 error:maxIdle is deprecated --><!-- <property name="maxIdle" value="20" /> --><!-- 连接池最小空闲 --><property name="minIdle" value="0" /><!-- 获取连接最大等待时间 --><property name="maxWait" value="60000" /><!-- <property name="poolPreparedStatements" value="true" /> <property name="maxPoolPreparedStatementPerConnectionSize" value="33" /> --><property name="validationQuery" value="${validationQuery}" /><property name="testOnBorrow" value="false" /><property name="testOnReturn" value="false" /><property name="testWhileIdle" value="true" /><!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 --><property name="timeBetweenEvictionRunsMillis" value="60000" /><!-- 配置一个连接在池中最小生存的时间,单位是毫秒 --><property name="minEvictableIdleTimeMillis" value="25200000" /><!-- 打开removeAbandoned功能 --><property name="removeAbandoned" value="true" /><!-- 1800秒,也就是30分钟 --><property name="removeAbandonedTimeout" value="1800" /><!-- 关闭abanded连接时输出错误日志 --><property name="logAbandoned" value="true" /><!-- 监控数据库 --><!-- <property name="filters" value="stat" /> --><property name="filters" value="mergeStat" /></bean><!-- 配置数据源-Slave --><bean name="slaveDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"><property name="driverClassName" value="${jdbc.driverClassName}" /><property name="url" value="${slave.jdbc.url}" /><property name="username" value="${jdbc.username}" /><property name="password" value="${jdbc.password}" /><!-- 初始化连接大小 --><property name="initialSize" value="0" /><!-- 连接池最大使用连接数量 --><property name="maxActive" value="20" /><!-- 连接池最大空闲 error:maxIdle is deprecated --><!-- <property name="maxIdle" value="20" /> --><!-- 连接池最小空闲 --><property name="minIdle" value="0" /><!-- 获取连接最大等待时间 --><property name="maxWait" value="60000" /><!-- <property name="poolPreparedStatements" value="true" /> <property name="maxPoolPreparedStatementPerConnectionSize" value="33" /> --><property name="validationQuery" value="${validationQuery}" /><property name="testOnBorrow" value="false" /><property name="testOnReturn" value="false" /><property name="testWhileIdle" value="true" /><!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 --><property name="timeBetweenEvictionRunsMillis" value="60000" /><!-- 配置一个连接在池中最小生存的时间,单位是毫秒 --><property name="minEvictableIdleTimeMillis" value="25200000" /><!-- 打开removeAbandoned功能 --><property name="removeAbandoned" value="true" /><!-- 1800秒,也就是30分钟 --><property name="removeAbandonedTimeout" value="1800" /><!-- 关闭abanded连接时输出错误日志 --><property name="logAbandoned" value="true" /><!-- 监控数据库 --><!-- <property name="filters" value="stat" /> --><property name="filters" value="mergeStat" /></bean>
</beans>
⑥transaction-context.xml事务配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:tx="http://www.springframework.org/schema/tx"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsdhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsdhttp://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd" default-lazy-init="true"><description>配置事物</description><!-- 注解方式配置事物 --><tx:annotation-driven transaction-manager="transactionManager" /><!-- 配置事务管理器 --><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource" /></bean><aop:config><aop:pointcut expression="(execution(* com.user.service.*.* (..)))" id="pointcut" /><aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut" /></aop:config><!-- 事务控制 --><tx:advice id="txAdvice" transaction-manager="transactionManager"><tx:attributes><tx:method name="load*" read-only="true" /><tx:method name="get*" read-only="true" /><tx:method name="create*" propagation="REQUIRED" rollback-for="java.lang.Exception" /><tx:method name="save*" propagation="REQUIRED" rollback-for="java.lang.Exception" /><tx:method name="update*" propagation="REQUIRED" rollback-for="java.lang.Exception" /><tx:method name="insert*" propagation="REQUIRED" rollback-for="java.lang.Exception" /><tx:method name="delete*" propagation="REQUIRED" rollback-for="java.lang.Exception" /><tx:method name="schedule*" propagation="REQUIRED" rollback-for="java.lang.Exception" /><tx:method name="do*" propagation="REQUIRED" rollback-for="java.lang.Exception" /><!-- 一个事务涉及一个数据源不能在事务内部去切换数据源成功,所以对多数据源的方法暂不开启事务~分布式事务 --><!-- <tx:method name="crud*" propagation="REQUIRED" rollback-for="java.lang.Exception" /> --><!-- <tx:method name="*" /> --></tx:attributes></tx:advice>
</beans>
⑦:log4j.properties日志配置:
# Set root category priority to INFO and its only appender to CONSOLE.
log4j.rootLogger=info, STDOUT, FILEOUT
# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.STDOUT=org.apache.log4j.ConsoleAppender
log4j.appender.STDOUT.encoding=UTF-8
log4j.appender.STDOUT.layout=org.apache.log4j.PatternLayout
log4j.appender.STDOUT.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m %n# FILEOUT is set to be a File appender using a PatternLayout.
log4j.appender.FILEOUT=org.apache.log4j.RollingFileAppender
#tomcat1
#log4j.appender.FILEOUT.File=/home/logs/timespacexstar1/timespacexstar.log
#tomcat2
log4j.appender.FILEOUT.File=/home/logs/mydemoweb/mydemoweb.log
log4j.appender.FILEOUT.MaxFileSize=5MB
log4j.appender.FILEOUT.MaxBackupIndex=10
log4j.appender.FILEOUT.encoding=UTF-8
log4j.appender.FILEOUT.layout=org.apache.log4j.PatternLayout
log4j.appender.FILEOUT.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m %n
# SQL
log4j.logger.java.sql.ResultSet=debug
log4j.logger.org.apache=debug
log4j.logger.java.sql.Connection=debug
log4j.logger.java.sql.Statement=debug
log4j.logger.java.sql.PreparedStatement=debug
6:代码实现:

①:dao层:

package com.user.dao;import com.common.readwriteseparate.DataSource;
import com.user.bean.Member;public interface MemberMapper {@DataSource("master")int deleteByPrimaryKey(Integer id);@DataSource("master")int insert(Member record);@DataSource("master")int insertSelective(Member record);@DataSource("master")int updateByPrimaryKeySelective(Member record);@DataSource("master")int updateByPrimaryKey(Member record);@DataSource("slave")Member selectByPrimaryKey(Integer id);
}
②:业务层代码:

package com.user.service;import com.user.bean.Member;public interface MemberService {Member crudMember(Member member);
}
实现:

package com.user.service.impl;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.interceptor.TransactionAspectSupport;import com.user.bean.Member;
import com.user.dao.MemberMapper;
import com.user.service.MemberService;@Service("memberService")
public class MemberServiceImpl implements MemberService {//log private static final Logger LOG = LoggerFactory.getLogger(MemberServiceImpl.class);@Autowiredprivate MemberMapper memeberDao;@Overridepublic Member crudMember(Member member) {Member resMember = null;try {resMember = memeberDao.selectByPrimaryKey(member.getId());LOG.info(member.getId() + "查询成功~");memeberDao.insert(member);LOG.info(member.getUsername() + "注册成功~");} catch (Exception e) {LOG.error("用户id:"+member.getId()+"保存和查询失败~~~" + e.getMessage(), e);TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}return resMember;}}
③:master数据库没有id为2的member注册,但slave中有一个用户,先查询从库用户,再插入主库这个member,不涉及业务仅对分库测试:

junit测试代码:

package com.user.test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;import com.common.definition.DictionaryDefinition;
import com.user.bean.Member;
import com.user.service.MemberService;@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:spring-context.xml","classpath:datasource-context.xml","classpath:mybatis-context.xml","classpath:transaction-context.xml"})
public class UserTest {//log private static final Logger LOG = LoggerFactory.getLogger(UserTest.class);@Autowiredprivate MemberService memberService;/*** 主从分离测试,非支持事务* */@Testpublic void crudTraM(){Member member = new Member();member.setId(2);member.setUsername("Tony5");member.setPassword("12345678");member.setStatus(DictionaryDefinition.LOGIN_NORMAL);Member member1 = memberService.crudMember(member);if(member1 != null){LOG.info("结果~~:" + member1.getUsername());}else{LOG.info("主从事物回滚测试失败数据为空");}}
}


效果:master插入前:



后:

slave前后不变:


junit代码测试结果log:

2016-04-11 18:48:32  [ main:2195 ] - [ INFO ]  
************************************************~~~DB:: slave
************************************************ 
2016-04-11 18:48:32  [ main:2879 ] - [ INFO ]  2查询成功~ 
2016-04-11 18:48:32  [ main:2880 ] - [ INFO ]  
************************************************~~~DB:: master
************************************************ 
2016-04-11 18:48:32  [ main:2977 ] - [ INFO ]  Tony5注册成功~ 
2016-04-11 18:48:32  [ main:2977 ] - [ INFO ]  结果~~:kekeai 
上边完成了在service层同一方法中数据源的切换,但这个方法必须不支持事务,不然切换失败!!!

A:spring事务的默然配置:

<tx:method name="*" />

默认意义:

①:事务传播设置是 REQUIRED
②:隔离级别是数据库 DEFAULT
③:事务是支持 读/写
④:事务超时默认是依赖于事务系统的,或者事务超时没有被支持。
⑤:任何 RuntimeException 将触发事务回滚,但是任何 checked Exception 将不触发事务回滚

<tx:method/> 有关事务自定义的设置:



源代码:Spring实现数据库读写分离 

数据库sql:db_rw_separate.sql

附录:

web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns="http://java.sun.com/xml/ns/javaee"xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"version="3.0"><display-name>Archetype Created Web Application</display-name><!-- Spring监听器 --><listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener><context-param><param-name>contextConfigLocation</param-name><param-value>classpath*:*-context.xml</param-value></context-param><!-- 编码utf-8 --><filter><filter-name>charFilter</filter-name><filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class><init-param><param-name>encoding</param-name><param-value>UTF-8</param-value></init-param></filter><filter-mapping><filter-name>charFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping><servlet><servlet-name>springMvc</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:spring-mvc.xml</param-value></init-param></servlet><servlet-mapping><servlet-name>springMvc</servlet-name><url-pattern>/</url-pattern></servlet-mapping><welcome-file-list><welcome-file>/index.jsp</welcome-file></welcome-file-list><!-- 配置SESSION超时,单位是分钟 --><session-config><session-timeout>45</session-timeout></session-config></web-app>
spring-mvc.xml:

<?xml version="1.0" encoding="UTF-8"?>  
<beans xmlns="http://www.springframework.org/schema/beans"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"  xmlns:context="http://www.springframework.org/schema/context"  xmlns:mvc="http://www.springframework.org/schema/mvc"  xsi:schemaLocation="http://www.springframework.org/schema/beans    http://www.springframework.org/schema/beans/spring-beans-3.1.xsd    http://www.springframework.org/schema/context    http://www.springframework.org/schema/context/spring-context-3.1.xsd    http://www.springframework.org/schema/mvc    http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">  <!-- 自动扫描该包,使SpringMVC认为包下用了@controller注解的类是控制器 -->  <context:component-scan base-package="com.ourtimes.controller" />  <!--避免IE执行AJAX时,返回JSON出现下载文件 -->  <bean id="mappingJacksonHttpMessageConverter"  class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">  <property name="supportedMediaTypes">  <list>  <value>text/html;charset=UTF-8</value>  </list>  </property>  </bean>  <!-- 启动SpringMVC的注解功能,完成请求和注解POJO的映射 -->  <bean  class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">  <property name="messageConverters">  <list>  <ref bean="mappingJacksonHttpMessageConverter" /> <!-- JSON转换器 -->  </list>  </property>  </bean>  <!-- 定义跳转的文件的前后缀 ,视图模式配置-->  <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">  <!-- 这里的配置我的理解是自动给后面action的方法return的字符串加上前缀和后缀,变成一个 可用的url地址 -->  <property name="prefix" value="/WEB-INF/views/" />  <property name="suffix" value=".jsp" />  </bean>  <!-- 配置文件上传,如果没有使用文件上传可以不用配置,当然如果不配,那么配置文件中也不必引入上传组件包 -->  <bean id="multipartResolver"    class="org.springframework.web.multipart.commons.CommonsMultipartResolver">    <!-- 默认编码 -->  <property name="defaultEncoding" value="utf-8" />    <!-- 文件大小最大值 -->  <property name="maxUploadSize" value="10485760000" />    <!-- 内存中的最大值 -->  <property name="maxInMemorySize" value="40960" />    </bean>   </beans>  

这篇关于Spring实现数据库读写分离/spring事务配置解释(Annotation/Spring AOP/Reflection)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/950530

相关文章

Spring Boot循环依赖原理、解决方案与最佳实践(全解析)

《SpringBoot循环依赖原理、解决方案与最佳实践(全解析)》循环依赖指两个或多个Bean相互直接或间接引用,形成闭环依赖关系,:本文主要介绍SpringBoot循环依赖原理、解决方案与最... 目录一、循环依赖的本质与危害1.1 什么是循环依赖?1.2 核心危害二、Spring的三级缓存机制2.1 三

pytorch自动求梯度autograd的实现

《pytorch自动求梯度autograd的实现》autograd是一个自动微分引擎,它可以自动计算张量的梯度,本文主要介绍了pytorch自动求梯度autograd的实现,具有一定的参考价值,感兴趣... autograd是pytorch构建神经网络的核心。在 PyTorch 中,结合以下代码例子,当你

如何在Mac上安装并配置JDK环境变量详细步骤

《如何在Mac上安装并配置JDK环境变量详细步骤》:本文主要介绍如何在Mac上安装并配置JDK环境变量详细步骤,包括下载JDK、安装JDK、配置环境变量、验证JDK配置以及可选地设置PowerSh... 目录步骤 1:下载JDK步骤 2:安装JDK步骤 3:配置环境变量1. 编辑~/.zshrc(对于zsh

在Spring Boot中浅尝内存泄漏的实战记录

《在SpringBoot中浅尝内存泄漏的实战记录》本文给大家分享在SpringBoot中浅尝内存泄漏的实战记录,结合实例代码给大家介绍的非常详细,感兴趣的朋友一起看看吧... 目录使用静态集合持有对象引用,阻止GC回收关键点:可执行代码:验证:1,运行程序(启动时添加JVM参数限制堆大小):2,访问 htt

SpringBoot集成Milvus实现数据增删改查功能

《SpringBoot集成Milvus实现数据增删改查功能》milvus支持的语言比较多,支持python,Java,Go,node等开发语言,本文主要介绍如何使用Java语言,采用springboo... 目录1、Milvus基本概念2、添加maven依赖3、配置yml文件4、创建MilvusClient

浅析Java中如何优雅地处理null值

《浅析Java中如何优雅地处理null值》这篇文章主要为大家详细介绍了如何结合Lambda表达式和Optional,让Java更优雅地处理null值,感兴趣的小伙伴可以跟随小编一起学习一下... 目录场景 1:不为 null 则执行场景 2:不为 null 则返回,为 null 则返回特定值或抛出异常场景

售价599元起! 华为路由器X1/Pro发布 配置与区别一览

《售价599元起!华为路由器X1/Pro发布配置与区别一览》华为路由器X1/Pro发布,有朋友留言问华为路由X1和X1Pro怎么选择,关于这个问题,本期图文将对这二款路由器做了期参数对比,大家看... 华为路由 X1 系列已经正式发布并开启预售,将在 4 月 25 日 10:08 正式开售,两款产品分别为华

JS+HTML实现在线图片水印添加工具

《JS+HTML实现在线图片水印添加工具》在社交媒体和内容创作日益频繁的今天,如何保护原创内容、展示品牌身份成了一个不得不面对的问题,本文将实现一个完全基于HTML+CSS构建的现代化图片水印在线工具... 目录概述功能亮点使用方法技术解析延伸思考运行效果项目源码下载总结概述在社交媒体和内容创作日益频繁的

Node.js 数据库 CRUD 项目示例详解(完美解决方案)

《Node.js数据库CRUD项目示例详解(完美解决方案)》:本文主要介绍Node.js数据库CRUD项目示例详解(完美解决方案),本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考... 目录项目结构1. 初始化项目2. 配置数据库连接 (config/db.js)3. 创建模型 (models/

SQL server配置管理器找不到如何打开它

《SQLserver配置管理器找不到如何打开它》最近遇到了SQLserver配置管理器打不开的问题,尝试在开始菜单栏搜SQLServerManager无果,于是将自己找到的方法总结分享给大家,对SQ... 目录方法一:桌面图标进入方法二:运行窗口进入方法三:查找文件路径方法四:检查 SQL Server 安