オランダProsusは6月2日(現地時間)、プログラマー向けQ&Aサイトを運営する米Stack Overflowを買収することで合意したと発表した。買収総額は約18億ドル(約1972億円)。取引は第3四半期(7~9月)に完了する見込みだ。
プログラマー向けQ&Aサイト「Stack Overflow」をProsusが18億ドルで買収 - ITmedia NEWS
Stack Overflowは2008年創業のニューヨークに拠点を置く非公開企業。プログラミングについての質問と回答をユーザー登録なしで投稿できる。ユーザー投票によって有用な質問や回答が上位に表示される仕組みが特徴だ。月間訪問者数は1億人以上。2014年から日本語版βも提供している。
プログラマー向けQ&Aサイト「Stack Overflow」をProsusが18億ドルで買収 - ITmedia NEWS
⇧ ニューヨーク発祥だったとは知らなんだ、どうもボクです。
というわけで、今回もJavaのフレームワーク「Spring Framework」なんかについてです。
レッツトライ~。
AOP(Aspect Oriented Programming)とは?
Wikipediaさんに聞いてみる。
アスペクト指向プログラミング(アスペクトしこうプログラミング、Aspect Oriented Programming、AOP)は、オブジェクト指向ではうまく分離できない特徴(クラス間を横断 (cross-cutting) するような機能)を「アスペクト」とみなし、アスペクト記述言語をもちいて分離して記述することでプログラムに柔軟性をもたせようとする試みで、極端に言えば、あるプログラムFの本質とは無関係だが使うかもしれないプログラムGを、プログラムFを実行したときに割り込ませるような機能である。
⇧ 例えば、Javaとかで共通的な処理を実施したい箇所が100000000カ所ぐらいあったとして、そのすべてにハードコーディングするとなると、修正することになった時なんかが辛過ぎる...ということで、「AOP(Aspect Oriented Programming)」であれば、コーディングを1つにまとめられますよ、ということですかね。
イメージ的には、
System Without AOP or Modularization
https://www.dineshonjava.com/spring-aop-tutorial-with-example-aspect-advice-pointcut-joinpoint/
⇧「AOP(Aspect Oriented Programming)」使わない場合が、上図のような感じでコーディング量が多いのに対し、
System With AOP or Modularization
https://www.dineshonjava.com/spring-aop-tutorial-with-example-aspect-advice-pointcut-joinpoint/
⇧「AOP(Aspect Oriented Programming)」使った場合は、上図のような感じで3つコーディングすれば、あとは良しなに処理を差し挟んでくれると。
大規模な開発になってくるほど、ファイル数やコーディング量が増えていくことになると思われるので「AOP(Aspect Oriented Programming)」の効果が実感できることになるのではないかと。
Spring AOPとAspectJの違いって?
で、Javaの「AOP(Aspect Oriented Programming)」 ライブラリとしては、
が有名らしいのですが、
ここで疑問が生じた。プロキシを介してしか織り込めないのだとしたら、自クラスから呼び出す自クラス内のメソッドへは織り込むことができないのか?ということで実験実験・・・
AspectJを使うっきゃないらしい?
⇧ 上記サイト様によりますと、機能に差異があるらしいですと。
ただ、
⇧「Spring AOP」に関して、アプローチが、
って2つあるみたいなんだけど、
⇧ 上記サイト様によりますと、「動的プロキシ」って仕組みを実現する際の実装の違いがあるってことらしい。
ちなみに、
⇧ 上記サイト様によりますと、「静的プロキシ」って仕組みもあるみたいね。
ちなみに、
we see that this is a class implementing an interface. The default for Spring AOP auto-proxying is that it uses JRE dynamic proxies, i.e. when creating the bean like this
Connector connector = ApplicationContextHolder.getBean("connector");
Spring will create a dynamic proxy implementing all interfaces the target class also implements. In this case this is Runnable
only. I.e. the proxy object will only have methods from that interface and can only be cast to that interface. This explains why you cannot cast the proxy to Connector
.
If you want Spring to create proxies for classes directly via CGLIB, you need to change your configuration in beans.xml
to
<aop:aspectj-autoproxy proxy-target-class="true"/>
Then the server will start up normally because the CGLIB proxy is a direct Connector
subclass, which also means that the assignment works as intended.
See the Spring manual for more information.
⇧ 上記サイト様によりますと、「Spring AOP」のデフォルトは「JRE dynamic proxies」を使ってるらしく、「CGLIB」を使いたい場合は、設定を記述する必要があるらしい。
というか、「JDK Proxy」「JDK動的プロキシ」「JRE dynamic proxies」って、みんな言い方が違うんだけど、同じものについて議論してるという認識で良いんかな?
ちなみに、
proxy-target-class="true"でCGLIBに強制した場合、実装クラスに対するAutowiredは上手くいくんですが
今度はProxyingの対象クラスが多いとOOMエラーになってしまうという現象も発生しました。
(JDK〜の場合は起こらない。詳細は未調査)
⇧ 上記サイト様によりますと、「プロキシ」させる対象のクラスが多い場合に「CGLIB」を使うと「OOMエラー」が起こるんだそうな。
「OOM」は、おそらく「OutOfMemory」のことを指しているかと。いや~、「OutOfMemory」が起こるのは厳しいですね...
実際に実装してみる
例のごとく、
⇧ 上記サイト様のお題を実施で。
「/hello
および /goodbye
の2つのパスにアクセスされた際に200 OK
を返すサーバーを実装せよ。」ってことみたいなので、「REST」で実装すれば良さ気っぽいですね。
⇧ 上記サイト様によりますと、
- spring-web
- spring-webmvc
のどちらの依存関係でも「REST」は実現できるっぽいのですが、「XML(Extensible Markup Language)」の設定ファイルで「spring-web」の設定が分からんかったので、「spring-webmvc」を使っていくことにします。
で、「AOP(Aspect Oriented Programming)」 ライブラリとしては、
⇧ 上記サイト様によりますと、
- spring-aspects
- spring-aop
の2つを依存関係に追加すれば良さ気ですと。
⇧ 上記サイト様によりますと、「Mavenプロジェクト」や「Gradleプロジェクト」で作り始めると相当に面倒くさいことになるみたいです。
まぁ、「Gradleプロジェクト」で作り始めてしまったんで、上記サイト様と同じように苦しんでいく感じになりますかね...
ここからは、「Gradleプロジェクト」や「Mavenプロジェクト」で無い場合は、読み飛ばしてもOK。
Eclipseの「パッケージ・エクスプローラー」上で対象のプロジェクトを選択した状態で右クリックし「プロパティー(R)」を選択。
「プロジェクト・ファセット」の「ファセット・フォームへ変換...」をクリック。
「プロジェクト・ファセット」の「動的Webモジュール」にチェックを入れ、
「適用して閉じる」をクリック。
「WebContent」って「ディレクトリ」ができるっぽい。
なんか、
⇧「web.xml」は必須らしい。
というわけで、「web.xml」を作成していきます。
「WEB-INF」フォルダを選択した状態で右クリックし、「新規(W)」>「ファイル」を選択。
「ファイル名(M):」に「web.xml」と入力し、「完了(F)」を押下。
「WEB-INF」直下に「web.xml」が作成されました。
⇧ ファイルの中身が空なのでエラーが出てます。「web.xml」を編集で。
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <!-- 文字のエンコーディングを指定し --> <filter> <filter-name>characterEncodingFilter</filter-name> <filter-class> <!-- org.Apache.catalina.filters.SetCharacterEncodingFilter --> org.springframework.web.filter.CharacterEncodingFilter </filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <!-- 上記フィルターをすべての URL で適用する。 --> <filter-mapping> <filter-name>characterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- リスナーを登録 --> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener> <!-- spring.xml --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:/config/spring-web.xml</param-value> </context-param> <!-- Spring MVC アプリの場合、だいたいは唯一のサーブレットを登録する。 --> <servlet> <servlet-name>dispatcherServlet</servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value></param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <!-- すべての URL リクエストについて、上記で登録したサーブレットで処理する。 --> <servlet-mapping> <servlet-name>dispatcherServlet</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> </web-app>
⇧ そうすると、エラーが消えます。
「サーバー」タブで、「アプリケーションサーバー(ここでは、Tomcat)」を選択した状態で右クリックし、「追加および除去(A)...」を選択。
「使用可能(A):」で、今回対象のプロジェクトを選択。「追加(D)>」を押下。
「構成済み(C):」に追加されてればOK。
他のプロジェクトは除去しときます。
今回対象のプロジェクトのみ「構成済み(C):」に配置されたら、「完了(F)」で。
続きまして、
「パッケージ・エクスプローラー」で対象のプロジェクトを選択した状態で右クリックし、「プロパティー(R)」を選択。
「デプロイメント・アセンブリー」で「追加(D)...」を押下。
「Java ビルド・パス・エントリー」を選択し、「次へ(N)>」を押下。
「プロジェクトと外部の依存関係」を選択し、「完了(F)」を押下。
そうすると、「サーバーで実行」が選択できるようになるようです。
「完了(F)」を押下すると、「アプリケーションサーバー」が起動するっぽい。
さらに、ファイルパスの取得で悲報...
これ、Eclipse特有の問題なのか分からんのだけど、「カレントディレクトリ」のパスを取得する方法が、軒並み「ワークスペース」のことが考慮されていないという...
何を言ってるかというと、例えば、
⇧ 上記サイト様を参考に、以下のように「C:\Eclipse-2020-06\pleiades-2020-06-java-win-64bit-jre_20200702\pleiades\workspace\Spring_Framework_Aop\src\main\java\Spring_Framework_Aop\aop\SpringAspect.java」で「カレントディレクトリ」のパスを取得するコーディングを実装すると、
Path root =Paths.get(System.getProperty("user.dir"));
な、何と!
C:\Eclipse-2020-06\pleiades-2020-06-java-win-64bit-jre_20200702\pleiades\eclipse
⇧ っていう結果が返ってくるというね...
いやいやいや、「SpringAspect.java」が配置されてる「ディレクトリ」って
C:\Eclipse-2020-06\pleiades-2020-06-java-win-64bit-jre_20200702\pleiades\workspace\Spring_Framework_Aop\src\main\java\Spring_Framework_Aop\aop\
なんですけど...、何で、
\workspace\Spring_Framework_Aop\src\main\java\Spring_Framework_Aop\aop\
が無かったことにされてるの?
っていうか、「\eclipse」とか意味分からん「ディレクトリ」返されても困るんだが...
システムプロパティの一覧で「user.dir」も確認できるらしいので、確認。
Properties properties = System.getProperties(); Enumeration<Object> enumeration = properties.keys(); for (int i = 0; i < properties.size(); i++) { Object obj = enumeration.nextElement(); System.out.println("Key: "+obj+"\tOutPut= "+System.getProperty(obj.toString())); }
⇧ 上記を実行した結果、
Key: sun.desktop OutPut= windows Key: awt.toolkit OutPut= sun.awt.windows.WToolkit Key: java.specification.version OutPut= 11 Key: sun.cpu.isalist OutPut= amd64 Key: sun.jnu.encoding OutPut= MS932 Key: java.class.path OutPut= C:\Eclipse-2020-06\pleiades-2020-06-java-win-64bit-jre_20200702\pleiades\tomcat\9\bin\bootstrap.jar;C:\Eclipse-2020-06\pleiades-2020-06-java-win-64bit-jre_20200702\pleiades\tomcat\9\bin\tomcat-juli.jar Key: java.vm.vendor OutPut= AdoptOpenJDK Key: sun.arch.data.model OutPut= 64 Key: user.variant OutPut= Key: java.vendor.url OutPut= https://adoptopenjdk.net/ Key: catalina.useNaming OutPut= true Key: user.timezone OutPut= Asia/Tokyo Key: os.name OutPut= Windows 10 Key: java.vm.specification.version OutPut= 11 Key: sun.java.launcher OutPut= SUN_STANDARD Key: user.country OutPut= JP Key: sun.boot.library.path OutPut= C:\Eclipse-2020-06\pleiades-2020-06-java-win-64bit-jre_20200702\pleiades\java\11\bin Key: sun.java.command OutPut= org.apache.catalina.startup.Bootstrap start Key: jdk.debug OutPut= release Key: sun.cpu.endian OutPut= little Key: user.home OutPut= C:\Users\Toshinobu Key: user.language OutPut= ja Key: java.specification.vendor OutPut= Oracle Corporation Key: java.naming.factory.url.pkgs OutPut= org.apache.naming Key: java.version.date OutPut= 2020-04-14 Key: java.home OutPut= C:\Eclipse-2020-06\pleiades-2020-06-java-win-64bit-jre_20200702\pleiades\java\11 Key: file.separator OutPut= \ Key: java.vm.compressedOopsMode OutPut= 32-bit Key: line.separator OutPut= Key: java.specification.name OutPut= Java Platform API Specification Key: java.vm.specification.vendor OutPut= Oracle Corporation Key: java.awt.graphicsenv OutPut= sun.awt.Win32GraphicsEnvironment Key: package.access OutPut= sun.,org.apache.catalina.,org.apache.coyote.,org.apache.jasper.,org.apache.tomcat. Key: package.definition OutPut= sun.,java.,org.apache.catalina.,org.apache.coyote.,org.apache.jasper.,org.apache.naming.,org.apache.tomcat. Key: user.script OutPut= Key: server.loader OutPut= Key: sun.management.compiler OutPut= HotSpot 64-Bit Tiered Compilers Key: java.runtime.version OutPut= 11.0.7+10 Key: java.naming.factory.initial OutPut= org.apache.naming.java.javaURLContextFactory Key: user.name OutPut= Toshinobu Key: path.separator OutPut= ; Key: common.loader OutPut= "${catalina.base}/lib","${catalina.base}/lib/*.jar","${catalina.home}/lib","${catalina.home}/lib/*.jar" Key: os.version OutPut= 10.0 Key: java.runtime.name OutPut= OpenJDK Runtime Environment Key: file.encoding OutPut= UTF-8 Key: java.vm.name OutPut= OpenJDK 64-Bit Server VM Key: java.vendor.version OutPut= AdoptOpenJDK Key: java.vendor.url.bug OutPut= https://github.com/AdoptOpenJDK/openjdk-support/issues Key: java.io.tmpdir OutPut= C:\Users\TOSHIN~1\AppData\Local\Temp\ Key: tomcat.util.scan.StandardJarScanFilter.jarsToScan OutPut= log4j-taglib*.jar,log4j-web*.jar,log4javascript*.jar,slf4j-taglib*.jar Key: catalina.home OutPut= C:\Eclipse-2020-06\pleiades-2020-06-java-win-64bit-jre_20200702\pleiades\tomcat\9 Key: java.version OutPut= 11.0.7 Key: tomcat.util.scan.StandardJarScanFilter.jarsToSkip OutPut= annotations-api.jar,ant-junit*.jar,ant-launcher.jar,ant.jar,asm-*.jar,aspectj*.jar,bootstrap.jar,catalina-ant.jar,catalina-ha.jar,catalina-ssi.jar,catalina-storeconfig.jar,catalina-tribes.jar,catalina.jar,cglib-*.jar,cobertura-*.jar,commons-beanutils*.jar,commons-codec*.jar,commons-collections*.jar,commons-daemon.jar,commons-dbcp*.jar,commons-digester*.jar,commons-fileupload*.jar,commons-httpclient*.jar,commons-io*.jar,commons-lang*.jar,commons-logging*.jar,commons-math*.jar,commons-pool*.jar,dom4j-*.jar,easymock-*.jar,ecj-*.jar,el-api.jar,geronimo-spec-jaxrpc*.jar,h2*.jar,hamcrest-*.jar,hibernate*.jar,httpclient*.jar,icu4j-*.jar,jasper-el.jar,jasper.jar,jaspic-api.jar,jaxb-*.jar,jaxen-*.jar,jdom-*.jar,jetty-*.jar,jmx-tools.jar,jmx.jar,jsp-api.jar,jstl.jar,jta*.jar,junit-*.jar,junit.jar,log4j*.jar,mail*.jar,objenesis-*.jar,oraclepki.jar,oro-*.jar,servlet-api-*.jar,servlet-api.jar,slf4j*.jar,taglibs-standard-spec-*.jar,tagsoup-*.jar,tomcat-api.jar,tomcat-coyote.jar,tomcat-dbcp.jar,tomcat-i18n-*.jar,tomcat-jdbc.jar,tomcat-jni.jar,tomcat-juli-adapters.jar,tomcat-juli.jar,tomcat-util-scan.jar,tomcat-util.jar,tomcat-websocket.jar,tools.jar,websocket-api.jar,wsdl4j*.jar,xercesImpl.jar,xml-apis.jar,xmlParserAPIs-*.jar,xmlParserAPIs.jar,xom-*.jar Key: user.dir OutPut= C:\Eclipse-2020-06\pleiades-2020-06-java-win-64bit-jre_20200702\pleiades\eclipse Key: os.arch OutPut= amd64 Key: java.vm.specification.name OutPut= Java Virtual Machine Specification Key: java.awt.printerjob OutPut= sun.awt.windows.WPrinterJob Key: sun.os.patch.level OutPut= Key: catalina.base OutPut= C:\Eclipse-2020-06\pleiades-2020-06-java-win-64bit-jre_20200702\pleiades\workspace\.metadata\.plugins\org.eclipse.wst.server.core\tmp0 Key: shared.loader OutPut= Key: wtp.deploy OutPut= C:\Eclipse-2020-06\pleiades-2020-06-java-win-64bit-jre_20200702\pleiades\workspace\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps Key: java.library.path OutPut= C:\Eclipse-2020-06\pleiades-2020-06-java-win-64bit-jre_20200702\pleiades\java\11\bin;C:\WINDOWS\Sun\Java\bin;C:\WINDOWS\system32;C:\WINDOWS;C:/Eclipse-2020-06/pleiades-2020-06-java-win-64bit-jre_20200702/pleiades/eclipse//jre/bin/server;C:/Eclipse-2020-06/pleiades-2020-06-java-win-64bit-jre_20200702/pleiades/eclipse//jre/bin;C:\python2.7.16\;C:\python2.7.16\Scripts;C:\app02\oracle\product\19.0.0\dbhome_1\bin;C:\Program Files (x86)\Common Files\Oracle\Java\javapath;C:\Python37\Scripts\;C:\Python37\;C:\app\product\12.2.0\dbhome_1\bin;C:\Program Files\Common Files\Microsoft Shared\Windows Live;C:\Program Files (x86)\Common Files\Microsoft Shared\Windows Live;C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Program Files (x86)\Windows Live\Shared;C:\Program Files\Intel\WiFi\bin\;C:\Program Files\Common Files\Intel\WirelessCommon;c:\xampp\perl\bin\;c:\xampp\php\;c:\xampp\mysql\bin\;C:\ProgramData\ComposerSetup\bin;C:\ProgramData\chocolatey\bin;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\Program Files (x86)\Skype\Phone\;C:\Users\Toshinobu\.dnx\bin;C:\Program Files\Microsoft DNX\Dnvm\;C:\Program Files\Microsoft SQL Server\130\Tools\Binn\;C:\Program Files\OpenJDK\jdk-16.0.1\bin;C:\Program Files\ISC BIND 9\bin;C:\Program Files\PostgreSQL\9.6\bin;C:\Program Files\Oracle\VirtualBox;C:\msys64\usr\bin;C:\Program Files\Docker Toolbox;C:\nginx-1.12.0;C:\Program Files\MySQL\MySQL Utilities 1.6\;C:\Program Files (x86)\GtkSharp\2.12\bin;C:\Program Files\Cloud Foundry;C:\spring-2.0.0.M5\bin;C:\Program Files\PuTTY\;C:\Program Files\TortoiseSVN\bin;C:\Program Files\kubectl;C:\Program Files\minikube;C:\Program Files\Microsoft VS Code\bin;C:\WINDOWS\System32\OpenSSH\;C:\Program Files\OpenSSH-Win64;C:\Users\Toshinobu\AppData\Roaming\nvm;C:\nodejs;C:\Program Files (x86)\DevDesktop\tools;C:\chocolatey-package\php73;C:\Program Files (x86)\Windows Kits\8.1\Windows Performance Toolkit\;C:\Program Files\TortoiseGit\bin;C:\ProgramData\chocolatey\lib\maven\apache-maven-3.6.2\bin;C:\Go\bin;C:\Program Files (x86)\sbt\bin;C:\Program Files (x86)\Subversion\bin;C:\HashiCorp\Vagrant\bin;C:\Users\Toshinobu\bin;C:\Program Files\Git\cmd;C:\Program Files\Docker\Docker\resources\bin;C:\ProgramData\DockerDesktop\version-bin;C:\Ruby24-x64\bin;C:\Program Files\Common Files\Microsoft Shared\Windows Live;C:\Program Files (x86)\Common Files\Microsoft Shared\Windows Live;C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Program Files (x86)\Windows Live\Shared;C:\Program Files\Intel\WiFi\bin\;C:\Program Files\Common Files\Intel\WirelessCommon;c:\xampp\perl\bin\;c:\xampp\php\;c:\xampp\mysql\bin\;C:\ProgramData\ComposerSetup\bin;C:\ProgramData\chocolatey\bin;C:\Program Files\TortoiseGit\bin;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\Program Files\nodejs\;C:\Program Files (x86)\Skype\Phone\;C:\WINDOWS\system32\config\systemprofile\.dnx\bin;C:\Program Files\Microsoft DNX\Dnvm\;C:\Program Files\Microsoft SQL Server\130\Tools\Binn\;C:\Program Files (x86)\apache-maven-3.3.9\bin;C:\Program Files\Java\jdk1.8.0_112\bin;C:\ProgramData\Oracle\Java\javapath;C:\msys64\usr;C:\Users\Toshinobu\AppData\Local\Microsoft\WindowsApps;C:\Program Files\Microsoft VS Code\bin;C:\Users\Toshinobu\AppData\Roaming\nvm;C:\nodejs;C:\go\bin;C:\Users\Toshinobu\go\bin;C:\Program Files\LibreOffice\program;C:\Program Files\LibreOffice\sdk\lib;C:\Users\Toshinobu\go\bin;;C:\Eclipse-2020-06\pleiades-2020-06-java-win-64bit-jre_20200702\pleiades\eclipse;;. Key: java.vendor OutPut= AdoptOpenJDK Key: java.vm.info OutPut= mixed mode Key: java.vm.version OutPut= 11.0.7+10 Key: sun.io.unicode.encoding OutPut= UnicodeLittle Key: tomcat.util.buf.StringCache.byte.enabled OutPut= true Key: java.class.version OutPut= 55.0
⇧ ってな感じで、「user.dir」の値は、
C:\Eclipse-2020-06\pleiades-2020-06-java-win-64bit-jre_20200702\pleiades\eclipse
⇧ ってなってました...
いや~、Javaカオス過ぎる...、っていうか、環境に依存するって駄目でしょ...
「開発環境」と「本番環境」で挙動が変わってきたりしたらアウトなんだが...
『Write once, run anywhere』にならないじゃん...
試しに、「Spring Framework」で用意されてる「ResourceUtils」ってので、「ディレクトリのパス」を取得してみたところ、
ResourceUtils.getURL("classpath:").getPath();
衝撃の結果が...
/C:/Eclipse-2020-06/pleiades-2020-06-java-win-64bit-jre_20200702/pleiades/workspace/.metadata/.plugins/org.eclipse.wst.server.core/tmp0/wtpwebapps/Spring_Framework_Aop/WEB-INF/classes/
⇧ え...なんか更にパスが変わったんだけど...
いやはや、普通に「src/main/resources」までの「ディレクトリのパス」を取得したいだけなんですが...
そして、さらなる悲報...
⇧ まさかのOracleさんが、フルパスをハードコーディングしてるというね...
それって、つまり「開発環境」と「本番環境」でソースコードを書き替えないといけないってシチュエーション発生してしまうんでないの?
いや~、残念過ぎる...
っていうか、Javaのファイル操作のAPIって仕様が破綻している気がしてならない...
あとは、
⇧「ビルドツール」依存になってしまうけども、「Gradle」を使ってる場合は、「プロジェクトルート」のパスが取得できるっぽいので、「Gradle」側で値を取得して、Java側に値を渡してあげるって方法か、
⇧ 上記サイト様によりますと、「ファイル出力先」で使うのはNGっぽいですが、既存の「ディレクトリ」のパスを取得する分には問題ないかと思われるので、「ServletContext」経由でパスが取得できるっぽいですかね。
というか、Javaで「ディレクトリのパス」を取得するのって、こんなに面倒くさいってのが衝撃的過ぎるんだけど...
というわけで、「Gradle」のプロパティファイル経由で「プロジェクトルート」 のパスを取得することにしようと思って、
⇧ 上記サイト様を参考に、
⇧「プロパティファイル」を配置してJava側でファイルを読み込もうとするも、「java.io.FileNotFoundException」が発生するんだけど...
そもそもとして、
Eclipseからクラスを実行した場合、カレントディレクトリはプロジェクトがあるディレクトリです。
例えば プロジェクトが /eclipse/workspace/sample_project にある場合、
String path = Paths.get(".").toFile().getAbsolutePath();
実行すると、
/eclipse/workspace/sample_project/.
が返されます。
ファイルパスの先頭にスラッシュを記載するとルートディレクトリからのファイルパスを示します。
⇧ という情報があったんですが、自分の環境では、
String path = Paths.get(".").toFile().getAbsolutePath(); System.out.println(path);
⇧ を実行したところ、
C:\Eclipse-2020-06\pleiades-2020-06-java-win-64bit-jre_20200702\pleiades\eclipse\.
ちなみに、プロジェクトがあるディレクトリは、
C:\Eclipse-2020-06\pleiades-2020-06-java-win-64bit-jre_20200702\pleiades\workspace\Spring_Framework_Aop
⇧ ってな感じになってるんですが...
『Eclipseからクラスを実行した場合、カレントディレクトリはプロジェクトがあるディレクトリです。』って言葉をどう解釈すれば良いのか...
そして、衝撃...
⇧ 上記サイト様によりますと、 「Servlet」使ってる場合は、「カレントディレクトリ」が変わってくるらしい...まじか...
Javaさん、環境依存しまくりやん...
というわけで、Eclipseの「サーバー」タブに表示されてる「アプリケーションサーバー(赤枠で囲ってる部分)」をダブルクリック。
設定ファイルが開くので、「起動構成を開く」をクリック。
「引数」タブを選択し、「作業ディレクトリー」があるので、編集すれば良さ気。
ようやく謎が解けたよ...
正解の情報に辿り着くために1週間ぐらい無駄にしたんだけど、どうしてくれよう...
ワークスペースでなくとも、ファイルシステムでも指定できる。
プロジェクトのあるディレクトリを指定したいなら「ワークスペース」
その他の任意のディレクトリを指定したいなら「ファイル・システム」をクリックすれば良いと思う。
⇧ ということらしいので、「ワークスペース」を選択しときますか。
対象のプロジェクトを選択して、「OK」で。
「OK」で。
で、改めて、Eclipse内蔵の「アプリケーションサーバー」で「デバッグ実行」を実施してみたところ、
String path = Paths.get(".").toFile().getAbsolutePath(); System.out.println(path);
以下のようなパスが表示されました。
C:\Eclipse-2020-06\pleiades-2020-06-java-win-64bit-jre_20200702\pleiades\workspace\Spring_Framework_Aop\.
⇧ 今度は無事に、「プロジェクトディレクトリ」までのパスが取得できました。
というか、「本番環境」だとどういう設定になってるのかな?
そして、さらなる残念過ぎる情報が見つかる...
厳密に言うと、 Java 11 でも user
システムプロパティの書き換え自体はできます。しかし、 Javauser
システムプロパティの値のコピーを保持するように変更されたため、 実行時に user
システムプロパティを書き換えても、 相対パス解決の開始点として反映されなくなりました。
このバグレポートは冷たい回答とともにクローズされてしまいました。
⇧ お、終わってる...
『誠実さのかけらもなく 笑っている奴がいるよ(「青空 THE BLUE HEARTS」)』みたいな所業、まさに、ゲスの極み!
粗を探したいわけじゃないのに、次から次へとイケてない部分ばかり見つかり、正直なところ、Javaに対しての失望感たるや、筆舌に尽くしがたいと言わざるを得ません...
いや~、本当にJavaのバージョンを上げるのは構わんのだけど、バージョンアップを慌ただしくすることよりも大事なことあると思うけどな...
はい、脱線しました。
ようやく、本題の「Spring Framework」で「AOP(Aspect Oriented Programming)」を実装で。
ディレクトリの構成とかは以下のような感じになりました。
では、各ファイルを編集で。
■/Spring_Framework_Aop/build.gradle
/* * This file was generated by the Gradle 'init' task. * * This generated file contains a sample Java Library project to get you started. * For more details take a look at the Java Libraries chapter in the Gradle * User Manual available at https://docs.gradle.org/6.3/userguide/java_library_plugin.html */ plugins { // Apply the java-library plugin to add support for Java Library id 'java-library' } repositories { // Use jcenter for resolving dependencies. // You can declare any Maven/Ivy/file repository here. jcenter() } dependencies { // This dependency is exported to consumers, that is to say found on their compile classpath. api 'org.apache.commons:commons-math3:3.6.1' // This dependency is used internally, and not exposed to consumers on their own compile classpath. implementation 'com.google.guava:guava:28.2-jre' // https://mvnrepository.com/artifact/org.springframework/spring-core implementation group: 'org.springframework', name: 'spring-core', version: '5.3.7' // https://mvnrepository.com/artifact/org.springframework/spring-context implementation group: 'org.springframework', name: 'spring-context', version: '5.3.7' // https://mvnrepository.com/artifact/org.springframework/spring-aspects implementation group: 'org.springframework', name: 'spring-aspects', version: '5.3.7' // https://mvnrepository.com/artifact/org.springframework/spring-aop implementation group: 'org.springframework', name: 'spring-aop', version: '5.3.7' // https://mvnrepository.com/artifact/org.springframework/spring-webmvc implementation group: 'org.springframework', name: 'spring-webmvc', version: '5.3.7' // Use JUnit test framework testImplementation 'junit:junit:4.12' }
⇧依存関係としては、「spring-core」「spring-context」「spring-aspects」「spring-aop」「spring-webmvc」
■/Spring_Framework_Aop/gradle.properties
projectDir = ${projectDir}
■/Spring_Framework_Aop/src/main/resources/config/spring-web.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:mvc="http://www.springframework.org/schema/mvc" xmlns:util="http://www.springframework.org/schema/util" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- Spring MVC の機能を使うことを宣言。 --> <!-- この宣言をすることで、 @Component などのアノテーションが使えるようになる。 --> <mvc:annotation-driven /> <!-- <context:annotation-config />--> <!-- AspectJスタイルのSpring AOPを有効化 --> <aop:aspectj-autoproxy/> <!-- Bean となるクラスファイルが格納されているパッケージを宣言。 --> <!-- Spring はこのパッケージ配下を自動でスキャンし、Bean として登録する。 --> <context:component-scan base-package="Spring_Framework_Aop.*" /> </beans>
■/Spring_Framework_Aop/WebContent/WEB-INF/web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <!-- 文字のエンコーディングを指定し --> <filter> <filter-name>characterEncodingFilter</filter-name> <filter-class> <!-- org.Apache.catalina.filters.SetCharacterEncodingFilter --> org.springframework.web.filter.CharacterEncodingFilter </filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <!-- 上記フィルターをすべての URL で適用する。 --> <filter-mapping> <filter-name>characterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- リスナーを登録 --> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener> <!-- spring.xml --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:/config/spring-web.xml</param-value> </context-param> <!-- Spring MVC アプリの場合、だいたいは唯一のサーブレットを登録する。 --> <servlet> <servlet-name>dispatcherServlet</servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value></param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <!-- すべての URL リクエストについて、上記で登録したサーブレットで処理する。 --> <servlet-mapping> <servlet-name>dispatcherServlet</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> </web-app>
■/Spring_Framework_Aop/src/main/java/Spring_Framework_Aop/controller/HelloController.java
package Spring_Framework_Aop.controller; import java.io.FileNotFoundException; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.util.ResourceUtils; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; @RestController public class HelloController { @RequestMapping(value="/", method = RequestMethod.GET) public String index() { String str; try { str = ResourceUtils.getURL("file:/Spring_Framework_Aop/src/main/resources").getPath(); System.out.println(str); } catch (FileNotFoundException e) { // TODO 自動生成された catch ブロック e.printStackTrace(); } return "うぇるかむ!!"; } @RequestMapping(value="/hello", method= RequestMethod.GET) public String hello() { return handleNotifications().getStatusCode().toString(); } @RequestMapping(value="/goodbye", method=RequestMethod.GET) public String goodbye() { return handleNotifications().getStatusCode().toString(); } // "200 OK" を返す private ResponseEntity<String> handleNotifications () { return new ResponseEntity<>("", HttpStatus.OK); } }
■/Spring_Framework_Aop/src/main/java/Spring_Framework_Aop/aop/SpringAspect.java
package Spring_Framework_Aop.aop; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.net.InetAddress; import java.net.UnknownHostException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Properties; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.ScopedProxyMode; import org.springframework.stereotype.Component; @Scope(proxyMode = ScopedProxyMode.TARGET_CLASS, value = "prototype") @Aspect @Component public class SpringAspect { //private static final String PROJECT_DIR = "Spring_Framework_Aop"; private static final String LOG_DIR_PATH = "src/main/resources/log"; private static final String LOG_FILE_NAME = "access.log"; @Before("execution(* Spring_Framework_Aop.controller.*.*(..))") public void requestMappingGet() { System.out.println("[AOP start]"); // Properties properties = System.getProperties(); // Enumeration<Object> enumeration = properties.keys(); // for (int i = 0; i < properties.size(); i++) { // Object obj = enumeration.nextElement(); // System.out.println("Key: "+obj+"\tOutPut= "+System.getProperty(obj.toString())); // } //Path root = Paths.get("").toAbsolutePath(); //Path root =Paths.get(System.getProperty("user.dir")); Path root = Paths.get(new File(".").getAbsoluteFile().getParent()); //Path root = Paths.get(new File("." + File.pathSeparator+File.pathSeparator+"Spring_Framework_Aop").getAbsolutePath()); //Path root = Paths.get(".").normalize().toAbsolutePath(); //String path = Paths.get(".").toFile().getAbsolutePath(); //System.out.println(path); try(InputStream input = new FileInputStream("gradle.properties");) { //String root = ResourceUtils.getURL("file:/Spring_Framework_Aop/src/main/resources").getPath(); Properties prop = new Properties(); prop.load(input); System.out.println(prop.getProperty("projectDir")); InetAddress addr = InetAddress.getLocalHost(); Path dir = Paths.get(root.toString(), LOG_DIR_PATH); if (Files.notExists(dir)) { Files.createDirectories(dir); } Path filePath = Paths.get(dir.toString(), LOG_FILE_NAME) ; if (Files.notExists(filePath)) { Files.createFile(filePath); } try(BufferedWriter writer = Files.newBufferedWriter(filePath)) { writer.append(addr.getHostAddress()); } } catch (UnknownHostException e) { // TODO 自動生成された catch ブロック e.printStackTrace(); } catch (IOException e) { // TODO 自動生成された catch ブロック e.printStackTrace(); } } // private static ServletContext getContext() { // return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest() // .getServletContext(); // } }
⇩ ってな感じの内容で保存。
そしたらば、「サーバー」タブで「アプリケーションサーバー」を選択した状態で右クリックし、「開始(S)」で。
そしたらば、ブラウザから「http://localhost:8080/プロジェクト名/RequestMappingで指定したパス」 にアクセス。
⇧ 無事、結果が表示されました。
確認できたら、「アプリケーションサーバー」を停止で。
「src/main/resources」ディレクトリを選択した状態で、「F5」キーを押下すると、
「/Spring_Framework_Aop/src/main/resources/log/access.log」が作成されてるかと。
「access.log」には取得したIPアドレスが記載されてればOK。
日本語が文字化けしてるのと、「gradle.properties」で変数展開させる方法が未解決だけども、一応「AOP(Aspect Oriented Programming)」できましたね。
この度もモヤモヤ感が半端ないのだが...
今回はこのへんで。