22 Haziran 2018 Cuma

Adding HATEOAS Support To Your REST APIs with spring-hateoas

HATEOAS (Hypertext as the Engine of Application State)

It means that the hypertext should be used to find your way through the API. With HATEOAS support in your REST APIs, clients should know just where to start. After getting the first response from the server, they will be able to find their way.

Sample REST API without HATEOAS

Below is a sample REST API with Spring MVC

public class Person extends ResourceSupport {

 private String personId;
 private String name;
 private String surname;

 ... getters and setters
}

/**
 * A builder class for building person objects. This class is not necessary. It is just 
 * a better way to create new objects instead of using multiple setters in each line.
 */
public final class PersonBuilder {
 private String personId;
 private String name;
 private String surname;

 private PersonBuilder() {
 }

 public static PersonBuilder aPerson() {
  return new PersonBuilder();
 }

 public PersonBuilder withPersonId(String personId) {
  this.personId = personId;
  return this;
 }

 public PersonBuilder withName(String name) {
  this.name = name;
  return this;
 }

 public PersonBuilder withSurname(String surname) {
  this.surname = surname;
  return this;
 }

 public Person build() {
  Person person = new Person();
  person.setPersonId(personId);
  person.setName(name);
  person.setSurname(surname);
  return person;
 }
}


@RestController
@RequestMapping("/people")
public class PersonController {

 @RequestMapping(value = "/{personId}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
 public ResponseEntity<Person> getPersonById(@PathVariable String personId) {
  Person person = PersonBuilder.aPerson().withPersonId("1").withName("furkan").withSurname("danismaz").build();
  return ResponseEntity.ok(new Resource<>(person, selfLink));
 }
}

The response of such an endpoint will be:

{
 personId: "1",
 name: "furkan",
 surname: "danismaz"
}

Adding HATEOAS Support with spring-hateoas

  • Add the spring-hateoas dependency to your project

<dependency>
 <groupid>org.springframework.hateoas</groupid>
 <artifactid>spring-hateoas</artifactid>
 <version>...</version>
</dependency>


  • Extend ResourceSupport in your model classes
  • Change return type of your Controller methods to return Response (or Responses if you are returning a collection).
  • Create links and associate them with the returning data using the Resource constructor (or Resources if you are returning a collection)
public class Person extends ResourceSupport {

 private String personId;
 private String name;
 private String surname;

 ... getters and setters 


Note that I just added ResourceSupport as parent class. Nothing has changed other than that.


@RestController
@RequestMapping("/people")
public class PersonController {

 @RequestMapping(value = "/{personId}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
 public ResponseEntity<Resource<Person>> getPersonById(@PathVariable String personId) {
  Person person = PersonBuilder.aPerson().withPersonId("1").withName("furkan").withSurname("danismaz").build();
  Link selfLink = linkTo(methodOn(PersonController.class).getPersonById(personId)).withSelfRel();
  return ResponseEntity.ok(new Resource<>(person, selfLink));
 }
}


In this controller method I've changed:
  • The return type of the method from
    ResponseEntity<Person>
    
    to
    ResponseEntity<Resource<Person>>
    
  • Created a link
  • Created a resource object with a person and a link and returned it as a response
Now the response becomes:
{
 personId: "1",
 name: "furkan",
 surname: "danismaz",
 links: [
  {
   rel: "self",
   href: "http://localhost:8080/people/1",
  }
 ],
}

21 Haziran 2018 Perşembe

Monitoring entities with spring-data

If you are using spring-data, you can easily add fields to your entity models to monitor which user has created them and when, and also see who is the last modifier and when was the last modification time.

Notice that spring data is enough for tracing when your data is created or modified, but it is not enough to trace which user is creating or updating them. In that case, we also need spring security integration

To achieve this we will:
1. Create an abstract base entity model and put those fields in it so that all the entity model classes have those properties
2. Create a concrete entity model class and extend the abstract base entity model.
3. Implement the AuditorAware interface and set the auditorAware bean.

In this article, I will not show how to integrate with Spring security. I just assume you already have done that and you have your authentication object in Spring security context.


1. The Base EntityModel class


@MappedSuperclass@EntityListeners(AuditingEntityListener.class)
public class EntityModelBase implements Serializable {

   @CreatedDate   @Column(name = "CREATED_DATE", nullable = false, updatable = false)
   protected long createdDate;

   @LastModifiedDate   @Column(name = "LAST_MODIFIED_DATE", nullable =  false)
   protected long lastModifiedDate;

   @CreatedBy
   @Column(name = "CREATED_BY")
   protected String createdBy;

   @LastModifiedBy
   @Column(name = "LAST_MODIFIED_BY")
   protected String lastModifiedBy;

   ...


2. A Concrete EntityModel class

@Entity@Table(name="ROLES")
public class RoleEntityModel extends EntityModelBase {

   @Column(name = "NAME", nullable = false)
   private String name;

   ...


3. The AuditorAware Bean

First, we need to create our own AuditorAware bean. Note that in this bean I used my own authentication accessor object that brings me the authentication object from Spring security context. Also, I used a custom authentication class (an extension of the spring security's UsernamePasswordAuthenticationToken) which holds extra information about the authenticated user that I need.

import org.springframework.data.domain.AuditorAware;

public class SpringSecurityAuditorAware implements AuditorAware {

   @Autowired
   private MyAuthenticationAccessor authAccessor;

   @Override   
   public String getCurrentAuditor() {
      MyUser user = this.authAccessor.getUser();
      if (user != null) {
         return user.getUsername();
      }
      return null;
   }
}

@Component
public class MyAuthenticationAccessor {

   @Override   
   public MyUser getUser() {
      Authentication auth = SecurityContextHolder.getContext().getAuthentication();
      if (auth != null && auth instanceof MyCustomAuthentication) {
         return ((MyCustomAuthentication)authentication).getUser();
      }
      return null;
   }

   ...

}

Finally, we need to set the AuditorAware bean. The best place to do that is in a @Configurationannotated class:

@Configuration@EnableJpaAuditing(auditorAwareRef = "auditorAware")
@EnableTransactionManagement@EnableJpaRepositories(basePackages = "...")
public class VeriErisimKatmaniKonfigurasyonu {

   @Bean
   public AuditorAware auditorAware() {
      return new SpringSecurityAuditorAware();
   }

   ...

}

Now, when we save an entity model to the database, we will have its created date, created by, last modified date, and last modified by properties.

6 Nisan 2018 Cuma

Service Locator Pattern with Spring Boot

1. Create a base type for your services

public interface MyService {
}

2. Create an enumaration for your service types and implement its toString() method. Return bean name in toString method.

public enum MyServiceType {
    MY_SERVICE_1("my_service_1"),
    MY_SERVICE_2("my_service_2");

    private final String value;

    MyServiceType(String value) {
        this.value = value;
    }

    @Override
    public String toString() {
        return this.value;
    }
}

3. Create a factory interface

public interface MyFactory {
    MyService getService(MyServiceType type);
}

4. Create your service types and register them as bean with bean names

@Bean("my_service_1")
@Component
public class MyService1 implements MyService {
    ...
}

@Bean("my_service_2")
@Component
public class MyService2 implements MyService {
    ...
}

5. Register your factory to spring context

@Bean
public FactoryBean serviceLocatorFactoryBean() {
ServiceLocatorFactoryBean factoryBean = new ServiceLocatorFactoryBean();
factoryBean.setServiceLocatorInterface(MyServiceFactory.class);
return factoryBean;
}

6. Get your bean using your factory and enum value.

@Autowired
private MyServiceFactory myServiceFactory;

public void myMethod() {
    MyService service = this.myServiceFactory.getService(MyServiceType.MY_SERVICE_1);
    ...
}

11 Ağustos 2017 Cuma

Custom Java Annotations & Implementing what to do using Aspect Oriented Programming

Creating Custom Annotations

Creating custom annotations in Java is easy. You just need to:

  1. define an @interface
  2. declare your retention policy with @Retention annotation
  3. declare target elements of your annotation type.
Below is an example:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
public @interface Authorize {

}

Retention Policy

Choosing the right retention policy is important. So, let's see the different retention policy options and understand their differences. There are 3 different retention policies:

  • SOURCE : The annotations will be ignored by the compiler and won't appear in decompiled .class file
  • CLASS : The annotations will NOT be ignored by the compiler but the VM cannot inspect them at runtime. In other words getAnnotations() method result will not contain your annotation
  • RUNTIME: The VM can inspect and your annotation will appear in the annotation list returned by the getAnnotations() method.

Target Element Type

Target on the other hand is about what do you want to annotate. Is it a class, method, a parameter of a method, field, constructor, or what?

You can provide one target type: 
@Target(ElementType.METHOD)

 or multiple targets in a list: 
@Target({ ElementType.TYPE, ElementType.METHOD })

Using Annotations for Cross-Cutting Concerns

Before going deep, let's talk about Aspect Oriented Programming a little bit. AOP is used for cross cutting concerns. 

So, what is a cross cutting concern? 

It can be defined as a behavior to an existing code which is logically not central to the business logic.

The most common example for cross-cutting concerns is logging, but I think transaction and authorization are better examples for this topic. I am sure there are also other cool examples.

Terminology of AOP and what do they mean?

JoinPoint : A point in an execution flow of a program
Pointcut : An expression to match join points
Advice : The logic of what to do at joinpoints, the additional behavior itself.

There are different types of advice:
  • Before : additional behavior will be executed before the join point
  • After : additional behavior will be executed after the join point
  • After Returning : additional behavior will be executed only after the join point is executed successfully.
  • After Throwing : additional behavior will be executed only after the join point throws execption.
  • Around : The most powerful kind of advice which encapsulates the join point. This means you can put additional behavior both before and after the join point.

The Best Part (Coding)

Using annotations in pointcut expressions is an easy way to detect join points in an execution flow. Annotations are commonly used for this purpose like Spring's @Transactional annotation. Spring detects methods annotated with @Transactional annotation and has an "Around Advice" for them which starts transaction before the method execution and ends it after the method execution is successfully completed.

My example is about "authorization". I have created a project with:
  • Spring boot
  • Spring MVC
  • AspectJ
I created 2 new annotations:
  • @Authorize : To detect the methods to be authorized
  • @Resource : To differentiate the parameters to be used as resource from the others. 
What "resource" means in my domain is irrelevant to us right now. The only thing important here is I defined an annotation type for method parameters to be able to differentiate some parameters from the others.


The Authorize Annotation

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
public @interface Authorize {

}

The Resource Annotation

import java.lang.annotation.ElementType
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface Resource {

}


Notice that the Target ElementType is PARAMETER.

Defining the Pointcuts

I have there pointcut expressions: 
  • One that matches the methods annotated with @Authorize annotation
  • Another to match all public methods of a class that is annoated with @Authorize annotation
  • One that joins them with OR
/**
 * All public methods that are annotated with Authorize.
 */
@Pointcut("execution(@com.fd.customannotation.annotations.Authorize * *(..))")
public void authorizeAnnotatedMethod() {}

/**
 * All public methods of a class annotated with Authorize.
 */
@Pointcut("execution(* (@com.fd.customannotation.annotations.Authorize *).*(..))")
public void authorizeAnnotatedClassMethod() {}

/**
 * Pointcut expression that joins the first two with OR
 */
@Pointcut("authorizeAnnotatedMethod() || authorizeAnnotatedClassMethod()")
public void authorizeMethod() {}

The Additional Behavior - The Advice

The below implementation,"beforeAuthorizeMethods", will be executed before my methods annotated with @Authorize annotation or the methods of a class that is annotated with @Authorize. The below code simply finds out which parameters of a @Authorize annotated method are annotated with @Resource and prints the value of them.

@Before("authorizeMethod()")
public void beforeAuthorizeMethods(final JoinPoint joinPoint) {
    HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
    System.out.println("  Request URI: " + request.getRequestURI());
    System.out.println("  Request URL: " + request.getRequestURL());

    List<Object> resourceAnnotatedArguments = this.getResourceAnnotatedArguments(joinPoint);
    System.out.println(String.format("There are %d @Resource annotated parameters", resourceAnnotatedArguments.size()));

    for (Object value : resourceAnnotatedArguments) {
        System.out.println(" Parameter value : " + value);
    }
}

/**
 * Returns the list of arguments that are annotated with @Resource as a list of Objects
 */
private List<Object> getResourceAnnotatedArguments(final JoinPoint joinPoint) {
    List<Object> result = new ArrayList<>();
    final MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature();
    
    Method method = methodSignature.getMethod();
    Object[] arguments = joinPoint.getArgs();

    Parameter[] parameters = method.getParameters();
    for (int i = 0; i < parameters.length; i++) {
        Parameter parameter = parameters[i];
        Annotation[] annotations = parameter.getAnnotations();
        for (Annotation annotation : annotations) {
            if (annotation.annotationType() == Resource.class) {
                result.add(arguments[i]);
                break;
            }
        }        
    }

    return result;
}

Usage

I've written a simple RestController to test my pointcut expressions and advice.

@RestController
@RequestMapping("/test")
public class TestController {

    @Authorize
    @RequestMapping(value = "/index", method = RequestMethod.GET)
    public String index(
        @Resource
        @RequestParam(value="name", required=false, defaultValue="World") String name,
        String surname) {

            return "Hello " + name;
        }
    }
}

In this code the index method is annotated with @Authorize annotation. So, my pointcut expression should match this join point. 

Also, my index method has two parameters: name and surname. Notice that name is annotated with @Resource. So, I should only see the value of the "name" parameter on the console when a request arrives to "http://base-address/test/index".




27 Ocak 2017 Cuma

Sending Date and Time parameter from Android to Spring @RestController action

You may encounter problems while passing Date parameters to Java RESTful Web service because of Date format.  To standardize the date format I send to server with HTTP requests and the date format that I expect in my services I add some extensions to both my client and server applications.

I use Android Annotations to consume RESTful Web Services.

Client Side
Here is a sample REST API client

import org.androidannotations.annotations.rest.Accept;
import org.androidannotations.annotations.rest.Post;
import org.androidannotations.annotations.rest.Rest;
import org.androidannotations.api.rest.MediaType;

@Rest(rootUrl = Constants.Services.UserApi.ServiceBase, converters = {WorkbookHttpMessageConverter.class})
public interface UserApi {

    @Post(Constants.Services.UserApi.ServiceMethods.PostUser)
    @Accept(MediaType.APPLICATION_JSON)
    OperationResult<User> postUser(SaveUserPostRequestModel model);

}



The key point here to set the expected date format is to provide your own converter class. As you can see, here I used my own converter class "WorkbookHttpMessageConverter" which is an extension to GsonHttpMessageConverter :


import com.adresyazilim.workbook.model.Constants;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import org.springframework.http.converter.json.GsonHttpMessageConverter;

import java.util.Date;

public class WorkbookHttpMessageConverter extends GsonHttpMessageConverter {

    public WorkbookHttpMessageConverter()  {
        super(buildGson());
    }

    protected static Gson buildGson() {
        GsonBuilder gsonBuilder = new GsonBuilder();
        gsonBuilder.setDateFormat(Constants.DateTimeConstants.DateTimeFormatString);
        gsonBuilder.registerTypeAdapter(Date.classnew WorkbookJsonDateSerializer());
        return gsonBuilder.create();
    }
}


In my converter class, notice that I provided my own DateSerializer which implements both JsonSerializer<Date> and JsonDeserializer<Date>.


import com.adresyazilim.workbook.business.helper.DateTimeUtil;

import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;

import java.lang.reflect.Type;
import java.util.Date;

public class WorkbookJsonDateSerializer implements JsonSerializer<Date>, JsonDeserializer<Date> {

    @Override
    public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext contextthrows JsonParseException {
        JsonObject jsonObject = (JsonObject)json;
        String dateString = jsonObject.get("datetime").getAsString();
        return DateTimeUtil.getDateTimeFromString(dateString);
    }

    @Override
    public JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext context) {
        final JsonObject jsonObject = new JsonObject();
        jsonObject.addProperty("datetime", DateTimeUtil.getDateTimeString(src));
        return jsonObject;
    }

}

You can implement DateSerializer and DateDeserializer instead of JsonSerializer and JsonDeserializer. If you do so, your date value will be serialized as a value, not a Json object. No big difference, do as you like. With the serialization strategy I provided here, my date values are serialized like the following.
    
     { "datetime" : "...." }
 
That's all you need to do at client side.


Server Side
At server side I used Spring-MVC. Below is a sample controller method :

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;

import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/user")
public class UserController {

@Autowired
IUserService userService;

@RequestMapping(value = "/save", method = RequestMethod.POST)
public OperationResult<User> saveUser(@Valid @RequestBody SaveUserPostRequestModel requestBody, BindingResult validationResult, HttpServletRequest request, HttpServletResponse response) {
...
}

}


The "SaveUserPostRequestModel" includes the User class inside it. Below I give the User class. Notice the annotations above the Date values.


import java.util.Date;

import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

import org.hibernate.validator.constraints.Email;

public class User {

private String id;
private String username;
private String password;

@Size(min = 2, max = 20)
private String name;

@Size(min = 2, max = 20)
private String surname;

@Email
@NotNull
private String email;

private String photoUrl;

@JsonSerialize(using = WorkbookDateSerializer.class)
@JsonDeserialize(using = WorkbookDateDeserializer.class)
private Date lastUpdateTime;

}


I used JsonSerialize and JsonDeserialize annotations of "com.fasterxml.jackson" and provided my own serializer and deserializer classes.

The Deserializer

import java.io.IOException;
import java.util.Date;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;

@JacksonStdImpl
public class WorkbookDateDeserializer extends JsonDeserializer<Date> {

@Override
public Date deserialize(JsonParser jp, DeserializationContext ctxtthrows IOException, JsonProcessingException {
JsonToken token = jp.getCurrentToken();
if (token == JsonToken.START_OBJECT) {
ObjectCodec oc = jp.getCodec();
JsonNode node = oc.readTree(jp);
String dateString = node.get("datetime").asText();
return DateTimeUtil.getDateTimeFromString(dateString);
}

return null;
}
}



The Serializer

import java.io.IOException;
import java.util.Date;

import com.adresyazilim.workbook.backend.util.DateTimeUtil;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
import com.fasterxml.jackson.databind.ser.std.DateSerializer;

@JacksonStdImpl
public class WorkbookDateSerializer extends DateSerializer {

@Override
public void serialize(Date value, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonGenerationException {
jgen.writeStartObject();
jgen.writeStringField("datetime", DateTimeUtil.getDateTimeString(value));
jgen.writeEndObject();
}
}


This way I standardize and take control over the date format expected in the server side and the format sent from the client side.

17 Ocak 2017 Salı

Uploading File/Image to Spring-MVC REST Service

In this article I'll show how to implement a RESTful Web Service with Spring-MVC that accepts file as request parameter and how to test it using Postman.

First add multipartResolver bean to your spring configuration file:

    <bean id="multipartResolver"
        class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    </bean>

Below is how I made my service to accept multipart form data:

@RestComtroller
@RequestMapping("/profile")
public class UserProfileController {

    @Autowired
    StorageService storageService;

    @RequestMapping(value="/post/image", method = RequestMethod.POST, consumes = "multipart/form-data")
    public OperationResult<Boolean> postUserImage(@RequestParam("file") MultipartFile file) {
        OperationResult<Boolean> result = null;
        try {
            String fileName = file.getOriginalFileName();
            this.storageService.store(file, fileName);
            result = new OperationResult<>(true);
        }
        catch (Exception e) {
            // handle exception here
            result = new OperationResult<>(ErrorCode.CannotStoreFile, "Error title", "Error message");
        }
        return result;
    }
}

In this code, the OperationResult is just a type that I coded for encapsulating response data. StorageService on the other hand is another class I coded which handles saving files into physical storage.

The important thing here is to add "consumes" attribute to @RequestMapping annotation and using @RequestParam annotation for the MultipartFile instead of @RequestBody.

Also don't forget to add commons-io and commons-fileupload dependencies. If you are using maven, just add the following dependencies to your pom.xml (change the version):

    <dependency>
        <groupId>commons-fileupload</groupId>
        <artifactId>commons-fileupload</artifactId>
        <version>${commons.version}</version>
    </dependency>

    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-io</artifactId>
        <version>${commons.io.version}</version>
    </dependency>


Now, let's call this service from Postman.

Postman is a great tool for testing your APIs. If you haven't checked yet, see https://www.getpostman.com/


  • While testing this service, do not send "Content-Type" header parameter. 
  • From the body section, choose form-data and add a parameter. 
  • The name of the parameter should be same with the name of the parameter of your service method. In this case, the name of the parameter is "file".
  • Change the type of the parameter to be "File"
  • Choose the file to be uploaded

Send your request and that's it.



12 Şubat 2016 Cuma

Sending E-mail with Spring Maven Project

Add your dependencies

Add spring-context-support and javax.mail dependencies to your project

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-support</artifactId>
        <version>3.2.0.RELEASE</version>
    </dependency>


    <dependency>
  <groupId>com.sun.mail</groupId>
        <artifactId>javax.mail</artifactId>
        <version>1.5.5</version>
</dependency>


Make your SMPTP your MailSender Object

Go to your bean definitions and add the following bean :

<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">  
          <property name="host"><value>smtp.gmail.com</value></property>
          <property name="port"><value>465</value></property>
          <property name="protocol"><value>smtp</value></property>
          <property name="username"><value>youremail</value></property>
          <property name="password"><value>password</value></property>
          <property name="javaMailProperties">
            <props>
               
<prop key="mail.smtp.auth">true</prop>
               
<prop key="mail.smtp.starttls.enable">false</prop>
               
<prop key="mail.smtp.quitwait">false</prop>
               
<prop key="mail.smtp.socketFactory.class">javax.net.ssl.SSLSocketFactory</prop>
               
<prop key="mail.smtp.socketFactory.fallback">false</prop>
               
<prop key="mail.debug">true</prop>
           
</props>
       
</property>
</bean>

Send your mail with MailSender


Now, all you need to do is to prepare your mail message object and send it using a MailSender instance :


    @Service
    public class WorkbookMailSender {

        @Autowired
        private MailSender mailSender;
public void sendMail(String to, String subject, String message) { SimpleMailMessage mailMessage = new SimpleMailMessage(); mailMessage.setSubject(subject); mailMessage.setTo(to); mailMessage.setText(message); mailMessage.setSentDate(new Date());
this.mailSender.send(mailMessage);
}
  }

That's it.