Статьи

Цепочка ответственности с помощью Spring @Autowired List

В Spring 3.1 есть способ автоматически заполнять типизированный список, который очень удобен, когда вы хотите немного отменить разделение и очистку в вашем коде.

Чтобы показать вам, как это работает, я реализую простую цепочку ответственности, которая позаботится о печати некоторых поздравлений для прошедшего пользователя.

Давайте начнем с (единственного) класса домена, который у нас есть, Пользователь:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
package com.marco.springchain;
public class User {
 
        private final String name;
        private final char gender;
 
        public User(String name, char gender) {
                super();
                this.name = name;
                this.gender = gender;
        }
 
        public String getName() {
                return name;
        }
 
        public char getGender() {
                return gender;
        }
}

Затем мы создаем интерфейс, который определяет тип объектов нашей команды, которые будут использоваться в нашей цепочке:

1
2
3
4
5
package com.marco.springchain;
public interface Printer {
 
        void print(User user);
}

Это универсальный класс (шаблон) для реализации принтера.

org.springframework.core.Ordered используется, чтобы сообщить AnnotationAwareOrderComparator, как мы хотим, чтобы наш список был упорядочен.

Вам не нужно реализовывать интерфейс Ordered и переопределять метод getOrder, если вам не нужна цепочка для выполнения порядка выполнения.

Также обратите внимание, что этот абстрактный класс возвращает Ordered.LOWEST_PRECEDENCE , потому что я хочу, чтобы некоторые команды Printer просто выполнялись в конце цепочки, и мне нет дела до их порядка выполнения (все будет ясно после, обещаю!).

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
package com.marco.springchain;
import org.springframework.core.Ordered;
public abstract class <code>GenericPrinter </code>implements Printer, Ordered {
 
        public void print(User user) {
                String prefix = 'Mr';
                if (user.getGender() == 'F') {
                        prefix = 'Mrs';
                }
                System.out.println(getGreeting() + ' ' + prefix + ' ' + user.getName());
        }
 
        protected abstract String getGreeting();
 
        public int getOrder() {
                return Ordered.LOWEST_PRECEDENCE;
        }
}

Это наша первая настоящая команда Принтер. Я хочу, чтобы это имело абсолютный приоритет в цепочке, поэтому порядок HIGHEST_PRECEDENCE .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
package com.marco.springchain;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
@Component
public class HelloPrinter extends GenericPrinter {
 
        private static final String GREETING = 'Hello';
 
        @Override
        protected String getGreeting() {
                return GREETING;
        }
 
        @Override
        public int getOrder() {
                return Ordered.HIGHEST_PRECEDENCE;
        }
}

WelcomePrinter который будет выполнен как первая команда ( после старших команд).

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
package com.marco.springchain;
import org.springframework.stereotype.Component;
@Component
public class WelcomePrinter extends GenericPrinter {
 
        private static final String GREETING = 'Welcome to the autowired chain';
 
        @Override
        protected String getGreeting() {
                return GREETING;
        }
 
        @Override
        public int getOrder() {
                return 1;
        }
}

GoodbyePrinter будет выполняться как вторая команда

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
package com.marco.springchain;
import org.springframework.stereotype.Component;
@Component
public class GoodbyePrinter extends GenericPrinter {
 
        private static final String GREETING = 'Goodbye';
 
        @Override
        protected String getGreeting() {
                return GREETING;
        }
 
        @Override
        public int getOrder() {
                return 2;
        }
}

Эти 2 команды должны быть выполнены после других, но мне не важен их конкретный порядок, поэтому я не буду переопределять метод getOrder, оставляя GenericPrinter для возврата Ordered.LOWEST_PRECEDENCE для обоих.

01
02
03
04
05
06
07
08
09
10
11
12
package com.marco.springchain;
import org.springframework.stereotype.Component;
@Component
public class CleaningMemoryPrinter extends GenericPrinter {
 
        private static final String GREETING = 'Cleaning memory after';
 
        @Override
        protected String getGreeting() {
                return GREETING;
        }
}
01
02
03
04
05
06
07
08
09
10
11
12
package com.marco.springchain;
import org.springframework.stereotype.Component;
@Component
public class CleaningSpacePrinter extends GenericPrinter {
 
        private static final String GREETING = 'Cleaning space after';
 
        @Override
        protected String getGreeting() {
                return GREETING;
        }
}

Это контекст цепочки.

Spring будет сканировать (см. Spring-config.xml) пакет, указанный в файле конфигурации, он увидит типизированный List<Printer> ( List<Printer> ) и заполнит список экземпляром любого @Component который реализует тип Принтер.

Чтобы упорядочить список, мы используем AnnotationAwareOrderComparator.INSTANCE который использует метод getOrder для переупорядочения списка ( объект с наименьшим значением имеет наивысший приоритет (в некоторой степени аналогичный значениям «загрузка при запуске» сервлета ) ).

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.marco.springchain;
import java.util.Collections;
import java.util.List;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.stereotype.Component;
@Component
public class PrinterChain {
 
        @Autowired
        private List<Printer> printers;
 
        @PostConstruct
        public void init() {
                Collections.sort(printers, AnnotationAwareOrderComparator.INSTANCE);
        }
 
        public void introduceUser(User user) {
                for (Printer printer : printers) {
                        printer.print(user);
                }
        }
}

Spring-config.xml в src / main / resources.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
<?xml version='1.0' encoding='UTF-8'?>
       xmlns:context='http://www.springframework.org/schema/context'
       xsi:schemaLocation='
  http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
  http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
  http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
  http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd' default-lazy-init='true'>
 
    <context:component-scan base-package='com.marco.springchain'/>
</beans>

Наконец, основной класс для тестирования нашей цепочки.

01
02
03
04
05
06
07
08
09
10
11
package com.marco.springchain;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainTest {
 
        public static void main(String[] args) {
                ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext('spring-config.xml');
                PrinterChain printerChain = (PrinterChain) context.getBean('printerChain');
                printerChain.introduceUser(new User('Marco Castigliego', 'M'));
                printerChain.introduceUser(new User('Julie Marot', 'F'));
        }
}

ВЫХОД:

01
02
03
04
05
06
07
08
09
10
Hello Mr Marco Castigliego
Welcome to the autowired chain Mr Marco Castigliego
Goodbye Mr Marco Castigliego
Cleaning space after Mr Marco Castigliego
Cleaning memory after Mr Marco Castigliego
Hello Mrs Julie Marot
Welcome to the autowired chain Mrs Julie Marot
Goodbye Mrs Julie Marot
Cleaning space after Mrs Julie Marot
Cleaning memory after Mrs Julie Marot

Надеюсь, вам понравился пример.

Ссылка: цепь ответственности с использованием Spring @Autowired List от нашего партнера по JCG Марко Кастильего из блога « Удалить дубликаты и исправить дурные имена» .