Вопрос по spring, java – Почему Spring @Value несовместим с @Controller?

4

Я ищу лучшего понимания этой проблемы. Обходной путь довольно прост, а именно перенести данные конфигурации в другой класс, в котором нет прокси / рекомендаций, но я думаю, что понимание этого лучше поможет мне избежать других связанных с этим проблем в будущем, поэтому я хотел бы получить любое объяснение от кого-либо может обеспечить.

Я использую Spring 3.1.0.RELEASE с Spring STS и сервером vFabric tc. Реализован простой маленький REST-сервер с использованием класса @Controller. Это все замечательно (правда, так и есть), но @Controller также является @Transactional, и между этим и ткачеством времени загрузки и сервером vFabric tc он разбивает @Value.

<code>@Controller
@RequestMapping("/hello")
public class MyAPI {

    @Value("${my.property}")
    private String prop;
    ...

    @Transactional
    handleRequest(...) ...


}
</code>

И файл свойств app.properties:

<code>my.property = SUCCESS
</code>

Это прекрасно работает в JUnit, при этом тест получает объект MyAPI, для которого установлено значение & quot; SUCCESS & quot ;. Но когда приложение загружается в vFabric, я предполагаю, что оно получает время загрузки и прокси. Что бы ни случилось, создаются два экземпляра MyAPI, один из которых имеет prop == & quot; SUCCESS & quot; и другой (который, к сожалению, обрабатывает http-запрос), который имеет prop == & quot; $ {my.prop} & quot ;.

В общем, я называю это провалом магии, и это моя самая большая проблема с использованием таких вещей, как AOP. Даже с STS я не знаю, как отследить причину проблемы или выяснить, является ли это серьезной ошибкой. Если это ошибка, я не знаю, является ли это ошибкой в Spring, AspectJ, ткачих во время загрузки или vFabric, поэтому я даже не знаю, куда подать отчет об ошибке.

Так что любая помощь в понимании этого будет принята с благодарностью. Благодарю.

@ axtavt ты указал мне на решениеВо. Смотрите мой ответ полностью ниже, но краткий ответ, чтоControllers создаются дважды в моей (неправильной) конфигурации. Не проблема с@Transactional поскольку это использует AspectJ, а не прокси. Old Pro
Ты уверен, что это вызвано АОП? Что делать, если вы удалите@Transactional? axtavt

Ваш Ответ

4   ответа
6

Я использовал Spring Roo в STS для генерации базового фреймворка приложений, а затем вычеркнул Roo из STS, поскольку мы не хотели его использовать.

Одна из лучших вещей, которую Ру делает в качестве «лучшей практики», - это создание двух контекстов Spring, одного для всего приложения и одного, ограниченного сервлетом диспетчера. Именно поэтому, я до сих пор не получил, но я думаю, что они хотят, чтобы материал уровня представления, такой как Контроллеры, не проникал в уровень обслуживания, который является общим. Это было хорошо объяснено axtavtВо. STS скрыло от меня все это.

В STS с Roo источник WEB-INF находится не там, где я ожидал, в каталоге / src / main / resources (где находится каталог META-INF), а в каталоге / src / main / webapp, который не является Исходный каталог Java и, таким образом, показан совершенно отдельно, чуть выше каталога / target, поэтому я принял его за выходную папку.

В applicationContext.xml Роо вставил фильтр, чтобы предотвратить создание контекста приложения Контроллерами, как объяснено в посте axtavt, но также вставил другой фильтр, чтобы исключить сканирование сгенерированных Roo классов. Я вынул оба фильтра одновременно, не зная, для чего они там, но думая, что они просто остатки Ру.

Так что теперь у меня проблема с Контроллеры создаются дважды как объяснялось ранее. А тот, что в контексте приложения, получает назначенное свойство, потому что он использует applicationContext.xml и находит файл свойств. Но почему они оба не получили набор свойств?

Что возвращает меня в скрытую папку веб-приложений. Внутри папки WEB-INF Ру разместил файл web.xml (естественно) и пружинную папку, содержащую файл webmvc-config.xml. Этот файл конфигурации был настроен для сканирования, создания и настройки только контроллеров. Файл web.xml настраивает веб-приложение для использования applicationContext.xml и dispatcherServlet для использования webmvc-config.xml, поэтому я должен был оставить фильтр в applicationContext.xml, чтобы избежать двойного создания.

Последней частью головоломки был этот файл webmvc-config.xml. Поскольку это контекст, в котором устанавливаются контроллеры,чт file необходимо также настроить <context: property-placeholder />, чтобы он мог найти файл свойств.

0

В моем случае я решаю это так: вspring-servlet.xml до<context:component-scan ... /> Я положил это:

<context:property-placeholder location="classpath:strings.properties"/>

В то время какstrings.properties файл, который я положил вsrc/main/resources/.

Заметка:context:property-placeholderег @ имеет ограниченную видимость, поэтому я прочитал где-то рекомендацию дублировать его в каждом контекстном файле, где вы используете строки из него.

1

Что касается оригинального плаката, я думаю, что у вас есть противоречивое поведение между двумя аннотациями (@Controller и @Transactional).

Использование аннотации @Transactional проксирует вашу реализацию (я думаю, чтобы обнаружить ошибку и запустить откат).

Теперь вы говорите, что видите 2 экземпляра вашего контроллера. Этого не должно быть. Обычно у вас должен быть только один экземпляр контроллера, загруженный в память.

Может быть связано с вашими файлами конфигурации или из-за наличия @Transactional и его прокси-природы?

Как примечание, я никогда не использую @Transactional в самих контроллерах, но ни в методе службы, ни в дао. Поскольку фактический процесс, который может завершиться с ошибкой и нуждается в откате, существует, и доступ к нему может быть получен из разных контроллеров / источника.

С уважением

рограммирование @lean прекрасно, но может иметь различное представление в зависимости от контекста. В случае Служб Отдыха вы должны принять во внимание, что: 1. они часто развиваются (новые требования / требования), 2. вы хотите, чтобы контроллеры были легко читаемыми для разработчиков, и чтобы они не «загромождались» бизнес-правилами. с самого начала требуется немного больше работы (создание службы), но в долгосрочной перспективе это дает вам реальную свободу. Farid
Farid, мое видение «худой» состоит в том, чтобы избегать «больше работы с самого начала», когда это не затрудняет будущий рост. Логика контроллера в основном: чтение запроса, выполнение запрошенного действия, запись ответа с «запрошенным действием» внутри блока try / catch. Позже, если требуется отдельный объект Service, вы можете просто вырезать внутреннюю часть блока try / catch из контроллера и вставить его в метод service. На самом деле нет ограничений на долгосрочную свободу, но определенно быстрее добраться до работающего продукта. Old Pro
Я понимаю желание перевести @Transactional изController и на сервисный уровень. Однако я верю в бережливое программирование, когда вы не выполняете дополнительную работу, которая вам никогда не понадобится. В этом случае, реализуя API-интерфейс службы REST, Контроллер практически ничего не может сделать, кроме реализации службы, и никто никогда не вызовет службу каким-либо другим способом. Если когда-нибудь они это сделают, тогда будет легко провести рефакторинг, но заранее я сохранил, создав целый слой. И используя AspectJ для @Transactional, мне не нужно беспокоиться о прокси. Old Pro
1


Чтобы заставить эти два сенарио работать

введите атрибут (через @Value) в контроллер Ввести управляемый компонент (через @Inject), имеющий атрибут hasan (через @Value), в контроллер

Надеюсь, это поможет

Вот веб.xml.

    <servlet>
    <servlet-name>spring</servlet-name>
    <servlet-class>
        org.springframework.web.servlet.DispatcherServlet
    </servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:/spring-config/spring-servlet.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>spring</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

Here is spring-servlet.xml: ключ должен был иметь ОБА: util: properties и context: property-placeholder. Хотя они находят один и тот же файл, они служат разным целям.

<util:properties id="propSource" location="classpath:/properties/prop1.properties" />
<context:property-placeholder location="classpath:/properties/prop1.properties" />

<context:component-scan base-package="com.concurrent.controller" />

<context:annotation-config/>
<mvc:annotation-driven/> 

Вот фрагмент моего класса контроллеров:

@Controller
@RequestMapping("/fooController")
public class MyController {

@Value("${message1}")
public String message1;

@Inject
public ConfigProperties configProperties;

Наконец, вот мой класс, в который я добавляю свойство:

@Service
public class ConfigProperties {

@Value("${message1}")
public String message1;
}

Это сработало для меня и будет работать для вас. Удачи

Похожие вопросы