问题
I am building a suite of web services that all access the same database but using different credentials. So to modularize the database code I am moving it out of each web app .WAR files and giving the database calls its own .JAR file. So that if WebApp1 and WebApp2 use a getAllColumnsFromTable1 MyBatis call I don't have to define the same mapper and domain object in both projects. That way I only have to change the MyBatis code in one place if Table1 ever changes, and less copy and paste mistakes.
WebApp1 applicationContext.xml:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.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.webapp1.service" />
<context:component-scan base-package="com.example.webapp1.controller" />
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:/example/ds/WebApp1DS" />
</bean>
<bean id="databaseConnector" class="com.example.databaseconnector.connector"
<property name="dataSource" ref="dataSource" />
</bean>
</beans>
So what I want to pass to the database connector .JAR file is just the data source that the .WAR is using.
WebApp1 pom.xml :
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>WebApp1</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</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>com.example</groupId>
<artifactId>databaseconnector</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
Then have something like this in one of the services in the .WAR
WebApp1 Service.java:
package com.example.webapp1.service;
import com.example.databaseconnector.domain.TableSearchResult;
import com.example.databaseconnector.persistence.TableMapper;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class Service {
@Autowired
private TableMapper tableMapper;
public List<TableSearchResult> getSearchResults(String param) {
return tableMapper.getAllColumnsFromTable1(param);
}
}
Then on the .JAR side I have
databaseconnector pom.xml:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>databaseconnector</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Database Connector</name>
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.2.3</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.2.1</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
</dependencies>
</project>
databaseconnector DatabaseConnection.java:
package com.example.databaseconnector.connector;
import javax.sql.DataSource;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.io.ClassPathResource;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
public class DatabaseConnection implements InitializingBean {
private DataSource dataSource;
private DataSourceTransactionManager dataSourceTransactionManager;
private SqlSessionFactoryBean sqlSessionFactoryBean;
private MapperScannerConfigurer mapperScannerConfigurer;
public DataSource getDataSource() {
return this.dataSource;
}
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
@Override
public void afterPropertiesSet() throws Exception {
if (getDataSource() == null) {
throw new IllegalArgumentException("Property 'dataSource' is required");
} else {
this.configureDataSourceTransactionManager();
this.configureSqlSessionFactoryBean();
this.configureMapperScannerConfigurer();
}
}
private void configureDataSourceTransactionManager() {
this.dataSourceTransactionManager = new DataSourceTransactionManager();
this.dataSourceTransactionManager.setDataSource(dataSource);
}
private void configureSqlSessionFactoryBean() {
this.sqlSessionFactoryBean = new SqlSessionFactoryBean();
this.sqlSessionFactoryBean.setDataSource(dataSource);
this.sqlSessionFactoryBean.setConfigLocation(
new ClassPathResource("mybatis-config.xml"));
this.sqlSessionFactoryBean.setTypeAliasesPackage(
"com.example.sewp5databaseconnector.domain");
}
private void configureMapperScannerConfigurer() {
this.mapperScannerConfigurer = new MapperScannerConfigurer();
this.mapperScannerConfigurer.setBasePackage(
"com.example.databaseconnector.persistence");
this.mapperScannerConfigurer.setSqlSessionFactoryBeanName("sqlSessionFactoryBean");
}
}
When I run WebApp1 the program fails because it could not find the TableMapper bean to autowire the mapper in the service. What Spring setup am I missing to get this to all work together, or am I going about this the completely wrong way?
回答1:
You seem to be missing your Spring configuration for the TableMapper
bean. That class need to be a Spring bean in order for it to be injected into Service
.
Add the relevant bean configuration to your applicationContext.xml
回答2:
Spring context hierarchy can help in common code scenario. If you have multiple webapps in a single EAR, EAR can have its own context, which is the parent of the individual webapp contexts. Also in each webapp, you can have one root context and individual children context as well. You can define this hierarchy in web.xml. Parent context can be specified through context parameters: locatorFactorySelector and parentContextKey. Root context through context parameter contextConfigLocation(outside servlet). Child context can be specified in the init param - context parameter of each of the servlet.
Have one jar in EAR holding all your common service and DAO layer code and define them in beanRefContext.xml(which is basically another application context xml). make this jar available in classpath.
In web.xml of each app where you want to refer parent context code:
<!-- root application context -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:rootContextBeans.xml</param-value>
</context-param>
<!-- shared service layer - parent application context -->
<context-param>
<param-name>locatorFactorySelector</param-name>
<param-value>classpath:beanRefContext.xml</param-value>
</context-param>
<context-param>
<param-name>parentContextKey</param-name>
<param-value>servicelayer-context</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<servlet>
<servlet-name>dispatcherServletApp1</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:webApp1.xml</param-value>
</init-param>
</servlet>
where beanRefContext.xml will be like:
<beans>
<bean id="servicelayer-context" class="org.springframework.context.support.ClassPathXmlApplicationContext">
<constructor-arg>
<list>
<value>data-layer-context.xml</value>
</list>
</constructor-arg>
</bean>
</beans>
This way you will achieve what you want.
来源:https://stackoverflow.com/questions/22871879/how-to-build-and-wire-spring-database-dependency-jar-project-for-use-with-multi