gauravbytes.com is now codefoundry.dev.

What is Dependency Injection?

Dependency injection is a process in which objects define their dependencies i.e. other objects they require to work, through a constructor, setter methods, factory methods. The container responsibility is to inject those while creating beans. With Dependency inject in place, we have cleaner code and clear way of decoupling. There are two prominent variants of Dependency Injection.

  • Constructor based Dependency Injection
  • Setter based Dependency Injection

Constructor based Dependency Injection

When you express your dependencies through constructor arguments and your container invoke your constructor with number of arguments, type of arguments expected by the constructor. Let's jump to one quick example.

@Component
public class ConstructorBasedFileParser {
  private Parser parser;

  @Autowired
  public ConstructorBasedFileParser(Parser parser) {
    this.parser = parser;
  }

  public void setParser(Parser parser) {
    this.parser = parser;
  }

  public void parseFile(File file) {
    if (parser.canParse(file)) {
      parser.parse(file);
    }
  }
}

In the above code snippet, ConstructorBasedFileParser is a component which express its dependency on Parser through constructor using @Autowired annotation.

Configuration class for the above code snippet looks like this.

@Configuration
@Import(value = ParserConfig.class)
@ComponentScan(basePackages = "com.gauravbytes.di.parser.constructor")
public class ConstructorBasedDIConfig {

}

@Configuration declares it as Spring Configuration file. @ComponentScan is used along with Configuration classes to scan for components. @Import imports the one or more Configuration classes. It is equivalent to <import/>.

Setter based Dependency Injection

Setter based dependency injection is accomplished by calling setter methods on beans after invoking no-args constructor by the container. Let's jump to example to see how to use setter method dependency injection.

@Component
public class SetterBasedFileParser {
  private Parser parser;

  public SetterBasedFileParser() {
  }

  @Autowired
  public void setParser(Parser parser) {
    this.parser = parser;
  }

  public void parseFile(File file) {
    if (parser.canParse(file)) {
      parser.parse(file);
    }
  }
}

In above code snippet, SetterBasedFileParser is a component class which expresses its dependency through setter method setParser() using @Autowired annotation.

When to use Constructor-based vs Setter-based DI?

Per se Spring documentation, use constructor-based DI for mandatory dependencies and setter-based DI for optional dependencies. It is advisable to use constructor-based DI. It makes your classes as Immutable object and also ensured that required dependencies are met before constructing that bean. Also, if you want to reconfigure your bean, then use setter-based DI.

Circular dependencies

There could be a case when your open bean say A is dependent on B and B is dependent on bean A (Both expressing dependencies through constructor). Spring IoC container will detect this at runtime and will throw BeanCurrentlyInCreationException.

Possible solution is to use setter based injection in some of beans.

I hope you find this post useful. You can grab the full example code used on Github.

In this post, we will learn about @Import annotation and its usage. You can see my previous post on how to create a simple spring core project.

What is @Import annotation and usage?

@Import annotation is equivalent to <import/> element in Spring XML configuration. It helps in splitting the single Java based configuration file into small, modular, maintainable and component based configuration. Let's see it with example.

@Configuration
@Import(value = { DBConfig.class, WelcomeGbConfig.class })
public class HelloGbAppConfig {

}

In above code snippet, we are importing two different configuration files viz. DBConfig, WelcomeGbConfig in application level configuration file HelloGbAppConfig.

The above code is equivalent to Spring XML based configuration below.

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

  <import resource="config/welcomegbconfig.xml"/>
  <import resource="config/dbconfig.xml"/>

</beans>

You can see the full example code for Java based configuration on Github.

In this post, we will create a spring context and will register bean via Java configuration file. You can see my previous post on how to create a simple spring core project.

What is @Configuration annotation?

@Configuration annotation indicates that there is one or more bean methods and spring containers can process to generate bean definitions at runtime. Also, @Bean annotation is used at method level to signifies that this will be registered as bean in spring context. Let's create a quick configuration class.

@Configuration
public class WelcomeGbConfig {

  @Bean
  GreetingService greetingService() {
    return new GreetingService();
  }
}

Now, we will create spring context as follows.

// using try with resources so that this context closes automatically
try (ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(
      WelcomeGbConfig.class);) {
  GreetingService greetingService = context.getBean(GreetingService.class);
  greetingService.greet();
}

1) we created the spring context.
2) We got the bean from context.
3. We call greet() on bean object.

This is how you can use configuration file (Java based) to define bean and being processed by spring context. You can also find the full example code on Github.

In this post, we will create a Spring context and will get a bean object from it.

What is Spring context?

Spring context is also termed as Spring IoC container which is responsible for instantiate, configure and assemble the beans by reading configuration meta data from XML, Java annotations and/ or Java code in configuration files.

Technologies used

Spring 4.3.6.RELEASE, Maven Compiler 3.6.0 and Java 1.8

We will first create a simple maven project. You can select the maven-archtype-quickstart as archtype.

Adding dependencies in pom.xml

We will add spring-framework-bom in the dependency management.

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-framework-bom</artifactId>
      <version>4.3.6.RELEASE</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

The benefit of adding this are to manage the version of the added spring dependencies from one place. By this, you can omit mentioning version number for spring dependencies.

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <scope>runtime</scope>
</dependency>

Now, we will create a class GreetingService which is eligible to get registered as bean in Spring context.

@Service
public class GreetingService {
  private static final Logger logger = Logger.getLogger(GreetingService.class.getName());

  public GreetingService() {

  }

  public void greet() {
    logger.info("Gaurav Bytes welcomes you for your first tutorial on Spring!!!");
  }
}

@Service annotation at class-level means that this is service and is eligible to be registered as bean in Spring context.

Instantiating a container

Now, we will create object of Spring context. We are using AnnotationConfigApplicationContext as spring container. Also, there exists other spring container like ClassPathXmlApplicationContext, GenericGroovyApplicationContext etc. which we will discuss in future posts.

ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(
      "com.gauravbytes.hellogb.service");

As you see at the time of object contruction of AnnotationConfigApplicationContext, I am passing one string parameter. This parameter ( of varags type) is the basePackages which spring context will scan for bean registration.

Now, we will get object of bean by calling getBean() on spring context.

GreetingService greetingService = context.getBean(GreetingService.class);
greetingService.greet();

At last, we are closing the spring container by calling close().

context.close();
It is important to close the spring context(container) after use. By closing it, we ensure that it will release all the resources and locks that its implementation might hold and will also destroy all the cached singleton beans.

We have also included maven-compiler-plugin in pom.xml to compile the java sources with the configured java version (in our case it is Java 1.8).

You can also find the example code on Github.

This article is in continuation to my other posts on Functional Interfaces, static and default methods and Lambda expressions.

Method references are the special form of Lambda expression. When your lambda expression are doing nothing other than invoking existing behaviour (method), you can achieve same by referring it by name.

  • :: is used to refer to a method.
  • Method type arguments are infered by JRE at runtime from context it is defined.

Types of method references

  • Static method reference
  • Instance method reference of particular object
  • Instance method reference of an arbitrary object of particular type
  • Constructor reference

Static method reference

When you refer static method of Containing class. e.g. ClassName::someStaticMethodName

class MethodReferenceExample {
  public static int compareByAge(Employee first, Employee second) {
    return Integer.compare(first.age, second.age);
  }
}

Comparator compareByAge = MethodReferenceExample::compareByAge;

Instance method reference of particular object

When you refer to the instance method of particular object e.g. containingObjectReference::someInstanceMethodName

static class MyComparator {
  public int compareByFirstName(User first, User second) {
    return first.getFirstName().compareTo(second.getFirstName());
  }
  
  public int compareByLastName(User first, User second) {
    return first.getLastName().compareTo(second.getLastName());
}

private static void instanceMethodReference() {
  System.err.println("Instance method reference");
  List<User> users = Arrays.asList(new User("Gaurav", "Mazra"),
      new User("Arnav", "Singh"), new User("Daniel", "Verma"));
  MyComparator comparator = new MyComparator();
  System.out.println(users);
  Collections.sort(users, comparator::compareByFirstName);
  System.out.println(users);
}

Instance method reference of an arbitrary object of particular type

When you refer to instance method of some class with ClassName. e.g. ClassName::someInstanceMethod;

Comparator<String> stringIgnoreCase = String::compareToIgnoreCase;
//this is equivalent to
Comparator<String> stringComparator = (first, second) -> first.compareToIgnoreCase(second);

Constructor reference

When you refer to constructor of some class in lambda. e.g. ClassName::new

Function<String, Job> jobCreator = Job::new;
//the above function is equivalent to
Function<String, Job> jobCreator2 = (jobName) -> return new Job(jobName);

You can find the full example on github.

You can also view my other article on Java 8

In this post, we will cover following topics.

  • What are Streams?
  • What is a pipeline?
  • Key points to remember for Streams.
  • How to create Streams?

What are Streams?

Java 8 introduced new package java.util.stream which contains classes to perform SQL-like operations on elements. Stream is a sequence of elements on which you can perform aggregate operations (reduction, filtering, mapping, average, min, max etc.). It is not a data structure that stores elements like collection but carries values often lazily computed from source through pipeline.

What is a pipeline?

A pipeline is sequence of aggregate (reduction and terminal) operations on the source. It has following components.

  • A source: Collections, Generator Function, array, I/O channel etc.
  • zero or more intermediate operations: filter, map, sequential, sorted, distinct, limit, flatMap, parallel etc. Intermediate operations returns/produces stream.
  • a termination operation: forEach, reduction, noneMatch, allMatch, count, findFirst, findAny, min, max etc.

Key points to remember for Streams

  • No storage.
  • Functional in nature.
  • Laziness-seeking.
  • Possibly unbounded. Operations, for example, limit(n) or findFirst() can permit calculations on infinite streams to finish in finite time.
  • Consumable. The elements can be visited only once. To revisit, you need to create a new stream.

How to create Streams?

1. In Collection, you can create streams by calling stream(), parallelStream().

Collection<Person> persons = StreamSamples.getPersons();
persons.stream().forEach(System.out::println);

// parallel stream
persons.parallelStream().forEach(System.out::println);

2. From Stream interface, calling static factory method of() which takes varargs of T type.

Stream.of("This", "is", "how", "you", "create", "stream", "from", "static", "factory",
      "method").map(s -> s.concat(" ")).forEach(System.out::print);

3. From Arrays class, by calling stream() static method.

Arrays.stream(new String[] { "This", "is", "how", "you", "create", "stream", ".",
      "Above", "function", "use", "this" }).map(s -> s.concat(" "))
      .forEach(System.out::print);

4. From Stream by calling iterate(). It is infinite stream function.

// iterate return infinite stream... beware of infinite streams
Stream.iterate(1, i -> i++).limit(10).forEach(System.out::print);

5. From IntStream by calling range.

int sumOfFirst10PositiveNumbers = IntStream.range(1, 10).reduce(0, Integer::sum);
System.out.println(sumOfFirst10PositiveNumbers);

6. From Random by calling ints(). It is infinite stream function.

// random.ints for random number
new Random().ints().limit(20).forEach(System.out::println);

7. From BufferedReader by calling lines(). Streams of file paths can be obtained by calling createDirectoryStream of Files class and some other classes like JarFile.stream(), BitSet.stream() etc.

try (BufferedReader br = new BufferedReader(new StringReader(myValue))) {
  br.lines().forEach(System.out::print);
  System.out.println();
}
catch (IOException io) {
  System.err.println("Got this:>>>> " + io);
}

I hope the post is informative and helpful in understanding Streams. You can find the full example code on Github.

You can also read on Aggregate opeations on Stream.

This post is in continuation with my earlier posts on Streams. In this post we will discuss about aggregate operations on Streams.

Aggregate operations on Streams

You can perform intermediate and terminal operations on Streams. Intermediate operations result in a new stream and are lazily evaluated and will start when terminal operation is called.

persons.stream().filter(p -> p.getGender() == Gender.MALE).forEach(System.out::println);

In the snippet above, filter() doesn't start filtering immediately but create a new stream. It will only start when terminal operation is called and in above case when forEach().

Intermediate operations

There are many intermediate operations that you can perform on Streams. Some of them are filter(), distinct(), sorted(), limit(), parallel(), sequential, map(), flatMap.

filter() operation

This takes Predicate functional interface as argument and the output stream of this operation will have only those elements which pass the conditional check of Predicate. You can learn a nice explanation on Predicates here.

// all the males
List<Person> allMales = persons.stream().filter(p -> p.getGender() == Gender.MALE).collect(Collectors.toList());
System.out.println(allMales);

map() operation

It is a mapper operation. It expects Function functional interface as argument. Purpose of Function is to transform from one type to other (The other type could be same).

// first names of all the persons
List<String> firstNames = persons.stream().map(Person::getFirstName).collect(Collectors.toList());
System.out.println(firstNames);

distinct()

It returns the unique elements and uses equals() under the hood to remove duplicates.

List<String> uniqueFirstNames = persons.stream().map(Person::getFirstName).distinct().collect(Collectors.toList());

System.out.println(uniqueFirstNames);

sorted()

Sorts the stream elements. It is stateful operation.

List<Person> sortedByAge = persons.stream().sorted(Comparator.comparingInt(Person::getAge)).collect(Collectors.toList());
System.out.println(sortedByAge);

limit() will reduce the number of records. It is helpful to end infinite streams in a finite manner.

Intemediate operations can be divided to two parts stateless and stateful. Most of the streams intermediate operations are stateless e.g. map, filter, limit etc. but some of them are stateful e.g. distinct and sorted because they have to maintain the state of previously visited element.

Terminal/ Reduction operations

There are many terminal operations such as forEach(), reduction(), max(), min(), average(), collect(), findAny, findFirst(), allMatch(), noneMatch().

forEach()

This takes Consumer functional interface as parameter and pass on the element for consumption.

persons.stream().forEach(System.out::println);

max(), min(), average() operations

average() returns OptionalDouble whereas max() and min() return OptionalInt.

//average age of all persons
persons.stream().mapToInt(Person::getAge).average().ifPresent(System.out::println);

// max age from all persons
persons.stream().mapToInt(Person::getAge).max().ifPresent(System.out::println);

// min age from all persons
persons.stream().mapToInt(Person::getAge).min().ifPresent(System.out::println);

noneMatch(), allMatch(), anyMatch()

matches if certain condition satisfies by none, all and/or any elements of stream respectively.

//age of all females in the group is less than 22
persons.stream().filter(p -> p.getGender() == Gender.FEMALE).allMatch(p -> p.getAge() < 22);
//not a single male's age is greater than 30
persons.stream().filter(p -> p.getGender() == Gender.MALE).noneMatch(p -> p.getAge() > 30);

persons.stream().anyMatch(p -> p.getAge() > 45);

Reduction operations

Reduction operations are those which provide single value as result. We have seen in previous snippet some of the reduction operation which do this. E.g. max(), min(), average(), sum() etc. Apart from this, Java 8 provides two more general purpose operations reduce() and collect().

reduce()

int sumOfFirst10 = IntStream.range(1, 10).reduce(0, Integer::sum);
System.out.println(sumOfFirst10);

collect()

It is a mutating reduction. Collectors has many useful collection methods like toList(), groupingBy(),

Collection<Person> persons = StreamSamples.getPersons();
List firstNameOfPersons = persons.stream().map(Person::getFirstName).collect(Collectors.toList());
System.out.println(firstNameOfPersons);

Map<Integer, List<Person>> personByAge = persons.stream().collect(Collectors.groupingBy(Person::getAge));
System.out.println(personByAge);

Double averageAge = persons.stream().collect(Collectors.averagingInt(Person::getAge));
System.out.println(averageAge);

Long totalPersons = persons.stream().collect(Collectors.counting());
System.out.println(totalPersons);

IntSummaryStatistics personsAgeSummary = persons.stream().collect(Collectors.summarizingInt(Person::getAge));

System.out.println(personsAgeSummary);

String allPersonsFirstName = persons.stream().collect(Collectors.mapping(Person::getFirstName, Collectors.joining("#")));
System.out.println(allPersonsFirstName);

The result would look like this.

[Gaurav, Gaurav, Sandeep, Rami, Jiya, Rajesh, Rampal, Nisha, Neha, Ramesh, Parul, Sunil, Prekha, Neeraj]
{32=[Person [firstName=Rami, lastName=Aggarwal, gender=FEMALE, age=32, salary=12000]], 35=[Person [firstName=Rampal, lastName=Yadav, gender=MALE, age=35, salary=12000]], 20=[Person [firstName=Prekha, lastName=Verma, gender=FEMALE, age=20, salary=3600]], 21=[Person [firstName=Neha, lastName=Kapoor, gender=FEMALE, age=21, salary=5500]], 22=[Person [firstName=Jiya, lastName=Khan, gender=FEMALE, age=22, salary=4500], Person [firstName=Ramesh, lastName=Chander, gender=MALE, age=22, salary=2500]], 24=[Person [firstName=Sandeep, lastName=Shukla, gender=MALE, age=24, salary=5000]], 25=[Person [firstName=Parul, lastName=Mehta, gender=FEMALE, age=25, salary=8500], Person [firstName=Neeraj, lastName=Shah, gender=MALE, age=25, salary=33000]], 26=[Person [firstName=Nisha, lastName=Sharma, gender=FEMALE, age=26, salary=10000]], 27=[Person [firstName=Sunil, lastName=Kumar, gender=MALE, age=27, salary=6875]], 28=[Person [firstName=Gaurav, lastName=Mazra, gender=MALE, age=28, salary=10000], Person [firstName=Gaurav, lastName=Mazra, gender=MALE, age=28, salary=10000]], 45=[Person [firstName=Rajesh, lastName=Kumar, gender=MALE, age=45, salary=55000]]}
27.142857142857142
14
IntSummaryStatistics{count=14, sum=380, min=20, average=27.142857, max=45}
Gaurav#Gaurav#Sandeep#Rami#Jiya#Rajesh#Rampal#Nisha#Neha#Ramesh#Parul#Sunil#Prekha#Neeraj

You can't consume same Streams twice

When the terminal operation is completed on stream, it is considered consumed and you can't use it again. You will end up with exception if you try to start new operations on already consumed stream.

Stream<String> stream = lines.stream();
stream.reduce((a, b) -> a.length() > b.length() ? a : b).ifPresent(System.out::println);

// below line will throw the exception
stream.forEach(System.out::println);
Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
 at java.util.stream.AbstractPipeline.sourceStageSpliterator(AbstractPipeline.java:279)
 at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580)
 at com.gauravbytes.java8.stream.StreamExceptionExample.main(StreamExceptionExample.java:18)

Parallelism

Streams provide a convenient way to execute operations in parallel. It uses ForkJoinPool under the hood to run stream operations in parallel. You can use parallelStream() or parallel() on already created stream to perform task parallelly. One thing to note parallelism is not automatically faster than running task in serial unless you have enough data and processor cores.

persons.parallelStream().filter(p -> p.getAge() > 30).collect(Collectors.toList());
Pass java.util.concurrent.ForkJoinPool.common.parallelism property while JVM startup to increase parallelism in fork-join pool.

Concurrent reductions

ConcurrentMap<Integer, List<Person>> personByAgeConcurrent = persons.stream().collect(Collectors.groupingByConcurrent(Person::getAge));
System.out.println(personByAgeConcurrent);
Prevent interference, side-effects and stateful lambda/functions.

Side effects

If the function is doing more than consuming and/ or returning value, like modifying the state is said to have side-effects. A common example of side-effect is forEach(), mutable reduction using collect(). Java handles side-effects in collect() in thread-safe manner.

Interference

You should avoid interference in your lambdas/ functions. It occurs when you modify the underlying collection while running pipeline operations.

Stateful Lambda expressions

A lambda expression is stateful if its result depends on any state which can alter/ change during execution. Avoid using stateful lambdas expressions. You can read more here.

I hope you find this post informative and helpful. You can find the example code for reduction, aggregate operation and stream creation on Github.