spring管理beanをpojoから呼び出す

# やりたいこと
springで管理されていないクラスからspring管理beanを呼び出したいケースがたまにあるので、実装方法をまとめる。

# 環境
java8
javaee7
spring4.3.9
eclipse4.6

# 実装案
* Spring管理beanにstaticアクセス
* @Configurable
* SpringBeanAutowiringInterceptor

※3つ目はJavaEEに限った方法なので注意。

## 呼び出したいサービス
```java:呼び出したいサービス
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メソッド経由でアクセスする。

```java:ApplicationContextを保持するSpring管理bean
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;
    }
}
```

```java:pojoクラス
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している個所を差し替える(たぶん。ソース確認したわけではないので推測)。

```java:@Configurableでspring管理beanになるクラス
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();
    }
}
```

```java:pojoクラス
package com.configurable;

public class Configurable {
    public static void main(String[] args) {
        // newで生成してもDIされる！
        new ConfigurableClient().execute();
    }
}
```

```xml:application-context.xml
<?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>
```

```xml: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>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>

```

```bash:mavenでcompileと実行
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)

```java:EJBクラス
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アクセス」のほうがシンプルで良さそう。