spring与其他常用常用框架实用功能例子

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

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

Profile

Spring3.1新属性管理API:PropertySource、Environment、Profile。

Environment:环境,本身是一个PropertyResolver,但是提供了Profile特性,即可以根据环境得到相应数据(即激活不同的Profile,可以得到不同的属性数据,比如用于多环境场景的配置(正式机、测试机、开发机DataSource配置))。

Profile:剖面,只有激活的剖面的组件/配置才会注册到Spring容器,类似于maven中profile,Spring3.1增加了一个在不同环境之间简单切换的profile概念,可以在不修改任何文件的情况下让工程分别在dev/test/production等环境下运行。

为了减小部署维护,可以让工程会默认运行在dev模式,而测试环境和生产环境通过增加jvm参数激活production的profile.

比如,对于如下的一个例子,由于测试环境和生产环境,连接数据库的方式不一样,可以有如下的解决办法:

1、首先ApplicationContext.xml中,xsi:schemaLocation需要引用3.2的xsd

2、ApplicationContext.xml配置如下:

<beansprofile="production">

<beanid="dataSourcejdbc"class="org.springframework.jndi.JndiObjectFactoryBean">

<propertyname="jndiName"value="java:/MySqlDS_JDBC"/>

</bean>

</beans>

<beansprofile="dev">

<beanid="dataSourcejdbc"class="org.springframework.jdbc.datasource.DriverManagerDataSource">

<propertyname="driverClassName"value="com.mysql.jdbc.Driver"/>

<propertyname="url"value="jdbc:mysql://IP:3306/db?characterEncoding=utf-8"/>

<propertyname="username"value="root"/>

<propertyname="password"value="root"/>

</bean>

</beans>

3、开发环境配置,在web.xml中,如下配置:

<context-param>

<param-name>spring.profiles.default</param-name>

<param-value>dev</param-value>

</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,命令如下:

mvncleanpackage-Dmaven.test.skip=true-Ponline

通过P参数采用不同的profile,这样可以实现为开发、测试、生产打出不同的包。

不过,不推荐这种打包方式,应该是对于开发、测试、生产打出一样的包,然后根据机器本身的环境,来决定程序是按照那种环境来运行。

WebService

Java生态下的WebService框架非常多,apachecxf是与spring结合最好的一种。配置步骤如下:

1、pom.xml,增加依赖:

<dependency>

<groupId>org.apache.cxf</groupId>

<artifactId>cxf-rt-frontend-jaxws</artifactId>

<version>2.7.5</version>

</dependency>

<dependency>

<groupId>org.apache.cxf</groupId>

<artifactId>cxf-rt-transports-http</artifactId>

<version>2.7.5</version>

</dependency>

2、web.xml,增加servlet:

<!--WebService声明开始-->

<servlet>

<servlet-name>cxf</servlet-name>

<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>

<load-on-startup>2</load-on-startup>

</servlet>

<servlet-mapping>

<servlet-name>cxf</servlet-name>

<url-pattern>/*</url-pattern>

</servlet-mapping>

<!--WebService声明结束-->

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

<?xmlversion="1.0"encoding="UTF-8"?>

<beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:jaxws="http://cxf.apache.org/jaxws"

xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-3.2.xsd

http://cxf.apache.org/jaxwshttp://cxf.apache.org/schemas/jaxws.xsd">

<!--<importresource="classpath:META-INF/cxf/cxf.xml"/>

<importresource="classpath:META-INF/cxf/cxf-servlet.xml"/>-->

<jaxws:endpointimplementor="#basicWebService"address="/BasicWebService"/>

</beans>

4、BasicWebService来的内容大致如下:

@WebService(name="BasicWebService",servicename="BasicWebService",portname="BasicWebServicePort",targetNamespace="http://api.domain.com/ws")

@Service

publicclassBasicWebService{

@WebMethod

publicvoidsendHtmlMail(@WebParam(name="headName")StringheadName,

@WebParam(name="sendHtml")StringsendHtml){

sendMail.doSendHtmlEmail(headName,sendHtml);

}

}

使用ApacheCXF框架,是被Spring容器管理的,也就是说,BasicWebService本身可以设置@Service标记,也可以在BasicWebService中使用@Autowired进行注入。

而其他框架的WebService,比如Jboss直接通过Servlet方式暴露的WebService就不能这样,只能通过一个SpringContextHolder手动从Spring容器中拿,大致如下:

1、首先在web.xml中增加WebService类的servlet,如下:

<!--WebService声明开始-->

<servlet>

<servlet-name>BasicWebService</servlet-name>

<servlet-class>com.xx.BasisWebService</servlet-class>

</servlet>

<servlet-mapping>

<servlet-name>BasicWebService</servlet-name>

<url-pattern>/BasicWebService</url-pattern>

</servlet-mapping>

<!--WebService声明结束-->

2、BasicWebService的内容大致如下:

@WebService(name="BasicWebService",servicename="BasicWebService",portname="BasicWebServicePort",targetNamespace="http://api.sina.com/ws")

publicclassBasicWebService{

//这是从Spring容器中拿对象,SpringContextHolder是一个实现了org.springframework.context.ApplicationContextAware的类

privateISystemConfigServicesystemConfigService=SpringContextHolder.getBean(ISystemConfigService.class);

@WebMethod

publicStringtest(@WebParam(name="inputpara")Stringinputpara){

returninputpara+"_100";

}

}

Redis

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

1、pom.xml增加依赖:

<dependency>

<groupId>org.springframework.data</groupId>

<artifactId>spring-data-redis</artifactId>

<version>1.0.6.RELEASE</version>

</dependency>

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

<?xmlversion="1.0"encoding="UTF-8"?>

<beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:context="http://www.springframework.org/schema/context"

xmlns:cache="http://www.springframework.org/schema/cache"

xmlns:p="http://www.springframework.org/schema/p"

xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-3.2.xsd

http://www.springframework.org/schema/cachehttp://www.springframework.org/schema/cache/spring-cache.xsd">

<description>Spring-cache</description>

<cache:annotation-driven/>

<beanid="cacheManager"class="org.springframework.data.redis.cache.RedisCacheManager">

<constructor-argname="template"index="0"ref="redisTemplate"/>

</bean>

<beanid="jedisPoolConfig"class="redis.clients.jedis.JedisPoolConfig">

<propertyname="maxActive"value="${redis.pool.maxActive}"/>

<propertyname="maxIdle"value="${redis.pool.maxIdle}"/>

<propertyname="maxWait"value="${redis.pool.maxWait}"/>

<propertyname="testOnBorrow"value="${redis.pool.testOnBorrow}"/>

</bean>

<!--工厂实现:-->

<beanid="jedisConnectionFactory"class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">

<propertyname="hostName"value="${redis.ip}"/>

<propertyname="port"value="${redis.port}"/>

<propertyname="poolConfig"ref="jedisPoolConfig"/>

</bean>

<!--模板类:-->

<beanid="redisTemplate"class="org.springframework.data.redis.core.RedisTemplate"p:connection-factory-ref="jedisConnectionFactory">

<propertyname="keySerializer"ref="stringRedisSerializer"/>

</bean>

<beanid="stringRedisSerializer"class="org.springframework.data.redis.serializer.StringRedisSerializer"/>

</beans>

3、缓存写入参考实现:

@Service

publicclassBrandBaseServiceImplimplementsIBrandBaseService{

@Override

@Cacheable(value=CacheClientConstant.COMMODITY_BRAND_REDIS_CACHE,key="'commodity:webservice:all:brand:list'")

publicList<Brand>getAllBrands(){

try

{

List<Brand>brands=brandMapper.getAllBrands();

returnbrands;

}catch(Exceptionex)

{

logger.error(ex.toString());

returnnull;

}

}

@Override

@Cacheable(value=CacheClientConstant.COMMODITY_BRAND_REDIS_CACHE,key="'commodity:webservice:brand:no:'+#brandNo")

publicBrandgetBrandByNo(StringbrandNo){

if(StringUtils.isBlank(brandNo))

returnnull;

returnbrandMapper.getBrandByNo(brandNo);

}

}

4、缓存清除参考实现:

@Service

publicclassRedisCacheUtil{

privatefinalLoggerlogger=LoggerFactory.getLogger(this.getClass());

@Autowired

privateRedisTemplate<String,Object>redisTemplate;

@Autowired

privateJedisConnectionFactoryjedisConnectionFactory;

@CacheEvict(value=CacheClientConstant.COMMODITY_CATEGORY_REDIS_CACHE,key="'commodity:webservice:category:no:'+#categoryNo")

publicvoidcleanCatCacheByNo(StringcategoryNo)

{

List<String>keys=newArrayList<String>();

logger.info("[商品服务端]清理分类categoryNo:{}缓存,REDISSERVER地址:{}",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)

publicvoidcleanSystemConfigAll()

{

logger.info("[商品服务端]清楚SystemConfig缓存");

}

/**

*考虑到主从延迟可能会导致缓存更新失效,延迟再清理一次缓存

*@paramkeys需要清除缓存的KEY

*/

privatevoidcleanAgain(List<String>keys){

if(CollectionUtils.isEmpty(keys)){

return;

}

for(Stringkey:keys){

logger.info("清理缓存,KEY:{}",key);

redisTemplate.delete(key);

}

}

}

RabbitMQ

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

1、pom.xml增加依赖:

<dependency>

<groupId>org.springframework.amqp</groupId>

<artifactId>spring-amqp</artifactId>

<version>${spring.amqp.version}</version>

</dependency>

<dependency>

<groupId>org.springframework.amqp</groupId>

<artifactId>spring-rabbit</artifactId>

<version>${spring.amqp.version}</version>

</dependency>

2、发送消息代码例子:

@Service

publicclassMessageSendServiceImplimplementsIMessageSendService{

privatestaticfinalStringEXCHANGE="amq.topic";

@Autowired

privatevolatileRabbitTemplaterabbitTemplate;

privatefinalLoggerlogger=LoggerFactory.getLogger(this.getClass());

@Override

publicBooleansendMessage(StringcommodityNo){

Commodityc=getCommodity(commodityNo);

//发送rabbitMQ消息(topic)

rabbitTemplate.convertAndSend(EXCHANGE,"commodity.update.topic",c);

logger.info("发送消息成功(topic):商品编号:"+commodityNo);

returntrue;

}

}

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

<?xmlversion="1.0"encoding="UTF-8"?>

<beansxmlns="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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd

http://www.springframework.org/schema/rabbithttp://www.springframework.org/schema/rabbit/spring-rabbit-1.1.xsd

http://www.springframework.org/schema/taskhttp://www.springframework.org/schema/task/spring-task.xsd

http://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd">

<!--定义rabbitmq连接工厂,生产环境使用集群配置,支持failover,rabbitmq.host=192.168.211.230:5672-->

<rabbit:connection-factoryid="connectionFactory"addresses="${rabbitmq.host}"/>

<rabbit:adminconnection-factory="connectionFactory"/>

<rabbit:templateid="amqpTemplate"connection-factory="connectionFactory"channel-transacted="true"

message-converter="jsonMessageConverter"/>

<beanid="jsonMessageConverter"class="org.springframework.amqp.support.converter.JsonMessageConverter">

<propertyname="classMapper">

<beanclass="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:queueid="queue的id,可以和name一样"name="queue的名字,在rabbitmq控制台可以看到,例如commodity.update.topic.queue">

<rabbit:queue-arguments>

<entrykey="x-ha-policy"value="all"/>

</rabbit:queue-arguments>

</rabbit:queue>

<!--CONSUMER-->

<!--这里的error-handler最好都配置,因为rabbitmq报的异常默认是不被捕获的,如果这里没有error-handler,log级别又没指定到amqp的包,那么错误将不会被察觉-->

<rabbit:listener-containerconnection-factory="connectionFactory"message-converter="jsonMessageConverter"

channel-transacted="true"error-handler="rabbitMqErrorHandler"concurrency="10"

auto-startup="true">

<rabbit:listenerqueues="rabbit:queue中定义的name或者id"ref="commodityUpdateListener"method="handleMessage"/>

</rabbit:listener-container>

<rabbit:topic-exchangename="amq.topic">

<rabbit:bindings>

<!--这里的queue是<rabbit:queue里的ID-->

<rabbit:bindingpattern="发送方的routingKey,对于上面的发送就是commodity.update.topic"queue="queue的名字,在rabbitmq控制台可以看到,例如commodity.update.topic.queue"/>

</rabbit:bindings>

</rabbit:topic-exchange>

</beans>

4、接收消息代码例子:

@Component

publicclassCommodityUpdateListener{

publicvoidhandleMessage(Commoditycommodity){

if(commodity==null)

{

logger.info("XXX");

return;

}

//处理逻辑

}

}

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

@Component

publicclassRabbitMqErrorHandlerimplementsErrorHandler{

privatestaticLoggerlogger=LoggerFactory.getLogger(RabbitMqErrorHandler.class);

@Override

publicvoidhandleError(Throwablet){

logger.error("Receiverabbitmqmessageerror:{}",t);

}

}

MyBatis

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

大致配置步骤如下:

1、pom.xml增加依赖:

<dependency>

<groupId>org.mybatis</groupId>

<artifactId>mybatis-spring</artifactId>

<version>1.1.1</version>

</dependency>

<dependency>

<groupId>org.mybatis.caches</groupId>

<artifactId>mybatis-ehcache</artifactId>

<version>1.0.1</version>

</dependency>

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

<?xmlversion="1.0"encoding="UTF-8"?>

<beansxmlns="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/beanshttp://www.springframework.org/schema/beans/spring-beans-3.2.xsd

http://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx-3.2.xsd

http://www.springframework.org/schema/jeehttp://www.springframework.org/schema/jee/spring-jee-3.2.xsd

http://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-3.2.xsd

http://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-3.2.xsd">

<description>Spring公共配置</description>

<!--开启注解-->

<context:annotation-config/>

<!--开启自动切面代理-->

<aop:aspectj-autoproxy/>

<context:component-scanbase-package="com.xx">

<context:exclude-filtertype="annotation"expression="org.springframework.stereotype.Controller"/>

</context:component-scan>

<!--定义受环境影响易变的变量-->

<beanclass="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">

<propertyname="systemPropertiesModeName"value="SYSTEM_PROPERTIES_MODE_OVERRIDE"/>

<propertyname="ignoreResourceNotFound"value="true"/>

<propertyname="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>

<!--propertyname="ignoreUnresolvablePlaceholders"value="true"/-->

</bean>

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

<beanid="transactionManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

<propertyname="dataSource"ref="dataSourcejdbc"/>

</bean>

<!--强烈建议用JdbcTemplate代替JdbcUtils-->

<beanid="jdbcTemplate"class="org.springframework.jdbc.core.JdbcTemplate">

<propertyname="dataSource"ref="dataSourcejdbc"/>

</bean>

<beanid="sqlSessionFactoryWrite"class="org.mybatis.spring.SqlSessionFactoryBean">

<propertyname="dataSource"ref="dataSourcejdbc"/>

</bean>

<!--会自动将basePackage中配置的包路径下的所有带有@Mapper标注的Dao层的接口生成代理,替代原来我们的Dao实现。-->

<beanclass="org.mybatis.spring.mapper.MapperScannerConfigurer">

<propertyname="sqlSessionFactory"ref="sqlSessionFactoryWrite"/>

<propertyname="basePackage"value="com/xx/pc/template"/>

</bean>

<beansprofile="production">

<beanid="dataSourcejdbc"class="org.springframework.jndi.JndiObjectFactoryBean">

<propertyname="jndiName"value="java:/MySqlDS_JDBC"/>

</bean>

</beans>

<beansprofile="dev">

<beanid="dataSourcejdbc"class="org.springframework.jdbc.datasource.DriverManagerDataSource">

<propertyname="driverClassName"value="com.mysql.jdbc.Driver"/>

<propertyname="url"value="jdbc:mysql://ip:3306/dbname?characterEncoding=utf-8"/>

<propertyname="username"value="root"/>

<propertyname="password"value="root"/>

</bean>

</beans>

</beans>

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

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

相关推荐