XMLのないSpring Framework ...まったく!

あらゆるものをすべてアノテーションで定義する現在の時代に照らして、Spring Frameworkとプロジェクトにアノテーションを付ける可能性についての記事を提供します。 ご注意 perev。

最初はEJB 2.1で、可能な限り膨大な数のXMLファイルがありました。 ビジネスロジックの1行のコードで、フレームワークと2つのXMLページから少なくとも10行のコードを記述する必要があると言っても、特別な誇張ではありません。 ローカルおよびリモートインターフェース、手動JNDIルックアップ、マルチレベルトライキャッチ、RemoteException ...エンタープライズのチェック全般。 ツールも、この「キッチン」全体の自動生成に適していました。



それから数人の男がSpringフレームワークを作成しました。 PortableRemoteObject.narrow()のあいまいなカーストの後、新鮮な空気の息吹になりました。 時間が経ちました(ところで、JREの最後のメジャーリリースが何年前だったかを誰かが覚えていますか?)そして、Sunはこのレッスンを実現しました。 EJB 3.0は、Springよりも単純で、XMLを使用せず、注釈、依存性注入を備えています。 3.1は、簡素化に向けたもう1つの大きなステップでした。 論理的には、EJBはSpringが提供するものの一部と見なすことができ、JPA 1.0 / 2.0、JSR-250のサポートが提供されているため、プレーンなSpringにEJB実装が存在しないことに驚かされます(ああ、 待って ...) 、JSR-330、JAX-WS / RSなど。 現在、Springフレームワークは、主にXML記述子のために、低速で、重く、保守が困難なフレームワークとして認識されています。 一般的に、Springは鞭打ち少年としてのJEEフレームワークに反対しています。



私は政治が好きではなく、長いエッセイを書くことによって私の好きな枠組みを擁護しません。 代わりに、単純ではあるが些細ではないSpringアプリケーションを取り上げ、XMLなしで書き直します。 XMLがまったくなく、1行もありません。

この記事のために、タスクを単純化しないように、JDBC、JMS、およびJMXを使用して、Springで非常に単純なWebアプリケーション (xmlブランチの通常バージョン、マスターで最終)を作成しました。 各変更は、リポジトリへの個別のコミットに反映され、ステップバイステップで、XMLが完全になくなるまで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:tx="http://www.springframework.org/schema/tx" xmlns:amq="http://activemq.apache.org/schema/core" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core-5.4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:mbean-export /> <bean id="fooService" class="com.blogspot.nurkiewicz.FooService"> <property name="jmsOperations" ref="jmsTemplate" /> </bean> <bean id="fooRequestProcessor" class="com.blogspot.nurkiewicz.FooRequestProcessor"> <property name="fooRepository" ref="fooRepository" /> </bean> <bean id="fooRepository" class="com.blogspot.nurkiewicz.FooRepository" init-method="init"> <property name="jdbcOperations" ref="jdbcTemplate" /> </bean> <!-- JDBC --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="org.h2.Driver" /> <property name="url" value="jdbc:h2:~/workspace/h2/spring-noxmal;DB_CLOSE_ON_EXIT=FALSE;TRACE_LEVEL_FILE=4;AUTO_SERVER=TRUE" /> <property name="username" value="sa" /> <property name="password" value="" /> </bean> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <constructor-arg ref="dataSource" /> </bean> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <constructor-arg ref="dataSource" /> </bean> <tx:annotation-driven /> <!-- JMS --> <bean id="jmsConnectionFactory" class="org.apache.activemq.pool.PooledConnectionFactory"> <constructor-arg> <bean class="org.apache.activemq.ActiveMQConnectionFactory"> <property name="brokerURL" value="tcp://localhost:61616" /> </bean> </constructor-arg> </bean> <amq:queue id="requestsQueue" physicalName="requests" /> <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate"> <constructor-arg ref="jmsConnectionFactory" /> <property name="defaultDestination" ref="requestsQueue" /> </bean> <bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer"> <property name="connectionFactory" ref="jmsConnectionFactory" /> <property name="destination" ref="requestsQueue" /> <property name="sessionTransacted" value="true"/> <property name="concurrentConsumers" value="5"/> <property name="messageListener"> <bean class="org.springframework.jms.listener.adapter.MessageListenerAdapter"> <constructor-arg ref="fooRequestProcessor" /> <property name="defaultListenerMethod" value="process"/> </bean> </property> </bean> </beans>
      
      





ユーザーBeanのペア、トランザクションとJMSを備えたJDBS、送信および受信。 詳細は特に重要ではありません。JMXを介してBeanの1つがJMSメッセージを送信し、メッセージが受信されてデータベースに配置(永続化)されます。



SpringでXMLレイヤーを削減するための最も一般的で確立されたアプローチは、ユーザーBeanの<context:component-scan />とともにServiceおよびResourceアノテーションを使用することです( 変更 )。

 <?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:tx="http://www.springframework.org/schema/tx" xmlns:amq="http://activemq.apache.org/schema/core" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core-5.4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:mbean-export /> <context:component-scan base-package="com.blogspot.nurkiewicz"/> <!-- JDBC --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="org.h2.Driver" /> <property name="url" value="jdbc:h2:~/workspace/h2/spring-noxmal;DB_CLOSE_ON_EXIT=FALSE;TRACE_LEVEL_FILE=4;AUTO_SERVER=TRUE" /> <property name="username" value="sa" /> <property name="password" value="" /> </bean> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <constructor-arg ref="dataSource" /> </bean> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <constructor-arg ref="dataSource" /> </bean> <tx:annotation-driven /> <!-- JMS --> <bean id="jmsConnectionFactory" class="org.apache.activemq.pool.PooledConnectionFactory"> <constructor-arg> <bean class="org.apache.activemq.ActiveMQConnectionFactory"> <property name="brokerURL" value="tcp://localhost:61616" /> </bean> </constructor-arg> </bean> <amq:queue id="requestsQueue" physicalName="requests" /> <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate"> <constructor-arg ref="jmsConnectionFactory" /> <property name="defaultDestination" ref="requestsQueue" /> </bean> <bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer"> <property name="connectionFactory" ref="jmsConnectionFactory" /> <property name="destination" ref="requestsQueue" /> <property name="sessionTransacted" value="true"/> <property name="concurrentConsumers" value="5"/> <property name="messageListener"> <bean class="org.springframework.jms.listener.adapter.MessageListenerAdapter"> <constructor-arg ref="fooRequestProcessor" /> <property name="defaultListenerMethod" value="process"/> </bean> </property> </bean> </beans>
      
      





10行以下のXML、特別なものは何もありません...そして、ユーザーBeans?

 @Service public class FooRepository { @Resource private JdbcOperations jdbcOperations; @PostConstruct public void init() { log.info("Database server time is: {}", jdbcOperations.queryForObject("SELECT CURRENT_TIMESTAMP", Date.class)); } //... }
      
      





セッターとinitメソッドは注釈に置き換えられます。 それでは、次は何ですか? 好きなアノテーションのほとんどは栄誉にかかっていますが、ご覧のとおり、多くのXMLが残っています。 ここでの問題は、接続プールなどのサードパーティのものの場合に注釈を使用する方法です。

ここからが楽しみです。 まず、データソースXMLを削除し、すべてを...に置き換える必要があります( 変更

 import javax.sql.DataSource; import org.apache.commons.dbcp.BasicDataSource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class ContextConfiguration { @Bean public DataSource dataSource() { final BasicDataSource ds = new BasicDataSource(); ds.setDriverClassName("org.h2.Driver"); ds.setUrl("jdbc:h2:~/workspace/h2/spring-noxmal;DB_CLOSE_ON_EXIT=FALSE;TRACE_LEVEL_FILE=4;AUTO_SERVER=TRUE"); ds.setUsername("sa"); return ds; } }
      
      





@Configuration、 Bean 、dataSource()、this ...?!? しかし、思ったとおりに機能します。Springは、ContextConfigurationクラスと、 Beanとして注釈が付けられたすべてのメソッドを検出します。 このような各メソッドはXML-ev <bean ...>(ここではScope 、@DependsOnおよびLazyでも )と同等であるため、dataSource BeanのXML宣言を削除できます。 実際には、JdbcTemplateとトランザクションマネージャーを削除できます( 変更 )。

 @Bean public JdbcOperations jdbcOperations() { return new JdbcTemplate(dataSource()); } @Bean public PlatformTransactionManager transactionManager() { return new DataSourceTransactionManager(dataSource()); }
      
      





データソースビンを他のビンに挿入するのがどれほど簡単かに注意してください。 データソースを作成するメソッドがあり、このデータソースを必要とする2つのメソッド(JdbcTemplateとトランザクションマネージャー)があります。 それは決して簡単ではなく、おそらくあなたのガールフレンドは依存性注入を実装したでしょう(ええと、 Guice?



気になる点は1つです... dataSource()を2回呼び出すと、DataSourceの2つの独立したインスタンスが作成されるということですか? これは私たちが望むものではありません。 それは私を困惑させたが、再び春は賢い獣であることが判明した。 Scopeアノテーションが見つからなかったため、彼はデータソースがシングルトンであるべきだと考え始め、dataSource()メソッドに少しのCGLIBプロキシマジックを適用して、呼び出し回数を1回に制限します。 まあ、またはより具体的には、何度も呼び出すことができますが、同じBeanを返します。 かっこいい!

その結果、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:tx="http://www.springframework.org/schema/tx" xmlns:amq="http://activemq.apache.org/schema/core" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core-5.4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:mbean-export /> <context:component-scan base-package="com.blogspot.nurkiewicz"/> <!-- JDBC --> <tx:annotation-driven /> <!-- JMS --> <bean id="jmsConnectionFactory" class="org.apache.activemq.pool.PooledConnectionFactory"> <constructor-arg> <bean class="org.apache.activemq.ActiveMQConnectionFactory"> <property name="brokerURL" value="tcp://localhost:61616" /> </bean> </constructor-arg> </bean> <amq:queue id="requestsQueue" physicalName="requests" /> <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate"> <constructor-arg ref="jmsConnectionFactory" /> <property name="defaultDestination" ref="requestsQueue" /> </bean> <bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer"> <property name="connectionFactory" ref="jmsConnectionFactory" /> <property name="destination" ref="requestsQueue" /> <property name="sessionTransacted" value="true"/> <property name="concurrentConsumers" value="5"/> <property name="messageListener"> <bean class="org.springframework.jms.listener.adapter.MessageListenerAdapter"> <constructor-arg ref="fooRequestProcessor" /> <property name="defaultListenerMethod" value="process"/> </bean> </property> </bean> </beans>
      
      





これで、構成内の残りのBeanを書き換える方法を停止して考えることができます。 それは価値がありません、すべてが透明です( 変更 )。

 @Bean public ConnectionFactory jmsConnectionFactory() { final ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(); factory.setBrokerURL("tcp://localhost:61616"); return new PooledConnectionFactory(factory); } @Bean public Queue requestsQueue() { return new ActiveMQQueue("requests"); } @Bean public JmsOperations jmsOperations() { final JmsTemplate jmsTemplate = new JmsTemplate(jmsConnectionFactory()); jmsTemplate.setDefaultDestination(requestsQueue()); return jmsTemplate; }
      
      





DefaultMessageListenerContainer宣言には、親Bean内で一度使用される匿名の内部ビンが含まれています。 順序のプライベートメソッド( 変更 ):

 @Bean public AbstractJmsListeningContainer jmsContainer() { final DefaultMessageListenerContainer container = new DefaultMessageListenerContainer(); container.setConnectionFactory(jmsConnectionFactory()); container.setDestination(requestsQueue()); container.setSessionTransacted(true); container.setConcurrentConsumers(5); container.setMessageListener(messageListenerAdapter()); return container; } private MessageListenerAdapter messageListenerAdapter() { final MessageListenerAdapter adapter = new MessageListenerAdapter(fooRequestProcessor); adapter.setDefaultListenerMethod("process"); return adapter; }
      
      







Springの単純なJava構成は些細で気取らないため、コメントする必要はありません。コード自体が語っています。 あなたが混乱した場合に備えて、私たちはこれに来ました:



<?xml version = "1.0" encoding = "UTF-8"?>

<beans xmlns = " www.springframework.org/schema/beans "

xmlns:xsi = " www.w3.org/2001/XMLSchema-instance "

xmlns:tx = " www.springframework.org/schema/tx "

xmlns:context = " www.springframework.org/schema/context "

xsi:schemaLocation = " www.springframework.org/schema/beans www.springframework.org/schema/beans/spring-beans-3.0.xsd

www.springframework.org/schema/tx www.springframework.org/schema/tx/spring-tx-2.5.xsd

www.springframework.org/schema/context www.springframework.org/schema/context/spring-context.xsd »>



<コンテキスト:mbean-export />



<context:component-scan base-package = "com.blogspot.nurkiewicz" />



<tx:アノテーション駆動型/>







正直なところ、これらすべてはそれほど難しくありませんでしたが、XMLの最後の数行を取り除くのは本当に困難でした。 私を信じて、あなたはこれらの美しい小さな名前空間を使った広告とうまくやっていくために私がしたことをしたくありません。 しかし、数分、いくつかの悪い経験、スキャンされたSpringコードの束の後、ついにJMX宣言( 変更 )とトランザクション( 変更 )を削除しました。 無邪気に見えるので、これを実現するためにSpringコードを掘り下げる必要がなかったことを嬉しく思います。

 @Bean public AnnotationMBeanExporter annotationMBeanExporter() { return new AnnotationMBeanExporter(); } @Bean public TransactionAttributeSource annotationTransactionAttributeSource() { return new AnnotationTransactionAttributeSource(); } @Bean public TransactionInterceptor transactionInterceptor() { return new TransactionInterceptor(transactionManager(), annotationTransactionAttributeSource()); }
      
      





行くぞ Beanとweb.xmlスニペットの場所に関するSpringのXML命令を削除することは残ります。

 <context:component-scan base-package="com.blogspot.nurkiewicz"/> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> </web-app>
      
      





Web環境で通常Springを起動する他の方法はありませんが、Springが必要であることをWebコンテナに伝える必要があります。 私は知っています、私は知っています、私はXMLがまったくないことを約束しました、まあ、私は嘘をつきました、そして今...ごめん、OKですか? ビジネス...うん、冗談です。 thisこれも取り除きましょう。 数秒! まあ...それは、最新のTomcat 7または別のJSR 315対応(Servlet 3.0として知られている)Webコンテナをどれだけ速くダウンロードするかに依存しますが...



Webフラグメントは、さまざまなWebフレームワークをサーブレットコンテナに繊細に統合できる技術です。 フレームワークの使用経験がある場合は、すべてのフレームワークにweb.xmlの特別なサーブレット、フィルター、またはリスナーの説明が必要であることを知っています。 ほとんどの場合、これはサーブレットの依存関係が発生する唯一の時間であり、Springも例外ではありません。 Webフラグメントの本質は、開発者をこれから解放しようとしていることです。 サーブレット3.0コンテナは/ WEB-INF / lib内のすべてのJARをスキャンする必要があり、JARのいずれかが/ META-INF内にweb-fragment.xmlを含む場合、web.xmlに登録されます。



わかった、どこに行くの? XMLなしでSpringを実行するためだけに小さなWebフラグメントで小さなJARを作成するとどうなりますか? WAR構造:

 . |-- META-INF `-- WEB-INF |-- classes | |-- com | | `-- blogspot | | `-- nurkiewicz | | |-- ContextConfiguration.class | | |-- FooRepository.class | | |-- FooRequestProcessor.class | | |-- FooService$1.class | | `-- FooService.class | `-- logback.xml |-- lib | |-- spring-web-3.0.5.RELEASE.jar | |-- spring-web-fragment-0.0.1-SNAPSHOT.jar | | `-- META-INF | | |-- MANIFEST.MF | | |-- web-fragment-context.xml | | `-- web-fragment.xml | `-- spring-beans-3.0.5.RELEASE.jar `-- web.xml
      
      





spring-web-fragmentの唯一の目的は*です。web-fragment.xmlをコンテナに提供するJar:

 <web-fragment xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-fragment_3_0.xsd" version="3.0"> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:/META-INF/web-fragment-context.xml</param-value> </context-param> </web-fragment>
      
      





新しい要素の1つは、web-fragment-context.xml Springコンテキストファイルです。 このファイルはもはや(!)ではないため、デフォルトの設定(/WEB-INF/applicationContext.xml)は使用できません。 しかし、私たちの小さなJARは、これが最適な場所のように見えます。

 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" 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.xsd "> <context:component-scan base-package="." /> </beans>
      
      





「。」としてのパッケージ宣言 覚醒を引き起こします。 これは非常に悲しいことですが、少なくとも1つのパッケージを宣言する際の要件を回避しようとしました。 この要件はおそらく正当化されます(CLASSPATHスキャンには時間がかかると思います)が、パッケージを示すことはできません。そうしないと、新しいプロジェクトごとにこの発表を変更する必要があります。 しかし、これはWebフラグメントを使用するアプローチの主な利点に反します。2つの小さなXMLファイルでJARを作成する場合、すべてのプロジェクトに使用できます。 必要なのは、それをWARのライブラリのリストに含めて、POJOをサービスとして(および/または@Configurationを使用して)注釈を付けることだけです。



ボックスからSpringが提供するものの中で利用できる場合(アイデアが好きなら、 投票してください )、初心者はpom.xmlにSpringを追加するとすぐにSpringへの旅行を楽しむことができます。 実際、pom.xmlはlogback.xmlのようなさまざまな言語で記述できます。 ちょっと、XMLなし! よろしいですか XMLとJavaのどちらが好きですか? それともGroovy? 黙ってください。 Springは、あなたが望むようにシンプルで気取らない使用の機会をあなたに与えます。 機能が切り捨てられるほど単純化されていません。




All Articles