Tuesday, June 15, 2010

Spring Recipes blog - Chapter 5

Ok - read chapter 5. It's introduces classic spring AOP. Starts from the ground level.

I'm just going to run and comment the code, in the interests of time.

Ok, imported and changed the compiler version to 1.5. Now, lets run main.

Ok, problem. It needs c:/spring-framework-2.5/lib/log4j/log4j-1.2.14.jar, which is reference by the classpath, does not exist.

I know he mentioned something about this. Did he say where to get it from? On page 139, he says it's located in lib/log4j directory of the spring installation. It probably has a different number - let's check.

Yep, log4j-1.2.15.jar

Change in eclipse -

run it...

2010-06-15 07:03:58 INFO LoggingAroundAdvice:15 - The method add() begins with [1.0, 2.0]
1.0 + 2.0 = 3.0
2010-06-15 07:03:58 INFO LoggingAroundAdvice:20 - The method add() ends with 3.0
2010-06-15 07:03:58 INFO LoggingAroundAdvice:15 - The method sub() begins with [4.0, 3.0]
4.0 - 3.0 = 1.0
2010-06-15 07:03:58 INFO LoggingAroundAdvice:20 - The method sub() ends with 1.0
2010-06-15 07:03:58 INFO LoggingAroundAdvice:15 - The method mul() begins with [2.0, 3.0]
2.0 * 3.0 = 6.0
2010-06-15 07:03:58 INFO LoggingAroundAdvice:20 - The method mul() ends with 6.0
2010-06-15 07:03:58 INFO LoggingAroundAdvice:15 - The method div() begins with [4.0, 2.0]
4.0 / 2.0 = 2.0
2010-06-15 07:03:58 INFO LoggingAroundAdvice:20 - The method div() ends with 2.0
2010-06-15 07:03:58 INFO LoggingAroundAdvice:15 - The method kilogramToPound() begins with [10.0]
10.0 kilogram = 22.0 pound
2010-06-15 07:03:58 INFO LoggingAroundAdvice:20 - The method kilogramToPound() ends with 22.0
2010-06-15 07:03:58 INFO LoggingAroundAdvice:15 - The method kilometerToMile() begins with [5.0]
5.0 kilometer = 3.1 mile
2010-06-15 07:03:58 INFO LoggingAroundAdvice:20 - The method kilometerToMile() ends with 3.1

Ok.


Now the commented programs...

package com.apress.springrecipes.calculator;

// need app context
import org.springframework.context.ApplicationContext;
// using classpath version to locate beans.xml
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {

public static void main(String[] args) {

// get the context
ApplicationContext context =
new ClassPathXmlApplicationContext("beans.xml");

// get the caculator from the context
ArithmeticCalculator arithmeticCalculator =
(ArithmeticCalculator) context.getBean("arithmeticCalculator");

// invoke methods
arithmeticCalculator.add(1, 2);
arithmeticCalculator.sub(4, 3);
arithmeticCalculator.mul(2, 3);
arithmeticCalculator.div(4, 2);

// do same for calculator
UnitCalculator unitCalculator =
(UnitCalculator) context.getBean("unitCalculator");
unitCalculator.kilogramToPound(10);
unitCalculator.kilometerToMile(5);
}
}

// interface

package com.apress.springrecipes.calculator;

public interface ArithmeticCalculator {

public double add(double a, double b);
public double sub(double a, double b);
public double mul(double a, double b);
public double div(double a, double b);
}

// implementation
package com.apress.springrecipes.calculator;

public class ArithmeticCalculatorImpl implements ArithmeticCalculator {

public double add(double a, double b) {
double result = a + b;
System.out.println(a + " + " + b + " = " + result);
return result;
}

public double sub(double a, double b) {
double result = a - b;
System.out.println(a + " - " + b + " = " + result);
return result;
}

public double mul(double a, double b) {
double result = a * b;
System.out.println(a + " * " + b + " = " + result);
return result;
}

public double div(double a, double b) {
if (b == 0) {
throw new IllegalArgumentException("Division by zero");
}
double result = a / b;
System.out.println(a + " / " + b + " = " + result);
return result;
}
}

// this is the hand-built version - you don't really need to know this,
// since spring provides implementations.

package com.apress.springrecipes.calculator;

// java reflection classes
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// array class
import java.util.Arrays;

// use logging and log factory
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;


// invocation handler implements invoke
public class CalculatorLoggingHandler implements InvocationHandler {


// create the proxy, passing in the target class
public static Object createProxy(Object target) {

// use reflection's Proxy; needs the classes class loader, interfaces
// returns the logging handler which wraps the target and acts as a proxy
// for each of it's interfaces methods.
// you can now use this to do stuff around the methods
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new CalculatorLoggingHandler(target));
}

// getting the log class
private Log log = LogFactory.getLog(this.getClass());


// inject the target
private Object target;

public CalculatorLoggingHandler(Object target) {
this.target = target;
}

// here's the invoke;
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// Log the method beginning with the method name and arguments.
log.info("The method " + method.getName() + "() begins with "
+ Arrays.toString(args));

// Perform the actual calculation on the target calculator object by calling
// Method.invoke() and passing in the target object and method arguments.
Object result = method.invoke(target, args);

// Log the method ending with the returning result.
log.info("The method " + method.getName() + "() ends with " + result);
return result;
}
}

I'm actually not seeing where the "invoke" method gets called, but main class shows how to get the proxy by calling the create proxy. So, maybe you call it
by saying arithematicCalculator.add

because you are actually calling that on the dynamic proxy. That's got to be it.

Ok, moving on.

// this is the same as above, but instead of logging, it's calling
// it's own validation method on each of the arguments.


package com.apress.springrecipes.calculator;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class CalculatorValidationHandler implements InvocationHandler {

public static Object createProxy(Object target) {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new CalculatorValidationHandler(target));
}

private Object target;

public CalculatorValidationHandler(Object target) {
this.target = target;
}

public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
for (Object arg : args) {
validate((Double) arg);
}
Object result = method.invoke(target, args);
return result;
}

private void validate(double a) {
if (a < 0) {
throw new IllegalArgumentException("Positive numbers only");
}
}
}

// this is just your implementation of AfterReturningAdvice

package com.apress.springrecipes.calculator;

import java.lang.reflect.Method;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.AfterReturningAdvice;

public class LoggingAfterAdvice implements AfterReturningAdvice {

private Log log = LogFactory.getLog(this.getClass());

public void afterReturning(Object returnValue, Method method,
Object[] args, Object target) throws Throwable {
log.info("The method " + method.getName() + "() ends with "
+ returnValue);
}
}

// your implementation of method return advice

package com.apress.springrecipes.calculator;

import java.lang.reflect.Method;
import java.util.Arrays;

import org.apache.commons.logging.Log;

ackage com.apress.springrecipes.calculator;

import java.lang.reflect.Method;
import java.util.Arrays;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.ThrowsAdvice;

public class LoggingThrowsAdvice implements ThrowsAdvice {

private Log log = LogFactory.getLog(this.getClass());

public void afterThrowing(Method method, Object[] args, Object target,
IllegalArgumentException e) throws Throwable {
log.error("Illegal argument " + Arrays.toString(args) + " for method "
+ method.getName() + "()");
}
}

// your implmentation of methodBeforeAdvice

import org.apache.commons.logging.LogFactory;
import org.springframework.aop.MethodBeforeAdvice;

public class LoggingBeforeAdvice implements MethodBeforeAdvice {

private Log log = LogFactory.getLog(this.getClass());

public void before(Method method, Object[] args, Object target)
throws Throwable {
log.info("The method " + method.getName() + "() begins with "
+ Arrays.toString(args));
}
}

// your implementation of ThrowsAdvice

ackage com.apress.springrecipes.calculator;

import java.lang.reflect.Method;
import java.util.Arrays;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.ThrowsAdvice;

public class LoggingThrowsAdvice implements ThrowsAdvice {

private Log log = LogFactory.getLog(this.getClass());

// note you have to specify each exception to capture
public void afterThrowing(Method method, Object[] args, Object target,
IllegalArgumentException e) throws Throwable {
log.error("Illegal argument " + Arrays.toString(args) + " for method "
+ method.getName() + "()");
}
}


// Your implementation of MethodInterceptor
// don't forget to call "proceed"
// note how it lets you do before, after *and* exceptions all in one class



package com.apress.springrecipes.calculator;

import java.util.Arrays;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class LoggingAroundAdvice implements MethodInterceptor {

private Log log = LogFactory.getLog(this.getClass());

public Object invoke(MethodInvocation methodInvocation) throws Throwable {
log.info("The method " + methodInvocation.getMethod().getName()
+ "() begins with "
+ Arrays.toString(methodInvocation.getArguments()));
try {
Object result = methodInvocation.proceed();
log.info("The method " + methodInvocation.getMethod().getName()
+ "() ends with " + result);
return result;
} catch (IllegalArgumentException e) {
log.error("Illegal argument "
+ Arrays.toString(methodInvocation.getArguments())
+ " for method " + methodInvocation.getMethod().getName()
+ "()");
throw e;
}
}
}


// beans.xml

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-2.5.xsd">

// define calculator with the impl class

class="com.apress.springrecipes.calculator.ArithmeticCalculatorImpl" />

// same for unit calculator
class="com.apress.springrecipes.calculator.UnitCalculatorImpl" />

// set up logging around advice - see implementation above
class="com.apress.springrecipes.calculator.LoggingAroundAdvice" />

// define pointcuts, i.e. where to call logging around advisor, by method name
class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">


add
sub





// by regex

class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">


.*mul.*
.*div.*





// by aspect j syntax
class="org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor">

execution(* *.*To*(..))






// use autoproxy default advisor to create a proxy for each bean in the IoC
// container. This is the simplest, but must powerful, because the advisors will
// be called for every method

No comments:

Post a Comment