企业级Spring最佳实践 - 项目配置


  • 项目目录
  • 项目依赖
  • Smart Logging
  • Jetty 和 Tomcat
  • Spring 配置文件
  • 完成 Maven 配置
  • 常用的 Maven 命令


项目目录

Production
src/main/java - JAVA源码包目录和类
src/main/resources - JAVA资源目录(例如:*.properties 文件,spring 配置文件等)

Test
src/test/java - 测试代码包和类
src/test/resources - 测试资源目录(例如:*.properties 文件,spring 配置文件等)

项目目录结构例子:

── pom.xml
└── src
    ├── main
    │   ├── java
    │   │   └── com
    │   │       └── gordondickens
    │   │           └── sample
    │   │               ├── domain
    │   │               │   └── MyDomain.java
    │   │               ├── repository
    │   │               │   └── MyDomainRepository.java
    │   │               ├── service
    │   │               │   ├── MyDomainService.java
    │   │               │   └── internal
    │   │               │       └── MyDomainServiceImpl.java
    │   │               └── web
    │   │                   └── MyDomainController.java
    │   ├── resources
    │   │   ├── META-INF
    │   │   │   └── spring
    │   │   │       ├── applicationContext.xml
    │   │   │       └── database.properties
    │   │   ├── logback-access.xml
    │   │   └── logback.xml
    │   └── webapp
    │       ├── WEB-INF
    │       │   ├── classes
    │       │   ├── i18n
    │       │   ├── layouts
    │       │   ├── spring
    │       │   │   └── webmvc-config.xml
    │       │   ├── views
    │       │   │   ├── myDomain
    │       │   │   │   ├── create.jsp
    │       │   │   │   ├── list.jsp
    │       │   │   │   ├── show.jsp
    │       │   │   │   └── update.jsp
    │       │   │   ├── dataAccessFailure.jsp
    │       │   │   ├── index.jsp
    │       │   │   ├── resourceNotFound.jsp
    │       │   │   ├── uncaughtException.jsp
    │       │   │   └── views.xml
    │       │   └── web.xml
    │       ├── images
    │       └── styles
    ├── site
    │   ├── apt
    │   ├── fml
    │   ├── site.xml
    │   └── xdoc
    └── test
        ├── java
        │   └── com
        │       └── gordondickens
        │           └── sample
        │               └── service
        │                   └── MyDomainServiceTests.java
        └── resources
            ├── com
            │   └── gordondickens
            │       └── sample
            │           └── service
            │               └── MyDomainServiceTests-context.xml
            └── logback-test.xml

项目依赖

我是 maven 的超级粉丝,它提供了一个一致的构建架构,并且拥有很多插件。
Gradle 是一个新兴的基于 Groovy 的构建工具,它支持maven架构。如果你仍旧在使用Ant,
我强烈建议你迁移到Maven或者Gradle上来。企业构建工具所面临的挑战之一是管理传递性
依赖,这里有一些建议可以面对这些挑战。

依赖版本
不要使用低于 <properties/> 节点中指定的版本号,这样更容易升级和测试新的版本;
要包括所有插件的版本号!不要依赖于 Maven 内置的父POM中的插件版本。


管理依赖
使用 Maven的 <DependencyManagement /> 节点控制显式和隐式的版本!将版本号在这里指定,
将解决传递性依赖


Enforcer Plugin (强制执行插件)
禁止直接(或间接)包含不兼容的(或旧的)jar。例如,SLF4J 1.5,1.6和SLF4J 1.7 是不能
在一起运行的,因此我们需要在项目构建中禁止带有混合依赖的版本。Spring被很多开源项目
所使用,有的会引用老的版本,因此,我们要控制在我们构建中包含的spring jar版本。


强制执行例子:
· 确保使用 JAVA 1.6
· 确保 MAVEN 的版本是 2.2.1 - 3.0.x
· 确保 spring 的 jar 是 3.1 或更新版本
· 禁止旧的 javassist, 应该是 org.javassist
· 确保没有 commons-logging 或者 commons-logging-api 依赖
· 确保没有 log4j 依赖
· 确保没有 slf4j 1.5 或者 1.6 依赖
· 禁止旧的 hsqldb,应该使用 org.hsqldb
· 禁止旧的 aspectj,应该使用 org.aspectj

<properties>
...
  <java.version>1.6</java.version>
...
  <maven.enforcer.plugin>1.2</maven.enforcer.plugin>
  <maven.version.range>[2.2.1,3.1.0)</maven.version.range>
...
</properties>
 
<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-enforcer-plugin</artifactId>
  <version>${maven.enforcer.plugin}</version>
  <executions>
    <execution>
      <id>enforce-banned-dependencies</id>
      <goals>
        <goal>enforce</goal>
      </goals>
      <configuration>
        <rules>
          <bannedDependencies>
            <searchTransitive>true</searchTransitive>
            <excludes>
              <exclude>javassist:javassist</exclude>
              <exclude>commons-logging</exclude>
              <exclude>aspectj:aspectj*</exclude>
              <exclude>hsqldb:hsqldb</exclude>
              <exclude>log4j:log4j</exclude>
              <exclude>org.slf4j:1.5*</exclude>
              <exclude>org.springframework:2.*</exclude>
              <exclude>org.springframework:3.0.*</exclude>
            </excludes>
          </bannedDependencies>
          <requireMavenVersion>
            <version>${maven.version.range}</version>
          </requireMavenVersion>
          <requireJavaVersion>
            <version>${java.version}</version>
          </requireJavaVersion>
        </rules>
        <fail>true</fail>
      </configuration>
    </execution>
  </executions>
</plugin>

Smart Logging

· 不要使用 System.out
· 不要使用 System.err
· 总是使用 SLF4j
· 总是使用 Logback
· 禁止 Apache commons logging (JCL) aka Jakarta Commons Logging
· 禁止使用 Java util logging (JUL)

在类中应该使用 SFL4J(不是 log4j、jcl、jul 和 logback):

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
...
public class MyClass {
  private static final Logger logger =
    LoggerFactory.getLogger(MyClass.class);
...
}
在下面的例子中,SLF4J提供 jar包(jcl-over-slf4j和jul-to-slf4j)用来路由 JCL 和 JUL。
Spring 使用 JCL,因此我们必须使用 jcl-over-slf4j 来处理 spring 的日志内容。
<properties>
...
  <logback.version>1.0.10</logback.version>
...
  <slf4j.version>1.7.4</slf4j.version>
...
</properties>
 
...
 
<dependencies>
...
  <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>jcl-over-slf4j</artifactId>
  </dependency>
  <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>jul-to-slf4j</artifactId>
  </dependency>
  <dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
  </dependency>
...
</dependencies>
 
...
 
<dependencyManagement>
  <dependencies>
...
   <!-- Logging with SLF4J & LogBack -->
  <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>jcl-over-slf4j</artifactId>
    <version>${slf4j.version}</version>
  </dependency>
  <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>${slf4j.version}</version>
  </dependency>
  <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>jul-to-slf4j</artifactId>
    <version>${slf4j.version}</version>
  </dependency>
  <dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>${logback.version}</version>
  </dependency>
  <dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-core</artifactId>
    <version>${logback.version}</version>
  </dependency>
  <dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-access</artifactId>
    <version>${logback.version}</version>
  </dependency>
...
  </dependencies>
</dependencyManagement>
日志配置文件
src/main/resources/logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
 
  <contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator">
    <resetJUL>true</resetJUL>
  </contextListener>
 
  <!-- To enable JMX Management -->
  <jmxConfigurator/>
 
  <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%.-1level|%-40.40logger{0}|%msg%n</pattern>
    </encoder>
  </appender>
 
  <logger name="com.mycompany.myapp" level="debug" />
  <logger name="org.springframework" level="info" />
 
  <logger name="org.springframework.beans" level="debug" />
 
  <root level="warn">
    <appender-ref ref="console" />
  </root>
</configuration>
src/main/resources/logback-test.xml
同样的配置,只不过,这个配置用于测试环境


src/main/resources/logback-access.xml
配置服务器访问日志,当在 web.xml 中使用 Logback TeeFilter时,
HttpRequest 和 HttpResponses 消息能被显示或被记录。用于 创建 RESTful 测试。

NOTE:在日志配置中使用 ${user.dir},日志文件将被创建在项目根目录下。
我们将根据这个来配置不同的生产环境。
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" />
 
  <filter class="ch.qos.logback.access.filter.CountingFilter">
    <name>countingFilter</name>
  </filter>
 
  <appender name="accessfile" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>${user.dir}/logs/app-access.log</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <fileNamePattern>${user.dir}/logs/app-access.%d{yyyy-MM-dd}.log.zip</fileNamePattern>
    </rollingPolicy>
 
    <encoder>
        <pattern>combined</pattern>
    </encoder>
  </appender>
 
  <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%n%fullRequest%n%fullResponse%n</pattern>
    </encoder>
  </appender>
 
  <appender name="reqrespfile" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>${user.dir}/logs/app-req-resp.log</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      <fileNamePattern>${user.dir}/logs/app-req-resp.%d{yyyy-MM-dd}.log.zip</fileNamePattern>
    </rollingPolicy>
 
    <encoder>
      <pattern>%n%fullRequest%n%fullResponse%n</pattern>
    </encoder>
  </appender>
 
  <appender-ref ref="accessfile" />
  <appender-ref ref="reqrespfile" />
  <appender-ref ref="console" />
</configuration>

Jetty 和 Tomcat

开发人员能根据MAVEN插件配置运行 Jetty 或 Tomcat 用于测试。
下面的插件配置了 JMX,SLF4j,Logback和 Logback Access。

运行 Jetty
$ mvn clean install jetty:run


运行 Tomcat 7
$ mvn clean install tomcat7:run

NOTE:不要使用 tomcat:run,这是老的 tomcat 插件。

<properties>
...
  <maven.jetty.plugin>8.1.10.v20130312</maven.jetty.plugin>
...
  <maven.tomcat.plugin>2.1</maven.tomcat.plugin>
...
</properties>
 
...
 
<plugins>
...
  <plugin>
  <groupId>org.apache.tomcat.maven</groupId>
  <artifactId>tomcat7-maven-plugin</artifactId>
  <version>${maven.tomcat.plugin}</version>
  <configuration>
    <systemProperties>
    <com.sun.management.jmxremote>true</com.sun.management.jmxremote>
    <com.sun.management.jmxremote.port>8050</com.sun.management.jmxremote.port>
    <com.sun.management.jmxremote.ssl>false</com.sun.management.jmxremote.ssl>
    <com.sun.management.jmxremote.authenticate>false</com.sun.management.jmxremote.authenticate>
    <java.util.logging.manager>org.apache.juli.ClassLoaderLogManager</java.util.logging.manager>
    <logback.ContextSelector>JNDI</logback.ContextSelector>
  </systemProperties>
  </configuration>
  <dependencies>
  <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>jcl-over-slf4j</artifactId>
    <version>${slf4j.version}</version>
    <scope>runtime</scope>
  </dependency>
  <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>${slf4j.version}</version>
    <scope>runtime</scope>
  </dependency>
  <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>jul-to-slf4j</artifactId>
    <version>${slf4j.version}</version>
    <scope>runtime</scope>
  </dependency>
  <dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>${logback.version}</version>
    <scope>runtime</scope>
  </dependency>
  <dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-access</artifactId>
    <version>${logback.version}</version>
    <scope>runtime</scope>
  </dependency>
  </dependencies>
  </plugin>
  <plugin>
    <groupId>org.mortbay.jetty</groupId>
    <artifactId>jetty-maven-plugin</artifactId>
    <version>${maven.jetty.plugin}</version>
    <configuration>
      <webAppConfig>
        <contextPath>/${project.name}</contextPath>
      </webAppConfig>
      <stopPort>9966</stopPort>
      <stopKey>shutterdown</stopKey>
      <requestLog implementation="ch.qos.logback.access.jetty.RequestLogImpl">
        <fileName>./src/main/resources/logback-access.xml</fileName>
      </requestLog>
      <systemProperties>
        <systemProperty>
          <name>logback.configurationFile</name>
          <value>./src/main/resources/logback.xml</value>
        </systemProperty>
        <systemProperty>
          <name>com.sun.management.jmxremote</name>
          <value>true</value>
        </systemProperty>
        <systemProperty>
          <name>com.sun.management.jmxremote.port</name>
          <value>8050</value>
        </systemProperty>
        <systemProperty>
          <name>com.sun.management.jmxremote.ssl</name>
          <value>false</value>
        </systemProperty>
        <systemProperty>
          <name>com.sun.management.jmxremote.authenticate</name>
          <value>false</value>
        </systemProperty>
      </systemProperties>
    </configuration>
    <dependencies>
      <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>jcl-over-slf4j</artifactId>
        <version>${slf4j.version}</version>
        <scope>runtime</scope>
      </dependency>
      <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>${slf4j.version}</version>
        <scope>runtime</scope>
      </dependency>
      <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>jul-to-slf4j</artifactId>
        <version>${slf4j.version}</version>
        <scope>runtime</scope>
      </dependency>
      <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>${logback.version}</version>
        <scope>runtime</scope>
      </dependency>
      <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-access</artifactId>
        <version>${logback.version}</version>
        <scope>runtime</scope>
      </dependency>
    </dependencies>
  </plugin>
...
</plugins>
Logback 助手配置(web.xml)
能查看 Logback 状态

...
  <servlet>
    <servlet-name>ViewStatusMessages</servlet-name>
    <servlet-class>ch.qos.logback.classic.ViewStatusMessagesServlet</servlet-class>
  </servlet>
 
  <servlet-mapping>
    <servlet-name>ViewStatusMessages</servlet-name>
    <url-pattern>/logbackStatus</url-pattern>
  </servlet-mapping>
...
使用 Logback Tee Filter 过滤记录 HttpRequest 和 HttpResponse 的数据
...
  <filter>
    <filter-name>TeeFilter</filter-name>
    <filter-class>ch.qos.logback.access.servlet.TeeFilter</filter-class>
  </filter>
 
  <filter-mapping>
    <filter-name>TeeFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
...

Spring 配置文件

使用一致的文件命名规则,所有配置文件拥有相同的前缀,例如:applicationConfig*.xml。
(译注:我一直使用 app-*.xml)
例如:app-bootstrap.xml,app-database.xml,app-cache.xml,app-security.xml ……

在后面的博客中,我将具体说说企业级spring配置最佳实践。

配置目录:
src/main/resources/META-INF/spring - Spring XML 配置目录
src/main/webapp/WEB-INF/spring - Spring MVC 配置目录

完成 Maven 配置

Maven最佳实践配置文件 POM.xml 

MAVEN 最佳实践配置文件的特点:
· 所有的版本号在 properties 节点中配置;
· DependencyManagement 节点控制依赖传递;
· 所有的带版本号的插件在 Plugin Management 节点中定义;
· 强制执行插件会停止构建不兼容的依赖;
· 使用 site 插件配置带有公用报告的插件;
· Eclipse 插件使用新的 Eclipse 的 Maven插件,旧的是 Sonatype;
· Idea(IntelliJ)插件已经过时,不包括在内;
· 版本号插件检查依赖包和插件的更新

常用的 Maven 命令

显示更新依赖
$ mvn versions:display-dependency-updates

显示插件更新
$ mvn versions:display-plugin-updates

显示依赖树
$ mvn dependency:tree -Ddetail

显示依赖列表
$ mvn dependency:list

显示有效的POM
$ mvn help:effective-pom

显示项目设置
$ mvn help:effective-settings

显示系统和环境变量
$ mvn help:system

显示构建的类路径(Class Path)
$ mvn dependency:build-classpath

原文:Enterprise Spring Best Practices – Part 1 – Project Config
作者:
Gordon

你可能感兴趣的:(spring,maven,logback,slf4j,最佳实践)