задний план
Представлено в проектеshiroFramework, однако, исключения доступа к базе данных всегда возникают, когда пользователи входят в систему. Информация об исключении выглядит следующим образом:
org.springframework.jdbc.BadSqlGrammarException:
### Error querying database. Cause: java.sql.SQLSyntaxErrorException: Table 'ems.sys_user_manager' doesn't exist
### The error may exist in com/greenet/platform/common/mapper/UserMapper.java (best guess)
### The error may involve defaultParameterMap
### The error occurred while setting parameters
### SQL: SELECT account,password,grouptype,account_tmtype,email,notes FROM sys_user_manager WHERE account = ?
### Cause: java.sql.SQLSyntaxErrorException: Table 'ems.sys_user_manager' doesn't exist
; bad SQL grammar []; nested exception is java.sql.SQLSyntaxErrorException: Table 'ems.sys_user_manager' doesn't exist
at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.doTranslate(SQLErrorCodeSQLExceptionTranslator.java:234) ~[spring-jdbc-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:72) ~[spring-jdbc-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:73) ~[mybatis-spring-1.3.2.jar:1.3.2]
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:446) ~[mybatis-spring-1.3.2.jar:1.3.2]
at com.sun.proxy.$Proxy63.selectOne(Unknown Source) ~[na:na]
at org.mybatis.spring.SqlSessionTemplate.selectOne(SqlSessionTemplate.java:166) ~[mybatis-spring-1.3.2.jar:1.3.2]
at com.baomidou.mybatisplus.core.override.PageMapperMethod.execute(PageMapperMethod.java:101) ~[mybatis-plus-core-3.0.7.1.jar:3.0.7.1]
at com.baomidou.mybatisplus.core.override.PageMapperProxy.invoke(PageMapperProxy.java:64) ~[mybatis-plus-core-3.0.7.1.jar:3.0.7.1]
at com.sun.proxy.$Proxy64.selectOne(Unknown Source) ~[na:na]
at com.greenet.platform.common.service.impl.UserServiceImpl.findUserByUsername(UserServiceImpl.java:48) ~[classes/:na]
at com.greenet.platform.config.UserRealm.doGetAuthenticationInfo(UserRealm.java:47) ~[classes/:na]
Судя по журналу исключений, найдена неверная база данных, так как в проекте используется технология multi-data source.@DS(Database.DATABASE_CA_SYSTEM)Аннотация, указывающая, какой источник данных должен использовать сервис. Это исключение указывает на то, что аннотация не выполняет предназначенную функцию, что приводит к сбою успешного переключения источника данных.
Использование нескольких источников данныхbaomidouРазработанный фреймворк имеет следующие зависимости. Его ядром является использование технологии АОП для переключения источника данных, который должен использовать каждый сервис. Конкретная реализация здесь обсуждаться не будет.
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
</dependency>
Описание исключения вышеuserServiceэтоbeanОн не проксируется, так в чем проблема?
выявить проблему
springbootОдин примечательный момент, обнаруженный в журнале запуска:
Warn: Could not find @TableId in Class: com.greenet.platform.common.entity.User. 2019-11-14 15:50:42.487 INFO 17752 --- [ main] trationDelegate
BeanPostProcessorChecker : Bean 'userMapper' of type [com.sun.proxy.
BeanPostProcessorChecker : Bean 'userService' of type [com.greenet.platform.common.service.impl.UserServiceImpl] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying) 2019-11-14 15:50:42.555 INFO 17752 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'cacheManager' of type [org.apache.shiro.cache.ehcache.EhCacheManager] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying) 2019-11-14 15:50:42.556 INFO 17752 --- [ main] o.a.shiro.cache.ehcache.EhCacheManager : Using existing EHCache named [passwordRetryCache
Ударение на этой фразе:
trationDelegate$BeanPostProcessorChecker : Bean 'userService' of type [com.greenet.platform.common.service.impl.UserServiceImpl] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
это значитuserServiceПоскольку он не соответствует определенному условию, он не будет автоматически проксирован, так что же это за условие?
ПроверятьBeanPostProcessorCheckerИсходный код, место для печати журнала выглядит следующим образом:
@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) {
if (!(bean instanceof BeanPostProcessor) && !isInfrastructureBean(beanName) && this.beanFactory.getBeanPostProcessorCount() < this.beanPostProcessorTargetCount) {
if (logger.isInfoEnabled()) {
logger.info("Bean '" + beanName + "' of type [" + bean.getClass().getName() + "] is not eligible for getting processed by all BeanPostProcessors " + "(for example: not eligible for auto-proxying)"); } }
return bean;
}
Причина, по которой журнал распечатывается,this.beanFactory.getBeanPostProcessorCount() < this.beanPostProcessorTargetCountвернулсяtrue, то есть зарегистрироваться наbeanFactoryКоличество постпроцессоров меньше общего количества постпроцессоров, а это значит, что другие постпроцессоры в данный момент не готовы.userServiceуже создан экземпляр;
Так как это место говоритuserServiceне будет проксироваться, то этоbeanкогда это былоspringсоздан. Это легко понять, вы можетеAbstractBeanFactory.doGetBeanНажмите условную точку останова в методе, а затем посмотрите на стек вызовов.
Получите следующий стек вызовов:
"main@1" prio=5 tid=0x1 nid=NA runnable
java.lang.Thread.State: RUNNABLE
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:242)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:277)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1247)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1167)
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:857)
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:760)
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:509)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1305)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1144)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
at org.springframework.beans.factory.support.AbstractBeanFactory?Lambda$121.1862994526.getObject(Unknown Source:-1)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
- locked <0x12b7> (a java.util.concurrent.ConcurrentHashMap)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:277)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1247)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1167)
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:857)
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:760)
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:509)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1305)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1144)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getSingletonFactoryBeanForTypeCheck(AbstractAutowireCapableBeanFactory.java:991)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getTypeForFactoryBean(AbstractAutowireCapableBeanFactory.java:865)
at org.springframework.beans.factory.support.AbstractBeanFactory.isTypeMatch(AbstractBeanFactory.java:574)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doGetBeanNamesForType(DefaultListableBeanFactory.java:514)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(DefaultListableBeanFactory.java:477)
at org.springframework.beans.factory.BeanFactoryUtils.beanNamesForTypeIncludingAncestors(BeanFactoryUtils.java:227)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1411)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1210)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1167)
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:857)
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:760)
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:509)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1305)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1144)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
at org.springframework.beans.factory.support.AbstractBeanFactory?Lambda$121.1862994526.getObject(Unknown Source:-1)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:204)
at org.springframework.context.support.PostProcessorRegistrationDelegate.registerBeanPostProcessors(PostProcessorRegistrationDelegate.java:228)
at org.springframework.context.support.AbstractApplicationContext.registerBeanPostProcessors(AbstractApplicationContext.java:721)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:534)
- locked <0x12ea> (a java.lang.Object)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:142)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:775)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:316)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1260)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1248)
at com.greenet.platform.Application.main(Application.java:24)
Из стека вызовов можно увидеть две важные части информации:
- Этот стек появляется три раза
AbstractBeanFactory.doGetBean, указывающий, что экземпляр userService создается из-за внедрения зависимостей при создании экземпляров других bean-компонентов; - Вход в стек
AbstractApplicationContext.registerBeanPostProcessors
Эти две точкиuserServiceне в нормеAbstractApplicationContext.finishBeanFactoryInitializationСтадии создаются, но создаются при регистрации постпроцессора.
Почему он создается заранее?
MethodValidationPostProcessor
Вернитесь с точки останова, указанной вышеregisterBeanPostProcessors, вы можете увидеть постпроцессор, зарегистрированный в это времяMethodValidationPostProcessor, скриншот отладки выглядит следующим образом:
Соответствующий код выглядит следующим образом:
for (String ppName : orderedPostProcessorNames) {
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
orderedPostProcessors.add(pp);
if (pp instanceof MergedBeanDefinitionPostProcessor) {
internalPostProcessors.add(pp);
}
}
то естьuserServiceсуществуетMethodValidationPostProcessorВ процессе инстанцирования до того, как этот постпроцессор будет зарегистрирован, по какой-то причине он создается заранее.
Так почему же он создан?MethodValidationPostProcessorнеобходимо создать экземплярuserServiceШерстяная ткань
Через волну отладки выяснилось, что проблема вышла ко мнеshiroвнутри конфигурации
@Bean("securityManager")
public SecurityManager securityManager(UserRealm userRealm,
SessionManager sessionManager,
EhCacheManager cacheManager) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(userRealm);
securityManager.setSessionManager(sessionManager);
securityManager.setRememberMeManager(null);
securityManager.setCacheManager(cacheManager);
return securityManager;
}
/**
* shiro权限验证
*
* @param securityManager 安全管理器
* @return 安全管理factorybean
*/
@Bean("shiroFilter")
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
shiroFilter.setSecurityManager(securityManager);
shiroFilter.setLoginUrl("/login");
Map<String, String> filterMap = new LinkedHashMap<>();
filterMap.put("/static/**", "anon");
filterMap.put("/", "anon");
filterMap.put("/login/getVerifyCodeImage", "anon");
filterMap.put("/**", "authc");
shiroFilter.setFilterChainDefinitionMap(filterMap);
return shiroFilter;
}
Виновник в этомShiroFilterFactoryBean, эта вещь определяется следующим образом:
public class ShiroFilterFactoryBean implements FactoryBean, BeanPostProcessor {
он достигаетFactoryBean, BeanPostProcessorЭти два специальных интерфейса, всеFactoryBeanпостпроцессорMethodValidationPostProcessorОн создается во время процесса создания экземпляра. Почему? Это связано с определением этого постпроцессора. Этот постпроцессор находится вValidationAutoConfigurationОпределенный в классе код конкретного метода выглядит следующим образом:
@Bean
@ConditionalOnMissingBean
public static MethodValidationPostProcessor methodValidationPostProcessor(
Environment environment, @Lazy Validator validator) {
MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
boolean proxyTargetClass = environment
.getProperty("spring.aop.proxy-target-class", Boolean.class, true);
processor.setProxyTargetClass(proxyTargetClass);
processor.setValidator(validator);
return processor;
}
Для инстанцирования этого класса нужно знать параметры этого метода, проблема кроется в этомEnvironmentвнутри, чтобы найти тип какEnvironmentпараметр, пружина пройдет через весьbeanDefinitionNames, а затем перейти к поискуEnvironmentЭтот типbeanНайти процесс найден некоторыеbeanдаfactoryBean, извините, нужно поставить этоfactoryBeanСоздайте экземпляр, посмотрите на вышеShiroFilterFactoryBeanОпределенный метод можно увидеть, это зависит отsecurityManager,иsecurityManagerположился наuserRealm,иuserRealmположился наuserService:
@Component
public class UserRealm extends AuthorizingRealm {
private static final String ACCOUNT_TYPE_WEB = "2";
private static final String ACCOUNT_TYPE_ALL = "255";
@Autowired
UserService userService;
шаг за шагомuserServiceОн создается.В это время, прежде чем постпроцессор обработает bean-компонент, постпроцессор, ответственный за прокси-сервер AOP, больше не может его обрабатывать.userService.
решение
Итак, как решить эту проблему, идея не в том, чтобыfactoryBeanВнутри впрыснуть бизнес-класс. Более простой способ - инъекцияApplicationContext, то черезgetBeanспособ получить бизнесservice
private UserService getUserService() {
return (UserService) applicationContext.getBean("userService");
}