Skip to content

Разделяемый бин JSR-330 для разных контейнеров IoC

Синопсис

Иногда очень часто бины выполняют достаточно общие операции, это может быть обращение к базе данных или вывод чего-нибудь куда-нибудь, в общем в разных бинах приходится реализовывать схожую логику. В этом случае, если в разных приложениях, запускаемые в разных контейнерах, будет схожий функционал, то нам придется переписывать одинаковый код для разных бинов в разных приложениях. Поэтому хорошим решением будет написать некий общий бин, который будет использоваться во всех приложениях один раз и везде, где он будет требоваться просто подключать к этому приложению библиотеку с этим бином. В этом подходе есть только один недостаток, если бин аннотирован спринговыми аннотациями, а приложение, которому требуется логика из этого бина построено с использованием контейнера Google Guice, то такой бин становится не совместимым с этим приложением. Такой проблемы не будет, если этот бин будет старого типа, то есть POJO класс. В этом случае достаточно прописать этот бин в конфигурационном файле или в конфигурационном классе. Но если этот бин с аннотациями то его можно использовать только в том контейнере, который эти аннотации поддерживает. Чтобы было возможно использовать аннотированный бин в разных контейнерах, то его аннотации должны соответствовать аннотациям, которые поддерживаются всеми контейнерами. Одним из таких стандартов можно называть JSR-330 который мы рассмотрим в этом посте.

Пример проекта

Создадим 3 разных проекта. Два проекта будут запускаться в IoC контейнерах, которые будут называться Spring и Google guice и будут запускаться соответственно в spring и google guice контейнерах. Тритий проект будет называться JSR330 в котором будут создаваться независимые от контейнера бины. Каждое приложение, запущенное на spring или google guice будет зависеть от того бина, которой создается в проекте JSR330.

Схема проекта

Ниже представлены структуры всех трех проектов, так же показано какой класс от какого зависит:

Проект JSR-330

jsr330
    ├─src
    │   └─main
    │       └─java
    │           └─com
    │               └─dev
    │                   └─blogs
    │                       └─jsr330            ┌─────────────────────────────┐
    │                           └─service       ↓                             │ 
    │                               ├─MessageService.java<────────────────────┼────────┐
    │                               └─impl                                    │        │
    │                                   └─MessageServiceImpl.java             │        │
    └─pom.xml                                                                 │        │

Проект Spring

spring                                                                        │        │
    ├─src                                                                     │        │
    │   └─main                               Класс ServiceConsumer обращается │        │
    │       ├─java                                к классу MessageServiceImpl │        │
    │       │   └─com                          через интерфейс MessageService │        │
    │       │       └─dev                                                     │        │
    │       │           └─blogs                                               │        │
    │       │               └─spring                                          │        │
    │       │                   ├─App.java                                    │        │
    │       │                   └─ServiceConsumer.java────────────────────────┘        │
    │       └─resources                                                                │
    │           └─lookup.xml                                                           │
    └─pom.xml                                                                          │

Проект Google guice

google-guice                                                                           │
    ├─src                                                                              │
    │   └─main                                                                         │
    │       └─java                                                                     │
    │           └─com                                                                  │ Класс ServiceConsumer обращается
    │               └─dev                                                              │ к классу MessageServiceImpl
    │                   └─blogs                                                        │ через интерфейс MessageService
    │                       └─google                                                   │
    │                           └─guice                                                │
    │                               ├─App.java                                         │
    │                               ├─Configurer.java                                  │
    │                               └─ServiceConsumer.java─────────────────────────────┘
    └─pom.xml

Обратим внимание, что у контейнеров spring и google guice разные инфраструктуры, и, соответственно, они по разному работают с сёрд пати бином. Приложение на спринге использует для этого конфигурационный xml файл в котором прописывается информация из какого класса создается бин. Так же это делается в приложении гугл гуйс, но для этого используется конфигурационный класс Configurer. При этом проекту JSR-330 все равно, какой контейнер и как будет создавать бин из класса, который он предоставляет.

Проект JSR-330

Интерфейс MessageService.java

package com.dev.blogs.jsr330.service;

public interface MessageService {
	public String getMessage();
}

Класс MessageServiceImpl.java

package com.dev.blogs.jsr330.service.impl;

import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;

import com.dev.blogs.jsr330.service.MessageService;

@Named("messageService")
@Singleton
public class MessageServiceImpl implements MessageService {
	@Inject
	@Named("message")
	private String message = "Default message";

	public String getMessage() {
		return message;
	}
}

Конфигурационный файл 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.dev.blogs</groupId>
	<artifactId>jsr330</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>jsr330</name>
	<url>http://maven.apache.org</url>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>

	<dependencies>
		<dependency>
			<groupId>javax.inject</groupId>
			<artifactId>javax.inject</artifactId>
			<version>1</version>
		</dependency>

		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.11</version>
			<scope>test</scope>
		</dependency>
	</dependencies>
</project>

Проект Spring

Класс ServiceConsumer.java

package com.dev.blogs.spring;

import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;

import com.dev.blogs.jsr330.service.MessageService;

@Named("serviceConsumer")
@Singleton
public class ServiceConsumer {
	@Inject
	private MessageService messageService;
	
	public void print() {
		System.out.println(messageService.getMessage());
	}
}

Класс App.java

package com.dev.blogs.spring;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.dev.blogs.jsr330.service.MessageService;

public class App {
	public static void main(String[] args) {
		ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("lookup.xml");
		context.refresh();
		
		ServiceConsumer serviceConsumer = context.getBean("serviceConsumer", ServiceConsumer.class);
		serviceConsumer.print();
	}
}

Конфигурационный файл lookup.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:util="http://www.springframework.org/schema/util"
	xmlns:p="http://www.springframework.org/schema/p"
	xmlns:c="http://www.springframework.org/schema/c"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
	http://www.springframework.org/schema/context
	http://www.springframework.org/schema/context/spring-context-3.1.xsd
	http://www.springframework.org/schema/util
	http://www.springframework.org/schema/util/spring-util-3.1.xsd">
	<context:component-scan base-package="com.dev.blogs"/>
	<bean id="message" class="java.lang.String">
		<constructor-arg value="Spring. Test message"/>
	</bean>
</beans>

В билд файл добавлена зависимость на джарник созданный в проекте JSR-330 строчки 19-23:

Конфигурационный файл 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.dev.blogs</groupId>
	<artifactId>spring</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>spring</name>
	<url>http://maven.apache.org</url>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<spring.version>3.2.13.RELEASE</spring.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>com.dev.blogs</groupId>
			<artifactId>jsr330</artifactId>
			<version>0.0.1-SNAPSHOT</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>javax.inject</groupId>
			<artifactId>javax.inject</artifactId>
			<version>1</version>
		</dependency>

		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.11</version>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.codehaus.mojo</groupId>
				<artifactId>exec-maven-plugin</artifactId>
				<version>1.2.1</version>
				<executions>
					<execution>
						<goals>
							<goal>java</goal>
						</goals>
					</execution>
				</executions>
				<configuration>
					<mainClass>com.dev.blogs.spring.App</mainClass>
					<arguments>
						<argument>foo</argument>
						<argument>bar</argument>
					</arguments>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

Здесь с помощью тега build мавен подтягивает плагин с помощью которого мы позже запустим джарник как обычное приложение, то есть этот плагин позволят запустить мэйн метод.

Проект Google guice

Класс ServiceConsumer.java

package com.dev.blogs.google.guice;

import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;

import com.dev.blogs.jsr330.service.MessageService;

@Named("serviceConsumer")
@Singleton
public class ServiceConsumer {
	@Inject
	private MessageService messageService;
	
	public void printMessage() {
		System.out.println(messageService.getMessage());
	}
}

Класс Configurer.java

package com.dev.blogs.google.guice;

import com.dev.blogs.jsr330.service.MessageService;
import com.dev.blogs.jsr330.service.impl.MessageServiceImpl;
import com.google.inject.AbstractModule;
import com.google.inject.name.Names;
import com.google.inject.util.Providers;

public class Configurer extends AbstractModule {
	@Override
	protected void configure() {
		bind(MessageService.class).to(MessageServiceImpl.class);
		bind(String.class).annotatedWith(Names.named("message")).toProvider(Providers.<String>of("Google guice. Test message"));
	}
}

Класс App.java

package com.dev.blogs.google.guice;

import com.google.inject.Guice;
import com.google.inject.Injector;

public class App {
	public static void main(String[] args) {
		Injector injector = Guice.createInjector(new Configurer());        
        ServiceConsumer serviceConsumer = injector.getInstance(ServiceConsumer.class);
        serviceConsumer.printMessage();
	}
}

В билд файл добавлена зависимость на джарник созданный в проекте JSR-330 строчки 19-23:

Конфигурационный файл 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.dev.blogs</groupId>
	<artifactId>google.guice</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>google.guice</name>
	<url>http://maven.apache.org</url>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<google.guice.version>3.0</google.guice.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>com.dev.blogs</groupId>
			<artifactId>jsr330</artifactId>
			<version>0.0.1-SNAPSHOT</version>
		</dependency>
		<dependency>
			<groupId>com.google.inject</groupId>
			<artifactId>guice</artifactId>
			<version>${google.guice.version}</version>
		</dependency>
		<dependency>
			<groupId>javax.inject</groupId>
			<artifactId>javax.inject</artifactId>
			<version>1</version>
		</dependency>

		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.11</version>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.codehaus.mojo</groupId>
				<artifactId>exec-maven-plugin</artifactId>
				<version>1.2.1</version>
				<executions>
					<execution>
						<goals>
							<goal>java</goal>
						</goals>
					</execution>
				</executions>
				<configuration>
					<mainClass>com.dev.blogs.google.guice.App</mainClass>
					<arguments>
						<argument>foo</argument>
						<argument>bar</argument>
					</arguments>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

Так же как для проекта Spring тег build подтягивает плагин, с помощью которого мы запустим мэйн метод.

Сборка и запуск

Для того, чтобы проекты Spring и Google-guice можно было собрать и запустить им нужна зависимость на джарник из проекта JSR-330. По умолчанию, после сборки проекта JSR-330 его джарник положится в локальный мавенский репозиторий. А так как все проекты собираются на той же машине и на любую зависимость в локальном репозитории установлен класспасс, то проекты, которым нужен джарник JSR-330 соберутся без проблем, нужно только в помчике прописать зависимость на JSR-330:

<dependency>
	<groupId>com.dev.blogs</groupId>
	<artifactId>jsr330</artifactId>
	<version>0.0.1-SNAPSHOT</version>
</dependency>

Но если проект JSR-330 был собран на другой машине, затем мы взяли собранный джарник и перенесли его на текущую машину (то есть на машину, где собираются проекты spring и google-guice), то для того, чтобы собрать эти проекты (spring и google-guice, которые зависят от JSR-330), этот джарник (JSR-330) нужно положить в локальный репозиторий вручную. Положить вручную это не значит, что просто взять его туда и скопировать, нужно положить его с помощью мавена, тогда мавен будет знать о нём. Кладется сёрд пати джарник в локальный репозиторий с помощью мавенской команды install:install-file. Чтобы положить наш получившийся после сборки проекта JSR-330 джарник нужно выполнить такую команду:

mvn install:install-file -Dfile=C:\practice\google-guice\jsr330\target\jsr330-0.0.1-SNAPSHOT.jar -DgroupId=com.dev.blogs -DartifactId=jsr330 -Dversion=0.0.1-SNAPSHOT -Dpackaging=jar

После выполнения этой команды в локальном репозитории появится джарник jsr330-0.0.1-SNAPSHOT.jar и класспас, для мавенских сборок, будет ссылаться на этот джарник. Вот тогда можно будет собирать любые проекты зависящие от этой зависимости. Еще раз повторю, делать так надо только в том случае, если проект JSR-330 был собран на другой машине.
А теперь соберем все имеющиеся у нас проекты, начнем с проекта JSR-330:

Сборка и запуск проекта JSR-330

cd /path/to/JSR-330
mvn install

Если будет BUILD SUCCESS, то джарник собран и положен в локальный репозиторий. Теперь соберем остальные проекты, которые от него зависят:

Сборка и запуск проекта Spring

cd /path/to/Spring
mvn install
mvn exec:java -Dexec.mainClass="com.dev.blogs.spring.App"

Результатом должна быть строка Spring. Test message:

[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building spring 0.0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] >>> exec-maven-plugin:1.2.1:java (default-cli) > validate @ spring >>>
[INFO]
[INFO] <<< exec-maven-plugin:1.2.1:java (default-cli) < validate @ spring <<<
[INFO]
[INFO] --- exec-maven-plugin:1.2.1:java (default-cli) @ spring ---

Здесь будет всякий спринговый мусор

Spring. Test message
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.650 s
[INFO] Finished at: 2015-05-20T16:44:05+03:00
[INFO] Final Memory: 6M/134M
[INFO] ------------------------------------------------------------------------

Сборка и запуск проекта Google-guice

cd /path/to/Google-guice
mvn install
mvn exec:java -Dexec.mainClass="com.dev.blogs.google.guice.App"

Результатом должна быть строка Google guice. Test message:

[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building google.guice 0.0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] >>> exec-maven-plugin:1.2.1:java (default-cli) > validate @ google.guice
>>>
[INFO]
[INFO] <<< exec-maven-plugin:1.2.1:java (default-cli) < validate @ google.guice
<<<
[INFO]
[INFO] --- exec-maven-plugin:1.2.1:java (default-cli) @ google.guice ---
Google guice. Test message
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.939 s
[INFO] Finished at: 2015-05-20T16:39:47+03:00
[INFO] Final Memory: 9M/155M
[INFO] ------------------------------------------------------------------------

Весь проект можно взять с gitHub: https://github.com/dev-blogs/jsr330

Поделиться в социальных сетях

Опубликовать в Google Plus
Опубликовать в LiveJournal
Опубликовать в Мой Мир
Опубликовать в Одноклассники
Опубликовать в Яндекс

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *