项目目录
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。
<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>日志配置文件
<?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
<?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)
... <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