やりたいこと
springで管理されていないクラスからspring管理beanを呼び出したいケースがたまにあるので、実装方法をまとめる。
環境
java8
javaee7
spring4.3.9
eclipse4.6
実装案
- Spring管理beanにstaticアクセス
- @Configurable
- SpringBeanAutowiringInterceptor
※3つ目はJavaEEに限った方法なので注意。
呼び出したいサービス
package com;
import org.springframework.stereotype.Service;
@Service
public class TestService {
public void execute(){
System.out.println("com.TestService#execute");
}
}
案1 Spring管理beanにstaticアクセス
ApplicationContextを保持したSpring管理beanを作成して、pojoからstaticメソッド経由でアクセスする。
package com.bridge;
import com.TestService;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class SpringContextBridge implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Autowired
private TestService testService;
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public static SpringContextBridge getInstance() {
return applicationContext.getBean(SpringContextBridge.class);
}
public TestService getTestService() {
return testService;
}
}
package com.bridge;
public class SpringContextBridgeClient {
public static void main(String[] args) {
SpringContextBridge.getInstance().getTestService().execute();
}
}
参考サイト
https://blog.jdriven.com/2015/03/using-spring-managed-bean-in-non-managed-object/
案2 @Configurable
AspectJを使ってコンパイル時に、@Configurableを付けたクラスをnewしている個所を差し替える(たぶん。ソース確認したわけではないので推測)。
package com.configurable;
import com.TestService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
@Configurable
public class ConfigurableClient {
@Autowired
private TestService testService;
public void execute() {
testService.execute();
}
}
package com.configurable;
public class Configurable {
public static void main(String[] args) {
// newで生成してもDIされる!
new ConfigurableClient().execute();
}
}
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd">
<context:spring-configured/>
<context:component-scan base-package="com.configurable"/>
<context:annotation-config />
</beans>
<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>test1</groupId>
<artifactId>artifact1</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>artifact1</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<aspectj.version>1.6.12</aspectj.version>
<spring.version>4.3.9.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>persistence-api</artifactId>
<version>1.0.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.4</version>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>${aspectj.version}</version>
</dependency>
</dependencies>
<executions>
<execution>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
<configuration>
<aspectLibraries>
<aspectLibrary>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</aspectLibrary>
</aspectLibraries>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
mvn clean compile
mvn exec:java -Dexec.mainClass="com.configurable.Configurable"
mavenコマンドではなくeclipseで実行したい場合
eclipseでpom.xmlを開くと下記エラー出ているはず。
ライフサイクル構成でカバーされていないプラグインの実行: org.codehaus.mojo:aspectj-maven-plugin:1.4:compile (実行: default, phase: compile)
aspectjのcompileを使えるようにAJDTを入れやる必要がある。
AJDTインストール
- 「ヘルプ」-「新規ソフトウェアのインストール」-「http://www.eclipse.org/ajdt/downloads/」
- 「AspectJ 開発ツール」(AJDT)を選択してインストール
※他のオプションも一緒にインストールしたら後続手順でエラーになったので、いったんAJDTだけ入れるのがよさそう
- pom.xmlのエラー個所をクリックして
「新規 m2eコネクターを発見する」でコネクターをインストール
これで実行できる!
参考サイト
http://ryu-htn.hatenablog.com/entry/2013/04/25/151240
https://github.com/kenyattaclark/Spring-Configurable-Example
https://tamasgyorfi.net/2013/11/13/spring-dependency-injection-configurable/
参考サイト(eclipseエラー)
http://tyru.hatenablog.com/entry/20140905/eclipse_lifecycle_error
案3 SpringBeanAutowiringInterceptor
JavaEEのInterceptorと、SpringのSpringBeanAutowiringInterceptorを使う。
下記はEJBクラスをSpring管理Beanにした例。
(もちろんEJBじゃなくてもOK。@Stateless, @RemoteはEJBのアノテーションなので消してOK)
package com.autowired;
import com.TestService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ejb.interceptor.SpringBeanAutowiringInterceptor;
import javax.ejb.Remote;
import javax.ejb.Stateless;
import javax.interceptor.Interceptors;
@Stateless
@Remote
@Interceptors(SpringBeanAutowiringInterceptor.class)
public class TestEjb {
@Autowired
private TestService testService;
public void execute() {
testService.execute();
}
}
評価
- javaEEを使っているなら「案3 SpringBeanAutowiringInterceptor」が一番シンプルで良いと思う。
- javaEEを使っていないなら「案1 Spring管理beanにstaticアクセス」が良いと思う。
※初めは「案2 @Configurable」が良さそうに見えたが、すべてのnewを差し替えるのは何か課題がありそう(テストモックとか)。
AspectJのライブラリが追加が必要な点も考慮すると「案1 Spring管理beanにstaticアクセス」のほうがシンプルで良さそう。