投资菜地

屋后的一亩三分绿色菜园地

2017-01-17

JAVA Web 开发

Configuration

我们可以用@Configuration来声明一个配置类,用来替换原来的xml配置。@Configuration本身使用了@Component进行注解。所以用@Configuration注解的类可以被组件扫描到并注入到容器中。

了实现组件自动扫描和实例化并注入到容器中,我们要在配置类上加上@ComponnetScan注解,并设定扫描的包的范围。默认@SpringBootApplication会扫描所有包中的类。

Feign

在JAVA项目中接口调用的方式:

  1. Httpclient
  2. Okhttp
  3. Httpurlconnection
  4. RestTemplate
  5. Feign

Feign是一个声明式的REST客户端,它的目的就是让REST调用更加简单。FeignClient简化了请求的编写,且通过动态负载进行选择要使用哪个服务进行消费,而这一切都由Spring动态配置实现,我们不用关心这些,只管使用方法即可。Feign可以与Eureka和Ribbon组合使用以支持负载均衡。

Spring Cloud Ribbon

Spring Cloud Ribbon 是一个基于Http和TCP的客服端负载均衡工具,它是基于Netflix Ribbon实现的。它不像服务注册中心、配置中心、API网关那样独立部署,但是它几乎存在于每个微服务的基础设施中。包括前面的提供的声明式服务调用也是基于该Ribbon实现的。理解Ribbon对于我们使用Spring Cloud来讲非常的重要,因为负载均衡是对系统的高可用、网络压力的缓解和处理能力扩容的重要手段之一。在上节的例子中,我们采用了声明式的方式来实现负载均衡。实际上,内部调用维护了一个RestTemplate对象,该对象会使用Ribbon的自动化配置,同时通过@LoadBalanced开启客户端负载均衡。其实RestTemplate是Spring自己提供的对象,不是新的内容。读者不知道RestTemplate可以查看相关的文档。

Hystrix

Hystrix是Netflix针对微服务分布式系统的熔断保护中间件,当我们的客户端连接远程的微服务时,有两种情况需要考虑:

首先,如果远程系统当机了我们怎么办?

其次,我们如何管理对远程微服务的调用性能,以保证每个微服务以最小延迟最快性能响应?

Hystrix是一个有关延迟和失败容错的开源库包,用来设计隔离访问远程系统端点或微服务等,防止级联爆炸式的失败,也就是由一个小问题引起接二连三扩大的疯狂的错误爆炸直至整个系统瘫痪,能够让复杂的分布式系统更加灵活具有弹性。

想象这样一个情况:一个电子商务网站因为在黑色周五发生过载,因为过多访问负载,在线支付延迟半天都没有响应,用户的支付因为太多并发请求导致响应时间过长,在等待很长时间以后最终失败。这样的错误导致退出购物车的用户重新刷新或者再次尝试支付,更加增加了服务器的负载,如同滚雪球一样爆炸,导致更多长时间等待线程和网络拥塞。

Swagger2

Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。

通过@Configuration注解,表明它是一个配置类,@EnableSwagger2 注解开启swagger2。 apiInfo() 方法配置一些基本的信息。createRestApi() 方法指定扫描的包会生成文档, 默认是显示所有接口,可以用@ApiIgnore注解标识该接口不显示。

数据库访问

数据库访问最核心的是DataSource,我们可以使用Druid配置数据源。

Druid Spring Boot Starter

  • JDBC 配置
    # Druid 数据源配置,继承spring.datasource.* 配置,相同则覆盖
    spring.datasource.druid.url= # 或spring.datasource.url= 
    spring.datasource.druid.username= # 或spring.datasource.username=
    spring.datasource.druid.password= # 或spring.datasource.password=
    spring.datasource.druid.driver-class-name= #或 spring.datasource.driver-class-name=
    # Druid 数据源 1 配置,继承spring.datasource.druid.* 配置,相同则覆盖
    spring.datasource.druid.one.max-active=10
    spring.datasource.druid.one.max-wait=10000
    # Druid 数据源 2 配置,继承spring.datasource.druid.* 配置,相同则覆盖
    spring.datasource.druid.two.max-active=20
    spring.datasource.druid.two.max-wait=20000
    
  • 连接池配置
spring.datasource.druid.initial-size=
spring.datasource.druid.max-active=
spring.datasource.druid.min-idle=
spring.datasource.druid.max-wait=
spring.datasource.druid.pool-prepared-statements=
spring.datasource.druid.max-pool-prepared-statement-per-connection-size= 
spring.datasource.druid.max-open-prepared-statements= #和上面的等价
spring.datasource.druid.validation-query=
spring.datasource.druid.validation-query-timeout=
spring.datasource.druid.test-on-borrow=
spring.datasource.druid.test-on-return=
spring.datasource.druid.test-while-idle=
spring.datasource.druid.time-between-eviction-runs-millis=
spring.datasource.druid.min-evictable-idle-time-millis=
spring.datasource.druid.max-evictable-idle-time-millis=
spring.datasource.druid.filters= #配置多个英文逗号分隔
  • 其它配置
# 监控配置
spring.datasource.druid.web-stat-filter.enabled= #是否启用StatFilter默认值true
# 配置StatFilter 
spring.datasource.druid.filter.stat.db-type=h2
spring.datasource.druid.filter.stat.log-slow-sql=true
spring.datasource.druid.filter.stat.slow-sql-millis=2000
# 配置WallFilter 
spring.datasource.druid.filter.wall.enabled=true
spring.datasource.druid.filter.wall.db-type=h2
spring.datasource.druid.filter.wall.config.delete-allow=false
spring.datasource.druid.filter.wall.config.drop-table-allow=false

在配置类中获取DataSource

@Primary
@Bean(name = "writeDataSource")
@ConfigurationProperties("spring.datasource.druid.write")
public DataSource dataSourceWrite(){
    return DruidDataSourceBuilder.create().build();
}
//对比正常获取DataSource

@Bean(name="devWriteDataSource")
@Profile("dev")
@ConfigurationProperties("spring.datasource")
public DataSource dataSourceDev() {
    return DataSourceBuilder
        .create()
        .url(generateDevUrl())
        .build();
}

有了DataSource后就可以使用其它ORM框架实现DAO层,比如:MyBatis。

MyBatis

每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为中心的。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。在mybatis-spring整合时使用SqlSessionFactoryBean替代SqlSessionFactoryBuilder来创建SqlSessionFactory。

SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先定制的 Configuration 的实例构建出 SqlSessionFactory 的实例。

@Bean(name = "writeSqlSessionFactory")
@Primary
public SqlSessionFactory masterSqlSessionFactory(@Qualifier("writeDataSource") DataSource masterDataSource)
    throws Exception {
    final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
    PathMatchingResourcePatternResolver pr=new PathMatchingResourcePatternResolver();
    sessionFactory.setDataSource(masterDataSource);
    sessionFactory.setConfigLocation(pr.getResource(config_location));
    sessionFactory.setMapperLocations(pr.getResources(mapper_location));
    return sessionFactory.getObject();
}

在获得SqlSessionFactory 方法中指定了上面配置的DataSource,即:@Qualifier(“writeDataSource”)。

或者使用SqlSessionFactoryBuilder 构建出 SqlSessionFactory 的实例。

@Bean(name = "otherSqlSessionFactory")
public SqlSessionFactory testSqlSessionFactory(@Qualifier("writeDataSource") DataSource dataSource)
    throws IOException {
    String resource = "mybatis-config.xml";
    InputStream reader = Resources.getResourceAsStream(resource);
    SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
    SqlSessionFactory factory = builder.build(reader);
    return factory;
}

SQL 映射配置

单个参数直接使用方法的参数名,比如:#{id} ,后面可以通过逗号指定类型和模式。

<select id="selectPerson" parameterType="int" resultType="hashmap">
  SELECT * FROM PERSON WHERE ID = #{id,jdbcType=INTEGER,mode=IN}
</select>

多个参数不能直接使用方法的参数名,可以使用#{index},比如:#{0} 代表第一个参数,或使用#{arg0}。

<select id="selectPerson" parameterType="int" resultType="hashmap">
  SELECT * FROM PERSON WHERE ID = #{arg0,jdbcType=INTEGER,mode=IN}
</select>

如果非要使用参数名,可以使用Map封装多参数:

public HashMap getSomethingList(HashMap map); 

或者使用@Param注解方法中的参数:

Something getSomethingByTypeAndCode(@Param(value = "type") int queryType, @Param(value = "code") String code);

对于List参数:

public List<XXXBean> getXXXBeanList(List<String> list);  

<select id="getXXXBeanList" resultType="XXBean">
  select 字段... from XXX where id in
  <foreach item="item" index="index" collection="list" open="(" separator="," close=")">  
    #{item}  
  </foreach>  
</select>  

相关资料参考官网文档

Maven管理多模块项目

多模块项目是指一个应用中包含多个module。一般来说,一个应用单独部署成服务,只是打包的时候,maven会把各个module组合在一起。各模块一般单独打成jar放到lib目录中,当然web应用也生成war包。

maven对多模块项目的管理大概是这样的,它存在一个parent模块,但实际没有程序代码,只包含一个pom.xml,该pom是用来给子模块来引用的。

目录结构与下面的类似:

simple-parent 
+-simple-weather 
     +-src 
     +-target 
     \-pom.xml 
+-simple-webapp 
     +-src 
     +-target 
     \-pom.xml 
\-pom.xml  

各个pom.xml的内容大致如下:

pom.xml:

Xml代码

  1. org.sonatype.mavenbook.multi
  2. parent
  3. 0.8-SNAPSHOT
  4. pom
  5. simple-weather
  6. simple-webapp
  7. </modules>
  8. velocity
  9. velocity
  10. 1.5
  11. </dependency>
  12. </dependencies>

simple-weather/pom.xml:

Xml代码

  1. org.sonatype.mavenbook.multi
  2. simple-parent
  3. 0.8-SNAPSHOT
  4. </parent>
  5. junit
  6. junit
  7. 3.8.1
  8. test
  9. </dependency>
  10. </dependencies>

simple-webapp/pom.xml:

Xml代码

  1. org.sonatype.mavenbook.multi
  2. simple-parent
  3. 0.8-SNAPSHOT
  4. </parent>
  5. org.apache.geronimo.specs
  6. geronimo-servlet_2.4_spec
  7. 1.1.1
  8. </dependency>
  9. </dependencies>

如果按父pom.xml打包,会输出 simple-weather.jar,simple-webapp.war两个包; 如果按simple-weather/pom.xml打包,则只会输出 simple-weather.jar;
如果按simple-webapp/pom.xml打包,则只会输出 simple-webapp
.war。 另外,子模块会继承父模块的包依赖,使用mvn dependency:tree可以查看各个模块的包依赖列表,simple-weather,simple-webapp项目都有引用到 velocity包。
虽然这是一个application下包含了多个module的结构,但是在eclipse中,还是得对每个子module单独建project来管理源码。 具体可以分别在simple-weather、simple-webapp目录下使用mvn eclipse:eclipse来创建eclipse project, 创建完毕后,你就可以在文件.classpath中看到,包依赖关系已经按照pom.xml中的配置自动生成了。

开源项目(更多)