まず、トランザクション構成の問題であると思われるので、同じトランザクション構成で単純な spring-jdbc プロジェクトを作成します。正常に動作し、ログを確認して、「BeanPostProcessor」と関係がある可能性があることを確認します。以下のログ、私はこれに多くの時間を費やしましたが、運が悪かったので、助けていただければ幸いです。
INFO : org.springframework.web.context.support.AnnotationConfigWebApplicationContext - Bean 'securityManager' of type [class org.apache.shiro.web.mgt.DefaultWebSecurityManager] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
INFO : org.springframework.web.context.support.AnnotationConfigWebApplicationContext - Bean 'configAuthorizationAttributeSourceAdvisor' of type [class org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
INFO : org.springframework.web.context.support.AnnotationConfigWebApplicationContext - Bean 'transactionAttributeSource' of type [class org.springframework.transaction.annotation.AnnotationTransactionAttributeSource] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
INFO : org.springframework.web.context.support.AnnotationConfigWebApplicationContext - Bean 'transactionInterceptor' of type [class org.springframework.transaction.interceptor.TransactionInterceptor] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
INFO : org.springframework.web.context.support.AnnotationConfigWebApplicationContext - Bean 'org.springframework.transaction.config.internalTransactionAdvisor' of type [class org.springframework.transaction.interceptor.BeanFactoryTransactionAttributeSourceAdvisor] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
INFO : com.mchange.v2.log.MLog - MLog clients using log4j logging.
INFO : com.mchange.v2.c3p0.C3P0Registry - Initializing c3p0-0.9.1.2 [built 21-May-2007 15:04:56; debug? true; trace: 10]
INFO : org.springframework.web.context.support.AnnotationConfigWebApplicationContext - Bean 'dataSource' of type [class com.mchange.v2.c3p0.ComboPooledDataSource] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
INFO : org.springframework.web.context.support.AnnotationConfigWebApplicationContext - Bean 'jdbcTemplate' of type [class org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
INFO : org.springframework.web.context.support.AnnotationConfigWebApplicationContext - Bean 'fileStoreConfig' of type [class com.myapp.config.FileStoreConfig$$EnhancerByCGLIB$$e05149b7] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
INFO : org.springframework.web.context.support.AnnotationConfigWebApplicationContext - Bean 'pictureStorage' of type [class com.myapp.core.data.S3FileStorage] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
INFO : org.springframework.web.context.support.AnnotationConfigWebApplicationContext - Bean 'userDao' of type [class com.myapp.account.dao.UserDao] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
INFO : org.springframework.web.context.support.AnnotationConfigWebApplicationContext - Bean 'ticketDao' of type [class com.myapp.common.dao.TicketDao] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
INFO : org.springframework.web.context.support.AnnotationConfigWebApplicationContext - Bean 'ticketService' of type [class com.myapp.common.service.impl.TicketServiceImpl] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
INFO : org.springframework.web.context.support.AnnotationConfigWebApplicationContext - Bean 'imageDao' of type [class com.myapp.common.dao.ImageDao] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
INFO : org.springframework.web.context.support.AnnotationConfigWebApplicationContext - Bean 'imageService' of type [class com.myapp.common.service.impl.ImageServiceImpl] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
INFO : org.springframework.web.context.support.AnnotationConfigWebApplicationContext - Bean 'mailConfig' of type [class com.myapp.config.MailConfig$$EnhancerByCGLIB$$196df0ef] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
アプリ構成を追加
AppConfig.java
@Configuration
@ComponentScan(basePackages = {"com.myapp.account.web", "com.myapp.account.interceptor", "com.myapp.account.security", "com.myapp.common.web", "com.myapp.email"})
@PropertySource(value = {"classpath:config.properties", "classpath:app.properties"})
public class AppConfig {
}
DataConfig.java
@Configuration
@ComponentScan(basePackages = {"com.myapp.account.service", "com.myapp.account.dao", "com.myapp.common.service", "com.myapp.common.dao"}) // Specifies which package to scan
@EnableTransactionManagement
public class DataConfig {
@Inject
private Environment environment;
@Bean(destroyMethod="close")
public ComboPooledDataSource dataSource() throws PropertyVetoException {
ComboPooledDataSource cpds = new ComboPooledDataSource();
cpds.setDriverClass(environment.getProperty("jdbc.driverClassName"));
cpds.setJdbcUrl(environment.getProperty("jdbc.url"));
cpds.setUser(environment.getProperty("jdbc.username"));
cpds.setPassword(environment.getProperty("jdbc.password"));
cpds.setInitialPoolSize(environment.getProperty("jdbc.initialPoolSize", Integer.class));
cpds.setMinPoolSize(environment.getProperty("jdbc.minPoolSize", Integer.class));
cpds.setMaxPoolSize(environment.getProperty("jdbc.maxPoolSize", Integer.class));
cpds.setCheckoutTimeout(environment.getProperty("jdbc.checkoutTimeout", Integer.class));
cpds.setMaxIdleTime(environment.getProperty("jdbc.maxIdleTime", Integer.class));
cpds.setIdleConnectionTestPeriod(environment.getProperty("jdbc.idleConnectionTestPeriod", Integer.class));
cpds.setAcquireIncrement(environment.getProperty("jdbc.acquireIncrement", Integer.class));
return cpds;
}
@Bean
public NamedParameterJdbcTemplate jdbcTemplate() throws PropertyVetoException {
return new NamedParameterJdbcTemplate(dataSource());
}
/**
* Allows transactions to be managed against the RDBMS using the JDBC API.
* @throws PropertyVetoException
*/
@Bean
public PlatformTransactionManager transactionManager() throws PropertyVetoException {
return new DataSourceTransactionManager(dataSource());
}
/**
@Bean
public DataSourceTransactionManager transactionManager() throws PropertyVetoException {
return new DataSourceTransactionManager(dataSource());
}
*/
@Bean
public UserDao userDao() throws PropertyVetoException {
return new UserDao(jdbcTemplate());
}
@Bean
public TicketDao ticketDao() throws PropertyVetoException {
return new TicketDao(jdbcTemplate());
}
}
SecurityConfig.java
@Configuration
public class SecurityConfig {
@Bean
public Realm configureShiroOAuthDbRealm() {
return new ShiroOAuthDbRealm();
}
@Bean(name = "ehCacheManager")
public EhCacheManager configCacheManager() {
return new EhCacheManager();
}
@Bean(name = "securityManager")
public DefaultWebSecurityManager configSecurityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
ArrayList<Realm> realms = new ArrayList<Realm>();
realms.add(configureShiroPasswordDbRealm());
realms.add(configureShiroTicketDbRealm());
realms.add(configureShiroOAuthDbRealm());
securityManager.setRealms(realms);
securityManager.setCacheManager(configCacheManager());
return securityManager;
}
@Bean(name = "lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessor configLifecycleBeanPostProcessor() {
LifecycleBeanPostProcessor lifecycleBeanPostProcessor = new LifecycleBeanPostProcessor();
return lifecycleBeanPostProcessor;
}
/**
* Enable Shiro Annotations for Spring-configured beans.
* Only run after the lifecycleBeanProcessor has run.
*/
@Bean
public DefaultAdvisorAutoProxyCreator configDefaultAdvisorAutoProxyCreator() {
return new DefaultAdvisorAutoProxyCreator();
}
@Bean
public AuthorizationAttributeSourceAdvisor configAuthorizationAttributeSourceAdvisor() {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(configSecurityManager());
return authorizationAttributeSourceAdvisor;
}
@Bean(name = "shiroFilter")
public ShiroFilterFactoryBean configShiroFilterFactoryBean() {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(configSecurityManager());
shiroFilterFactoryBean.setLoginUrl("/login");
shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized");
shiroFilterFactoryBean.setSuccessUrl("/");
Map<String, javax.servlet.Filter> filters = new HashMap<String, javax.servlet.Filter>();
filters.put("authc", new PassThruAuthenticationFilter());
shiroFilterFactoryBean.setFilters(filters);
Map<String, String> filterChainDefinitionMap = new HashMap<String, String>();
filterChainDefinitionMap.put("/control_panel/**", "authc, roles[administrator]");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
}
UserController.java (userService からテスト メソッドtestTransactionを呼び出します)
@Controller
public class UserController {
private static final Logger logger = LoggerFactory.getLogger(UserController.class);
@Resource(name="userService")
protected UserService userService;
@Resource(name="ticketService")
protected TicketService ticketService;
@RequestMapping(value = "/test-transaction", method = RequestMethod.GET)
public String testTransaction(Model model, HttpServletRequest request) throws Exception {
try{
User user = new User();
user.setUsername("test.transaction");
userService.testTransaction(user);
}catch(Exception e) {
logger.error(e.getMessage(), e);
throw e;
}
return "redirect:/";
}
}
UserServiceImpl.java ( testTransactionはRuntimeException をスローするテストメソッドです)
@Service("userService")
public class UserServiceImpl implements UserService {
/**
*
*/
private static final long serialVersionUID = 1L;
@Resource
private UserDao userDao;
@Resource
private TicketService ticketService;
@Transactional
public void testTransaction(User user) throws SystemException {
userDao.add(user);
Long.valueOf("Throw RuntimeException");
}
}
UserDao.java
public class UserDao extends BaseDao {
/**
*
*/
private static final long serialVersionUID = 1L;
public UserDao(NamedParameterJdbcTemplate jdbcTemplate) {
super(jdbcTemplate);
}
private static final String ADD = "INSERT INTO user_(username, email, password_, create_date, modified_date, status_) VALUES(:username, :email, :password, :createDate, :modifiedDate, :status)";
public long add(User user) throws SystemException {
try{
return updateForLongKey(ADD, user);
}catch(Exception e) {
throw processException(e);
}
}
}
BaseDao.java
public class BaseDao implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
protected NamedParameterJdbcTemplate jdbcTemplate;
protected BaseDao(NamedParameterJdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
protected <T> long updateForLongKey(final String sql, T t) {
SqlParameterSource paramSource = new BeanPropertySqlParameterSource(t);
KeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(sql, paramSource, keyHolder);
return keyHolder.getKey().longValue();
}
public SystemException processException(Exception e) {
if (!(e instanceof DataAccessException)) {
logger.error("Caught unexpected exception " + e.getClass().getName());
}
if (logger.isDebugEnabled()) {
logger.debug(e.getMessage(), e);
}
return new SystemException(e);
}
}