SpringMVC Proxy issue

2019-07-23 11:07发布

问题:

I have been trying to find an answer to this problem for days, and I hope someone can point me in the right direction. I have a SpringMVC app that uses Java Configuration and I was doing fine until I tried to integrate Apache-Shiro into it. I am able to build and run my tests. But my deploy fails because of Proxy/CGLIB issues.

Here is the exception I get on deploy/restart:

 Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'menuRepository': Post-processing of the FactoryBean's object failed; nested exception is org.springframework.aop.framework.AopConfigException: Could not generate CGLIB subclass of class [class com.sun.proxy.$Proxy69]: Common causes of this problem include using a final class or a non-visible class; nested exception is java.lang.IllegalArgumentException: Cannot subclass final class class com.sun.proxy.$Proxy69
        at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:165)
        at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getObjectFromFactoryBean(FactoryBeanRegistrySupport.java:102)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getObjectForBeanInstance(AbstractBeanFactory.java:1454)
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:306)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:198)
        at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.autowireResource(CommonAnnotationBeanPostProcessor.java:442)
        at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.getResource(CommonAnnotationBeanPostProcessor.java:416)
        at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor$ResourceElement.getResourceToInject(CommonAnnotationBeanPostProcessor.java:550)
        at org.springframework.beans.factory.annotation.InjectionMetadata$InjectedElement.inject(InjectionMetadata.java:150)
        at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)
        at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessPropertyValues(CommonAnnotationBeanPostProcessor.java:303)
        ... 55 more
        Caused by: org.springframework.aop.framework.AopConfigException: Could not generate CGLIB subclass of class [class com.sun.proxy.$Proxy69]: Common causes of this problem include using a final class or a non-visible class; nested exception is java.lang.IllegalArgumentException: Cannot subclass final class class com.sun.proxy.$Proxy69
            at org.springframework.aop.framework.CglibAopProxy.getProxy(CglibAopProxy.java:217)
            at org.springframework.aop.framework.ProxyFactory.getProxy(ProxyFactory.java:111)
            at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.createProxy(AbstractAutoProxyCreator.java:477)
            at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.wrapIfNecessary(AbstractAutoProxyCreator.java:362)
            at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.postProcessAfterInitialization(AbstractAutoProxyCreator.java:322)
            at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(AbstractAutowireCapableBeanFactory.java:409)
            at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.postProcessObjectFromFactoryBean(AbstractAutowireCapableBeanFactory.java:1625)
            at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:162)
            ... 65 more
        Caused by: java.lang.IllegalArgumentException: Cannot subclass final class class com.sun.proxy.$Proxy69
            at org.springframework.cglib.proxy.Enhancer.generateClass(Enhancer.java:446)
            at org.springframework.cglib.transform.TransformingClassGenerator.generateClass(TransformingClassGenerator.java:33)
            at org.springframework.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25)
            at org.springframework.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:216)
            at org.springframework.cglib.proxy.Enhancer.createHelper(Enhancer.java:377)
            at org.springframework.cglib.proxy.Enhancer.create(Enhancer.java:285)
            at org.springframework.aop.framework.CglibAopProxy.getProxy(CglibAopProxy.java:205)
            ... 72 more

Here is my current setup:

pom.xml

<org.springframework.version>3.2.3.RELEASE</org.springframework.version>
<shiro.version>1.2.2</shiro.version>
<org.hibernate.version>4.1.7.Final</org.hibernate.version>

<dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>3.2.3.RELEASE</version>
            <exclusions>
                <exclusion>
                    <artifactId>commons-logging</artifactId>
                    <groupId>commons-logging</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>3.2.3.RELEASE</version>
            <exclusions>
                <!-- Exclude Commons Logging in favor of SLF4j -->
                <exclusion>
                    <groupId>commons-logging</groupId>
                    <artifactId>commons-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>3.2.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>$3.2.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>3.2.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>3.2.3.RELEASE</version>
        </dependency>
          <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>2.2</version>
        </dependency>
 <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>${shiro.version}</version>
        </dependency>

        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-web</artifactId>
            <version>${shiro.version}</version>
        </dependency>
        <!--<dependency>-->
            <!--<groupId>org.apache.shiro</groupId>-->
            <!--<artifactId>shiro-aspectj</artifactId>-->
            <!--<version>${shiro.version}</version>-->
        <!--</dependency>-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.6.11</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.6.12</version>
        </dependency>

        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-ehcache</artifactId>
            <version>${shiro.version}</version>
        </dependency>

        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>${shiro.version}</version>
            </dependency>
<dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-jpa</artifactId>
            <version>1.0.3.RELEASE</version>
        </dependency>
<dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>${org.hibernate.version}</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>${org.hibernate.version}</version>
        </dependency>
        <!-- Hibernate metamodel annotation processor -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-jpamodelgen</artifactId>
            <version>1.1.1.Final</version>
        </dependency>
...
</>

I use this web config:

public class EdmWebInitializer implements WebApplicationInitializer {

    private static final String DISPATCHER_SERVLET_NAME = "dispatcher";
    private static final String DISPATCHER_SERVLET_MAPPING = "/*";

    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {

        // Create the 'root' Spring application context
        AnnotationConfigWebApplicationContext rootContext = new 
AnnotationConfigWebApplicationContext();

       //I have two @Configuration classes:
       rootContext.register( EdmConfiguration.class, SecurityConfig.class );

        // Manage the lifecycle of the root application context
        servletContext.addListener( new ContextLoaderListener( rootContext ) );


        // Create the dispatcher servlet's Spring application context
        AnnotationConfigWebApplicationContext dispatcherContext = new AnnotationConfigWebApplicationContext();
        dispatcherContext.setServletContext( servletContext );
        dispatcherContext.setParent( rootContext );

        // it seems I have to register the Configuration classes again or I can't @Autowire
         dispatcherContext.register( EdmConfiguration.class, SecurityConfig.class );



        // Register and map the dispatcher servlet
        ServletRegistration.Dynamic dispatcher = servletContext.addServlet( "dispatcher", new DispatcherServlet( dispatcherContext ) );
        dispatcher.setLoadOnStartup( 1 );
        dispatcher.addMapping( "/" );


        servletContext.addFilter( "shiroFilter", new DelegatingFilterProxy( "shiroFilter", dispatcherContext ) )
                .addMappingForUrlPatterns( null, false, "/*" );
    }  
}

Here is my primary Configuration class:

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = { "com.company.product.service", "com.company.product.utility",
        "com.company.product.controller", "com.company.product.utility.startup",
        "com.company.product.security", "com.company.product.repository.people" })
@EnableTransactionManagement(proxyTargetClass=false)
@ImportResource({ "classpath:applicationContext.xml" })
@PropertySource({ "classpath:application.properties", "classpath:mail.properties" })
public class EdmConfiguration extends WebMvcConfigurationSupport {

    @Resource
    private Environment environment;

    @Autowired
    private org.apache.shiro.web.mgt.WebSecurityManager securityManager;

    @Bean
    public DataSource dataSource() {
        BoneCPDataSource dataSource = new BoneCPDataSource();

        dataSource.setDriverClass( environment.getRequiredProperty( PROPERTY_NAME_DATABASE_DRIVER ) );
        dataSource.setJdbcUrl( environment.getRequiredProperty( PROPERTY_NAME_DATABASE_URL ) );
        dataSource.setUsername( environment.getRequiredProperty( PROPERTY_NAME_DATABASE_USERNAME ) );
        dataSource.setPassword( environment.getRequiredProperty( PROPERTY_NAME_DATABASE_PASSWORD ) );

        return dataSource;
    }

    @Bean
    public JpaTransactionManager transactionManager() throws ClassNotFoundException {
        JpaTransactionManager transactionManager = new JpaTransactionManager();

        transactionManager.setEntityManagerFactory( entityManagerFactoryBean().getObject() );

        return transactionManager;
    }

    @Bean
    public DelegatingFilterProxy springSecurityFilterChain() {
        return new DelegatingFilterProxy();
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean() throws ClassNotFoundException {
        LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();

        entityManagerFactoryBean.setDataSource( dataSource() );
        entityManagerFactoryBean.setPackagesToScan( environment
                .getRequiredProperty( PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN ) );
        entityManagerFactoryBean.setPersistenceProviderClass( HibernatePersistence.class );

        Properties jpaProperties = new Properties();

        ...

        entityManagerFactoryBean.setJpaProperties( jpaProperties );

        return entityManagerFactoryBean;
    }

    @Bean
    public PersistenceExceptionTranslator exTranslator() {
        return new HibernateExceptionTranslator();
    }

    @Bean(initMethod = "init")
    public StartupListener startupListener() {
        return new StartupListener();
    }

    @Bean
    public StandardPasswordEncoder encoder() {
        return new org.springframework.security.crypto.password.StandardPasswordEncoder();
    }


    @Bean
    public ShiroFilterFactoryBean shiroFilter() {
        ShiroFilterFactoryBean shiroFilter = new org.apache.shiro.spring.web.ShiroFilterFactoryBean();
        shiroFilter.setSecurityManager( securityManager);
        shiroFilter.setLoginUrl( "/login" );
        shiroFilter.setUnauthorizedUrl( "/" );
        return shiroFilter;
    }



    @Bean
    @DependsOn(value = "lifecycleBeanPostProcessor")
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
        creator.setProxyTargetClass(true);
        return creator;
    }
}

And here is the other Configuration class. It is the addition of this that causes the proxy issues.

@Configuration
public class SecurityConfig {

    @Bean
    public SaltAwareJdbcRealm saltAwareJdbcRealm() {
        return new SaltAwareJdbcRealm();
    }

    @Bean
    public WebSecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm( saltAwareJdbcRealm() );
        return securityManager;
    }

    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    @Bean
    public MethodInvokingFactoryBean methodInvokingFactoryBean() {
        MethodInvokingFactoryBean methodInvokingFactoryBean = new MethodInvokingFactoryBean();
        methodInvokingFactoryBean.setStaticMethod( "org.apache.shiro.SecurityUtils.setSecurityManager" );
        methodInvokingFactoryBean.setArguments( new Object[]{ securityManager() } );
        return methodInvokingFactoryBean;
    }

    @Bean
    @DependsOn(value = "lifecycleBeanPostProcessor")
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authori

zationAttributeSourceAdvisor.setSecurityManager( securityManager() );
            return authorizationAttributeSourceAdvisor;
        }       
    }

the offending class is just a spring jparepository:

public interface MenuRepository extends CrudRepository<Menu, Long>, JpaSpecificationExecutor<Menu> {

...}

I have added the @EnableTransactionManagement(proxyTargetClass=false) which I thought would address the Proxy issue but apparently isn't.

Thank you for your time in reading all this.

回答1:

Caused by: java.lang.IllegalArgumentException: Cannot subclass final class class com.sun.proxy.$Proxy69

This exception indicates that, due to some misconfiguration or multiple ways of applying AOP, a proxy of a proxy is being generated. Now with JDK Dynamic Proxies this isn't a problem however with class based proxies it is. Because cglib makes the classes final (as indicated by the stacktrace).

Your configuration has multiple ways of generation proxies, @EnableTransactionManagement triggers the registration of an AutoProxyCreator already. Next you are adding another one.

The solution in this case is to remove the DefaultAdvisorAutoProxyCreator as there is already an instance registered for you. This will disable proxying a proxy.