This course provides you with a deep dive into the various JDK features for accessing different resources when developing with Java. We’ll cover areas such as JDBC, Annotations, CDI, and JPA.
After completing this course, why not do our Accessing Resources using Java Annotations, CDI, JDBC, and JPA lab to put your knowledge into practice?
Learning Objectives
- Understand what the JDBC API is how to implement it to access databases
- Understand what Annotations are and the benefits of working with them to access resources
- Understand what CDI is and how you can use it for dependency injection and interception
- And understand what JPA is and how you can use it for object relational mapping and as a persistence framework
Prerequisites
- A basic understanding of the Java programming language
- A basic understanding of software development
- A basic understanding of the software development life cycle
Intended Audience
- Software Engineers interested in advancing their Java skills
- Software Architects interested in using advanced features of Java to design and build both applications and frameworks
- Anyone interested in advanced Java application development and associated tooling
- Anyone interested in understanding the advanced areas and features of the Java SDK
Okay, welcome back. In this lecture, we'll explore annotations in detail and how they can and should be used to provide source code metadata. In particular, we'll review the following topics. We'll discuss how annotations work in Java. We'll look at understanding what is required to work with Java annotations, using annotations themselves, and we'll take a close look at other technologies that are also using annotations. At the conclusion of this lecture, you should be able to perform each of the items listed above. Take a moment to rate yourself on each of these items on a scale of one, three, to five. At the conclusion of this lecture, these objectives will be reviewed. You should rate yourself again to see how much benefit you've received.
To start with, annotations are a form of metadata which provide data about a program that is not part of the program itself. Annotations have no direct effect on the operation of the code they annotate. Annotations have a number of uses. Among them, they provide information to the compiler. In this case, annotations can be used by the compiler to detect errors or suppress warnings. They can be used to provide compile-time and deployment-time processing. Again, in this case, software tools can process annotations to generate code, HTML files, and so forth. And finally, at runtime, some annotations are available to be examined at runtime.
An annotation consists of an at-sign or @ symbol followed by the type of annotation. You can optionally provide a set of parenthesis containing a comma-separated list of name/value pairs. Java itself defines a small set of standard annotations. Common ones which are often used are the Deprecated, Override, and SuppressWarnings annotations. APIs and/or libraries might also define technology-related annotations as seen in JUnit, Servlets, JPA, etc. Custom annotations can also be created as we will show later on in this lecture. And finally, by convention, annotations precede other modifiers such as public, static, or final.
The deprecated annotation indicates that the marked element is deprecated and should no longer be used. The compiler generates a warning whenever a program uses a method class or field that has been marked with the deprecated annotation. Until Java 9, elements were never removed from the core Java classes. When an element became deprecated, a warning was provided by the compiler, but this did not mean that the element would disappear in future versions of Java. In Java 9, two new elements have been added to the deprecated annotation. The forRemoval element can be set to true to inform developers that this element might be removed in future versions of Java, allowing a developer to start considering alternatives. The since element also allows us to define the version in which the element became deprecated. The override annotation informs the compiler that the element is meant to override an element declared in a superclass. While it is not required to use this annotation when overriding a method, it helps to prevent errors.
If the method marked with the override annotation fails to correctly override a method in one of its superclasses, the compiler generates an error. The SuppressWarnings annotation tells the compiler to suppress specific warnings that it would otherwise generate. In the following example, a deprecated method is used and the compiler would usually generate a warning. However, in this case, the annotation causes the warning to be suppressed. Each compiler has its own set of warnings. In Oracles Java compiler you can find out the list by using the command Javac with the parameter -X. Note: this list can change between versions of Java.
The FunctionalInterface annotation is used to indicate that an interface type declaration is intended to be a functional interface as defined by the Java language specification. Conceptually, a functional interface has exactly one abstract method. Annotations that apply to other annotations are called meta-annotations. There are several meta-annotation types defined in the Java.lang.annotation package. The retention annotation defines the scope of an annotation. The target annotation defines where the annotation may be used and the documented annotation indicates that the annotation should be included in Javadoc. The inherited annotation indicates that the annotation is inherited by subclasses and the repeatable annotation indicates that the annotation can be applied more than once to the same declaration.
You can create your own custom annotations using a special @interface syntax. The @interface looks a lot like an annotation, but isn't exactly. Consider it a sudo annotation, something that just marks the class as an annotation. In the example shown here, we're creating a new custom development annotation. The new custom development annotation would be declared in a standard Java source file named development.Java and which would eventually be compiled down into a development.class file.
You need to define the scope and usage for any custom annotation you create. This is done by applying the target and retention annotations. They are mandatory. Beyond these mandatory annotations, there are several other optional annotations that can be applied. Once the new custom development annotation has been defined, you can apply it elsewhere within your application code base so long as it adheres to the scoping restrictions you defined via the target annotation properties.
Custom annotations can contain members defined as method definitions within the annotation itself. This allows you to assign different values against the members when the custom annotation is being applied. As seen here, we are now able to specify an author version and status when applying the custom development annotation. Default values can be defined for annotation members, such that members containing a default value do not have to be defined when the annotation is being applied. The Java Reflection API allows for runtime examination and modification of applications. Reflection enables Javacode to discover information about the fields, methods, and constructors of loaded classes including any of the custom annotations that have been applied.
Using the Java Reflection API, we now have the ability to query and examine annotations at runtime, assuming the annotations in question have been tagged with the retention annotation set to runtime. This runtime capability provides us with a mechanism to change and customize the runtime behavior of our application in response to any detected custom annotation. In the example shown here, we're examining the class that was used to instantiate the service object to see whether it has been tagged with the custom development annotation. Additionally, we're examining all methods on the same class to, again, see whether any of them were also tagged with the custom development annotation. When this annotation is detected, we can then implement custom logic to do whatever we require. We are able to also query for the member information specified when the annotation was applied. For example, in this case, it could give us the ability to complete the implementation such that it logs out to a file that author and version values sit on the development annotation when it was applied.
Now that we understand what annotations are, let's take a quick look at some of the Java APIs and third-party libraries where annotations are used. We'll start with the Java Persistence API or JPA. The JPA makes extensive use of annotations. Annotations are used to define the mapping between an object instance and a database table. In the example shown here, the employee class has been tagged with several different JPA annotations, which dictate how the object is mapped to the database table.
Java EE also relies on annotations. Annotations are used to define and control the runtime behaviors of various components. In the example shown here, the WebServlet annotation is used to map the AddEmployee HTTP URL path to the EmployeeServlet. Additionally, this example also uses the inject annotation to perform dependency injection on the service field. In this last example, JUnit 5, a third-party unit testing library for Java, uses annotations to control the behavior of how the unit test is performed at runtime. The unit test class displayed here leverages the following JUnit annotations: AfterAll, AfterEach, ParametrizedTest, and MethodSource.
Okay, before we complete this lecture, pause this video and consider the following questions to test yourself on the content that we've just reviewed. Write down each of your answers and then resume the video to compare. Okay, the answers to the above questions are, one: Annotations provide metadata information about a class. Two: Override, SuppressWarning, and Deprecated. Three: Only when retention type of the annotation is set to runtime.
Jeremy is a Content Lead Architect and DevOps SME here at Cloud Academy where he specializes in developing DevOps technical training documentation.
He has a strong background in software engineering, and has been coding with various languages, frameworks, and systems for the past 25+ years. In recent times, Jeremy has been focused on DevOps, Cloud (AWS, Azure, GCP), Security, Kubernetes, and Machine Learning.
Jeremy holds professional certifications for AWS, Azure, GCP, Terraform, Kubernetes (CKA, CKAD, CKS).