最佳实践 - 独立使用
使用 LCGYL Framework 独立构建应用的最佳实践。
项目结构
推荐的目录结构
my-app/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/example/app/
│ │ │ ├── Application.java # 应用主类
│ │ │ ├── config/ # 配置类
│ │ │ │ ├── DataSourceConfig.java
│ │ │ │ └── WebConfig.java
│ │ │ ├── controller/ # 控制器
│ │ │ │ └── UserController.java
│ │ │ ├── service/ # 服务层
│ │ │ │ └── UserService.java
│ │ │ ├── repository/ # 数据访问层
│ │ │ │ └── UserRepository.java
│ │ │ ├── model/ # 实体类
│ │ │ │ └── User.java
│ │ │ ├── event/ # 事件
│ │ │ │ └── UserCreatedEvent.java
│ │ │ └── listener/ # 事件监听器
│ │ │ └── UserEventListener.java
│ │ └── resources/
│ │ ├── application.properties # 配置文件
│ │ ├── logback.xml # 日志配置
│ │ └── static/ # 静态资源
│ └── test/
│ └── java/
│ └── com/example/app/
│ └── service/
│ └── UserServiceTest.java
├── build.gradle # Gradle 配置
└── README.md依赖注入
使用构造器注入
java
// ✅ 推荐:构造器注入
@Component
public class UserService {
private final UserRepository userRepository;
private final EventBus eventBus;
@Inject
public UserService(UserRepository userRepository, EventBus eventBus) {
this.userRepository = userRepository;
this.eventBus = eventBus;
}
}
// ❌ 不推荐:字段注入
@Component
public class UserService {
@Inject
private UserRepository userRepository;
@Inject
private EventBus eventBus;
}优点:
- 依赖明确
- 易于测试
- 支持 final 字段
- 避免循环依赖
避免循环依赖
java
// ❌ 错误:循环依赖
@Component
public class ServiceA {
@Inject
private ServiceB serviceB;
}
@Component
public class ServiceB {
@Inject
private ServiceA serviceA;
}
// ✅ 正确:使用事件解耦
@Component
public class ServiceA {
@Inject
private EventBus eventBus;
public void doSomething() {
eventBus.publish(new SomeEvent());
}
}
@Component
public class ServiceB {
@EventListener
public void onEvent(SomeEvent event) {
// 处理事件
}
}配置管理
配置文件组织
resources/
├── application.properties # 通用配置
├── application-dev.properties # 开发环境
├── application-test.properties # 测试环境
└── application-prod.properties # 生产环境使用环境变量
properties
# application.properties
datasource.url=${DB_URL:jdbc:mysql://localhost:3306/db}
datasource.username=${DB_USERNAME:root}
datasource.password=${DB_PASSWORD:password}
app.name=${APP_NAME:My Application}
app.version=${APP_VERSION:1.0.0}配置类型安全
java
// ✅ 推荐:使用配置类
@ConfigurationProperties(prefix = "datasource")
public class DataSourceConfig {
private String url;
private String username;
private String password;
private int maxPoolSize = 20;
// Getters and Setters
}
// 使用
@Component
public class DataSourceProvider {
@Inject
private DataSourceConfig config;
public DataSource dataSource() {
return DataSourceBuilder.create()
.url(config.getUrl())
.username(config.getUsername())
.password(config.getPassword())
.maxPoolSize(config.getMaxPoolSize())
.build();
}
}异常处理
统一异常处理
java
@Component
public class GlobalExceptionHandler {
@ExceptionHandler(NotFoundException.class)
public ErrorResponse handleNotFound(NotFoundException e) {
return ErrorResponse.builder()
.status(404)
.message(e.getMessage())
.build();
}
@ExceptionHandler(BusinessException.class)
public ErrorResponse handleBusiness(BusinessException e) {
return ErrorResponse.builder()
.status(400)
.message(e.getMessage())
.code(e.getCode())
.build();
}
@ExceptionHandler(Exception.class)
public ErrorResponse handleGeneral(Exception e) {
logger.error("未处理的异常", e);
return ErrorResponse.builder()
.status(500)
.message("服务器内部错误")
.build();
}
}自定义异常
java
public class BusinessException extends RuntimeException {
private final String code;
public BusinessException(String code, String message) {
super(message);
this.code = code;
}
}
public class NotFoundException extends BusinessException {
public NotFoundException(String message) {
super("NOT_FOUND", message);
}
}事务管理
事务最佳实践
java
// ✅ 推荐:事务方法简洁
@Component
public class UserService {
@Transactional
public User createUser(User user) {
// 只包含数据库操作
return userRepository.save(user);
}
public void registerUser(User user) {
// 非事务操作
validateUser(user);
// 事务操作
User savedUser = createUser(user);
// 非事务操作
sendWelcomeEmail(savedUser);
}
}
// ❌ 不推荐:事务方法包含非数据库操作
@Component
public class UserService {
@Transactional
public User registerUser(User user) {
validateUser(user); // 不需要事务
User savedUser = userRepository.save(user);
sendWelcomeEmail(savedUser); // 不需要事务
return savedUser;
}
}事务传播
java
@Component
public class OrderService {
@Transactional
public void createOrder(Order order) {
orderRepository.save(order);
// 独立事务,不影响主事务
logService.log("订单已创建");
}
}
@Component
public class LogService {
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void log(String message) {
logRepository.save(new Log(message));
}
}性能优化
使用缓存
java
@Component
public class UserService {
private final Cache<Long, User> cache = CacheBuilder.newBuilder()
.maximumSize(1000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.build();
public User findById(Long id) {
return cache.get(id, () -> userRepository.findById(id)
.orElseThrow(() -> new NotFoundException("用户不存在"))
);
}
@Transactional
public User update(User user) {
User updated = userRepository.save(user);
cache.put(user.getId(), updated);
return updated;
}
}批量操作
java
// ❌ 不推荐:循环查询
for (Long id : ids) {
User user = userRepository.findById(id);
process(user);
}
// ✅ 推荐:批量查询
List<User> users = userRepository.findByIds(ids);
for (User user : users) {
process(user);
}异步处理
java
@Component
public class OrderService {
@Inject
private EventBus eventBus;
@Transactional
public Order createOrder(Order order) {
Order saved = orderRepository.save(order);
// 异步发送通知
eventBus.publishAsync(new OrderCreatedEvent(saved));
return saved;
}
}
@Component
public class NotificationService {
@EventListener(async = true)
public void onOrderCreated(OrderCreatedEvent event) {
// 异步发送邮件
sendEmail(event.order());
}
}日志记录
日志级别
java
@Component
public class UserService {
private static final Logger logger = LoggerFactory.getLogger(UserService.class);
public User findById(Long id) {
logger.debug("查找用户: {}", id);
User user = userRepository.findById(id)
.orElseThrow(() -> {
logger.warn("用户不存在: {}", id);
return new NotFoundException("用户不存在");
});
logger.info("找到用户: {}", user.getUsername());
return user;
}
public void processOrder(Order order) {
try {
// 处理订单
logger.info("处理订单: {}", order.getId());
} catch (Exception e) {
logger.error("处理订单失败: {}", order.getId(), e);
throw e;
}
}
}日志配置
xml
<!-- logback.xml -->
<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/app.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logs/app.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="FILE" />
</root>
<logger name="com.example.app" level="DEBUG" />
</configuration>测试
单元测试
java
public class UserServiceTest {
private UserService userService;
private UserRepository userRepository;
@BeforeMethod
public void setUp() {
userRepository = mock(UserRepository.class);
userService = new UserService(userRepository);
}
@Test
public void testFindById() {
// Given
User user = new User(1L, "John", "john@example.com");
when(userRepository.findById(1L)).thenReturn(Optional.of(user));
// When
User found = userService.findById(1L);
// Then
assertEquals(found.getId(), 1L);
assertEquals(found.getName(), "John");
}
}集成测试
java
public class UserServiceIntegrationTest {
private LcgylApplication app;
private UserService userService;
@BeforeClass
public void setUp() {
app = LcgylApplication.create(TestApplication.class);
app.loadPlugin("lcgyl-cache-plugin");
app.start();
userService = app.getContainer().get(UserService.class);
}
@AfterClass
public void tearDown() {
app.stop();
}
@Test
public void testCreateUser() {
User user = new User();
user.setName("John");
user.setEmail("john@example.com");
User created = userService.save(user);
assertNotNull(created.getId());
assertEquals(created.getName(), "John");
}
}安全
密码加密
java
@Component
public class UserService {
@Inject
private PasswordEncoder passwordEncoder;
public void register(String username, String password) {
// 加密密码
String encodedPassword = passwordEncoder.encode(password);
User user = new User();
user.setUsername(username);
user.setPassword(encodedPassword);
userRepository.save(user);
}
public boolean login(String username, String password) {
User user = userRepository.findByUsername(username)
.orElseThrow(() -> new BusinessException("用户不存在"));
// 验证密码
return passwordEncoder.matches(password, user.getPassword());
}
}SQL 注入防护
java
// ✅ 推荐:参数化查询
String sql = "SELECT * FROM users WHERE username = ?";
User user = jdbcTemplate.queryForObject(sql, User.class, username);
// ❌ 危险:字符串拼接
String sql = "SELECT * FROM users WHERE username = '" + username + "'";
User user = jdbcTemplate.queryForObject(sql, User.class);资源管理
优雅关闭
java
public class Application {
public static void main(String[] args) {
LcgylApplication app = LcgylApplication.create(Application.class);
// 注册关闭钩子
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
logger.info("应用正在关闭...");
app.stop();
logger.info("应用已关闭");
}));
app.start();
app.startWebServer(8080);
}
}连接池管理
java
@Component
public class DataSourceConfig {
public DataSource dataSource() {
return DataSourceBuilder.create()
.url("jdbc:mysql://localhost:3306/db")
.username("root")
.password("password")
// 连接池配置
.maxPoolSize(20)
.minPoolSize(5)
.maxWait(30000)
.testQuery("SELECT 1")
.build();
}
}