Autowiring a Spring 3.2 standalone application fails

ⅰ亾dé卋堺 提交于 2019-12-23 06:05:46

问题


This is the first time I can't find a solution to my problem on stackoverflow:

I'm trying to create an executable-jar standalone application with annotation-based autowiring, but I can't get the runnable jar file (exported from Eclipse as runnable JAR file) to do it's job. It does run as Java application from Eclipse directly, just not as standalone app via console > java -jar test.jar.

Exception in thread "main" java.lang.reflect.InvocationTargetException
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader.main(JarRsrcLoader.java:58)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'handler': Injection of autowired dependen
cies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.example.serv
ice.UserService com.example.controller.TestHandler.userService; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionEx
ception: No qualifying bean of type [com.example.service.UserService] found for dependency: expected at least 1 bean which qualifies as auto
wire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
        at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBe
anPostProcessor.java:288)

I tried to avoid such problems by following the pattern 'Initialize autowiring manually' described on this blog: http://sahits.ch/blog/?p=2326 to no avail. Note that I'm also using Hibernate JPA without a persistence.xml (therefore <tx:annotation-driven/> ) - and don't get obstructed by the two databases I use, it works fine when running from within Eclipse.

Main.java:

public class Main {
    @Autowired
    private TestRunner testRunner;

    public Main() {
        final ApplicationContext context = new ClassPathXmlApplicationContext("app-context.xml");
        AutowireCapableBeanFactory acbFactory = context.getAutowireCapableBeanFactory();
        acbFactory.autowireBean(this);
    }

    public static void main(String[] args) throws Exception {
        Main main = new Main();

        main.testRunner.run();
    }
}

TestRunner.java:

@Component
public class TestRunner {
    @Autowired
    Handler handler;

    public void run() {
        handler.doTest();
    }
}

TestHandler.java (groupId and groupName will be set in a later stage):

@Controller
public class TestHandler implements Handler {
    private static Log syslog = LogFactory.getLog(TestHandler.class);

    @Autowired
    private UserService userService;

    @Autowired
    private GroupService groupService;

    private Long groupId;
    private String groupName;

    public void doTest() {
        if (groupId != null) {
            List<User> users = userService.loadUsersByGroupId(groupId);
            syslog.debug("Amount of users found: " + users.size());
        } else {
            syslog.debug("groupId is null");
        }

        if (groupName != null) {
            List<Group> groups = groupService.loadGroupsByName(groupName);
            syslog.debug("Amount of groups found: " + groups.size());
        } else {
            syslog.debug("groupName is null");
        }

    }

    public void setGroupId(Long groupId) {
        this.groupId = groupId;
    }

    public void setGroupName(String groupName) {
        this.groupName = groupName;
    }
}

UserServiceImpl.java:

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    UserDAO userDAO;

    @Override
    public List<User> loadUsersByGroupId(long id) {
        return userDAO.findAllByGroupId(id);
    }

...

UserDAOImpl.java:

@Repository
@Transactional("db1")
public class UserDAOImpl implements UserDAO {

    @PersistenceContext(unitName="entityManagerDb1")
    private EntityManager em;

    @SuppressWarnings("unchecked")
    @Override
    public List<User> findAllByGroupId(long id) {
        Query query = em.createQuery("select distinct u from User u " +
                "where u.groupId = :groupId");
        query.setParameter("groupId", id);

        return query.getResultList();
    }

    ...

app-context.xml (uses annotation-configured JPA, uses two dbs):

<?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:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:jpa="http://www.springframework.org/schema/data/jpa"
       xsi:schemaLocation="
            http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/tx
            http://www.springframework.org/schema/tx/spring-tx.xsd
            http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/aop/spring-aop.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd
        "
        >

    <context:annotation-config/>

    <context:component-scan base-package="com.example"/>

    <!-- db1 -->
    <bean id="dataSourceDb1" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
      <property name="driverClass" value="com.mysql.jdbc.Driver" />
      <property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/db1?characterEncoding=UTF-8" />
      <property name="user" value="root" />
      <property name="password" value="root" />
      <property name="minPoolSize" value="5" />
      <property name="maxPoolSize" value="10" />
      <property name="maxStatements" value="0" />
      <property name="preferredTestQuery" value="SELECT 1" />
      <property name="idleConnectionTestPeriod" value="600" />
    </bean>

     <bean id="entityManagerFactoryDb1" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="persistenceUnitName" value="entityManagerDb1"/>
         <property name="dataSource" ref="dataSourceDb1"/>
        <property name="packagesToScan">
            <list>
                <value>com.example.domain.db1</value>
            </list>
        </property>
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="showSql" value="false"/>
                <property name="generateDdl" value="true"/>
                <property name="databasePlatform" value="org.hibernate.dialect.MySQL5InnoDBDialect"/>
            </bean>
        </property>

        <property name="jpaProperties">
            <props>
               <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
               <prop key="hibernate.connection.show_sql">true</prop>
               <prop key="hibernate.hbm2ddl.auto">validate</prop>
               <prop key="hibernate.use_outer_join">true</prop>
               <prop key="hibernate.connection.characterEncoding">UTF-8</prop>
               <prop key="hibernate.connection.useUnicode">true</prop>
            </props>
        </property>

    </bean>

    <!-- Configure transaction manager for JPA -->
    <bean id="transactionManagerDb1" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactoryDb1"/>
        <qualifier value="db1"/>
    </bean>

    <!-- db2 -->
    <bean id="dataSourceDb2" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
      <property name="driverClass" value="com.mysql.jdbc.Driver" />
      <property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/db2?characterEncoding=UTF-8" />
      <property name="user" value="root" />
      <property name="password" value="root" />
      <property name="minPoolSize" value="5" />
      <property name="maxPoolSize" value="10" />
      <property name="maxStatements" value="0" />
      <property name="preferredTestQuery" value="SELECT 1" />
      <property name="idleConnectionTestPeriod" value="600" />
    </bean>

     <bean id="entityManagerFactoryDb2" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="persistenceUnitName" value="entityManagerDb2"/>
         <property name="dataSource" ref="dataSourceDb2"/>
        <property name="packagesToScan">
            <list>
                <value>com.example.domain.db2</value>
            </list>
        </property>
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="showSql" value="false"/>
                <property name="generateDdl" value="true"/>
                <property name="databasePlatform" value="org.hibernate.dialect.MySQL5InnoDBDialect"/>
            </bean>
        </property>

        <property name="jpaProperties">
            <props>
               <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
               <prop key="hibernate.connection.show_sql">true</prop>
               <prop key="hibernate.hbm2ddl.auto">validate</prop>
               <prop key="hibernate.use_outer_join">true</prop>
               <prop key="hibernate.connection.characterEncoding">UTF-8</prop>
               <prop key="hibernate.connection.useUnicode">true</prop>
            </props>
        </property>

    </bean>

    <bean id="transactionManagerDb2" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactoryDb2"/>
        <qualifier value="db2"/>
    </bean>

    <tx:annotation-driven />

</beans>

It drives me crazy that running it inside Eclipse just works as expected. Is the Eclipse export feature somewhat limited? Any hint to another framework for creating runnable jar files?


回答1:


If it is running from eclipse and not running from the exported jar file, then it is a problem with the export

In the export window there is a checkbox saying Add directory entries that should be checked for component-scan to work




回答2:


When you run it inside Eclipse all the Spring dependencies and others are loaded into your classpath, but when you export you application as a jar, only your classes get exported, not the dependencies classes.

There are generally two way you can achieve a standlone jar. You can create an 'uber jar' (see Is it possible to create an "uber" jar containing the project classes and the project dependencies as jars with a custom manifest file?) where all you dependency jars got expanded into one single jar. However this method could be risky because if the jars have same file names (eg: same config file inside META-INF) they can overwrite each other

The other more appropriate method is to use maven-dependency-plugin (see dependency:copy) to copy all your dependency into a folder "dependencies". And then run your jar with

java -cp "myjar.jar;dependencies/*" org.mycompany.MainClass

This isn't really a standalone jar, but more a standalone folder. The downside is everytime you add/remove the dependencies (eg: from maven) you have to do the same to your dependencies folder



来源:https://stackoverflow.com/questions/16961397/autowiring-a-spring-3-2-standalone-application-fails

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!