Monday, June 22, 2015

Java 8 key features

  1. Lambda expressions
  2. Method references
  3. Default Methods (Defender methods)
  4. A new Stream API.
  5. Optional
  6. A new Date/Time API.
  7. Nashorn, the new JavaScript engine
  8. Removal of the Permanent Generation

1) Lambda Expressions

The biggest new feature of Java 8 is language level support for lambda expressions (Project Lambda). A lambda expression is like syntactic sugar for an anonymous class with one method whose type is inferred. However, it will have enormous implications for simplifying development.

Syntax

The main syntax of a lambda expression is “parameters -> body”. The compiler can usually use the context of the lambda expression to determine the functional interface being used and the types of the parameters. There are four important rules to the syntax:
  • Declaring the types of the parameters is optional.
  • Using parentheses around the parameter is optional if you have only one parameter.
  • Using curly braces is optional (unless you need multiple statements).
  • The “return” keyword is optional if you have a single expression that returns a value.
Here are some examples of the syntax:
1 () -> System.out.println(this)
2 (String str) -> System.out.println(str)
3 str -> System.out.println(str)
4 (String s1, String s2) -> { return s2.length() - s1.length(); }
5 (s1, s2) -> s2.length() - s1.length()
The last expression could be used to sort a list; for example:
1 Arrays.sort(strArray, 
2   (String s1, String s2) -> s2.length() - s1.length());
In this case the lambda expression implements the Comparator interface to sort strings by length.

Scope

Here’s a short example of using lambdas with the Runnable interface:
 1 import static java.lang.System.out;
 2 
 3 public class Hello { 
4  Runnable r1 = () -> out.println(this);
 5  Runnable r2 = () -> out.println(toString());
 6 
 7  public String toString() { return "Hello, world!"; }
 8 
 9  public static void main(String... args) {
10   new Hello().r1.run(); //Hello, world!
11   new Hello().r2.run(); //Hello, world!
12  }
13 }
The important thing to note is both the r1 and r2 lambdas call the toString() method of the Hello class. This demonstrates the scope available to the lambda.
You can also refer to final variables or effectively final variables. A variable is effectively final if it is only assigned once.
For example, using Spring’s HibernateTemplate:
1 String sql = "delete * from User";
2 getHibernateTemplate().execute(session -> 
3     session.createSQLQuery(sql).uniqueResult());
In the above, you can refer to the variable sql because it is only assigned once. If you were to assign to it a second time, it would cause a compilation error

Method references

Since a lambda expression is like an object-less method, wouldn’t be nice if we could refer to existing methods instead of using a lamda expression? This is exactly what we can do with method references.
For example, imagine you frequently need to filter a list of Files based on file types. Assume you have the following set of methods for determining a file’s type:
1 public class FileFilters {
2  public static boolean fileIsPdf(File file) {/*code*/}
3  public static boolean fileIsTxt(File file) {/*code*/}
4  public static boolean fileIsRtf(File file) {/*code*/}
5} 
Whenever you want to filter a list of files, you can use a method reference as in the following example (assuming you already defined a methodgetFiles() that returns a Stream):
1 Stream<File> pdfs = getFiles().filter(FileFilters::fileIsPdf);
2 Stream<File> txts = getFiles().filter(FileFilters::fileIsTxt);
3 Stream<File> rtfs = getFiles().filter(FileFilters::fileIsRtf);
Method references can point to:
  • Static methods.
  • Instance methods.
  • Methods on particular instances.
  • Constructors (ie. TreeSet::new)
For example, using the new java.nio.file.Files.lines method:
1 Files.lines(Paths.get("Nio.java"))
2.             map(String::trim)
3.             forEach(System.out::println);
The above reads the file “Nio.java”, calls trim() on every line, and then prints out the lines. Notice that System.out::println refers to the println method on an instance  of PrintStream

Functional Interfaces

In Java 8 a functional interface is defined as an interface with exactly one abstract method. This even applies to interfaces that were created with previous versions of Java.
Java 8 comes with several new functional interfaces in the package, java.util.function.
  • Function - takes an object of type T and returns R.
  • Supplier - just returns an object of type T.
  • Predicate - returns a boolean value based on input of type T.
  • Consumer - performs an action with given object of type T.
  • BiFunction - like Function but with two parameters.
  • BiConsumer - like Consumer but with two parameters.
It also comes with several corresponding interfaces for primitive types, such as:
  • IntConsumer
  • IntFunction
  • IntPredicate
  • IntSupplier
See java.util.function Javadocs for more information
The coolest thing about functional interfaces is that they can be assigned to anything that would fulfill their contract. Take the following code for example:
1 Function<String, String> atr = (name) -> {return "@" + name;};
2 Function<String, Integer> leng = (name) -> name.length();
3 Function<String, Integer> leng2 = String::length;
This code is perfectly valid Java 8. The first line defines a function that prepends “@” to a String. The last two lines define functions that do the same thing: get the length of a String.
The Java compiler is smart enough to convert the method reference to String’s length() method into a Function (a functional interface) whose applymethod takes a String and returns an Integer. For example:
1 for (String s : args) out.println(leng2.apply(s));
This would print out the lengths of the given strings.
Any interface can be functional interface, not merely those that come with Java. To declare your intention that an interface is functional, use the@FunctionalInterface annotation. Although not necessary, it will cause a compilation error if your interface does not satisfy the requirements (ie. one abstract method)

Comparisons to Java 7

To better illustrate the benefit of Lambda-expressions, here are some examples of how code from Java 7 can be shortened in Java 8.

Creating an ActionListener

1 // Java 7
2 ActionListener al = new ActionListener() {
3     @Override
4     public void actionPerformed(ActionEvent e) {
5         System.out.println(e.getActionCommand());
6     }
7 };
8 // Java 8
9 ActionListener al8 = e -> System.out.println(e.getActionCommand());

Printing out a list of Strings

1 // Java 7
2 for (String s : list) {
3     System.out.println(s);
4 }
5 //Java 8
6 list.forEach(System.out::println);

Sorting a list of Strings

 1 // Java 7
 2 Collections.sort(list, new Comparator<String>() {
 3     @Override
 4     public int compare(String s1, String s2) {
 5         return s1.length() - s2.length();
 6     }
 7 });
 8 //Java 8
 9 Collections.sort(list, (s1, s2) -> s1.length() - s2.length());
10 // or
11 list.sort(Comparator.comparingInt(String::length));

Sorting

For the sorting examples, assume you have the following Person class:
 1 public static class Person {
 2 
 3     String firstName;
 4     String lastName;
 5 
 6     public String getFirstName() {
 7         return firstName;
 8     }
 9 
10     public String getLastName() {
11         return lastName;
12     }
13 }
Here’s how you might sort this list in Java 7 by last-name and then first-name:
 1 Collections.sort(list, new Comparator<Person>() {
 2     @Override
 3     public int compare(Person p1, Person p2) {
 4         int n = p1.getLastName().compareTo(p2.getLastName());
 5         if (n == 0) {
 6             return p1.getFirstName().compareTo(p2.getFirstName());
 7         }
 8         return n;
 9     }
10 });
In Java 8, this can be shortened to the following:
1 list.sort(Comparator.comparing(Person::getLastName)
2         .thenComparing(Person::getFirstName));

2) Default Methods

In order to add the stream method (or any others) to the core Collections API, Java needed another new feature, Default methods (also known asDefender Methods or Virtual Extension methods). This way they could add new methods to the List interface for example without breaking all the existing implementations (backwards compatibility).
Default methods can be added to any interface. Like the name implies, any class that implements the interface but does not override the method will get the default implementation.
For example, the stream method in the Collection interface is defined something like the following:
1 default public Stream stream() {
2  return StreamSupport.stream(spliterator());
3 } 

Default and Functional
You can always override a default method if you need different behavior. An interface can have one or more default methods and still be functional. For example, take a look at the Iterable interface:
 1 @FunctionalInterface
 2 public interface Iterable {
 3  Iterator iterator();
 4  default void forEach(Consumer super T> action) {
 5   Objects.requireNonNull(action);
 6   for (T t : this) {
 7    action.accept(t);
 8   }
 9  }
10 }
It has both the iterator() method and the forEach method.

Multiple Defaults

In the unlikely case that your class implements two or more interfaces that define the same default method, Java will throw a compilation error. You will need to override the method and choose from one of the methods. For example:
 1 interface Foo {
 2  default void talk() {
 3   out.println("Foo!");
 4  }
 5 }
 6 interface Bar {
 7  default void talk() {
 8   out.println("Bar!");
 9  }
10 }
11 class FooBar implements Foo, Bar {
12  @Override
13  void talk() { Foo.super.talk(); }   
14 }
In the above code, talk is overridden and calls Foo’s talk method. This is similar to the way you refer to a super class in pre-Java-8.

Static Methods on Interface

Although not strictly related to default methods, the ability to add static methods to interfaces is a similar change to the Java language.
For example, there are many static methods on the new Stream interface. This makes “helper” methods easier to find since they can be located directly on the interface, instead of a different class such as StreamUtil or Streams.
Here’s an example in the new Stream interface:
1 public static<T> Stream<T> of(T... values) {
2     return Arrays.stream(values);
3 }
The above method creates a new stream based on the given values

3) Streams

The Stream interface is such a fundamental part of Java 8 it deserves its own chapter.
What is a Stream?
The Stream interface is located in the java.util.stream package. It represents a sequence of objects somewhat like the Iterator interface. However, unlike the Iterator, it supports parallel execution.
The Stream interface supports the map/filter/reduce pattern and executes lazily, forming the basis (along with lambdas) for functional-style programming in Java 8.
There are also corresponding primitive streams (IntStream, DoubleStream, and LongStream) for performance reasons
Generating Streams
There are many ways to create a Stream in Java 8. Many of the existing Java core library classes have Stream returning methods in Java 8.

Streaming Collections

The most obvious way to create a stream is from a Collection.
The Collection interface has two default methods on it for creating streams:
  • stream(): Returns a sequential Stream with the collection as its source.
  • parallelStream(): Returns a possibly parallel Stream with the collection as its source.
The ordering of the Stream relies on the underlying collection just like an Iterator.

Streaming Files

The BufferedReader now has the lines() method which returns a Stream; for example3:
1 try (FileReader fr = new FileReader("file");
2     BufferedReader br = new BufferedReader(fr)) {
3     br.lines().forEach(System.out::println);
4 }
You can also read a file as a Stream using Files.lines(Path filePath); for example:
1 try (Stream st = Files.lines(Paths.get("file"))) {
2     st.forEach(System.out::println);
3 }
Note this populates lazily; it does not read the entire file when you call it.
Files.lines(Path): Any IOException that is thrown while processing the file (after the file is opened) will get wrapped in anUncheckedIOException and thrown.

Streaming File Trees

There are several static methods on the Files class for navigating file trees using a Stream.
  • list(Path dir) – Stream of files in the given directory.
  • walk(Path dir)4 – Stream that traverses the file tree depth-first starting at the given directory.
  • walk(Path dir, int maxDepth) – Same as walk(dir) but with a maximum depth.

Streaming Text Patterns

The Pattern class now has a method, splitAsStream(CharSequence), which creates a Stream.
For example:
1 import java.util.regex.Pattern;
2 // later on...
3 Pattern patt = Pattern.compile(",");
4 patt.splitAsStream("a,b,c")
5     .forEach(System.out::println);
The above uses a very simple pattern, a comma, and splits the text into a stream and prints it out. This would produce the following output:
1 a
2 b
3 c

Infinite Streams

Using the generate or iterate static methods on Stream, you can create a Stream of values including never ending streams. For example, you could call generate in the following way to create an infinite supply of objects:
1 Stream.generate(() -> new Dragon());
For example, you could use this technique to produce a stream of CPU load or memory usage. However, you should use this with caution. It is similar to an infinite loop.
You could also use generate to create an infinite random number supply; for example:
1 Stream.generate(() -> Math.random());
However, the java.util.Random class does this for you with the following new methods: ints()longs(), and doubles(). Each of those methods is overloaded with definitions similar to the following:
  • ints(): An infinite Stream of random integers.
  • ints(int n, int m): An infinite Stream of random integers from n (inclusive) to m (exclusive).
  • ints(long size): A Stream of given size of random integers.
  • ints(long size, int n, int m): A Stream of given size of random integers with given bounds.
The iterate method is similar to generate except it takes an initial value and a Function that modifies that value. For example, you can iterate over the Integers using the following code:
1 Stream.iterate(1, i -> i+1)
2     .forEach(System.out::print);
This would print out “1234…” continuously until you stop the program.
There are ways to limit an infinite stream which we will cover later (filter and limit).

Ranges

There are also new methods for creating ranges of numbers as Streams.
For example, the static method, range, on the IntStream interface:
1 IntStream.range(1, 11)
2     .forEach(System.out::println);
The above would print out the numbers one through ten.
Each primitive Stream (IntStream, DoubleStream, and LongStream) has a corresponding range method.

Streaming Anything

You can create a Stream from any number of elements or an array using the two following methods:
1 Stream<Integer> s = Stream.of(1, 2, 3);
2 Stream<Object> s2 = Arrays.stream(array);
Stream.of can take any number of parameters of any type.
For Each
The most basic thing you can do with a Stream is loop through it using the forEach method. For example, to print out all of the files in the current directory, you could do the following:
1 Files.list(Paths.get("."))
2     .forEach(System.out::println);
For the most part, this replaces the “for loop”. It is more concise, and more object-oriented since you are delegating the implementation of the actual loop.

Map/Filter/Reduce

Lambda expressions and default methods allow us to implement map/filter/reduce in Java 8. Actually it is already implemented for us in the standard library.
For example, imagine you want to get the current point scores from a list of player-names and find the player with the most points. You have a simple class, PlayerPoints, and a getPoints method defined as the following:
 1 public static class PlayerPoints {
 2  public final String name;
 3  public final long points;
 4  
 5  public PlayerPoints(String name, long points) {
 6    this.name = name;
 7    this.points = points;
 8  } 
 9  
10  public String toString() {
11    return name + ":" + points;
12  }
13 }
14 
15 public static long getPoints(final String name) {
16  // gets the Points for the Player
17 }
Finding the highest player could be done very simply in Java 8 as shown in the following code:
1 PlayerPoints highestPlayer = 
2   names.stream().map(name -> new PlayerPoints(name, getPoints(name)))
3  .reduce(new PlayerPoints("", 0.0), 
4    (s1, s2) -> (s1.points > s2.points) ? s1 : s2);
This could also be done in Java 7 with the dollar library (or similarly with Guava or Functional-Java), but it would be much more verbose as shown in the following:
 1 PlayerPoints highestPlayer = 
 2   $(names).map(new Function<String, PlayerPoints>() { 
 3   public PlayerPoints call(String name) { 
 4    return new PlayerPoints(name, getPoints(name));
 5   }
 6  })
 7  .reduce(new PlayerPoints("", 0.0), 
 8  new BiFunction<PlayerPoints, PlayerPoints, PlayerPoints>() {
 9   public PlayerPoints call(PlayerPoints s1, PlayerPoints s2) { 
10    return (s1.points > s2.points) ? s1 : s2;
11   }
12  });
The major benefit to coding this way (apart from the reduction in lines of code) is the ability to hide the underlying implementation of map/reduce. For example, it’s possible that map and reduce are implemented concurrently, allowing you to easily take advantage of multiple processors. We’ll describe one way to do this (ParallelArray) in the following section.

Parallel Array

The ParallelArray was part of JSR-166, but ended up being excluded from the standard Java lib. It does exist and was released to the public domain (you can download it from the JSR website).
Although it was already out there, it really wasn’t easy to use until closures were included in the Java language. In Java 7 using the ParallelArray looks like the following:
 1 // with this class
 2 public class Student {
 3     String name;
 4     int graduationYear;
 5     double gpa;
 6 }
 7 // this predicate
 8 final Ops.Predicate<Student> isSenior = 
 9  new Ops.Predicate<>() {
10   public boolean op(Student s) {
11    return s.graduationYear == Student.THIS_YEAR;
12   }
13  };
14 // and this conversion operation
15 final Ops.ObjectToDouble<Student> selectGpa = 
16  new Ops.ObjectToDouble<>() {
17   public double op(Student student) {
18    return student.gpa;
19   }
20  };
21 // create a fork-join-pool
22 ForkJoinPool fjPool = new ForkJoinPool();
23 ParallelArray<Student> students = new ParallelArray<>(fjPool, data);
24 // find the best GPA:
25 double bestGpa = students.withFilter(isSenior)
26                          .withMapping(selectGpa)
27                          .max();
In Java 8, you can do the following:
1 // create a fork-join-pool
2 ForkJoinPool pool = new ForkJoinPool();
3 ParallelArray<Student> students = new ParallelArray<>(pool,data);
4 // find the best GPA:
5 double bestGpa = students
6     .withFilter((Student s) -> (s.graduationYear == THIS_YEAR))
7     .withMapping((Student s) -> s.gpa)
8     .max();
However, Java 8’s addition of stream() and parallelStream() make this even easier:
1 double bestGpa = students
2     .parallelStream()
3     .filter(s -> (s.graduationYear == THIS_YEAR))
4     .mapToDouble(s -> s.gpa)
5     .max().getAsDouble();
This makes it extremely simple to switch between a sequential implementation and a concurrent one.

Peek

You can peek into a stream to do some action without interrupting the stream.
For example you could print out elements to debug code:
1 Files.list(Paths.get("."))
2     .map(Path::getFileName)
3     .peek(System.out::println)
4     .forEach(p -> doSomething(p));
You can use any action you want, but you should not try to modify elements; you should use map instead.

Limit

The limit(int n) method can be used to limit a stream to the given number of elements. For example:
1 Random rnd = new Random();
2 rnd.ints().limit(10)
3     .forEach(System.out::println);
The above would print out ten random integers.

Sort

Stream also has the sorted() method for sorting a stream. Like all intermediate methods on Stream (such as mapfilter, and peek), the sorted()method executes lazily. Nothing happens until a terminating operation (such as reduce or forEach) is called. However, you should call a limiting operation like limit before calling sorted() on an infinite stream.
For example, the following would throw a runtime exception (using build 1.8.0-b132):
1 rnd.ints().sorted().limit(10)
2     .forEach(System.out::println);
However, the following code works just fine:
1 rnd.ints().limit(10).sorted()
2     .forEach(System.out::println);
Also, you should call sorted() after any calls to filter. For example, this code prints out the first five Java file-names in the current directory:
1 Files.list(Paths.get("."))
2     .map(Path::getFileName) // still a path
3     .map(Path::toString) // convert to Strings
4     .filter(name -> name.endsWith(".java"))
5     .sorted() // sort them alphabetically
6     .limit(5) // first 5
7     .forEach(System.out::println);
The code above does the following:
  • Lists the files in the current directory.
  • Maps those files to file names.
  • Finds names that end with “.java”.
  • Takes only the first five (sorted alphabetically).
  • Prints them out.

Collectors and Statistics

Since Streams are lazily evaluated and support parallel execution, you need a special way to combine results; this is called a Collector.
Collector represents a way to combine the elements of a Stream into one result. It consists of three things:
  • supplier of an initial value.
  • An accumulator which adds to the initial value.
  • combiner which combines two results into one.
There are two ways to do this: collect(supplier,accumulator,combiner), or collect(Collector) (types left off for brevity).
Luckily, Java 8 comes with several Collectors built in. Import them the following way:
1 import static java.util.stream.Collectors.*;

Simple Collectors

The simplest collectors are things like toList() and toCollection():
1 // Accumulate names into a List
2 List<String> list = dragons.stream()
3         .map(Dragon::getName)
4         .collect(toList());
5 
6 // Accumulate names into a TreeSet
7 Set<String> set = dragons.stream()
8         .map(Dragon::getName)
9         .collect(toCollection(TreeSet::new));

Joining

If you’re familiar with Apache Commons’ StringUtil.join, the joining collector is similar to it. It combines the stream using a given delimiter. For example:
1 String names = dragons.stream()
2         .map(Dragon::getName)
3         .collect(joining(","));
This would combine all of the names into one String separated by commas.

Statistics

More complex collectors resolve to a single value. For example, you can use an “averaging” Collector to get the average; for example:
1 System.out.println("\n----->Average line length:");
2 System.out.println(
3     Files.lines(Paths.get("Nio.java"))
4         .map(String::trim)
5         .filter(s -> !s.isEmpty())
6         .collect(averagingInt(String::length))
7         );
The above code calculates the average length of non-empty lines in the file “Nio.java”.
Sometimes you want to collect multiple statistics about a collection. Because Streams are consumed when you call collect, you need to calculate all of your statistics at once. This is where SummaryStatistics comes in. First import the one you want to use:
1 import java.util.IntSummaryStatistics;    
Then use the summarizingInt collector; for example:
1 IntSummaryStatistics stats = Files.lines(Paths.get("Nio.java"))
2         .map(String::trim)
3         .filter(s -> !s.isEmpty())
4         .collect(summarizingInt(String::length));
5 
6 System.out.println(stats.getAverage());
7 System.out.println("count=" + stats.getCount());
8 System.out.println("max=" + stats.getMax());
9 System.out.println("min=" + stats.getMin());
The above code performs the same average as before, but also computes the maximum, minimum, and count of the elements.
There’s also summarizingLong and summarizingDouble.
Equivalently, you can map your stream to a primitive type and then call summaryStatistics(). For example:
1 IntSummaryStatistics stats = Files.lines(Paths.get("Nio.java"))
2     .map(String::trim)
3     .filter(s -> !s.isEmpty())
4     .mapToInt(String::length)
5     .summaryStatistics();

Grouping and Partitioning

The groupingBy collector groups elements based on a function you provide. For example:
1 // Group by first letter of name
2 List<Dragon> dragons = getDragons();
3 Map<Character,List<Dragon>> map = dragons.stream()
4         .collect(groupingBy(dragon -> dragon.getName().charAt(0)));
Similarly, the partitioningBy method creates a map with a boolean key. For example:
1 // Group by whether or not the dragon is green
2 Map<Boolean,List<Dragon>> map = dragons.stream()
3         .collect(partitioningBy(Dragon::isGreen));

Parallel Grouping

To execute grouping in parallel (if you don’t care about ordering) you should use the groupingByConcurrent method. The underlying stream should be unordered to allow grouping to occur in parallel; for example:dragons.parallelStream().unordered().collect(groupingByConcurrent(Dragon::getColor));.

Comparisons to Java 7

To better illustrate the benefit of Streams in Java 8, here are some examples of code from Java 7 compared to their new versions.

Finding a maximum

 1 // Java 7
 2 double max = 0;
 3 
 4 for (Double d : list) {
 5     if (d > max) {
 6         max = d;
 7     }
 8 }
 9 //Java 8
10 max = list.stream().reduce(0.0, Math::max);
11 // or
12 max = list.stream().mapToDouble(Number::doubleValue).max().getAsDouble();

Calculating an average

1 double total = 0;
2 double ave = 0;
3 // Java 7
4 for (Double d : list) {
5     total += d;
6 }
7 ave = total / ((double) list.size());
8 //Java 8
9 ave = list.stream().mapToDouble(Number::doubleValue).average().getAsDouble();

Printing the numbers one through ten

 1 // Java 7
 2 for (int i = 1; i < 11; i++) {
 3     System.out.println(i);
 4 }
 5 // Java 8
 6 IntStream.range(1, 11)
 7     .forEach(System.out::println);
 8 //or
 9 Stream.iterate(1, i -> i+1).limit(10)
10     .forEach(System.out::println);

Joining Strings


1 // Java 7 using commons-util
2 List<String> names = new LinkedList<>(); 3 for (Dragon dragon : dragons) 4 names.add(dragon.getName()); 5 String names = StringUtils.join(names, ","); 6 // Java 8 7 String names = dragons.stream() 8 .map(Dragon::getName) 9 .collect(Collectors.joining(","));








Monday, June 2, 2014

Speeding Up Spring's JavaMailSenderImpl With AOP


This material is based on an earlier version of Spring in Practice, chapter 8—one that predates the @Asyncannotation. Nowadays I would recommend using @Async and the Spring Task Execution API for making JavaMail calls asynchonous. The book covers the newer approach. The material in this post is still useful for understanding what you can do with AOP though.
In this article we’ll learn how we can speed up Spring’s JavaMailSenderImpl with some thread-forking AOP. Though we’re using JavaMail as an example, this tutorial should be useful to people looking for a code-based introduction to Spring’s support for AOP. Note at the outset that I don’t really go into AOP concepts and terminology, but I do show some simple code that you should be able to follow if you already know the basic concepts and just want to see what the code looks like.
There are lots of situations in which we want our application to send out an automated e-mail. You might for instance want to send a confirmation e-mail in response to new user registrations or mailing list subscriptions and unsubscriptions. In Spring this probably means that you would use one of the various JavaMailSenderImpl.send() methods. Here’s a sample applicationContext.xml file.

    
    
    
    
        
    ...
    
Here’s how it looks from the Java side:
package app.service;

... imports ...

public class MailingListServiceImpl implements MailingListService {
    private JavaMailSender mailSender;
    
    public void setMailSender(JavaMailSender mailSender) {
        this.mailSender = mailSender;
    }
    
    private void sendConfirmSubscriptionEmail(Subscriber subscriber) {
        MimeMessage message = mailSender.createMimeMessage();
        MimeMessageHelper helper = new MimeMessageHelper(message);
        
        String text = ...
        
        try {
            helper.setSubject("Please confirm your subscription");
            helper.setTo(subscriber.getEmail());
            helper.setFrom(noReplyEmailAddress);
            helper.setSentDate(subscriber.getDateCreated());
            helper.setText(text, true);
        } catch (MessagingException e) {
            throw new RuntimeException(e);
        }
        
        mailSender.send(message);
    }
    
    ...
}
(For more information on Spring/JavaMail integration, please see my article Send E-mail Using Spring and JavaMail.)
This works fine, but one thing your end users might notice is a fairly significant delay while JavaMailSenderImpl.send() does whatever it’s doing to send your e-mail (presumably negotiating with the SMTP server and sending the actual e-mail). While the delay probably isn’t large enough to provoke rioting in the streets, it’s certainly noticeable, and in many use cases it’s unnecessary. E-mail is itself an asynchronous communications medium, so unless there’s an important reason to let the end user know about errors that may occur while trying to send the e-mail (and there may be), one option you might consider is making the send()call on a separate thread.
Now let’s look at a few different ways to do that.

Method 1: Spawn a new thread manually

One possibility would be to spawn a new thread manually whenever you want to call JavaMailSenderImpl.send(). In other words, you implement the Runnable interface with a call to send(), you pass it into a Thread, and then you start the thread.
This technique has some advantages. It’s conceptually straightforward. Also it’s easy to be selective about the cases in which you do and don’t want to fork. Again, there may well be times where you want the end user to know if the send() call generated an exception, and if that’s true, then you simply refrain from forking the thread.
If you’re not careful, the approach can lead to widespread violation of the DRY principle. You might end up rewriting the same thread-forking code every time you send an e-mail. You can of course control this by creating one or more utility methods to send an e-mail on a separate thread, and that is a good approach.
One drawback with this approach, though, is that it may be either inconvenient or else a non-option. If you have an existing app with lots of calls to create e-mail, then you’d need to update all the instances of that code with the new code. In most cases that’s probably doable though it may be inconvenient. But it may be that you’re not in a position to change the client code. (Maybe it’s a third-party library, for instance.) The client code calls an injected JavaMailSender instance, say, and that’s the way it is. In that event you’ll want to consider one of the two following alternative methods.

Method 2: Create a JavaMailSender wrapper

Another method would be to implement a JavaMailSender wrapper. (JavaMailSender is of course the interface to which JavaMailSenderImpl conforms.) The JavaMailSender interface has six different send() methods (here are the Javadocs), and so you can just implement the thread-forking code for each of the six methods. (Probably each method would create a Runnable and then pass that to a thread-forking utility method.) Then you inject your wrapper into your service beans instead of injecting the JavaMailSenderImpl bean directly.
This approach is pretty good. It’s still straightforward, and it allows you to avoid violating DRY. Also, because it’s entirely transparent to client code, it can deal with cases in which you either can’t or else don’t want to modify said client code.
One possible challenge is that you may find it a little tough to exercise fine-grained control over the cases in which you use the wrapper and the cases in which you don’t. If it’s important for your code to exercise that kind of control, then arguably it would be reasonable to associate the forking/non-forking semantics injected JavaMailSender beans. You might for example inject two JavaMailSender instances into the service bean—one forking and one non-forking.
A minor grumble about the wrapper method is that it ties the thread-forking behavior to specific interfaces, such as JavaMailSender. That’s not too big a deal in this particular case, since it’s not such a problem to spawn a new thread. But if you have other cases where you decide you want to create a new thread, you might decide that you’d like to factor thread-forking out as a separate behavior and be able to apply that in multiple contexts.
So let’s see how to do that using Spring’s support for AspectJ-flavored AOP.

Method 3: Use AOP to wrap JavaMailSenderImpl.send()

This is a fun and elegant method. Even though this article is called “AOP 101”, I’m not planning to explain the concepts or weird terminology; rather I just want to show you the code and assume that you’ll be able to see what’s happening.
First we need to create an “advice” class. This is the code that we’re going to wrap around our send()invocations.
package app.aop;

import org.apache.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;

public class ForkAdvice {
    private static final Logger log = Logger.getLogger(ForkAdvice.class);
    
    public void fork(final ProceedingJoinPoint pjp) {
        new Thread(new Runnable() {
            public void run() {
                log.info("Forking method execution: " + pjp);
                try {
                    pjp.proceed();
                } catch (Throwable t) {
                    // All we can do is log the error.
                    log.error(t);
                }
            }
        }).start();
    }
}
The fork method is “around” advice that we’re going to use to advise our calls to JavaMailSenderImpl.send(). As you can see, it creates a new thread and starts it. In the run() body, we simply execute the advised method by calling pjp.proceed().
As an aside, the ProceedingJoinPoint class is provided by the AspectJ class library, but note that we’re not using full-blown AspectJ here—we’re in fact using Spring AOP. Full AspectJ involves a special aspect language and compiler to generate classes with the advice woven into the class bytecode itself. Spring AOP on the other hand uses dynamic proxies (either the interface variety that comes with Java, or else class proxies via CGLIB) to advise classes. While Spring AOP borrows classes and also the AspectJ pointcut language from AspectJ, its use of dynamic (runtime) proxies as opposed to bytecode-level advice integration distinguishes it from AspectJ.
Now it’s time to update our application context with our AOP configuration.

    
    
    
    
        
    
    
    
        
            
        
    
    
    ...
    
This is similar to what we had before, but there are a couple of differences. First, note that we’ve declared the aop namespace here. That of course allows us to use the namespace configuration feature that Spring 2.0 introduced. The other change is that we’ve added a definition for our advice bean as well as some AOP configuration. In aop:aspect we point to our forkAdvice as the advising class to be applied, we indicate that it will be “around” advice, we specify the advising method, and finally we specify a pointcut that indicates which method calls will be advised/wrapped. We use the AspectJ pointcut language to specify a pointcut. Here we’re indicating that we want all calls to any of the JavaMailSenderImpl.send() methods to be advised.
As mentioned previously, this technique is like the wrapper technique in that you can use it to add the forking behavior in a way that’s transparent to client code. Moreover you can use it not just for JavaMail but really for any method where you want to create a new thread before executing the method. You just add the appropriate aop:around definitions to the aop:aspect definition and you’re in business.