动态注入Controller、Service所需的类

This commit is contained in:
dengqichen 2024-11-29 17:43:44 +08:00
parent 1afd981805
commit 3e577cac5c
5 changed files with 254 additions and 281 deletions

View File

@ -1,165 +0,0 @@
package com.qqchen.deploy.backend.framework.config;
import com.qqchen.deploy.backend.framework.controller.BaseController;
import com.qqchen.deploy.backend.framework.domain.Entity;
import com.qqchen.deploy.backend.framework.dto.BaseDTO;
import com.qqchen.deploy.backend.framework.service.IBaseService;
import com.qqchen.deploy.backend.framework.utils.PackageScanner;
import com.qqchen.deploy.backend.framework.utils.ReflectionUtils;
import com.qqchen.deploy.backend.framework.utils.SpringUtils;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.core.GenericTypeResolver;
import org.springframework.stereotype.Component;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@Component
@Slf4j
public class ControllerBeanPostProcessor implements BeanPostProcessor, DisposableBean {
private final ApplicationContext applicationContext;
private final Map<Class<?>, IBaseService<?, ?, ?>> serviceCache = new ConcurrentHashMap<>();
public ControllerBeanPostProcessor(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@Override
@SuppressWarnings("unchecked")
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (!(bean instanceof BaseController<?, ?, ?, ?> controller)) {
return bean;
}
try {
return injectService(controller, beanName);
} catch (ServiceInjectionException e) {
log.error("Service injection failed for controller: {}", beanName, e);
throw e;
} catch (Exception e) {
log.error("Unexpected error during service injection for controller: {}", beanName, e);
throw new BeanCreationException("Failed to inject service for controller: " + beanName, e);
}
}
@SuppressWarnings("unchecked")
private <T extends Entity<ID>, D extends BaseDTO, ID extends Serializable>
Object injectService(BaseController<T, D, ID, ?> controller, String beanName) {
Class<T> entityClass = resolveEntityClass(controller);
if (entityClass == null) {
return controller;
}
IBaseService<?, ?, ?> cachedService = serviceCache.computeIfAbsent(
entityClass,
key -> findServiceForEntity(entityClass, beanName)
);
if (cachedService == null) {
throw new ServiceInjectionException("No service found for entity: " + entityClass.getSimpleName());
}
try {
IBaseService<T, D, ID> service = (IBaseService<T, D, ID>) cachedService;
ReflectionUtils.setField("service", controller, service);
log.debug("Successfully injected service {} for controller {}",
service.getClass().getSimpleName(),
controller.getClass().getSimpleName());
} catch (Exception e) {
throw new ServiceInjectionException("Failed to inject service field", e);
}
return controller;
}
@SuppressWarnings("unchecked")
private <T extends Entity<ID>, D extends BaseDTO, ID extends Serializable>
Class<T> resolveEntityClass(BaseController<T, D, ID, ?> controller) {
Class<?>[] genericTypes = GenericTypeResolver.resolveTypeArguments(
controller.getClass(), BaseController.class);
if (genericTypes == null || genericTypes.length < 3) {
log.warn("Could not resolve entity type for controller: {}",
controller.getClass().getSimpleName());
return null;
}
return (Class<T>) genericTypes[0];
}
@SuppressWarnings("unchecked")
private <T extends Entity<ID>, ID extends Serializable>
IBaseService<?, ?, ?> findServiceForEntity(Class<T> entityClass, String controllerName) {
// 先尝试通过命名约定查找
String serviceBeanName = "i" + entityClass.getSimpleName() + "Service";
log.debug("Looking for service bean: {}", serviceBeanName);
if (applicationContext.containsBean(serviceBeanName)) {
return (IBaseService<?, ?, ?>) applicationContext.getBean(serviceBeanName);
}
// 如果找不到再通过类型匹配查找
log.debug("Service not found by name, trying to find by type matching");
return Arrays.stream(applicationContext.getBeanNamesForType(IBaseService.class))
.map(name -> tryGetService(name, entityClass, controllerName))
.filter(Objects::nonNull)
.findFirst()
.orElseThrow(() -> new ServiceInjectionException(
"No service found for entity: " + entityClass.getSimpleName() +
" (tried bean name: " + serviceBeanName + " and type matching)"));
}
@SuppressWarnings("unchecked")
private <T extends Entity<ID>, ID extends Serializable>
IBaseService<?, ?, ?> tryGetService(String beanName, Class<T> entityClass, String controllerName) {
try {
Class<?> serviceClass = applicationContext.getType(beanName);
if (serviceClass == null) {
return null;
}
Class<?>[] serviceGenericTypes = GenericTypeResolver.resolveTypeArguments(
serviceClass, IBaseService.class);
if (serviceGenericTypes != null &&
serviceGenericTypes.length > 0 &&
entityClass.equals(serviceGenericTypes[0])) {
log.debug("Found matching service: {} for entity: {}", beanName, entityClass.getSimpleName());
return (IBaseService<?, ?, ?>) applicationContext.getBean(beanName);
}
} catch (Exception e) {
log.warn("Failed to analyze service bean: {} for controller: {}", beanName, controllerName, e);
}
return null;
}
@Override
public void destroy() {
serviceCache.clear();
}
public static class ServiceInjectionException extends BeanCreationException {
public ServiceInjectionException(String message) {
super(message);
}
public ServiceInjectionException(String message, Throwable cause) {
super(message, cause);
}
}
}

View File

@ -0,0 +1,243 @@
package com.qqchen.deploy.backend.framework.config;
import com.qqchen.deploy.backend.framework.controller.BaseController;
import com.qqchen.deploy.backend.framework.converter.BaseConverter;
import com.qqchen.deploy.backend.framework.domain.Entity;
import com.qqchen.deploy.backend.framework.dto.BaseDTO;
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
import com.qqchen.deploy.backend.framework.exception.BusinessException;
import com.qqchen.deploy.backend.framework.repository.IBaseRepository;
import com.qqchen.deploy.backend.framework.service.IBaseService;
import com.qqchen.deploy.backend.framework.service.impl.BaseServiceImpl;
import com.qqchen.deploy.backend.framework.utils.ReflectionUtils;
import com.querydsl.core.types.EntityPath;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.core.GenericTypeResolver;
import org.springframework.stereotype.Component;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
@Slf4j
@Component
public class DependencyInjectionPostProcessor implements BeanPostProcessor, DisposableBean {
private final ApplicationContext applicationContext;
private final Map<Class<?>, IBaseService<?, ?, ?>> serviceCache = new ConcurrentHashMap<>();
public DependencyInjectionPostProcessor(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// 处理 Controller 的依赖注入
if (bean instanceof BaseController<?, ?, ?, ?> controller) {
try {
return injectControllerDependencies(controller, beanName);
} catch (DependencyInjectionException e) {
log.error("Service injection failed for controller: {}", beanName, e);
throw e;
} catch (Exception e) {
log.error("Unexpected error during service injection for controller: {}", beanName, e);
throw new BeanCreationException("Failed to inject service for controller: " + beanName, e);
}
}
// 处理 Service 的依赖注入
if (bean instanceof BaseServiceImpl<?, ?, ?> service) {
try {
return injectServiceDependencies(service, beanName);
} catch (Exception e) {
log.error("Failed to inject dependencies for service: {}", beanName, e);
throw new BeanCreationException("Failed to inject dependencies for service: " + beanName, e);
}
}
return bean;
}
// Controller 依赖注入相关方法
private <T extends Entity<ID>, D extends BaseDTO, ID extends Serializable>
Object injectControllerDependencies(BaseController<T, D, ID, ?> controller, String beanName) {
Class<T> entityClass = resolveEntityClass(controller);
if (entityClass == null) {
return controller;
}
IBaseService<?, ?, ?> cachedService = serviceCache.computeIfAbsent(
entityClass,
key -> findServiceForEntity(entityClass, beanName)
);
try {
IBaseService<T, D, ID> service = (IBaseService<T, D, ID>) cachedService;
ReflectionUtils.setField("service", controller, service);
log.debug("Successfully injected service {} for controller {}",
service.getClass().getSimpleName(),
controller.getClass().getSimpleName());
} catch (Exception e) {
throw new DependencyInjectionException("Failed to inject service field", e);
}
return controller;
}
@SuppressWarnings("unchecked")
private <T extends Entity<ID>, ID extends Serializable>
IBaseService<?, ?, ?> findServiceForEntity(Class<T> entityClass, String controllerName) {
// 先尝试通过命名约定查找
String serviceBeanName = "i" + entityClass.getSimpleName() + "Service";
log.debug("Looking for service bean: {}", serviceBeanName);
if (applicationContext.containsBean(serviceBeanName)) {
return (IBaseService<?, ?, ?>) applicationContext.getBean(serviceBeanName);
}
// 如果找不到再通过类型匹配查找
log.debug("Service not found by name, trying to find by type matching");
return Arrays.stream(applicationContext.getBeanNamesForType(IBaseService.class))
.map(name -> tryGetService(name, entityClass, controllerName))
.filter(Objects::nonNull)
.findFirst()
.orElseThrow(() -> new BusinessException(ResponseCode.DEPENDENCY_INJECTION_ERROR, new Object[] {entityClass.getSimpleName(), serviceBeanName}));
}
@SuppressWarnings("unchecked")
private <T extends Entity<ID>, ID extends Serializable>
IBaseService<?, ?, ?> tryGetService(String beanName, Class<T> entityClass, String controllerName) {
try {
Class<?> serviceClass = applicationContext.getType(beanName);
if (serviceClass == null) {
return null;
}
Class<?>[] serviceGenericTypes = GenericTypeResolver.resolveTypeArguments(
serviceClass, IBaseService.class);
if (serviceGenericTypes != null &&
serviceGenericTypes.length > 0 &&
entityClass.equals(serviceGenericTypes[0])) {
log.debug("Found matching service: {} for entity: {}", beanName, entityClass.getSimpleName());
return (IBaseService<?, ?, ?>) applicationContext.getBean(beanName);
}
} catch (Exception e) {
log.warn("Failed to analyze service bean: {} for controller: {}", beanName, controllerName, e);
}
return null;
}
// Service 依赖注入相关方法
private <T extends Entity<ID>, D extends BaseDTO, ID extends Serializable>
Object injectServiceDependencies(BaseServiceImpl<T, D, ID> service, String beanName) {
Class<T> entityClass = resolveEntityClass(service);
log.debug("Resolving dependencies for entity: {}", entityClass.getSimpleName());
try {
injectRepository(service, entityClass);
injectConverter(service, entityClass);
injectEntityPath(service, entityClass);
log.debug("Successfully injected all dependencies for service: {}", beanName);
return service;
} catch (DependencyInjectionException e) {
throw e;
} catch (Exception e) {
log.error("Unexpected error while injecting dependencies for {}", entityClass.getSimpleName(), e);
throw new DependencyInjectionException("Failed to inject dependencies: " + e.getMessage(), e);
}
}
// 通用工具方法
@SuppressWarnings("unchecked")
private <T extends Entity<ID>, ID extends Serializable> Class<T> resolveEntityClass(Object bean) {
Class<?>[] genericTypes = GenericTypeResolver.resolveTypeArguments(
bean.getClass(), bean instanceof BaseController ? BaseController.class : BaseServiceImpl.class);
if (genericTypes == null || genericTypes.length < 3) {
log.warn("Could not resolve entity type for: {}", bean.getClass().getSimpleName());
return null;
}
return (Class<T>) genericTypes[0];
}
// ... 其他方法保持不变但重命名异常类 ...
@Override
public void destroy() {
serviceCache.clear();
}
public static class DependencyInjectionException extends BeanCreationException {
public DependencyInjectionException(String message) {
super(message);
}
public DependencyInjectionException(String message, Throwable cause) {
super(message, cause);
}
}
// 辅助方法用于注入具体的依赖
private <T extends Entity<ID>, D extends BaseDTO, ID extends Serializable>
void injectRepository(BaseServiceImpl<T, D, ID> service, Class<T> entityClass) {
String repositoryBeanName = "I" + entityClass.getSimpleName() + "Repository";
log.debug("Looking for repository bean: {}", repositoryBeanName);
if (!applicationContext.containsBean(repositoryBeanName)) {
throw new BusinessException(
ResponseCode.DEPENDENCY_INJECTION_ERROR,
new Object[] {entityClass.getSimpleName(), repositoryBeanName});
}
IBaseRepository<T, ID> repository = (IBaseRepository<T, ID>) applicationContext.getBean(repositoryBeanName);
ReflectionUtils.setField("repository", service, repository);
}
private <T extends Entity<ID>, D extends BaseDTO, ID extends Serializable>
void injectConverter(BaseServiceImpl<T, D, ID> service, Class<T> entityClass) {
String converterBeanName = entityClass.getSimpleName().substring(0, 1).toLowerCase()
+ entityClass.getSimpleName().substring(1) + "ConverterImpl";
log.debug("Looking for converter bean: {}", converterBeanName);
if (!applicationContext.containsBean(converterBeanName)) {
throw new BusinessException(
ResponseCode.DEPENDENCY_INJECTION_ERROR,
new Object[] {entityClass.getSimpleName(), converterBeanName});
}
BaseConverter<T, D> converter = (BaseConverter<T, D>) applicationContext.getBean(converterBeanName);
ReflectionUtils.setField("converter", service, converter);
}
private <T extends Entity<ID>, D extends BaseDTO, ID extends Serializable>
void injectEntityPath(BaseServiceImpl<T, D, ID> service, Class<T> entityClass) {
try {
EntityPath<T> entityPath = initEntityPath(entityClass);
ReflectionUtils.setField("entityPath", service, entityPath);
} catch (Exception e) {
throw new BusinessException(
ResponseCode.DEPENDENCY_INJECTION_ERROR,
new Object[] {entityClass.getSimpleName(), e.getMessage()});
}
}
@SuppressWarnings("unchecked")
private <T extends Entity<ID>, ID extends Serializable> EntityPath<T> initEntityPath(Class<T> entityClass) {
try {
String qClassName = entityClass.getPackageName() + ".Q" + entityClass.getSimpleName();
Class<?> qClass = Class.forName(qClassName);
Field instanceField = qClass.getDeclaredField(entityClass.getSimpleName().toLowerCase());
return (EntityPath<T>) instanceField.get(null);
} catch (Exception e) {
throw new IllegalStateException("Failed to get Q class for " + entityClass.getName(), e);
}
}
}

View File

@ -1,114 +0,0 @@
package com.qqchen.deploy.backend.framework.config;
import com.qqchen.deploy.backend.framework.domain.Entity;
import com.qqchen.deploy.backend.framework.dto.BaseDTO;
import com.qqchen.deploy.backend.framework.service.impl.BaseServiceImpl;
import com.qqchen.deploy.backend.framework.repository.IBaseRepository;
import com.qqchen.deploy.backend.framework.converter.BaseConverter;
import com.qqchen.deploy.backend.framework.utils.ReflectionUtils;
import com.querydsl.core.types.EntityPath;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.core.GenericTypeResolver;
import org.springframework.stereotype.Component;
import org.springframework.beans.factory.BeanCreationException;
import lombok.extern.slf4j.Slf4j;
import java.io.Serializable;
import java.lang.reflect.Field;
@Component
@Slf4j
public class ServiceBeanPostProcessor implements BeanPostProcessor {
private final ApplicationContext applicationContext;
public ServiceBeanPostProcessor(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@Override
@SuppressWarnings("unchecked")
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (!(bean instanceof BaseServiceImpl<?, ?, ?> service)) {
return bean;
}
try {
return injectDependencies(service, beanName);
} catch (Exception e) {
log.error("Failed to inject dependencies for service: {}", beanName, e);
throw new BeanCreationException("Failed to inject dependencies for service: " + beanName, e);
}
}
private <T extends Entity<ID>, D extends BaseDTO, ID extends Serializable>
Object injectDependencies(BaseServiceImpl<T, D, ID> service, String beanName) {
Class<T> entityClass = resolveEntityClass(service);
log.debug("Resolving dependencies for entity: {}", entityClass.getSimpleName());
try {
// 注入 Repository
String repositoryBeanName = "I" + entityClass.getSimpleName() + "Repository";
log.debug("Looking for repository bean: {}", repositoryBeanName);
if (!applicationContext.containsBean(repositoryBeanName)) {
throw new ControllerBeanPostProcessor.ServiceInjectionException("Repository not found: " + repositoryBeanName);
}
IBaseRepository<T, ID> repository = (IBaseRepository<T, ID>) applicationContext.getBean(repositoryBeanName);
ReflectionUtils.setField("repository", service, repository);
// 注入 Converter
String converterBeanName = entityClass.getSimpleName().substring(0, 1).toLowerCase()
+ entityClass.getSimpleName().substring(1) + "ConverterImpl";
log.debug("Looking for converter bean: {}", converterBeanName);
if (!applicationContext.containsBean(converterBeanName)) {
throw new ControllerBeanPostProcessor.ServiceInjectionException("Converter not found: " + converterBeanName);
}
BaseConverter<T, D> converter = (BaseConverter<T, D>) applicationContext.getBean(converterBeanName);
ReflectionUtils.setField("converter", service, converter);
// 初始化 EntityPath
try {
EntityPath<T> entityPath = initEntityPath(entityClass);
ReflectionUtils.setField("entityPath", service, entityPath);
} catch (Exception e) {
log.error("Failed to initialize EntityPath for {}", entityClass.getSimpleName(), e);
throw new ControllerBeanPostProcessor.ServiceInjectionException("Failed to initialize EntityPath: " + e.getMessage());
}
log.debug("Successfully injected all dependencies for service: {}", beanName);
return service;
} catch (ControllerBeanPostProcessor.ServiceInjectionException e) {
throw e;
} catch (Exception e) {
log.error("Unexpected error while injecting dependencies for {}", entityClass.getSimpleName(), e);
throw new ControllerBeanPostProcessor.ServiceInjectionException("Failed to inject dependencies: " + e.getMessage(), e);
}
}
@SuppressWarnings("unchecked")
private <T extends Entity<ID>, ID extends Serializable> Class<T> resolveEntityClass(BaseServiceImpl<T, ?, ID> service) {
Class<?>[] genericTypes = GenericTypeResolver.resolveTypeArguments(
service.getClass(), BaseServiceImpl.class);
if (genericTypes == null || genericTypes.length < 3) {
throw new IllegalStateException("Could not resolve entity type for service: "
+ service.getClass().getSimpleName());
}
return (Class<T>) genericTypes[0];
}
@SuppressWarnings("unchecked")
private <T extends Entity<ID>, ID extends Serializable> EntityPath<T> initEntityPath(Class<T> entityClass) {
try {
String qClassName = entityClass.getPackageName() + ".Q" + entityClass.getSimpleName();
Class<?> qClass = Class.forName(qClassName);
Field instanceField = qClass.getDeclaredField(entityClass.getSimpleName().toLowerCase());
return (EntityPath<T>) instanceField.get(null);
} catch (Exception e) {
throw new IllegalStateException("Failed to get Q class for " + entityClass.getName(), e);
}
}
}

View File

@ -26,7 +26,10 @@ public enum ResponseCode {
// 用户相关错误码2开头
USER_NOT_FOUND(2001, "user.not.found"),
USERNAME_EXISTS(2002, "user.username.exists"),
EMAIL_EXISTS(2003, "user.email.exists");
EMAIL_EXISTS(2003, "user.email.exists"),
// 依赖注入相关错误 (1100-1199)
DEPENDENCY_INJECTION_ERROR(1100, "dependency.injection.service.not.found");
private final int code;

View File

@ -26,4 +26,10 @@ system.retry.exceeded.error=操作重试次数超限,请稍后再试
# Entity Not Found Messages
entity.not.found.id=找不到ID为{0}的实体
entity.not.found.message={0}
entity.not.found.name.id=找不到ID为{1}的{0}
entity.not.found.name.id=找不到ID为{1}的{0}
# 依赖注入相关
dependency.injection.service.not.found=找不到实体 {0} 对应的服务 (尝试过的bean名称: {1})
dependency.injection.repository.not.found=找不到实体 {0} 对应的Repository: {1}
dependency.injection.converter.not.found=找不到实体 {0} 对应的Converter: {1}
dependency.injection.entitypath.failed=初始化实体 {0} 的EntityPath失败: {1}