На примере Spring
Пример от Мартина Фаулера
http://www.martinfowler.com/articles/injection.html
public class MovieLister {...
public Collection<Movie> moviesDirectedBy(String director) {
List<Movie> allMovies = finder.findAll();
for(Iterator<Movie> it = allMovies.iterator(); it.hasNext();) {
Movie movie = it.next();
if(!movie.getDirector().equals(director)) it.remove();
}
return allMovies;
}
public interface MovieFinder {
List<Movie> findAll();
}
Откуда мы возьмём конкретную реализацию?
public class MovieLister {
private MovieFinder finder;
public MovieLister() {
finder = new ColonDelimitedMovieFinder("movies1.txt");
}
На прошлой практике был вариант с Singleton
Схема зависимостей
В чём проблема?
Решение
Spring: внедрение через поле
@Named
public class ColonDelimitedMovieFinder implements MovieFinder {
}
@Named
public class MovieLister {
@Inject
private MovieFinder finder;
}
Было/Стало
public class MovieLister {
private MovieFinder finder;
public MovieLister() {
finder = new ColonDelimitedMovieFinder("movies1.txt");
}
@Named
public class MovieLister {
@Inject
private MovieFinder finder;
}
Spring: внедрение через конструктор
@Inject
public class ColonDelimitedMovieFinder implements MovieFinder {
}
@Named
public class MovieLister {
private MovieFinder finder;
@Inject
public MovieLister(MovieFinder fnd) {
this.finder = fnd;
}
...
Spring: внедрение через конструктор
Удобно для тестирования
Это объект системы, который
Пометить класс аннотацией
@Named
public class CsvMovieFinder implements MovieFinder {
private String fileName;
...
}
Может быть создан конфигурацией
@Configuration
public class MovieFinderConfiguration {
@Bean
public MovieFinder movieFinder() {
...
Collection<Movie> movies =
moviesDao.getAllMoviesFromDataBase();
...
return new CollectionMovieFinder(movies);
}
}
Например, если нужно выполнить дополнительные действия, которые не хочется делать частью логики бина
Bean может быть даже строкой
@Configuration
public class DatabaseConfiguration {
@Bean
public String databaseVendor() {
...
String vendor = getVendor();
...
return vendor;
}
}
Внедрение через поле
@Named
public class MovieLister {
@Inject
private MovieFinder finder;
}
Внедрение через конструктор
@Named
public class MovieLister {
private MovieFinder finder;
@Inject
public MovieLister(MovieFinder finder) {
this.finder = finder;
}
...
Внедрение списка бинов
@Named
public class MartinScorsese implements Director {
@Named
public class JamesCameron implements Director {
@Named
public class DirectorsService {
@Inject
private List<Director> allDirectors;
Если есть несколько реализаций одного интерфейса
Внедрение бина по имени
@Named("csvFinder")
public class CsvMovieFinder implements MovieFinder {
@Named("oracleFinder")
public class OracleMovieFinder implements MovieFinder {
@Named
public class MovieLister {
@Inject @Named("csvFinder")
private MovieFinder finder;
Если есть несколько реализаций одного интерфейса
Получение бина из фабрики
@Named
public class MovieLister {...
@Inject
private BeanFactory factory;
private String beanName;
public Collection moviesDirectedBy(String director) {
MovieFinder finder = factory.getBean(beanName);
List allMovies = finder.findAll();
@PostConstruct
@PostConstruct
@Named
public class CachingMovieLister {
@PostConstruct
public void populateMovieCache() {
// Загружаем данные из БД в кэш
cache.addAll(readFromDataBase());
}
}
@PreDestroy
Бин удаляется из фабрики
@PreDestroy
@Named
public class CachingMovieLister {
@PreDestroy
public void clearMovieCache() {
// Освобождаем кэш
cache.clear();
}
}
@Configuration
public class MovieFinderConfiguration {
@Bean
@Scope("prototype")
public CsvMovieFinder csvMovieFinder() {
return new CsvMovieFinder("movies.csv");
}
}
Какой шаблон здесь реализован?
@Value
@Named
public class SomeBean {
@Value("#{systemProperties['databaseName']}")
private String databaseName;
}
Системное свойство можно задать через аргумент VM
-DdatabaseName=Oracle
@Named
public class SomeBean {
@Value("${databaseName}")
private String databaseName;
}
Конфигурация задаётся в property файлах
Внимание! $ вместо #
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
Controller - это тоже Component, т.е. Bean
@Documented
@Controller
@ResponseBody
public @interface RestController {
RestController - это тоже Controller, т.е. Component...