One of the most underrated features of IntelliJ as well as all other editors based on said IDE (e.g. CLion, PyCharm, WebStorm, PhpStorm, etc.) is the ability to create custom templates. I use Spring Framework for several projects, so I create repositories, services, and controllers pretty often. Because they have a very similar structure, they are a perfect example of what to make a template for.
IntelliJ’s templates uses Apache Velocity, which is, as you’ve probably guessed, a template engine. It’s actually very powerful, and you can do much more than what this article shows, however, I don’t think that knowing every feature that comes with Apache Velocity is useful for productivity. If you end up spending more time creating templates than actually programming, doesn’t that defeat the purpose of using templates to increase your productivity?
Right-click on a project folder > New > Edit File Templates…
A window should appear, and by clicking on the +
button, you should be able to create a template:
If we create a template with the following content:
Hello ${recipient_name},
I am currently not at work, but I will get back to you in ${how_many_days} days.
Best wishes,
${your_name}
And then create a new file with our template as type of file:
The following dialog should appear:
By using Letter
, Ms. Doe
, 5
and John Doe
respectively, you would get a file called Letter.txt
with the
following content:
Hello Ms. Doe,
I am currently not at work, but I will get back to you in 5 days.
Best wishes,
John Doe
Though I’m going to focus on templates for Spring, you can use them for any project type, with any framework and any programming language.
For instance, a React component:
import React from 'react';
export default class ${NAME} extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
return (
<div>
#[[$END$]]#
</div>
);
}
}
Obviously, this isn’t all Velocity can do. There are many other instructions, such as #if
, #foreach
, etc. but we’ll
focus on the most basic here.
NOTE: #[[$END$]]#
is where the cursor will automatically be moved to when the template is created.
#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end
import javax.persistence.*;
@Entity
public class ${Resource_name} {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
public ${Resource_name}() {}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
With the resource name Example
, the template above would result in the following:
package org.twinnation.site.domain;
import javax.persistence.*;
@Entity
public class Example {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
public Example() {}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end
import org.springframework.stereotype.Repository;
import org.springframework.data.jpa.repository.JpaRepository;
@Repository
public interface ${Resource_name}Repository extends JpaRepository<${Resource_name}, Long> {
}
With the resource name Example
, the template above would result in the following:
package org.twinnation.site.repository;
import org.springframework.stereotype.Repository;
import org.springframework.data.jpa.repository.JpaRepository;
@Repository
public interface ExampleRepository extends JpaRepository<Example, Long> {
}
#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
#set($resourceFormatted = $Resource_name.substring(0,1).toLowerCase() + $Resource_name.substring(1))
@Service
public class ${Resource_name}Service {
@Autowired
private ${Resource_name}Repository ${resourceFormatted}Repository;
}
With the resource name Example
, the template above would result in the following:
package org.twinnation.site.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class ExampleService {
@Autowired
private ExampleRepository exampleRepository;
}
#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#set($resourceFormatted = $Resource_name.substring(0,1).toLowerCase() + $Resource_name.substring(1))
#set($resourceLowerCase = $Resource_name.toLowerCase())
@RestController
@RequestMapping(value = "/api/v1/${resourceLowerCase}s", produces = "application/json")
public class ${Resource_name}Controller {
@Autowired
private ${Resource_name}Service ${resourceFormatted}Service;
}
With the resource name Example
, the template above would result in the following:
package org.twinnation.site.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping(value = "/api/v1/examples", produces = "application/json")
public class ExampleController {
@Autowired
private ExampleService exampleService;
}
Those are just basic templates, you can bring it much further if you want. Here’s an example with a service and a controller:
#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
#set($resourceFormatted = $Resource_name.substring(0,1).toLowerCase() + $Resource_name.substring(1))
#set($repo = $resourceFormatted + "Repository")
@Service
public class ${Resource_name}Service {
@Autowired
private ${Resource_name}Repository ${repo};
public ${Resource_name} get${Resource_name}ById(Long id) {
${Resource_name} $resourceFormatted = ${repo}.findById(id).orElse(null);
Assert.notNull($resourceFormatted, "Invalid ${Resource_name} id");
return $resourceFormatted;
}
public ${Resource_name} create${Resource_name}(${Resource_name} $resourceFormatted) {
Assert.notNull($resourceFormatted, "Cannot create null ${Resource_name}");
return ${repo}.save($resourceFormatted);
}
public Message delete${Resource_name}ById(Long id) {
${Resource_name} $resourceFormatted = get${Resource_name}ById(id);
${repo}.delete($resourceFormatted);
return new Message(false, "Deleted ${Resource_name} with id #" + id);
}
}
#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
#set($resourceFormatted = $Resource_name.substring(0,1).toLowerCase() + $Resource_name.substring(1))
#set($resourcePath = $Resource_name.toLowerCase() + "s")
#set($service = $resourceFormatted + "Service")
@RestController
@RequestMapping(value = "/api/v1", produces = "application/json")
public class ${Resource_name}Controller {
@Autowired
private ${Resource_name}Service ${service};
@GetMapping("/${resourcePath}")
public List<${Resource_name}> getAll${Resource_name}s() {
return ${service}.getAll${Resource_name}s();
}
@GetMapping("/${resourcePath}/{id}")
public ${Resource_name} get${Resource_name}ById(@PathVariable Long id) {
return ${service}.get${Resource_name}ById(id);
}
@DeleteMapping("/${resourcePath}/{id}")
public Message delete${Resource_name}ById(@PathVariable Long id) {
return ${service}.delete${Resource_name}ById(id);
}
}
With this in mind, you could make a template for unit tests based on both of those templates and whatnot.
IntelliJ IDEA is a very powerful IDE not only because of its near perfect autocompletion, highly customizable interface and many plugins,but also because of its many embedded tools - templates included. Unfortunately, templates are very underrated despite their ability to save developers a lot of valuable time wasted on writing code they already know.