Java/Spring/ Apache Cxf
Webアプリで作業していて、明らかに単純な変更を行っているときに突然エラーが発生し、
25-Aug-2017 11:48:43.036 INFO [RMI TCP Connection(2)-127.0.0.1] org.Apache.jasper.servlet.TldScanner.scanJars At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
log4j:WARN No appenders could be found for logger (org.Apache.cxf.common.logging.LogUtils).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.Apache.org/log4j/1.2/faq.html#noconfig for more info.
25-Aug-2017 11:48:43.540 SEVERE [RMI TCP Connection(2)-127.0.0.1] org.Apache.catalina.core.StandardContext.startInternal One or more listeners failed to start. Full details will be found in the appropriate container log file
25-Aug-2017 11:48:43.554 SEVERE [RMI TCP Connection(2)-127.0.0.1] org.Apache.catalina.core.StandardContext.startInternal Context [] startup failed due to previous errors
[2017-08-25 11:48:43,586] Artifact jaxrs-tutorials:war exploded: Error during artifact deployment. See server log for details.
25-Aug-2017 11:48:49.258 INFO [localhost-startStop-1] org.Apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [/Applications/Tomcat-8.5.16/webapps/manager]
25-Aug-2017 11:48:49.310 INFO [localhost-startStop-1] org.Apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/Applications/Tomcat-8.5.16/webapps/manager] has finished in [51] ms
これがエラーの主なinfo
だと思いますが、
25-Aug-2017 11:48:43.540 SEVERE [RMI TCP Connection(2)-127.0.0.1] org.Apache.catalina.core.StandardContext.startInternal One or more listeners failed to start. Full details will be found in the appropriate container log file
25-Aug-2017 11:48:43.554 SEVERE [RMI TCP Connection(2)-127.0.0.1] org.Apache.catalina.core.StandardContext.startInternal Context [] startup failed due to previous errors
プロジェクト構造が提供され、
config
ディレクトリはJava annotation based config
に使用され、コードは以下のとおりです。
AppConfig
ファイル、
@Configuration
@ComponentScan(AppConfig.SERVICE_PACKAGE)
public class AppConfig {
public static final String BASE_PACKAGE = "mobi.puut";
public static final String SERVICE_PACKAGE = BASE_PACKAGE + ".services";
private static final String RESOURCES_PACKAGE = BASE_PACKAGE + ".rest";
private static final String PROVIDER_PACKAGE = BASE_PACKAGE + ".rest.provider";
public static final String API_BASE = "/api/*";
@ApplicationPath("/")
public class JaxRsApiApplication extends Application {
}
@Bean(destroyMethod = "shutdown")
public SpringBus cxf() {
return new SpringBus();
}
@Bean
@DependsOn("cxf")
public Server jaxRsServer(ApplicationContext appContext) {
JAXRSServerFactoryBean factory = RuntimeDelegate.getInstance().createEndpoint(jaxRsApiApplication(), JAXRSServerFactoryBean.class);
factory.setServiceBeans(restServiceList(appContext));
factory.setAddress("/" + factory.getAddress());
factory.setProviders(restProviderList(appContext, jsonProvider()));
return factory.create();
}
@Bean
public JaxRsApiApplication jaxRsApiApplication() {
return new JaxRsApiApplication();
}
@Bean
public JacksonJsonProvider jsonProvider() {
return new JacksonJsonProvider();
}
private List<Object> restServiceList(ApplicationContext appContext) {
return RestServiceBeanScanner.scan(appContext, AppConfig.RESOURCES_PACKAGE);
}
private List<Object> restProviderList(final ApplicationContext appContext,
final JacksonJsonProvider jsonProvider) {
final List<Object> providers = RestProviderBeanScanner.scan(appContext, PROVIDER_PACKAGE);
providers.add(jsonProvider);
return providers;
}
}
WebInitializer
が提供され、
public class WebAppInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
servletContext.addListener(new ContextLoaderListener(createWebAppContext()));
addApacheCxfServlet(servletContext);
}
private void addApacheCxfServlet(ServletContext servletContext) {
CXFServlet cxfServlet = new CXFServlet();
ServletRegistration.Dynamic appServlet = servletContext.addServlet("CXFServlet", cxfServlet);
appServlet.setLoadOnStartup(1);
Set<String> mappingConflicts = appServlet.addMapping(AppConfig.API_BASE);
}
private WebApplicationContext createWebAppContext() {
AnnotationConfigWebApplicationContext appContext = new AnnotationConfigWebApplicationContext();
appContext.register(AppConfig.class);
return appContext;
}
}
私はプラットフォームと同様の質問を見ましたが、それらは私を助けませんし、問題を解決するための情報がほとんどありません。
logger
を使用して詳細情報を入手するにはどうすればよいですか?また、問題の解決に関する洞察も役立ちます。 JAR
ファイルをmvn clean install
およびmvn idea:idea
コマンドで更新しました。
UPDATE
Tomcat Localhost Log
Tomcat Catalina Log
この問題の詳細な回答と、問題を解決するために実行した手順を書きたいと思います。
A.十分なログ情報がありませんでした。私はPOM
を調べて、これを見つけました、
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
<exclusions>
<!-- Exclude Commons Logging in favor of SLF4j -->
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
SLF4J
を優先してロギングを除外し、さらに詳細なロギング情報を取得するために除外XMLタグを削除しました。なので、こうなります、
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
B.今、私はロギング情報を取得します。私はこのようなエラーメッセージを持っています、
Caused by: org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name 'userService' for bean class [mobi.puut.services.UserServiceImpl] conflicts with existing, non-compatible bean definition of same name and class [mobi.puut.services.UserService2Impl]
同じ名前のBeanを作成するためのConflictingBeanDefinitionException
を取得します。 Spring
の使用経験はありますが、Apache Cxf
を使用してプロジェクトを作成する必要があるため、まさに目的のためにデモプロジェクトを複製します。それらは1つの同じエンティティUser
と同じdefinition - interface
と同じimplementation
を持っています。プロジェクト内のすべてのクラスにrefactored
があり、明らかなエラーはないように見えますが、問題は解決していません。インターフェイスの2つの実装で同じBean名"userService
を使用しています。
エンティティのUser2
クラス、
public class User2 {
private Integer id;
private String name;
public User2() {
}
public User2(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public String toString() {
return String.format("{id=%s,name=%s}", id, name);
}
}
エンティティのUser
クラス、
@Entity
@Table(name = "users")
public class User {
@Id
@Column
@NotNull
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
@NotNull
@Column(name = "name")
@Size(min = 5, max = 45, message = "Name must be between 5 and 45 characters.")
private String name;
public User() {
}
public User(int id, String name) {
super();
this.id = id;
this.name = name;
}
public User(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof User)) return false;
User user = (User) o;
if (getId() != user.getId()) return false;
return getName().equals(user.getName());
}
@Override
public int hashCode() {
int result = getId();
result = 31 * result + getName().hashCode();
return result;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
services
ディレクトリ内のインターフェース、
public interface IUserService2 {
Collection<User2> getUsers();
User2 getUser(Integer id);
Response add(User2 user);
}
public interface IUserService {
List<User> getCurrentStatuses();
void create(User user);
List<User> getAllUsers();
}
services
derectory内のインターフェースの実装、
@Service("userService")
public class UserService2Impl implements IUserService2 {
private static Map<Integer, User2> users = new HashMap<Integer, User2>();
static {
users.put(1, new User2(1, "foo"));
users.put(2, new User2(2, "bar"));
users.put(3, new User2(3, "baz"));
}
public UserService2Impl() {
}
@Override
public Collection<User2> getUsers() {
return users.values();
}
@Override
public User2 getUser(Integer id) {
return users.get(id);
}
@Override
public Response add(User2 user) {
user.setId(users.size()+1);
users.put(user.getId(), user);
//do more stuff to add user to the system..
return Response.status(Response.Status.OK).build();
}
}
@Service("userService")
public class UserServiceImpl implements IUserService {
@Autowired
@Qualifier("userDao")
public IUserDao userDao;
public List<User> getCurrentStatuses() {
return userDao.getAllUsers();
}
public void create(User user) {
userDao.saveOrUpdate(user);
}
public List<User> getAllUsers() {
List<User> users = userDao.getAllUsers();
if (Objects.isNull(users)) {
return null;
}
return users;
}
}
そして、error
がありました-"userService"
という同じBean名の2つのクラスに注釈を付けました。 Beanに別の名前を付けるには、このように変更する必要がありました。
@Service("user2Service")
public class UserService2Impl implements IUserService2 {
// some code
}
そして、そのエラーはなくなります。つまり、同じ名前の2つのBeanによるエラーConflictingBeanDefinitionException
であり、別の名前を指定する必要がありました。
C.まだ修正すべきことがありました。その後、プログラムを実行すると、
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userService': Unsatisfied dependency expressed through field 'userDao'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'mobi.puut.database.IUserDao' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true), @org.springframework.beans.factory.annotation.Qualifier(value=userDao)}
// some consequent error messages not necessay to solve the issue
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'mobi.puut.database.IUserDao' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true), @org.springframework.beans.factory.annotation.Qualifier(value=userDao)}
ここでのエラーはUnsatisfiedDependencyException
であり、userService
という名前のBeanを作成できないことを示しています。 OK、これは私が持っていてプロジェクトに統合したコードです。フィールド、つまりuserDao
によって表現された、満たされていない依存関係。 userDao
はIUserDao
のインターフェースのインスタンスであり、これは@autowired
でした。
@Autowired
public IUserDao userDao;
ここにコードのより深い洞察があります、
@Service("userService")
public class UserServiceImpl implements IUserService {
@Autowired
public IUserDao userDao;
public List<User> getCurrentStatuses() {
return userDao.getAllUsers();
}
public void create(User user) {
userDao.saveOrUpdate(user);
}
public List<User> getAllUsers() {
List<User> users = userDao.getAllUsers();
if (Objects.isNull(users)) {
return null;
}
return users;
}
}
database
ディレクトリにインターフェイスがあり、その結果ユーザーのためにインターフェイスが実装されています。インターフェース名はIUserDao
で、
soemthing like this,
public interface IUserDao {
boolean create(User user);
void saveOrUpdate(User user);
boolean create(List<User> users);
List<User> getAllUsers();
User getById(int id);
}
そして、実装、
@Repository("userDao")
public class UserDaoImpl implements IUserDao {
@Autowired
private SessionFactory sessionFactory;
// the HQL queries
}
エラーメッセージの結果として生じる部分はNoSuchBeanDefinitionException
であり、アプリはIUserDao
のタイプ(クラス)の正規のBeanを見つけません。 HQL
の実装にすべてのIUserDao
クエリがあり、コードは以前は完全に機能します。
私は少し考える必要があります。最後に、database
レイヤーがNOT
がアプリに統合されているかもしれないという直感があります。ここに私が使ったconfiguration
があります、
public class WebAppInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
servletContext.addListener(new ContextLoaderListener(createWebAppContext()));
addApacheCxfServlet(servletContext);
}
private void addApacheCxfServlet(ServletContext servletContext) {
CXFServlet cxfServlet = new CXFServlet();
ServletRegistration.Dynamic appServlet = servletContext.addServlet("CXFServlet", cxfServlet);
appServlet.setLoadOnStartup(1);
Set<String> mappingConflicts = appServlet.addMapping(AppConfig.API_BASE);
}
private WebApplicationContext createWebAppContext() {
AnnotationConfigWebApplicationContext appContext = new AnnotationConfigWebApplicationContext();
// register all the config classes here
appContext.register(AppConfig.class);
return appContext;
}
}
明らかに、データベースコードはWebInitializer
に統合されていません。私は、database connection
とhibernate integration
のすべての情報を提供する新しいクラスを作成しました。
@Configuration
@EnableWebMvc
@EnableTransactionManagement
@ComponentScan(basePackages = {"mobi.puut.database"})
public class DatabaseConfig {
@Bean
public LocalSessionFactoryBean sessionFactory() {
// mobi.puut.entities
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
sessionFactory.setPackagesToScan(
new String[]{"mobi.puut.entities"});
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
}
@Bean
@Autowired
public HibernateTransactionManager transactionManager(
SessionFactory sessionFactory) {
HibernateTransactionManager txManager
= new HibernateTransactionManager();
txManager.setSessionFactory(sessionFactory);
return txManager;
}
@Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
@Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
// dataSource.setUrl("jdbc:mysql://localhost:3306/wallet?createDatabaseIfNotExist=true");
dataSource.setUrl("jdbc:mysql://localhost:3306/Wallet");
dataSource.setUsername("testuser");
dataSource.setPassword("testpassword");
return dataSource;
}
Properties hibernateProperties() {
Properties properties = new Properties();
// properties.setProperty("hibernate.hbm2ddl.auto", "create-drop");
properties.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
return properties;
}
}
そして、最後にWebInitializer
に統合します。これは、以前にregistered
で設定をWebInitializer
するために使用したコードです。
appContext.register(AppConfig.class);
で行を更新しました、
appContext.register(AppConfig.class, DatabaseConfig.class);
最後に、すべてが正常に動作します。したがって、config
ディレクトリは次のようになります。
SUMMERY
この問題を3つのエラーで解決しなければなりませんでした。
i. ConflictingBeanDefinitionException
ii. UnsatisfiedDependencyException
iii. NoSuchBeanDefinitionException
ConflictingBeanDefinitionException
->同じ名前の2つの豆
UnsatisfiedDependencyException
->クラスに(= "userDao")
のBeanがある
NoSuchBeanDefinitionException
->コードは正しかったが、Spring IoC
がBeanを見つけるように、congigにdatbaseレイヤーを追加する必要がありました。
これが一部の人々に役立つことを心から願っています。