読者です 読者をやめる 読者になる 読者になる

感謝のプログラミング 10000時間

たどり着いた結果(さき)は、感謝でした。

【プログラミング 117.5時間目】朝日新聞が「ソースコード」を「遠隔操作ウイルスのプログラム」と書いた件について、ふと思ったこと。

<スポンサーリンク>

朝日新聞が

首輪に仕込まれたメモリーカードには、「ソースコード」と呼ばれる遠隔操作ウイルスのプログラムが記録されていた。

という記事を書いていたことに対して、エンジニア達からは朝日新聞馬鹿だ阿呆だダメだという声が聞こえてきます。
でも、システムに関わらない人から見ると、本当に「ソースコード」ってよくわからないものだと思うんですよね。

ソースコードとプログラムの違いって何?
そもそもソースコードって何者?
遠隔操作ウイルスとプログラムってどう違うの?

みたいな、エンジニアから見たら全てが同義に見えることも、普通の人から見ると何がなんだか区別がつかないことかと思うんです。

だから、先日炎上したエントリを書いた後に、すごく言いにくいことだけれど、、

エンジニアは「エンジニアじゃない人」の視点を意識して、自分たちがやっていることを簡単に噛み砕いて説明できるように、考えておくことが大事なのかなぁと思いました。

お客様に接するときにも、自分が「ITをよく知っている人」みたいに専門用語をたくさん並べるんじゃなくて、小学生に説明するように簡単なたとえ話を使って話せなければいけないのかな、と。
お客様を馬鹿にしてるわけじゃないですよ。
知ってる人から見たら当たり前のことが、知らない人から見たら意味不明だったりする。
知ってる人に囲まれていると、自分の世界が当たり前のように思えてしまう。
だから、そこは意識してちょっと俯瞰して見てみないといけないのかな、と。

自分の話している言葉は、世間一般で使われている言葉だろうか、って。

で、その1番の練習は、普段から彼女とか奥さんにわかりやすく説明してみることなんじゃないかと思います。
何も知らない女の子が「わかった!そういうことか!」と言える説明ができれば、みんながわかる説明ができる。彼女に自分の仕事のこともわかってもらえて一石二鳥だ。

というわけで、まずは彼女を作ることから始めます。

★★
話が変わって、Springの勉強。
色々とサンプルをいじって、やっとAOPの感じがちょっと掴めた気がします。
参考にさせていただいたのは、「SpringによるWebアプリケーションスーパーサンプル」

SpringによるWebアプリケーションスーパーサンプル 第2版

SpringによるWebアプリケーションスーパーサンプル 第2版


この本の1章を読んで、自分なりに書き換えて使ってみる事で、AOPが少しわかってきた。
まず、実行する対象のメソッドを持つクラス

package aop;

public class AopTest {
	private String message;
	private String name;
	
	public void setMessage(String message) {
		this.message = message;
	}
	public void setName(String name) {
		this.name = name;
	}
	
	public void printMessage() {
		try {
			Thread.sleep(3000);
		} catch(InterruptedException ignore) {
		}
		System.out.println(message);
	}
	
	public String printName() {
		System.out.println(name);
		return "DO printName() METHOD!";
	}
}

ここに書いてあるメソッドを実行する。
で、メソッドを実行する前後に、ロギング処理を差し込むイメージ。
注目すべきは、上のクラスにロギングの処理を書いていないことだ。
Logging.write("Hoge");
的なことは書かれていない。

では、どうなっているかというと・・・
ロギングを書いた別のクラスが、Springフレームワークによって差し込まれる。

package aop;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.util.StopWatch;

public class LoggingAdvice implements MethodInterceptor {
	/* */
	public Object invoke(MethodInvocation invocation) throws Throwable {
		
		/* bean.xmlで設定したメソッド名を取得する。
		 * <value>.*print.*</value>と書いたところ。
		 * */
		String methodName = invocation.getMethod().getName();
		System.out.println("methodName..." +methodName);
		
		/* 各メソッドを実行した時間を計測するために使う */
		StopWatch sw = new StopWatch();
		sw.start(methodName);
		
		/* メソッドを実行する前にここが差し込まれる*/
		System.out.println("[LOG] METHOD INVOKE " + methodName + " is calling...");
		
		/* ここでメソッド実行する。戻り値はrtnObjに入る*/
		Object rtnObj = invocation.proceed();
		sw.stop();
		
		System.out.println("[LOG] METHOD " + methodName +" is done.");
		System.out.println("[LOG] RUN TIME " + sw.getTotalTimeMillis() / 1000 + " SECONDS");
		System.out.println("RETURN " + rtnObj);
		return rtnObj;
	}

}

このクラスの処理が、メソッド実行の前後でログを出す役割を果たす。
SpringのAOPを利用することによって、LoggingAdviceとAopTestのクラスが疎結合になっている。

この処理を定義するbeans.xmlは以下のとおり

<?xml version="1.0" encoding="Shift_JIS" ?>

<beans xmlns="http://www.springframework.org/schema/beans"
	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">
	
    <bean id="targetBean" class="aop.AopTest">
        <property name="name">
            <value>SPRING</value>
        </property>
        <property name="message">
        	<value>HELLO!!</value>
        </property>
    </bean>
    
    <bean id="loggingAdvice" class="aop.LoggingAdvice" />
    
    <bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="target">
            <ref local="targetBean" />
        </property>
        <property name="interceptorNames">
            <list>
                <value>advisor</value>
            </list>
        </property>
    </bean>
    
    <bean id="advisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
        <property name="advice">
            <ref local="loggingAdvice" />
        </property>
        <property name="pointcut">
            <bean class="org.springframework.aop.support.JdkRegexpMethodPointcut">
                <property name="pattern">
                    <value>.*print.*</value>
                </property>
            </bean>
        </property>
    </bean>
</beans>

proxyが、メソッドの呼び出しをインターセプトする役割を果たす。
インターセプトとは、ある処理が呼び出されたときに、その処理が実行される前に割り込みをして別の処理を実行することである。

実行するMainクラスは以下の通り。
ここでprintMessage(),printName()が実行されるわけだが、その前後にロギングがはさみこまれる。

package aop;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.FileSystemResource;

public class Main {
	public static void main(String[] args) {
		BeanFactory factory = new XmlBeanFactory(new FileSystemResource("beans.xml"));
		AopTest bean = (AopTest)factory.getBean("proxy");
		bean.printMessage();
		bean.printName();
	}
}

antの設定ファイル(build.xml)

<?xml version="1.0"?>

<project name="my_sample" basedir="." default="run.aop">
	
	<property name="src.dir" value="src"/>
	<property name="lib.dir" value="lib"/>
	<property name="classes.dir" value="classes"/>

	<path id="classpath">
		<fileset dir="${lib.dir}">
			<include name="*.jar"/>
		</fileset>
	</path>
	
	<target name="init" description="作業ディレクトリを初期化します。">
		<delete dir="${classes.dir}"/>
		<mkdir dir="${classes.dir}"/>
	</target>

	<target name="compile" depends="init" description="コンパイルします。">
		<javac destdir="${classes.dir}" debug="on">
			<src path="${src.dir}"/>
			<classpath refid="classpath"/>
		</javac>
		<copy todir="${classes.dir}">
			<fileset dir="${src.dir}">
				<include name="**/*.xml" />
			</fileset>	
		</copy>
	</target>
	
	<target name="run.aop" depends="compile" description="アプリケーションを実行します。">
		<java classname="aop.Main" classpath="${classes.dir}" classpathref="classpath" fork="true"></java>
	</target>
</project>

実行結果

	 [java] methodName...printMessage
     [java] [LOG] METHOD INVOKE printMessage is calling...
     [java] HELLO!!
     [java] [LOG] METHOD printMessage is done.
     [java] [LOG] RUN TIME 3 SECONDS
     [java] RETURN null
     [java] methodName...printName
     [java] [LOG] METHOD INVOKE printName is calling...
     [java] SPRING
     [java] [LOG] METHOD printName is done.
     [java] [LOG] RUN TIME 0 SECONDS
     [java] RETURN DO printName() METHOD!
BUILD SUCCESSFUL
Total time: 5 seconds

ちなみに、
Cannot proxy target class because CGLIB2 is not available. Add CGLIB to the class path or specify proxy interfaces.
「CGLがないから、追加してくださいね」
というエラーが出たので、
http://sourceforge.net/projects/cglib/?source=dlp
からjarを取得しました。
この件は以下の記事を参考にさせていただきました。
http://copycat-memo.blogspot.jp/2010/08/cglib2asm.html