Spring实用功能--Profile、WebService

本篇介绍一些Spring与其他框架结合的实用功能,包括:Apache CXF WebService框架、Redis缓存、RabbitMQ消息、MyBatis框架。

另外对于Profile,也是Spring3.0开始新加的功能,对于开发测试环境、和生产环境分别采用不同的配置,有一定用处。

Spring可以简化调用Redis的操作,配置大致如下:

1、pom.xml增加依赖:

~~~ org.springframework.dataspring-data-redis1.0.6.RELEASE

2、resources目录下,增加applicationContext-redis.xml,内容如下:

<?xml version="1.0" encoding="UTF-8"?>

<description>Spring-cache</description>
<cache:annotation-driven/>
<bean id="cacheManager" class="org.springframework.data.redis.cache.RedisCacheManager">
    <constructor-arg name="template" index="0" ref="redisTemplate"/>
</bean>
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
    <property name="maxActive" value="${redis.pool.maxActive}"/>
    <property name="maxIdle" value="${redis.pool.maxIdle}"/>
    <property name="maxWait" value="${redis.pool.maxWait}"/>
    <property name="testOnBorrow" value="${redis.pool.testOnBorrow}"/>
</bean>
<!-- 工厂实现: -->
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
    <property name="hostName" value="${redis.ip}"/>
    <property name="port" value="${redis.port}"/>
    <property name="poolConfig" ref="jedisPoolConfig"/>
</bean>
<!--模板类: -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" p:connection-factory-ref="jedisConnectionFactory">
    <property name="keySerializer" ref="stringRedisSerializer"/>
</bean>
<bean id="stringRedisSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer"/>


3、缓存写入参考实现:

@Service
public class BrandBaseServiceImpl implements IBrandBaseService {

@Override
@Cacheable(value = CacheClientConstant.COMMODITY_BRAND_REDIS_CACHE, key = ""commodity:webservice:all:brand:list"")
public List<Brand> getAllBrands() {
 try
 {
  List<Brand> brands = brandMapper.getAllBrands();
  return brands;
 } catch (Exception ex)
 {
  logger.error(ex.toString());
  return null;     
 }
}
@Override
@Cacheable(value = CacheClientConstant.COMMODITY_BRAND_REDIS_CACHE, key = ""commodity:webservice:brand:no:"+#brandNo")
public Brand getBrandByNo(String brandNo) {
    if (StringUtils.isBlank(brandNo))
        return null;
    return brandMapper.getBrandByNo(brandNo);
}

}


4、缓存清除参考实现:

@Service
public class RedisCacheUtil {

private final Logger logger = LoggerFactory.getLogger(this.getClass());

@Autowired
private RedisTemplate<String,Object> redisTemplate;
@Autowired
private JedisConnectionFactory jedisConnectionFactory;    
@CacheEvict(value = CacheClientConstant.COMMODITY_CATEGORY_REDIS_CACHE, key = ""commodity:webservice:category:no:"+#categoryNo")
public void cleanCatCacheByNo(String categoryNo)
{
 List<String> keys = new ArrayList<String>();
    logger.info("[商品服务端]清理分类categoryNo:{}缓存,REDIS SERVER地址:{}", categoryNo, jedisConnectionFactory.getHostName() + ":" + jedisConnectionFactory.getPort());
    if (StringUtils.hasText(categoryNo)) {
     keys.add("commodity:webservice:category:no:" + categoryNo);
        cleanAgain(keys);
    }
}    
@CacheEvict(value = CacheClientConstant.COMMODITY_SYSTEMCONFIG_REDIS_CACHE, allEntries = true)
public void cleanSystemConfigAll()
{
 logger.info("[商品服务端]清楚SystemConfig缓存");
}    
/**
 * 考虑到主从延迟可能会导致缓存更新失效,延迟再清理一次缓存
 * @param keys 需要清除缓存的KEY
 */
private void cleanAgain(List<String> keys) {
    if (CollectionUtils.isEmpty(keys)) {
        return;
    }
    for (String key : keys) {
        logger.info("清理缓存,KEY:{}", key);
        redisTemplate.delete(key);
    }
}

}


  

# RabbitMQ

  

Spring也可以简化使用RabbitMQ的操作,配置大致如下:

  

1、pom.xml增加依赖:

org.springframework.amqpspring-amqp${spring.amqp.version}org.springframework.amqpspring-rabbit${spring.amqp.version}

2、发送消息代码例子:  

@Service
public class MessageSendServiceImpl implements IMessageSendService {
private static final String EXCHANGE = "amq.topic";
@Autowired
private volatile RabbitTemplate rabbitTemplate;
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public Boolean sendMessage(String commodityNo) {
Commodity c = getCommodity(commodityNo);
// 发送rabbitMQ消息(topic)
rabbitTemplate.convertAndSend(EXCHANGE, "commodity.update.topic", c);
logger.info("发送消息成功(topic):商品编号:" + commodityNo);
return true;
}
}


3、resources目录下,增加applicationContext-rabbitmq.xml,用来配置接收消息,内容如下:  

<?xml version="1.0" encoding="UTF-8"?>


<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" />






<!--

两种业务需求:
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> 

<!-- 这里的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>

4、接收消息代码例子:

@Component
public class CommodityUpdateListener {
public void handleMessage(Commodity commodity) {
if(commodity==null)
{
logger.info("XXX");
return;
}
//处理逻辑
}
}


5、处理消息错误代码例子:

@Component
public class RabbitMqErrorHandler implements ErrorHandler {

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

@Override
public void handleError(Throwable t) {
logger.error("Receive rabbitmq message error:{}", t);
}
}

  

# MyBatis

  

Spring可以大大简化使用MyBatis这种ORM框架,定义出接口和Mapper文件之后,Spring可以自动帮我们生成实现类。我曾经在DotNet框架下使用过MyBatis.Net,所有的Mapper的实现类都需要手工写代码,而Spring帮我节省了很多编码工作量。

大致配置步骤如下:

1、pom.xml增加依赖:  

org.mybatismybatis-spring1.1.1org.mybatis.cachesmybatis-ehcache1.0.1

2、resources目录下,applicationContext.xml中,一般放置关于mybatis的配置,内容如下:  

<?xml version="1.0" encoding="UTF-8"?>

Spring公共配置

<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>





<!-- 标准配置 -->
<value>classpath*:/application.properties</value>
<value>classpath*:/config.properties</value>
<!-- 本地开发环境配置 -->
<value>file:/d:/conf/pcconf/*.properties</value>
<!-- 服务器生产环境配置 -->
<value>file:/etc/conf/pcconf/*.properties</value>





<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" />

 <property name="dataSource" ref="dataSourcejdbc"/>    
</bean>

















<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> 

3、定义接口,及在src/main/resource对应接口的包路径下定义同名的xml配置文件即可。  

Spring初始化完毕后,会自动帮我们生成Mapper的实现类。
文章导航