Spring ProGuard 构建中 TransactionAttributeSource 的 NoUniqueBeanDefinitionException

Spring NoUniqueBeanDefinitionException for TransactionAttributeSource on ProGuard build

我有一个基于 Maven 的 Java Web 应用程序,其中包含 Spring 5.2.0.RELEASE。当我尝试使用 ProGuard 6.2.2 混淆代码时,它构建成功。但是,当我尝试在 Apache Tomcat.

上部署混淆的 war 文件时出现以下错误
[ERROR] 2020-06-08 09:23:27.748 [localhost-startStop-1] ContextLoader [][][] Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'messageSource' defined in class path resource [resources/spring/portal-context.xml]: BeanPostProcessor before instantiation of bean failed; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springframework.transaction.config.internalTransactionAdvisor' defined in class path resource [org/springframework/transaction/annotation/ProxyTransactionManagementConfiguration.class]: Unsatisfied dependency expressed through method 'transactionAdvisor' parameter 0; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'org.springframework.transaction.interceptor.TransactionAttributeSource' available: expected single matching bean but found 2: org.springframework.transaction.annotation.AnnotationTransactionAttributeSource#0,transactionAttributeSource
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:512) ~[spring-beans-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean[=11=](AbstractBeanFactory.java:323) ~[spring-beans-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) ~[spring-beans-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321) ~[spring-beans-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:207) ~[spring-beans-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.initMessageSource(AbstractApplicationContext.java:732) ~[spring-context-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:538) ~[spring-context-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:401) ~[spring-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:292) [spring-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:103) [spring-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4770) [catalina.jar:8.5.40]
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5236) [catalina.jar:8.5.40]
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150) [catalina.jar:8.5.40]
    at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:754) [catalina.jar:8.5.40]
    at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:730) [catalina.jar:8.5.40]
    at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:734) [catalina.jar:8.5.40]
    at org.apache.catalina.startup.HostConfig.deployWAR(HostConfig.java:980) [catalina.jar:8.5.40]
    at org.apache.catalina.startup.HostConfig$DeployWar.run(HostConfig.java:1851) [catalina.jar:8.5.40]
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) [?:1.8.0_201]
    at java.util.concurrent.FutureTask.run(FutureTask.java:266) [?:1.8.0_201]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [?:1.8.0_201]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [?:1.8.0_201]
    at java.lang.Thread.run(Thread.java:748) [?:1.8.0_201]
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springframework.transaction.config.internalTransactionAdvisor' defined in class path resource [org/springframework/transaction/annotation/ProxyTransactionManagementConfiguration.class]: Unsatisfied dependency expressed through method 'transactionAdvisor' parameter 0; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'org.springframework.transaction.interceptor.TransactionAttributeSource' available: expected single matching bean but found 2: org.springframework.transaction.annotation.AnnotationTransactionAttributeSource#0,transactionAttributeSource
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:787) ~[spring-beans-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:528) ~[spring-beans-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1338) ~[spring-beans-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1177) ~[spring-beans-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:557) ~[spring-beans-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517) ~[spring-beans-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean[=11=](AbstractBeanFactory.java:323) ~[spring-beans-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) ~[spring-beans-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321) ~[spring-beans-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:207) ~[spring-beans-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at org.springframework.aop.framework.autoproxy.BeanFactoryAdvisorRetrievalHelper.findAdvisorBeans(BeanFactoryAdvisorRetrievalHelper.java:91) ~[spring-aop-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.findCandidateAdvisors(AbstractAdvisorAutoProxyCreator.java:109) ~[spring-aop-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator.findCandidateAdvisors(AnnotationAwareAspectJAutoProxyCreator.java:92) ~[spring-aop-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator.shouldSkip(AspectJAwareAdvisorAutoProxyCreator.java:101) ~[spring-aop-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.postProcessBeforeInstantiation(AbstractAutoProxyCreator.java:251) ~[spring-aop-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInstantiation(AbstractAutowireCapableBeanFactory.java:1141) ~[spring-beans-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.resolveBeforeInstantiation(AbstractAutowireCapableBeanFactory.java:1114) ~[spring-beans-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:506) ~[spring-beans-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    ... 22 more
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'org.springframework.transaction.interceptor.TransactionAttributeSource' available: expected single matching bean but found 2: org.springframework.transaction.annotation.AnnotationTransactionAttributeSource#0,transactionAttributeSource
    at org.springframework.beans.factory.config.DependencyDescriptor.resolveNotUnique(DependencyDescriptor.java:220) ~[spring-beans-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1265) ~[spring-beans-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1207) ~[spring-beans-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:874) ~[spring-beans-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:778) ~[spring-beans-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:528) ~[spring-beans-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1338) ~[spring-beans-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1177) ~[spring-beans-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:557) ~[spring-beans-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517) ~[spring-beans-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean[=11=](AbstractBeanFactory.java:323) ~[spring-beans-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) ~[spring-beans-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321) ~[spring-beans-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:207) ~[spring-beans-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at org.springframework.aop.framework.autoproxy.BeanFactoryAdvisorRetrievalHelper.findAdvisorBeans(BeanFactoryAdvisorRetrievalHelper.java:91) ~[spring-aop-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.findCandidateAdvisors(AbstractAdvisorAutoProxyCreator.java:109) ~[spring-aop-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator.findCandidateAdvisors(AnnotationAwareAspectJAutoProxyCreator.java:92) ~[spring-aop-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator.shouldSkip(AspectJAwareAdvisorAutoProxyCreator.java:101) ~[spring-aop-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.postProcessBeforeInstantiation(AbstractAutoProxyCreator.java:251) ~[spring-aop-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInstantiation(AbstractAutowireCapableBeanFactory.java:1141) ~[spring-beans-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.resolveBeforeInstantiation(AbstractAutowireCapableBeanFactory.java:1114) ~[spring-beans-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:506) ~[spring-beans-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    ... 22 more

Spring 本身创建了获取错误的 bean。所以我无法追踪窃听代码。你能帮我解决这个问题吗?

我的 proguard.conf 和 context.xml 文件如下

proguard.conf

-injars portal.war
-outjars smartgate.war

-dontshrink
-dontoptimize
-dontusemixedcaseclassnames

-dontpreverify

-dontskipnonpubliclibraryclasses
-dontskipnonpubliclibraryclassmembers
#-skipnonpubliclibraryclasses

#-dontobfuscate

-forceprocessing

-optimizations !class/marking/final

-adaptresourcefilenames **.xsd,**.wsdl,**.xml,**.properties,META-INF/MANIFEST.MF,META-INF/spring.*
-adaptresourcefilecontents **.xsd,**.wsdl,**.xml,**.properties,META-INF/MANIFEST.MF,META-INF/spring.*


-keepdirectories

#-renamesourcefileattribute SourceFile


#-keep class *

-keep class com.foo.** {
    void set*(***);
    void set*(int, ***);

    boolean is*(); 
    boolean is*(int);

    *** get*();
    *** get*(int);
}

-keep class **MailThread {
   public void run;
}

-keep class !com.foo.** { *; }
-keep class com.foo.model.entity.** { *; }
-keep class com.foo.service.log.FatalLogAspect { *; }
-keep class com.foo.service.MailService** { *; }
-keepclassmembers class com.foo.service.MailService** { *; }
-keep class com.foo.smartgate.service.adapter.SMSCAdapter** { *; }
-keepclassmembers class com.foo.smartgate.service.adapter.SMSCAdapter** { *; }
-keep class com.foo.smartgate.filter.** { *; }
-keep class com.foo.service.context.** { *; }
-keep class com.foo.**.bean.** { *; }
-keep class com.foo.**.ws.** { *; }

-keep class com.foo.util.Custom** { *; }
-keep class com.foo.util.ConnectionUtils { *; }

-keepattributes RuntimeVisibleAnnotations,Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,*Annotation*,EnclosingMethod
-keep @javax.persistence.* class * {
   *;
}

-keep @org.springframework.transaction.annotation.Transactional class *

-keep @org.springframework.stereotype.Service class *

-keep @org.springframework.stereotype.Controller class *

-keep @org.springframework.beans.factory.annotation.Autowired class *

-keep @org.springframework.web.bind.annotation.ResponseBody class *

-keep @org.springframework.web.bind.annotation.RequestMapping class *

-keep @org.springframework.stereotype.Repository class *

-keep @javax.annotation.Resource class *

-keep @javax.persistence.Entity class *

-keep @javax.persistence.Table class *

-keep @javax.persistence.Id class *

-keep @javax.persistence.GeneratedValue class *

-keep @javax.persistence.Column class *

-keep @javax.persistence.Transient class *

-keep @org.springframework.ws.server.endpoint.annotation.Endpoint class *

-keep @org.springframework.ws.server.endpoint.annotation.PayloadRoot class *

-keep @org.springframework.ws.server.endpoint.annotation.ResponsePayload class *

-keepclassmembers class * {
    @javax.annotation.Resource *;
    @org.springframework.beans.factory.annotation.Autowired *;
    @org.springframework.beans.factory.annotation.Value *;
}

-keepclassmembers enum * {
    *;
}

-keepclassmembernames class * {
    com.foo.service.context.ConfigurationManager config; 
    java.lang.Class class$(java.lang.String); 
    java.lang.Class class$(java.lang.String, boolean);
}

-keepclasseswithmembers class com.foo.config.** {*;}

-keep class com.foo.utils.locker.** { *; }
-keepclassmembers class com.foo.utils.locker.** { *; }
-keepnames class com.foo.utils.locker.** { *; }


-dontwarn **
-dontnote "!DuplicateClassPrinter*"

context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:p="http://www.springframework.org/schema/p" xmlns:jee="http://www.springframework.org/schema/jee"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
        http://www.springframework.org/schema/tx
                            http://www.springframework.org/schema/tx/spring-tx-3.2.xsd">

    <context:annotation-config />
    <context:component-scan base-package="com.foo" />
    <aop:aspectj-autoproxy proxy-target-class="true" />
    <tx:annotation-driven transaction-manager="transactionManager" />

    <bean id="messageSource"
        class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
        <property name="basename" value="classpath:resources/locale/messages" />
        <property name="defaultEncoding" value="UTF-8" />
    </bean>

    <jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/edge" />

    <bean class="com.foo.service.context.PropertiesUtil">
        <property name="location" value="classpath:resources/application.properties" />
    </bean>

    <bean id="log4jFilter" class="com.foo.filter.Log4jFilter" />

    <bean id="contextApplicationContextProvider"
        class="com.foo.service.context.ApplicationContextProvider"></bean>

    <bean id="errorLogger" class="com.foo.service.log.FatalLogAspect" />

    <mvc:annotation-driven />

    <mvc:interceptors>
        <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
            <property name="paramName" value="lang" />
        </bean>
        <bean class="com.foo.context.LocaleInterceptor">
        </bean>
        <bean class="com.foo.filter.ViewHandlerInterceptor">
        </bean>
    </mvc:interceptors>

    <!-- freemarker config -->
    <bean id="freemarkerConfig"
        class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
        <property name="templateLoaderPath" value="/views">
        </property>
        <property name="freemarkerSettings">
            <props>
                <prop key="template_update_delay">10</prop>
                <prop key="default_encoding">UTF-8</prop>
            </props>
      </property>
    </bean>

    <bean id="viewResolver"
        class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
        <property name="order" value="2" />
        <property name="cache" value="true" />
        <property name="prefix" value="" />
        <property name="contentType" value="text/html;charset=UTF-8" />
        <property name="requestContextAttribute" value="context" />
        <property name="suffix">
            <value>.ftl</value>
        </property>
    </bean>

    <bean id="methodHandlerExceptionResolver"
        class="org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver">
        <property name="messageConverters">
            <list>
                <bean
                    class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" />
            </list>
        </property>
    </bean>

    <bean
        class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
        <property name="order" value="1" />
        <property name="defaultViews">
            <list>
                <bean
                    class="org.springframework.web.servlet.view.json.MappingJackson2JsonView" />
            </list>
        </property>
    </bean>

    <bean class="com.foo.context.LocaleResolver"
        id="localeResolver">
    </bean>

    <aop:config>
        <aop:aspect id="aspectLoggging" ref="errorLogger">
            <aop:pointcut id="pointCutAfterThrowing"
                expression="within(@org.springframework.stereotype.Controller *)" />
            <aop:after-throwing method="afterThrowing"
                throwing="e" pointcut-ref="pointCutAfterThrowing" />
        </aop:aspect>
    </aop:config>


</beans>

感谢您抽出时间。

这是因为定义了两次事务管理器(xml和java)

从 context.xml 中删除以下行解决了问题。

<tx:annotation-driven transaction-manager="transactionManager" />

事务管理器的java端

/* 
 * https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/transaction/annotation/EnableTransactionManagement.html
 * <tx:annotation-driven transaction-manager="transactionManager" />
 */
@EnableTransactionManagement

如果不混淆,这通常不会引起问题。删除 xml 行解决了问题。