🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[ Spring实用功能--Profile、WebService、缓存、消息、ORM](http://blog.csdn.net/puma_dong/article/details/38036667) [TOC=1,3] 本篇介绍一些Spring与其他框架结合的实用功能,包括:Apache CXF WebService框架、Redis缓存、RabbitMQ消息、MyBatis框架。 另外对于Profile,也是Spring3.0开始新加的功能,对于开发测试环境、和生产环境分别采用不同的配置,有一定用处。 # Profile Spring3.1新属性管理API:PropertySource、Environment、Profile。 Environment:环境,本身是一个PropertyResolver,但是提供了Profile特性,即可以根据环境得到相应数据(即激活不同的Profile,可以得到不同的属性数据,比如用于多环境场景的配置(正式机、测试机、开发机DataSource配置))。 Profile:剖面,只有激活的剖面的组件/配置才会注册到Spring容器,类似于maven中profile,Spring 3.1增加了一个在不同环境之间简单切换的profile概念, 可以在不修改任何文件的情况下让工程分别在 dev/test/production 等环境下运行。 为了减小部署维护,可以让工程会默认运行在dev模式,而测试环境和生产环境通过增加jvm参数激活 production的profile. 比如,对于如下的一个例子,由于测试环境和生产环境,连接数据库的方式不一样,可以有如下的解决办法: 1、首先ApplicationContext.xml中,xsi:schemaLocation需要引用3.2的xsd 2、ApplicationContext.xml配置如下: **[html]** [view plain](http://blog.csdn.net/puma_dong/article/details/38036667# "view plain") [copy](http://blog.csdn.net/puma_dong/article/details/38036667# "copy") [print](http://blog.csdn.net/puma_dong/article/details/38036667# "print")[?](http://blog.csdn.net/puma_dong/article/details/38036667# "?")[![](https://box.kancloud.cn/2015-10-13_561c56d3593bb.png)](https://code.csdn.net/snippets/432712 "在CODE上查看代码片")[![](https://box.kancloud.cn/2015-10-13_561c56d363c1b.svg)](https://code.csdn.net/snippets/432712/fork "派生到我的代码片") ~~~ 1. beans profile="production">   2.     bean id="dataSourcejdbc" class="org.springframework.jndi.JndiObjectFactoryBean">   3.         property name="jndiName" value="java:/MySqlDS_JDBC" />   4.     bean>   5. beans>   6. beans profile="dev">   7.     bean id="dataSourcejdbc" class="org.springframework.jdbc.datasource.DriverManagerDataSource">   8.         property name="driverClassName" value="com.mysql.jdbc.Driver"/>   9.         property name="url" value="jdbc:mysql://IP:3306/db?characterEncoding=utf-8"/>   10.         property name="username" value="root"/>   11.         property name="password" value="root"/>   12.     bean>   13. beans>   ~~~ 3、开发环境配置,在web.xml中,如下配置: **[html]** [view plain](http://blog.csdn.net/puma_dong/article/details/38036667# "view plain") [copy](http://blog.csdn.net/puma_dong/article/details/38036667# "copy") [print](http://blog.csdn.net/puma_dong/article/details/38036667# "print")[?](http://blog.csdn.net/puma_dong/article/details/38036667# "?")[![](https://box.kancloud.cn/2015-10-13_561c56d3593bb.png)](https://code.csdn.net/snippets/432712 "在CODE上查看代码片")[![](https://box.kancloud.cn/2015-10-13_561c56d363c1b.svg)](https://code.csdn.net/snippets/432712/fork "派生到我的代码片") ~~~ 1. context-param>     2.     param-name>spring.profiles.defaultparam-name>     3.     param-value>devparam-value>     4. context-param>   ~~~ 4、生产环境配置 比如,对于Jboss,在bin/run.conf里面,增加启动参数:-Dspring.profiles.active=production `JAVA_OPTS="-Xms2048m -Xmx2048m -XX:MaxPermSize=1024m -Dorg.jboss.resolver.warning=true -Dsun.rmi.dgc.client.gcInterval=3600000 -Dsun.rmi.dgc.server.gcInterval=3600000 -Dsun.lang.ClassLoader.allowArraySyntax=true -Dorg.terracotta.quartz.skipUpdateCheck=true -Dspring.profiles.active=production"` 以上是对于Web项目中如何利用profile的一种演示,如果是maven项目,也可以在maven打包时采用不同的profile,命令如下: mvn clean package -Dmaven.test.skip=true -Ponline 通过P参数采用不同的profile,这样可以实现为开发、测试、生产打出不同的包。 不过,不推荐这种打包方式,应该是对于开发、测试、生产打出一样的包,然后根据机器本身的环境,来决定程序是按照那种环境来运行。 如果公司有根据环境变量的自动化部署方式(比如dev/test/stage/online),则这个profile是非常管用的。 # WebService Java生态下的WebService框架非常多,apache cxf 是与spring结合最好的一种。配置步骤如下: 1、pom.xml,增加依赖: ~~~ 1. dependency>   2.     groupId>org.apache.cxfgroupId>   3.     artifactId>cxf-rt-frontend-jaxwsartifactId>   4.     version>2.7.5version>   5. dependency>   6. dependency>   7.     groupId>org.apache.cxfgroupId>   8.     artifactId>cxf-rt-transports-httpartifactId>   9.     version>2.7.5version>   10. dependency>   ~~~ 2、web.xml,增加servlet:    ~~~ 1.    2. <servlet>   3.     servlet-name>cxfservlet-name>   4.     servlet-class>org.apache.cxf.transport.servlet.CXFServletservlet-class>   5.     load-on-startup>2load-on-startup>   6. </servlet>   7. <servlet-mapping>   8.     servlet-name>cxfservlet-name>   9.     url-pattern>/*url-pattern>   10. </servlet-mapping>   11.    ~~~ 3、resources目录下,增加applicationContext-cxf.xml,内容如下: ~~~ 1. <xml version="1.0" encoding="UTF-8"?>   2. beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   3.  xmlns:jaxws="http://cxf.apache.org/jaxws"   4.  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd   5.     http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">   6.    7.  import resource="classpath:META-INF/cxf/cxf.xml" />   8.  import resource="classpath:META-INF/cxf/cxf-servlet.xml" /> -->   9.    10.  jaxws:endpoint implementor="#basicWebService" address="/BasicWebService" />   11. beans>   ~~~ 4、BasicWebService来的内容大致如下: ~~~ 1. @WebService(name = "BasicWebService", serviceName = "BasicWebService", portName = "BasicWebServicePort", targetNamespace = "http://api.domain.com/ws")   2. @Service   3. public class BasicWebService {   4.  @WebMethod   5.  public void sendHtmlMail(@WebParam(name = "headName") String headName,   6.    @WebParam(name = "sendHtml") String sendHtml) {   7.   sendMail.doSendHtmlEmail(headName, sendHtml);   8.  }   9. }   ~~~ 使用Apache CXF框架,是被Spring容器管理的,也就是说,BasicWebService本身可以设置@Service标记,也可以在BasicWebService中使用@Autowired进行注入。 而其他框架的WebService,比如Jboss直接通过Servlet方式暴露的WebService就不能这样,只能通过一个SpringContextHolder手动从Spring容器中拿,大致如下: 1、首先在web.xml中增加WebService类的servlet,如下: ~~~ 1.    2. servlet>   3.     servlet-name>BasicWebServiceservlet-name>   4.     servlet-class>com.xx.BasisWebServiceservlet-class>   5. servlet>   6. servlet-mapping>   7.     servlet-name>BasicWebServiceservlet-name>   8.     url-pattern>/BasicWebServiceurl-pattern>   9. servlet-mapping>   10.      ~~~ 2、BasicWebService的内容大致如下: ~~~ 1. @WebService(name = "BasicWebService", serviceName = "BasicWebService", portName = "BasicWebServicePort", targetNamespace = "http://api.sina.com/ws")   2. public class BasicWebService {   3.    4.     //这是从Spring容器中拿对象,SpringContextHolder是一个实现了org.springframework.context.ApplicationContextAware的类   5.     private ISystemConfigService systemConfigService = SpringContextHolder.getBean(ISystemConfigService.class);   6.    7.     @WebMethod   8.     public String test(@WebParam(name = "inputpara") String inputpara) {   9.         return inputpara + "_100";   10.     }   11. }   ~~~ # Redis Spring可以简化调用Redis的操作,配置大致如下: 1、pom.xml增加依赖: ~~~ 1. groupId>org.springframework.datagroupId>   2. artifactId>spring-data-redisartifactId>   3. version>1.0.6.RELEASEversion>   4. lt;/dependency>   ~~~ 2、resources目录下,增加applicationContext-redis.xml,内容如下: ~~~ 1. xml version="1.0" encoding="UTF-8"?>   2. beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   3.        xmlns:context="http://www.springframework.org/schema/context"   4.        xmlns:cache="http://www.springframework.org/schema/cache"   5.        xmlns:p="http://www.springframework.org/schema/p"   6.        xsi:schemaLocation="http://www.springframework.org/schema/beans  http://www.springframework.org/schema/beans/spring-beans-3.2.xsd   7.  http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">       8.     description>Spring-cachedescription>   9.     cache:annotation-driven/>   10.     bean id="cacheManager" class="org.springframework.data.redis.cache.RedisCacheManager">   11.         constructor-arg name="template" index="0" ref="redisTemplate"/>   12.     bean>   13.     bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">   14.         property name="maxActive" value="${redis.pool.maxActive}"/>   15.         property name="maxIdle" value="${redis.pool.maxIdle}"/>   16.         property name="maxWait" value="${redis.pool.maxWait}"/>   17.         property name="testOnBorrow" value="${redis.pool.testOnBorrow}"/>   18.     bean>   19.        20.     bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">   21.         property name="hostName" value="${redis.ip}"/>   22.         property name="port" value="${redis.port}"/>   23.         property name="poolConfig" ref="jedisPoolConfig"/>   24.     bean>   25.        26.     bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" p:connection-factory-ref="jedisConnectionFactory">   27.         property name="keySerializer" ref="stringRedisSerializer"/>   28.     bean>   29.     bean id="stringRedisSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer"/>   30. beans>   ~~~ 3、缓存写入参考实现: ~~~ 1. @Service   2. public class BrandBaseServiceImpl implements IBrandBaseService {   3.     @Override   4.     @Cacheable(value = CacheClientConstant.COMMODITY_BRAND_REDIS_CACHE, key = "'commodity:webservice:all:brand:list'")   5.     public List getAllBrands() {   6.      try   7.      {   8.       List brands = brandMapper.getAllBrands();   9.       return brands;   10.      } catch (Exception ex)   11.      {   12.       logger.error(ex.toString());   13.       return null;        14.      }   15.     }   16.     @Override   17.     @Cacheable(value = CacheClientConstant.COMMODITY_BRAND_REDIS_CACHE, key = "'commodity:webservice:brand:no:'+#brandNo")   18.     public Brand getBrandByNo(String brandNo) {   19.         if (StringUtils.isBlank(brandNo))   20.             return null;   21.         return brandMapper.getBrandByNo(brandNo);   22.     }   23. }   ~~~ 4、缓存清除参考实现: ~~~ 1. @Service   2. public class RedisCacheUtil {   3.     4.  private final Logger logger = LoggerFactory.getLogger(this.getClass());   5.     @Autowired   6.     private RedisTemplate redisTemplate;   7.     @Autowired   8.     private JedisConnectionFactory jedisConnectionFactory;       9.     @CacheEvict(value = CacheClientConstant.COMMODITY_CATEGORY_REDIS_CACHE, key = "'commodity:webservice:category:no:'+#categoryNo")   10.     public void cleanCatCacheByNo(String categoryNo)   11.     {   12.      List keys = new ArrayList();   13.         logger.info("[商品服务端]清理分类categoryNo:{}缓存,REDIS SERVER地址:{}", categoryNo, jedisConnectionFactory.getHostName() + ":" + jedisConnectionFactory.getPort());   14.         if (StringUtils.hasText(categoryNo)) {   15.          keys.add("commodity:webservice:category:no:" + categoryNo);   16.             cleanAgain(keys);   17.         }   18.     }       19.     @CacheEvict(value = CacheClientConstant.COMMODITY_SYSTEMCONFIG_REDIS_CACHE, allEntries = true)   20.     public void cleanSystemConfigAll()   21.     {   22.      logger.info("[商品服务端]清楚SystemConfig缓存");   23.     }       24.     /**  25.      * 考虑到主从延迟可能会导致缓存更新失效,延迟再清理一次缓存  26.      * @param keys 需要清除缓存的KEY  27.      */   28.     private void cleanAgain(List keys) {   29.         if (CollectionUtils.isEmpty(keys)) {   30.             return;   31.         }   32.         for (String key : keys) {   33.             logger.info("清理缓存,KEY:{}", key);   34.             redisTemplate.delete(key);   35.         }   36.     }   37. }     ~~~ # RabbitMQ Spring也可以简化使用RabbitMQ的操作,配置大致如下: 1、pom.xml增加依赖: ~~~ 1. <dependency>   2.     groupId>org.springframework.amqpgroupId>   3.     artifactId>spring-amqpartifactId>   4.     version>${spring.amqp.version}version>   5. </dependency>   6. <dependency>   7.     groupId>org.springframework.amqpgroupId>   8.     artifactId>spring-rabbitartifactId>   9.     version>${spring.amqp.version}version>   10. </dependency>   ~~~ 2、发送消息代码例子: ~~~ 1. @Service   2. public class MessageSendServiceImpl implements IMessageSendService {    3.  private static final String EXCHANGE = "amq.topic";    4.  @Autowired   5.  private volatile RabbitTemplate rabbitTemplate;    6.  private final Logger logger = LoggerFactory.getLogger(this.getClass());   7.  @Override   8.  public Boolean sendMessage(String commodityNo) {   9.   Commodity c = getCommodity(commodityNo);   10.   // 发送rabbitMQ消息(topic)   11.   rabbitTemplate.convertAndSend(EXCHANGE, "commodity.update.topic", c);   12.   logger.info("发送消息成功(topic):商品编号:" + commodityNo);   13.   return true;   14.  }    15. }   ~~~ 3、resources目录下,增加applicationContext-rabbitmq.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:rabbit="http://www.springframework.org/schema/rabbit" xmlns:task="http://www.springframework.org/schema/task" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit-1.1.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 定义rabbitmq连接工厂,生产环境使用集群配置,支持failover,rabbitmq.host=192.168.211.230:5672 --> <rabbit:connection-factory id="connectionFactory" addresses="${rabbitmq.host}" /> <rabbit:admin connection-factory="connectionFactory" /> <rabbit:template id="amqpTemplate" connection-factory="connectionFactory" channel-transacted="true" message-converter="jsonMessageConverter" /> <bean id="jsonMessageConverter" class="org.springframework.amqp.support.converter.JsonMessageConverter"> <property name="classMapper"> <bean class="org.springframework.amqp.support.converter.DefaultClassMapper"> </bean> </property> </bean> <!-- 两种业务需求: 1. 同一个服务部署在多台服务器上,如果想消息被一个服务收取,则要配置name,<rabbit:listener 里的queues=这里的name 2. 同一个服务部署在多台服务器上,如果想消息被所有的服务收取,刚不要配置name,用rabbitmq自动创建的匿名name,这时要去掉这里的name属性, 并且<rabbit:listener里的queues=这里的id 一般来说,都是第一种业务需求较多 --> <rabbit:queue id="queue的id,可以和name一样" name="queue的名字,在rabbitmq控制台可以看到,例如commodity.update.topic.queue"> <rabbit:queue-arguments> <entry key="x-ha-policy" value="all" /> </rabbit:queue-arguments> </rabbit:queue> <!-- CONSUMER --> <!-- 这里的error-handler最好都配置,因为rabbitmq报的异常默认是不被捕获的,如果这里没有error-handler,log级别又没指定到amqp的包,那么错误将不会被察觉 --> <rabbit:listener-container connection-factory="connectionFactory" message-converter="jsonMessageConverter" channel-transacted="true" error-handler="rabbitMqErrorHandler" concurrency="10" auto-startup="true"> <rabbit:listener queues="rabbit:queue中定义的name或者id" ref="commodityUpdateListener" method="handleMessage" /> </rabbit:listener-container> <rabbit:topic-exchange name="amq.topic" > <rabbit:bindings> <!-- 这里的queue是<rabbit:queue 里的ID --> <rabbit:binding pattern="发送方的routingKey,对于上面的发送就是commodity.update.topic" queue="queue的名字,在rabbitmq控制台可以看到,例如commodity.update.topic.queue"/> </rabbit:bindings> </rabbit:topic-exchange> </beans> ~~~ 4、接收消息代码例子: ~~~ 1. @Component   2. public class CommodityUpdateListener {   3.  public void handleMessage(Commodity commodity) {   4.   if(commodity==null)   5.   {   6.    logger.info("XXX");   7.    return;   8.   }   9.   //处理逻辑   10.  }   11. }   ~~~ 5、处理消息错误代码例子: ~~~ 1. @Component   2. public class RabbitMqErrorHandler implements ErrorHandler {   3.     4.  private static Logger logger = LoggerFactory.getLogger(RabbitMqErrorHandler.class);   5.     6.  @Override   7.  public void handleError(Throwable t) {   8.   logger.error("Receive rabbitmq message error:{}", t);    9.  }   10. }   ~~~ # MyBatis Spring可以大大简化使用MyBatis这种ORM框架,定义出接口和Mapper文件之后,Spring可以自动帮我们生成实现类。我曾经在DotNet框架下使用过MyBatis.Net,所有的Mapper的实现类都需要手工写代码,而Spring帮我节省了很多编码工作量。 大致配置步骤如下: 1、pom.xml增加依赖: ~~~ 1. dependency>   2.     groupId>org.mybatisgroupId>   3.     artifactId>mybatis-springartifactId>   4.     version>1.1.1version>   5. dependency>   6. dependency>   7.     groupId>org.mybatis.cachesgroupId>   8.     artifactId>mybatis-ehcacheartifactId>   9.     version>1.0.1version>   10. dependency>   ~~~ 2、resources目录下,applicationContext.xml中,一般放置关于mybatis的配置,内容如下: ~~~ <?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:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xmlns:task="http://www.springframework.org/schema/task" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd"> <description>Spring公共配置</description> <!--开启注解 --> <context:annotation-config /> <!-- 开启自动切面代理 --> <aop:aspectj-autoproxy /> <context:component-scan base-package="com.xx"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" /> </context:component-scan> <!-- 定义受环境影响易变的变量 --> <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" /> <property name="ignoreResourceNotFound" value="true" /> <property name="locations"> <list> <!-- 标准配置 --> <value>classpath*:/application.properties</value> <value>classpath*:/config.properties</value> <!-- 本地开发环境配置 --> <value>file:/d:/conf/pcconf/*.properties</value> <!-- 服务器生产环境配置 --> <value>file:/etc/conf/pcconf/*.properties</value> </list> </property> <!--property name="ignoreUnresolvablePlaceholders" value="true" / --> </bean> <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" /> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSourcejdbc"/> </bean> <!-- 强烈建议用JdbcTemplate代替JdbcUtils --> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSourcejdbc" /> </bean> <bean id="sqlSessionFactoryWrite" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSourcejdbc" /> </bean> <!-- 会自动将basePackage中配置的包路径下的所有带有@Mapper标注的Dao层的接口生成代理,替代原来我们的Dao实现。 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="sqlSessionFactory" ref="sqlSessionFactoryWrite" /> <property name="basePackage" value="com/xx/pc/template" /> </bean> <beans profile="production"> <bean id="dataSourcejdbc" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName" value="java:/MySqlDS_JDBC" /> </bean> </beans> <beans profile="dev"> <bean id="dataSourcejdbc" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://ip:3306/dbname?characterEncoding=utf-8"/> <property name="username" value="root"/> <property name="password" value="root"/> </bean> </beans> </beans> ~~~ 3、定义接口,及在src/main/resource对应接口的包路径下定义同名的xml配置文件即可。 Spring初始化完毕后,会自动帮我们生成Mapper的实现类。