楼主: 程序猿闯子

[互联网技术] 约定大于配置--实战

发表于 2014-8-26 18:14:12

本文同步至http://www.waylau.com/convention-over-configuration-in-action/

约定优于配置是一个简单的概念。 系统,类库,框架应该假定合理的默认值,而非要求提供不必要的配置。 在大部分情况下,你会发现使用框架提供的默认值会让你的项目运行的更快。

零配置并不是完全没有配置,而是通过约定来减少配置, 减少 XML

约定代码结构或命名规范来减少配置数量

如果模型中有个名为Sale的类,那么数据库中对应的表就会默认命名为sale。只有在偏离这一约定时,例如将该表命名为"products_sold”,才需写有关这个名字的配置。

比如 EJB3 持久化,将一个特殊的Bean持久化,你所需要做的只是将这个类标注为 @Entity 。 框架将会假定表名和列名是基于类名和属性名。 系统也提供了一些钩子,当有需要的时候你可以重写这些名字.

比如 maven 项目约定,在没有自定义的情况下,源代码假定是在 /workspace/content-zh/src/main/java,资源文件假定是在/workspace/content-zh/src/main/resources
。测试代码假定是在 /workspace/content-zh/src/test。项目假定会产生一个 JAR 文件。Maven假定你想要把编译好的字节码放到 /workspace/content-zh/target/classes 并且在 /workspace/content-zh/target 创建一个可分发的 JAR 文件。Maven 对约定优于配置的应用不仅仅是简单的目录位置,Maven 的核心插件使用了一组通用的约定,以用来编译源代码,打包可分发的构件,生成 web 站点,还有许多其他的过程。 Maven 的力量来自它的"武断”,它有一个定义好的生命周期和一组知道如何构建和装配软件的通用插件。如果你遵循这些约定,Maven 只需要几乎为零的工作——仅仅是将你的源代码放到正确的目录,Maven 将会帮你处理剩下的事情。

比如 HTML5 Boilerplate,为App的默认模板以及文件路径规范,无论是网站或者富UI的App,都可以采用这个模板作为起步。HTML5 Boilerplate的模板核心部分不过30行,但是每一行都可谓千锤百炼,可以用最小的消耗解决一些前端的顽固问题:

Boilerplate

采用更简洁的配置方式来替代XML

比如:hibernate.properties的 c3p0 方式:

  1. hibernate.connection.driver_class = org.postgresql.Driver
  2. hibernate.connection.url = jdbc:postgresql://localhost/mydatabase
  3. hibernate.connection.username = myuser
  4. hibernate.connection.password = secret
  5. hibernate.c3p0.min_size=5
  6. hibernate.c3p0.max_size=20
  7. hibernate.c3p0.timeout=1800
  8. hibernate.c3p0.max_statements=50
  9. hibernate.dialect = org.hibernate.dialect.PostgreSQL82Dialect
复制代码

比如:hibernate.properties 的 JNDI 方式:

  1. hibernate.connection.datasource = java:/comp/env/jdbc/test
  2. hibernate.transaction.factory_class = \org.hibernate.transaction.JTATransactionFactory
  3. hibernate.transaction.manager_lookup_class = \ org.hibernate.transaction.JBossTransactionManagerLookup
  4. hibernate.dialect = org.hibernate.dialect.PostgreSQL82Dialect
复制代码

比如 Apache Shiro 的 ini 配置

  1. [users]
  2. root = secret, admin
  3. guest = guest, guest
  4. presidentskroob = 12345, president
  5. darkhelmet = ludicrousspeed, darklord, schwartz
  6. lonestarr = vespa, goodguy, schwartz
  7. [roles]
  8. admin = *
  9. schwartz = lightsaber:*
  10. goodguy = winnebago:drive:eagle5
复制代码

Hibernate通过代码配置

  1. Configuration cfg = new Configuration()
  2. .addClass(org.hibernate.auction.Item.class)
  3. .addClass(org.hibernate.auction.Bid.class)
  4. .setProperty("hibernate.dialect", "org.hibernate.dialect.MySQLInnoDBDialect")
  5. .setProperty("hibernate.connection.datasource", "java:comp/env/jdbc/test")
  6. .setProperty("hibernate.order_updates", "true");
复制代码

Gradle 替代 Maven

采用 Maven

  1. <dependency>
  2. <groupId>org.hibernate</groupId>
  3. <artifactId>hibernate-core</artifactId>
  4. <version>4.3.6.Final</version>
  5. </dependency>
复制代码

采用 Gradle 只需一行

  1. org.hibernate:hibernate-core:4.3.6.Final
复制代码

通过注解约定其含义来减少配置数量

spring注解

Spring会自动搜索某些路径下的Java类,并将这些java类注册为Bean实例,这样就省去了将所有 bean 都在 XML 配置。

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans
  6. http://www.springframework.org/schema/beans/spring-beans.xsd
  7. http://www.springframework.org/schema/context
  8. http://www.springframework.org/schema/context/spring-context.xsd">
  9. <context:component-scan base-package="com.waylau.rest"/>
  10. </beans>
复制代码

注意:如果配置了 那么 标签就可以不用再xml中配置了,因为前者包含了后者。另外 还提供了两个子标签 用来控制扫描文件的颗粒度,例如

  1. <beans>
  2. <context:component-scan base-package="com.waylau.rest">
  3. <context:include-filter type="regex" expression=".*Stub.*Repository"/>
  4. <context:exclude-filter type="annotation"expression="org.springframework.stereotype.Repository"/>
  5. </context:component-scan>
  6. </beans>
复制代码

代码实现

  1. public static void main(String[] args) {
  2. AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
  3. ctx.scan("com.waylau.rest");
  4. ctx.refresh();
  5. MyService myService = ctx.getBean(MyService.class);
  6. }
复制代码

Spring会合适的将显示指定路径下的类全部注册成Spring Bean。 Spring通过使用特殊的Annotation来标注Bean类。

  • @Component :标注一个普通的Spring Bean类。
  • @Controller : 标注一个控制器组件类。
  • @Service : 标注一个业务逻辑组件类。
  • @Repository : 标注一个DAO组件类。

Hibernate注解

表与实体映射

  1. @Entity
  2. @Table(name="TBL_FLIGHT",
  3. schema="AIR_COMMAND",
  4. uniqueConstraints=
  5. @UniqueConstraint(
  6. name="flight_number",
  7. columnNames={"comp_prefix", "flight_number"} ) )
  8. public class Flight implements Serializable {
  9. @Column(name="comp_prefix")
  10. public String getCompagnyPrefix() { return companyPrefix; }
  11. @Column(name="flight_number")
  12. public String getNumber() { return number; }
  13. }
复制代码

甚至 SQL 也可以注解

  1. @Entity
  2. @Table(name="CHAOS")
  3. @SQLInsert( sql="INSERT INTO CHAOS(size, name, nickname, id) VALUES(?,upper(?),?,?)")
  4. @SQLUpdate( sql="UPDATE CHAOS SET size = ?, name = upper(?), nickname = ? WHERE id = ?")
  5. @SQLDelete( sql="DELETE CHAOS WHERE id = ?")
  6. @SQLDeleteAll( sql="DELETE CHAOS")
  7. @Loader(namedQuery = "chaos")
  8. @NamedNativeQuery(name="chaos", query="select id, size, name, lower( nickname ) as nickname from CHAOS where xml:id= ?", resultClass = Chaos.class)
  9. public class Chaos {
  10. @Id
  11. private Long id;
  12. private Long size;
  13. private String name;
  14. private String nickname;
  15. //...
  16. }
复制代码

Jersey 2.X 实现 REST 和 MVC

  1. @POST
  2. @Produces({"text/html”})
  3. @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
  4. @Template(name = "/short-link”) @ErrorTemplate(name = "/error-form")
  5. @Valid
  6. public ShortenedLink createLink(@NotEmpty @FormParam("link") final String link) {
  7. // ...
  8. }
复制代码

Shiro 的注解

没有注解的情况

  1. //get the current Subject
  2. Subject currentUser =
  3. SecurityUtils.getSubject();
  4. if (currentUser.hasRole(“administrator”)) {
  5. //do something in here that only a administrator?
  6. } else {
  7. //don’t do ?
  8. }
复制代码

用了注解,简洁很多

  1. @RequiresRoles( “administrator” )
  2. public void openAccount( Account acct ) {
  3. //do something in here that only a administrator
  4. }
复制代码

Struts 2.x 注解

  1. package com.waylau.actions;
  2. import com.opensymphony.xwork2.ActionSupport;
  3. import org.apache.struts2.convention.annotation.Action;
  4. import org.apache.struts2.convention.annotation.Actions;
  5. import org.apache.struts2.convention.annotation.Result;
  6. import org.apache.struts2.convention.annotation.Results;
  7. @Results({
  8. @Result(name="failure", location="fail.jsp")
  9. })
  10. public class HelloWorld extends ActionSupport {
  11. @Action(value="/different/url",
  12. results={@Result(name="success", location="http://struts.apache.org", type="redirect")}
  13. )
  14. public String execute() {
  15. return SUCCESS;
  16. }
  17. @Action("/another/url")
  18. public String doSomething() {
  19. return SUCCESS;
  20. }
  21. }
复制代码

参考

  • Spring
  • Hibernate
  • Jersey
  • Apache Shiro
  • Apache Struts
  • http://www.waylau.com/build-java-project-with-maven/
  • https://github.com/waylau/Gradle-2-User-Guide