Spring & Hibernate ==================================== - Framework for building java applications - Initially it was a lightweight alternative to J2EE - It provides a large no of helper classes which makes things easier. In J2EE ------------ Client Server Server Side <----> Side <----> Side <----> Database Presentation Presentation Business Logic (JSP,Servlet,XML (Enterprise Java Beans Web Services) Web Services) LifeLine of J2EE ------------------------------ 1999 2001 2003 2006 2009 2013 2016/17 ------ ------- ------ ------- ------ --------- -------- J2EE 1.2 J2EE 1.3 J2EE 1.4 Java EE 5 Java EE 6 Java EE 7 Java EE 8 Servlet EJB Web Ease of Use Pruning JMS2 JSP CMP Services EJB 3 Ease of Use Batch EJB JCA Mgmt JPA JAX-RS TX JMS Deployment JSF CDI Concurrency RMI JAXB Bean-Validation Web Sockets JAX-WS EJB - Enterprise JAVA Beans JMS - Java Message Service RMI - Remote Method Invocation CMP - Container Managed Persistence JCA - Java Connector Architecture JPA - Java Persistance API JSF - Java Server Faces JAXB - Java API for XML Binding JAX-WS - Java Web Services (SOAP) JAX-RS - Java Web Services (REST) CDI - Context Dependency Injection (IoC) - Early versions of EJB (version 1 and 2) were extermly complex, XML deployment descriptors were needed, had to define multiple interfaces, so complex and had poor performance while building an EE application. - Rod JohnSon - Founder Of Spring Lifeline of Spring --------------------------- 2004 2006 2009 2013 2016(March) 2017 (September) ------- ------- ------ ------ ---------------- ---------------- Spring 1.0 Spring 2.0 Spring 3.0 Spring 4.0 Spring 4.3 Spring 5 Spring 5 ================ - Must use JAVA8 or Higher version needed - Deprecated legacy integration for Tiles, Velocity, Portlet, Guava etc - Use Upgraded verison of Servlet API 4.0 - New Reactive Programming Framework - Spring Webflux WWW.spring.io - Official Website Goals of Spring -------------------- - LightWeight development with Java POJOs - Dependency injection to Promote loose coupling - Declarative Programming with Aspect Oriented Programming (AOP) - Minimize boilerplate Java Code Core Container ========================= Beans Core SpEL (Spring Expression Language) Context - Core container is the main, the main item of spring - It manages how the Beans are created. - It has a bean factory for creating Beans - It can reconfig files for setting properties, independencies - It holds the Beans in memory. Primary Functions -------------------------- 1. Create and manage objects (Inversion of Control) 2. Inject Objects Dependencies (Dependency Injection) Infrastructure =============================== AOP(Aspect Oriented Programming) Aspects Instrumentation Messaging - It adds functionality to objects declaratively like logging, security, transactions etc... - It allowsa you to create these applicationwide services - No need to change the code, just add a config file or an annotation and that service will be applied to your application. Data Access Layer ================================ JDBC ORM Transactions OXM JMS - ORM - Object to Relational Mapping (Hibernate/JPA) Web Layer (Spring MVC) ========================== Servlet WebSocket Web Portlet Test Layer ================== Unit Integration Mock There are Spring projects like --------------------------------------- Spring Cloud, Spring Data Spring Batch, Spring Security Spring Android, Spring Web Flow Spring Web Services, Spring LDAP ...etc Requirements ====================== - JDK8 or Higher - Eclipse IDE - Tomcat Server Do This ================ 1. Download Tomcat 9.0 and Eclipse IDE and add the server to the IDE in server tab side to console tab. 2. Create an Eclipse Project 3. Download the Spring JAR files (www.luv2code.com/downloadspring) OR from https://repo.spring.io/release/org/springframework/spring And Select the latest one and select the Release-dist.zip 4. Unzip it and copy all the JAR files from lib folder 5. Go to Eclipse, Create a folder in the project named lib and paste all the JAR files in that folder. 6. Right Click on Project --> Properties --> Java build Path --> Libraries --> classpath --> Add JARS --> Select lib folder --> Select all the JAR files --> OK --> Apply --> Apply and Close --> Check the Referenced Libraries ********************** Q. Why Spring is lightweight Spring, on the other hand, is lightweight because it allows minimally invasive development with POJOs. A lightweight framework makes itself as invisible as possible. ... As others have already pointed out, Spring is considered lightweight compared to traditional Java EE applications. Q. Why Spring is Loosely Coupled Tight coupling is when a group of classes are highly dependent on one another. This scenario arises when a class assumes too many responsibilities, or when one concern is spread over many classes rather than having its own class. Loose coupling is achieved by means of a design that promotes single-responsibility and separation of concerns. A loosely-coupled class can be consumed and tested independently of other (concrete) classes. Interfaces are a powerful tool to use for decoupling. Classes can communicate through interfaces rather than other concrete classes, and any class can be on the other end of that communication simply by implementing the interface. Loose coupling is the approach in which components of an application are interconnected to each other in least dependent way. ... In Spring Framework loose coupling is achieved by a core feature called Dependency Injection and IoC container. In dependency injection, components define their own dependencies when they need. Q. Why spring is Aspect Oriented Programming An aspect is a common feature that's typically scattered across methods, classes, object hierarchies, or even entire object models. It is behavior that looks and smells like it should have structure, but you can't find a way to express this structure in code with traditional object-oriented techniques. Spring AOP enables Aspect-Oriented Programming in spring applications. In AOP, aspects enable the modularization of concerns such as transaction management, logging or security that cut across multiple types and objects (often termed crosscutting concerns). *********************** Spring Inversion Of Control (IoC) =============================================== - It is design process of externalizing, the construction and the management of your objects, in other words it is approach of outsourcing the construction and management of Objects. The outsourcing will be handled by a Object Factory. 1_Spring_Demo Created =============================== 1. A package demo is created and BaseballCoach.java Created in the package package demo; public class BaseballCoach implements Coach { @Override public String getDailyWorkout() { return "Spent 30 Minutes on BaseBall"; } } 2. Test.java Created package demo; // Main Program public class Test { public static void main(String[] args) { //Create the object Coach theCoach = new BadmintonCoach(); //use the object and print the message System.out.println(theCoach.getDailyWorkout()); } } 3. Coach.java interface created package demo; public interface Coach { public String getDailyWorkout(); } 4. BadmintonCoach.java class Created package demo; public class BadmintonCoach implements Coach { @Override public String getDailyWorkout() { return "Spent 2 Hours on Badminton"; } } ================================================================================= - On Test.java at object creation, it is hardcoded. Means if we create object of BadmintonCoach, it will get executed, if we create the object of BaseballCoach, it will get executed. But we have to do it on our own. - To overcome this situation, Spring is gonna help us. Primary Functions -------------------------- 1. Create and manage objects (Inversion of Control) 2. Inject Objects Dependencies (Dependency Injection) - There are 3 ways to configure a Spring container 1. XML configuration file (leagacy) 2. Java Annotation (modern) 3. Java Source Code (modern) Spring Development Process ================================== 1. Configure Spring Beans 2. Create a Spring Container 3. Retrieve Beans from Spring Container 1. Configure Spring Beans ---------------------------------- Creation of an XML file XML startup file code ------------------------------- applicationContext.xml -------------------------------- 2. Create a Spring Container ------------------------------------ - Spring container is generally known as ApplicationContext - they have specialized implementation for it - ClassPathXMLApplicationContext - for reading XML form classpath - AnnotationConfigApplicationContext - for reading annotation configuration - GenericWebApplicationContext - ...etc syntax ---------- Present in (org.springframework.context.support.ClassPathXmlApplicationContext) ClassPathXMLApplicationContext context = new ClassPathXMLApplicationContext("applicationContext.xml") 3. Retrieve Beans from Spring Container ----------------------------------------------------- syntax -------------- Class_name object_name = context.getBean(bean_id, class_name) - bean_id must match the id in xml file - class_name must match the interface that the class in XMl implements =============================================================================== In 1_Spring_Demo ---------------------------- 5. Create a XML file in src folder applicationContext.xml and write this first now define your bean like this 6. Create SpringTest.java and do this package demo; import org.springframework.context.support.ClassPathXmlApplicationContext; //Main Class for Spring Test public class SpringTest { public static void main(String[] args) { //load the spring configuration file or create and add file into a spring container ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); //retrieve the bean from spring container Coach theCoach = context.getBean("myCoach", Coach.class); //call methods on the bean System.out.println(theCoach.getDailyWorkout()); //close the context context.close(); } } 7. Run SpringTest.java as Java Application - Advantage is, you do not need to change the source code each and every time, only change in the configuration file i.e, XML file and it will do the work for you. Question ---------------------- Why do we specify the Coach interface in getBean()? For example: ----------------------- Coach theCoach = context.getBean("myCoach", Coach.class); Answer ---------------------------- When we pass the interface to the method, behind the scenes Spring will cast the object for you. context.getBean("myCoach", Coach.class) However, there are some slight differences than normal casting. From the Spring docs: Behaves the same as getBean(String), but provides a measure of type safety by throwing a BeanNotOfRequiredTypeException if the bean is not of the required type. This means that ClassCastException can not be thrown on casting the result correctly, as can happen with getBean(String). Spring Dependency Injection ======================================= - The Spring container or the Object Factory has all the Dependencies one will need, You just have to outsource them and utilize them in your application. - We just outsource the construction and injection of our object to an external entity i.e. Object Factory. - In the above program when we retrieve the Coach Object, the coach object may have some additional dependencies, and these dependencies are just helper objects that it needs to perform the operation. - So instead of manually build the coach object and all of its dependencies, Spring framework/factory does all the job for you. Injection Types ============================= - There are many tyoe of injection in Spring. - Some are Constructor Injection Setter Injection auto-wiring Constructor Injection =============================== - Dependencies will be added through constructor of the Dependency class/interface. Development Process ================================ 1. Define Dependency interface and class 2. Create a constructor in your class for injection 3. Configure the Dependency injection in Spring configuration file. Do this in 1_Spring_Demo (Constructor Injection) ========================================================= 1. Create an interface FortuneService.java (Define Dependency interface and class) package demo; public interface FortuneService { public String getfortune(); } 2. Create an implementation class HappyFortuneService.java package demo; public class HappyFortuneService implements FortuneService { @Override public String getfortune() { return "Today is your Lucky Day"; } } 3. In Coach.java interface add a method package demo; public interface Coach { public String getDailyWorkout(); public String getDailyFortune(); } 4. Implement those methods in all the implemented classes 5. In BadmintonCoach.java class (Create a constructor in your class for injection) package demo; public class BadmintonCoach implements Coach { //define a private field private FortuneService fortuneService; //define parameter constructor for dependency injection public BadmintonCoach(FortuneService thefortuneService) { fortuneService = thefortuneService; } @Override public String getDailyWorkout() { return "Spent 2 Hours on Badminton"; } @Override public String getDailyFortune() { //Use my fortuneService to get a Fortune return fortuneService.getfortune(); } } 6. In XML file (Configure the Dependency injection in Spring configuration file) 7. In SpringTest.java call the method // call the methods for fortune System.out.println(theCoach.getDailyFortune()); 8. run the SpringTest.java and See the output NOTE:- - Over here in the XML file what it does it, the Dependency section is actually doing this HappyFortuneService fortune = new HappyFortuneService(); BadmintonCoach badminton = new BadmintonCoach(fortune); - in the SpringTest.java the bean object theCoach, already has all the methods embeded to it. - Only you have to change the class name in the configuration file and see the different output. This is what happens ------------------------------------------ **** System.out.println(theCoach.getDailyFortune()); ---> public String getDailyFortune(); of Coach interface --> Goes to implementation in BadmintonCoach.java ---> goes to application.xml and the bean Dependency creates the object in the object Factory and points to the implementation class HappyFortuneService.java , Context Object in SpringTest already binds all the dependencies ---> from BadmintonCoach.java , return fortuneService.getFortune(); fortuneService is the object reference and getFortune() is the methood in interface ---> goes to implementation via context object --> return return "Today is your Lucky Day"; **** Setter Injection =========================== - Spring will inject dependencies through Setter methood on your class Development Process ----------------------------- 1. Create setter methods in your class for injection 2. Configure injection in Spring configuration file. 1_Spring_Demo (Setter Injection) --------------------------------------------- 1. Create a class CricketCoach.java and write this package demo; public class CricketCoach implements Coach { private FortuneService fortuneService; //Create a no-args constructor public CricketCoach() { System.out.println("CricketCoach : inside no-args constructor"); } //Setter method public void setFortuneService(FortuneService fortuneService) { System.out.println("CricketCoach : inside setter method - setFortuneService"); this.fortuneService = fortuneService; } @Override public String getDailyWorkout() { return "Cricket Coach : Practice fast bowling and it is a great sport"; } @Override public String getDailyFortune() { return fortuneService.getFortune(); } } 2. In applicationContext.xml add this 3. Create a class SetterDemoApp.java and write this package demo; import org.springframework.context.support.ClassPathXmlApplicationContext; public class SetterDemoApp { public static void main(String[] args) { // load the spring configuration file ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); // retrieve bean from spring container CricketCoach theCoach = context.getBean("myCricketCoach", CricketCoach.class); // call methods on bean object System.out.println(theCoach.getDailyWorkout()); System.out.println(theCoach.getDailyFortune()); // close the context context.close(); } } *** NOTE : - - In config file, the property name should be the name as the setter name without "set" word e.g, setFortuneService(FortuneService fortuneService){ --> } ----> this will be replacement of HappyFortuneService fortuneService = new HappyFortuneService(); ---> this will create CricketCoach myCricketCoach = new CricketCoach(); ---> this will create myCricketCoach.setFortuneService( fortuneService); - SetterDemoApp will load and create the spring container and the objects will be created in Bean Factory - getBean will get the objects and will call the respective methods - First the Constructor will be called and then setter method will be executed in Bean/Object Factory then the methods that you want to call will be executed *** Injecting Literal values ====================================== - Adding of some variables like string Literals ...etc Development Process ============================= 1. Create setter methods in your class for Injection 2. Configure the Injection in Spring config file Do this in 1_Spring_Demo ================================== 1. In CricketCoach.java create private fields and generate setters and getters // Literal injection // Add new private field and create setter methods private String email; private String team; public void setEmail(String email) { System.out.println("CricketCoach : Inside Setter method setEmail"); this.email = email; } public void setTeam(String team) { System.out.println("CricketCoach : Inside setter method setTeam"); this.team = team; } public String getEmail() { return email; } public String getTeam() { return team; } 2. In applicationContext.xml under bean id = myCricketCoach add this properties for setter injection - This will set the property to the values that we have assigned. 3. In SetterDemoApp.java call those getter methods to check the output // call methods to get Literal values System.out.println(theCoach.getEmail()); System.out.println(theCoach.getTeam()); *** Note :- - Through xml file first no args constructor will be called for object creation in bean/object factory - then the setter methods will be called automatically to assign the values through objects - All the objects will be embeded to context object in ClassPathXMLApplicationContext in SetterDemoApp - The objects are retrieved as the getBean() is called with referencing the class in the xml file - Now the methods are called using the object in SetterDemoApp class *** Question ------------------ For the CricketCoach example with Setter Injection, why do we use the CricketCoach class instead of the Coach interface? Answer ------------------ The getTeam() method is only defined in the CricketCoach class. It is not part of the Coach interface. As a result, you would need the following code: CricketCoach theCricketCoach = context.getBean("myCricketCoach", CricketCoach.class); The Coach interface has two methods: getDailyWorkout and getDailyFortune The CricketCoach class has four methods: getDailyWorkout, getDailyFortune, getTeam and setTeam When you retrieve a bean from the Spring container using the Coach interface: Coach theCricketCoach = context.getBean("myCricketCoach", Coach.class); You only have access to the methods defined in the Coach interface: getDailyWorkout and getDailyFortune. Even though the actual implementation has additional methods, you only have visibility to methods that are defined at the Coach interface level. When you retrieve a bean from the Spring container using the CricketCoach class: CricketCoach theCricketCoach = context.getBean("myCricketCoach", CricketCoach.class); You have access to the methods defined in the Coach interface: getDailyWorkout and getDailyFortune. ALSO, you have access to the additional methods defined in the CricketCoach class: getTeam, setTeam. The bottom line is it depends on how you retrieve the object and assign it ... that determines the visibility you have to the methods. Injecting Values from Properties File ================================================== - In above program the values were hardcoded into the config file of the application i.e. applicationContext.xml - Here we can put everything into a external Properties file and read the values from it. Development Process ============================ 1. Create a properties File 2. Load Properties file into Spring Config File 3. Reference value from Properties file 1. Create a properties File ------------------------------- sport.properties created and this was written foo.email=debiprasadmishra50@gmail.com foo.team=Chennai Super kings format is name=value ***name can be anything ***If no value is there, default value is null 2. Load Properties file into Spring Config File ------------------------------------------------------- - In applicationContext.xml load the properties file in first section or in first line [line (10)] This will load the properties file into the config file/Bean factory 3. Reference value from Properties file ------------------------------------------------ - Under bean myCricketCoach inject properties like this - Comment the literal injection part as the value should be assigned once through one type i.e. either literal injection or through properties file 4. Run the SetterDemoApp.java and see the output *** Note :- - values should be written like this ${name in the properties file} *** Spring Bean Scopes ================================================= - Default scope of bean is singleton i.e, Spring container creates only one instance of bean by default - It is in cached in memory - All requests for bean will return a shared referenceto the same bean - It is just like String literal, e.g. Coach theCoach = context.getBean("myCoach", Coach.class) Coach alphaCoach = context.getBean("myCoach", Coach.class) - Here the bean will be created for theCoach and in the next the alphaCoach will just be a reference to the previously created bean. They will point to the same memory area. Spring Scopes (5 scopes) -------------------- - singleton - we can explicitely declare the scope of a bean 1. singleton - Create a single shared instance of bean. It is the Default scope 2. prototype - Creates a new bean instance for each container request, it is like the new keyword 3. request - Scoped to HTTP web request, Only used for Web Apps 4. session - Scoped to HTTP web session, Only used for Web Apps 5. global-session - Scoped to global HTTP session. Only used for Web Apps Do this =================== 1. Copy and paste 1_Spring_Demo to 2_Spring_Scope 2. Delete the properties file, all main() classes, all coach Classes except BadmintonCoach.java 3. Rename applicationContext to beanScope-applicationContext.xml and write this 4. Create a main() class BeanScopeDemoApp.java and write this package demo; import org.springframework.context.support.ClassPathXmlApplicationContext; public class BeanScopeDemoApp { public static void main(String[] args) { // load the spring config file ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beanScope-applicationContext.xml"); // retrieve the spring container Coach theCoach = context.getBean("myCoach", Coach.class); Coach alphaCoach = context.getBean("myCoach", Coach.class); // same hashcode, Point to same direction System.out.println(theCoach); System.out.println(alphaCoach); System.out.println(System.identityHashCode(theCoach)); System.out.println(System.identityHashCode(alphaCoach)); boolean result = (theCoach == alphaCoach); System.out.println(result); } } 5. Run BeanScopeDemoApp.java and see this For scope=prototype the object will have different hashcode, i.e, they are different instances and for scope = singleton, the objects will have same hashcode and they are same instances Spring Bean LifeCycle =================================== Spring Container Starts ---> Bean Instantiated ---> Dependencies Injected ---> Internal Spring Processing ---> [Your Custom Init method] ---> Bean is ready to Use ---> Methods are called using Bean Object ---> Container/Application shutdown ---> [Your custom Destroy method] Init and Destroy method configuration ----------------------------- Development Process ------------------------------- 1. Define your init and destroy methods in bean Class 2. Configure those methods in Spring Configuration File init and destroy methods can have ----------------------------------------- Access modifier The method can have any access modifier (public, protected, private) Return type The method can have any return type. However, "void" is most commonly used. If you give a return type just note that you will not be able to capture the return value. As a result, "void" is commonly used. Method name The method can have any method name. Arguments The method can not accept any arguments. The method should be no-arg. Do this ===================== 1. Copy and paste and rename 2_Spring_Scope to 3_Spring_LifeCycle 2. Rename the XML file to beanLifecycle-applicationContext.xml 3. Rename the main() app i.e. the BeanScopeDemoApp.java to BeanLifecycleDemoApp.java and change the file_name in the context object creation and write this // load the spring config file ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beanLifecycle-applicationContext.xml"); // retrieve bean from spring container Coach theCoach = context.getBean("myCoach", Coach.class); System.out.println(theCoach.getDailyWorkout()); System.out.println(theCoach.getDailyFortune()); // close the context context.close(); 4. Add init and destroy method in BadmintonCoach.java package demo; public class BadmintonCoach implements Coach { public BadmintonCoach() { } // define a private field private FortuneService fortuneService; // define parameter constructor for dependency injection public BadmintonCoach(FortuneService thefortuneService) { fortuneService = thefortuneService; } @Override public String getDailyWorkout() { return "Spent 2 Hours on Badminton"; } @Override public String getDailyFortune() { // Use my fortuneService to get a Fortune return "In BadmintonCoach : " + fortuneService.getFortune(); } // add an init method public void doMyStartupStuff() { System.out.println("BadmintonCoach : Inside init method : doMyStartupStuff"); } // add an destroy method public void doMyCleanupStuff() { System.out.println("BadmintonCoach : Inside destroy method : doMyCleanupStuff"); } } 5. In xml file write this 6. run the BeanLifecycleDemoApp.java and see the output, first the init() will execute, then your custom methods, then the destroy method *** Note :- There is a subtle point you need to be aware of with "prototype" scoped beans. For "prototype" scoped beans, Spring does not call the destroy method. In contrast to the other scopes, Spring does not manage the complete lifecycle of a prototype bean: the container instantiates, configures, and otherwise assembles a prototype object, and hands it to the client, with no further record of that prototype instance. Thus, although initialization lifecycle callback methods are called on all objects regardless of scope, in the case of prototypes, configured destruction lifecycle callbacks are not called. The client code must clean up prototype-scoped objects and release expensive resources that the prototype bean(s) are holding. *** ***** Important Application To call destroy method on prototype scoped beans ============================================================= 1. Create a custom bean processor. This bean processor will keep track of prototype scoped beans. During shutdown it will call the destroy() method on the prototype scoped beans. The custom processor is configured in the spring config file. 2. The prototype scoped beans MUST implement the DisposableBean interface. This interface defines a "destory()" method. public class TrackCoach implements Coach, DisposableBean { ... // add a destroy method @Override public void destroy() throws Exception { System.out.println("TrackCoach: inside method doMyCleanupStuffYoYo"); } } 3. The spring configuration must be updated to use the destroy-method of "destroy". 4. In this app, BeanLifeCycleDemoApp.java is the main program. BadmintonCoach.java is the prototype scoped bean. BadmintonCoach implements the DisposableBean interface and provides the destroy() method. The custom bean processing is handled in the MyCustomBeanProcessor class. Programs -------------------- 1. Coach.java ---------------- package demo; public interface Coach { public String getDailyWorkout(); public String getDailyFortune(); } 2. BadmintonCoach.java ------------------------------- package demo; import org.springframework.beans.factory.DisposableBean; public class BadmintonCoach implements Coach, DisposableBean { private FortuneService fortuneService; public BadmintonCoach() { } public BadmintonCoach(FortuneService fortuneService) { this.fortuneService = fortuneService; } @Override public String getDailyWorkout() { return "Spend 2 hours on Badminton"; } @Override public String getDailyFortune() { return "Just Do It: " + fortuneService.getFortune(); } // add an init method public void doMyStartupStuff() { System.out.println("BadmintonCoach: inside method doMyStartupStuff"); } // add a destroy method @Override public void destroy() throws Exception { System.out.println("BadmintonCoach: inside method doMyCleanupStuff"); } } 3. FortuneService.java ------------------------------- package demo; public interface FortuneService { public String getFortune(); } 4. HappyFortuneService.java package demo; public class HappyFortuneService implements FortuneService { @Override public String getFortune() { return "Today is your lucky day!"; } } 5. BeanLifeCycleDemoApp.java ------------------------------- package demo; import org.springframework.context.support.ClassPathXmlApplicationContext; public class BeanLifeCycleDemoApp { public static void main(String[] args) { // load the spring configuration file ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beanLifeCycle-applicationContext.xml"); // retrieve bean from spring container Coach theCoach = context.getBean("myCoach", Coach.class); System.out.println(theCoach.getDailyWorkout()); System.out.println(theCoach.getDailyFortune()); // close the context context.close(); } } 6. MyCustomBeanProcessor.java ------------------------------------ package demo; import java.util.LinkedList; import java.util.List; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.config.BeanPostProcessor; public class MyCustomBeanProcessor implements BeanPostProcessor, BeanFactoryAware, DisposableBean { private BeanFactory beanFactory; private final List prototypeBeans = new LinkedList<>(); @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { // after start up, keep track of the prototype scoped beans. // we will need to know who they are for later destruction if (beanFactory.isPrototype(beanName)) { synchronized (prototypeBeans) { prototypeBeans.add(bean); } } return bean; } @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { this.beanFactory = beanFactory; } @Override public void destroy() throws Exception { // loop through the prototype beans and call the destroy() method on each one synchronized (prototypeBeans) { for (Object bean : prototypeBeans) { if (bean instanceof DisposableBean) { DisposableBean disposable = (DisposableBean)bean; try { disposable.destroy(); } catch (Exception e) { e.printStackTrace(); } } } prototypeBeans.clear(); } } } 7. beanLifecycle-applicationContext.xml ------------------------------------------- 8. Run the BeanLifecycleDemoApp.java and Test ***** ==================================================================================== Spring Annotaions ======================== - Special labels/markers that are added to java class - They provide meta-data about the class - Processed at the compile time or run time for special processing Why Annotation - XML config can be very verbose for very large projects - Configuring spring beans with annotations minimizes XML configuration - When you add an annotation to a class Spring will scan your java class for special annotations, After finding the annotation, it will automatically register that bean in the spring container. Development Process ------------------------- 1. Enable component scanning in Spring config file 2. Add the @Component annotation to your java class 3. Retrieve bean from spring container 1. Enable component Scanning Spring will search all the calsses and as it finds the @Component Annotaion it will automatically register the class to Spring Container 2. Add the @Component annotation to your java class @Component("thatSillyCoach") public class TennisCoach implements Coach{ @Override public String getDailyWorkout(){ return "Practice Tennis Harder"; } } @Component : It teels the Spring that it is a Spring Bean Class "thatSillyCoach" : Bean ID 3. Retrieve bean from spring container Coach theCoach = context.getBean("thatSillyCoach", Coach.class); Do this -------------- 1. Create a new java project 5_Spring_Annotation_Demo and build path all the JARs 2. Add the applicationContext.xml file to src folder and create a 'demo' package and in XML file write this 3. Coach.java interface created package demo; public interface Coach { public String getDailyWorkout(); } 4. BadmintonCoach.java class created package demo; import org.springframework.stereotype.Component; @Component("theBadmintonCoach") //Spring will automatically register bean for this class public class BadmintonCoach implements Coach { @Override public String getDailyWorkout() { return "In Badminton : Practice Badminton smash 2 hours a day"; } } 5. AnnotaitonDemoApp.java class created package demo; import org.springframework.context.support.ClassPathXmlApplicationContext; public class AnnotationDemoApp { public static void main(String[] args) { // Read the spring config file ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); // Retrieve the bean Coach theCoach = context.getBean("theBadmintonCoach", Coach.class); //Call the methods System.out.println(theCoach.getDailyWorkout()); // Close the context context.close(); } } 6. Run the AnnotationDemoApp.java and see the output ---------------------------------------------------------------------------------- NOTE : Default Component Names ============================== 1. Specify the bean_id in the component annotation - If we do not specify the bean_id the default bean_id will be the name same as the class name with lowercase first letter. For e.g, Class Name = BadmintonCoach Default bean_id = badmintonCoach RESTFortuneService = RESTFortuneService It is like this because : Rules :- If the annotation's value doesn't indicate a bean name, an appropriate name will be built based on the short name of the class (with the first letter lower-cased). HappyFortuneService --> happyFortuneService However, for the special case of when BOTH the first and second characters of the class name are upper case, then the name is NOT converted. For the case of RESTFortuneService RESTFortuneService --> RESTFortuneService Spring uses the Java Beans Introspector to generate the default bean name public static String decapitalize(String name) Utility method to take a string and convert it to normal Java variable name capitalization. This normally means converting the first character from upper case to lower case, but in the (unusual) special case when there is more than one character and both the first and second characters are upper case, we leave it alone. Thus "FooBah" becomes "fooBah" and "X" becomes "x", but "URL" stays as "URL". Parameters: name - The string to be decapitalized. Returns: The decapitalized version of the string. https://docs.oracle.com/javase/8/docs/api/java/beans/Introspector.html#decapitalize(java.lang.String) Spring Dependency Injection with Annotation and Autowiring ================================================================== - Coach was providing the getDailyWorkout() - Now another depedency class(HappyFortuneService.java) and interface(FortuneService.java) will be there for getDailyFortune() Spring Autowiring ----------------------- - Sprig can automatically wire-up your objects together for depedency injection - Spring will look for a class that matches the given property and it will actually match by type (Class/Interface) - Spring will automatically inject it, hence Autowiring What we will do ------------------- - Inject FortuenService into Coach implementation - Spring will scan @Component - If anyone implements FortuneService interface - lets inject them, like HappyFortuneService - For Autowiring they have 3 types of injections 1. Constructor Injection 2. Setter Injection 3. Field Injection 1. Constructor Injection ------------------------------- Development Process -------------------------- 1.1. Define the depedency interface and class 1.2. Create a constructor in your class for injection 1.3. Configure the depedency injection with @Autowired Annotation 1.1. Define the depedency interface and class public interface FortuneService{ public String getFortune(); } @Component public class HappyFortuneService implements FortuneService{ @Override public String getFortune(){ return "Today is your lucky day"; } } 1.2. Create a constructor in your class for injection & 1.3. Configure the depedency injection with @Autowired Annotation @Component public class BadmintonCoach implements Coach{ private FortuneService fortuneService; @Autowired public BadmintonCoach(FortuneService fortuneService){ this.fortuneService = fortuneService; } } Note : Q. What if there are multiple FortuneService implementations? Ans: Spring has special support to handle this case. Use the @Qualifier annotation. We'll cover this later in the course with slides and code examples. But don't worry, we will address all scenarios. Do this --------------------- 1. 6_Spring_Annotation_Autowiring Created and all JARs were builed path 2. Copy the files from 5_Spring_Annotation_Demo to this one (Coach.java, BadmintonCoach.java, AnnotationDemoApp.java and xml file) 3. Create an depedency interface FortuneService.java and write this package demo; public interface FortuneService { public String getFortune(); } 4. Create an implemented class HappyFortuneService.java and write this package demo; import org.springframework.stereotype.Component; @Component public class HappyFortuneService implements FortuneService { @Override public String getFortune() { return "In HappyFortuneService : Today is your lucky day"; } } 5. Create a method in Coach.java interface package demo; public interface Coach { public String getDailyWorkout(); public String getDailyFortune(); } 6. Add a private depedency field in BadmintonCoach.java, Add the constructor for injection and override the method package demo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; // @Component //if this is there, then the default bean id is "badmintonCoach" @Component("theBadmintonCoach") //Spring will automatically register bean for this class public class BadmintonCoach implements Coach { private FortuneService fortuneService; public BadmintonCoach() { } @Autowired public BadmintonCoach(FortuneService fortuneService) { this.fortuneService = fortuneService; } @Override public String getDailyFortune() { return "In BadmintonCoach : "+fortuneService.getFortune(); } @Override public String getDailyWorkout() { return "In Badminton : Practice Badminton smash 2 hours a day"; } } 7. In AnnotationDemoApp.java call the method and run the app and see the output NOTE: AFter Spring 4.3 It is not required to write @Autowired annotation As of Spring Framework 4.3, an @Autowired annotation on such a constructor is no longer necessary if the target bean only defines one constructor to begin with. However, if several constructors are available, at least one must be annotated to teach the container which one to use. ------------------------------------------------------------------------------------- 2. Setter Injection ------------------------- - Spring inject dependencies by calling setter methods on your class Development Process ---------------------- 1. Create setter method(s) in you class for injection 2. Configure the depedency by @Autowired annotation Do this ---------------- 1. In 6_Spring_Annotation_Autowiring in BadmintonCoach.java comment out the constructor injection part and generate getter and setter and write this package demo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; // @Component //if this is there, then the default bean id is "badmintonCoach" @Component("theBadmintonCoach") //Spring will automatically register bean for this class public class BadmintonCoach implements Coach { private FortuneService fortuneService; public BadmintonCoach() { System.out.println(">> Inside Default Constructor"); } // Constructor Injection // @Autowired // public BadmintonCoach(FortuneService fortuneService) { // this.fortuneService = fortuneService; // } // Setter Injection public FortuneService getFortuneService() { return fortuneService; } @Autowired public void setFortuneService(FortuneService fortuneService) { System.out.println(">> Inside Setter Method setFortuneService()"); this.fortuneService = fortuneService; } @Override public String getDailyFortune() { return "In BadmintonCoach : "+fortuneService.getFortune(); } @Override public String getDailyWorkout() { return "In Badminton : Practice Badminton smash 2 hours a day"; } } 2. Run the AnnotationDemoApp.java and see the output ------------------------------------------------------------------------------------- Method Injection ========================== - Insted of using setter method we can use any method, all we have to do is give @Autowired annotation on that method Do this ---------------- 1. In 6_Spring_Annotation_Autowiring in BadmintonCoach.java comment out the constructor injection part and generate getter and setter and write this package demo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; // @Component //if this is there, then the default bean id is "badmintonCoach" @Component("theBadmintonCoach") //Spring will automatically register bean for this class public class BadmintonCoach implements Coach { private FortuneService fortuneService; public BadmintonCoach() { System.out.println(">> Inside Default Constructor"); } // Constructor Injection // @Autowired // public BadmintonCoach(FortuneService fortuneService) { // this.fortuneService = fortuneService; // } // Setter Injection // public FortuneService getFortuneService() { // return fortuneService; // } // // @Autowired // public void setFortuneService(FortuneService fortuneService) { // System.out.println(">> Inside Setter Method setFortuneService()"); // this.fortuneService = fortuneService; // } @Autowired public void doSomething(FortuneService fortuneService) { System.out.println(">> Inside regular method : doSomething()"); this.fortuneService = fortuneService; } @Override public String getDailyFortune() { return "In BadmintonCoach : "+fortuneService.getFortune(); } @Override public String getDailyWorkout() { return "In Badminton : Practice Badminton smash 2 hours a day"; } } 2. Run the AnnotationDemoApp.java and see the output --------------------------------------------------------------------------------------- 3. Field Injection =========================== - Inject depedencies by setting field values on your class directly (even private fields) - It uses Reflection API to achieve this Development Process ---------------------- 1. Configure the depedency injection with @Autowired Annotation - Applied directly to the field - No need for setter method public class BadmintonCoach implements Coach { @Autowired private FortuneService fortuneService; public BadmintonCoach(){ } //no need for setter method .... } - Even though the field is private, spring will set this value behind the scenes. It will construct the object of the class by calling the default constructor, then it will inject a FortuneService implementation directly into the class and it happens through Reflection API. Do this ---------------- 1. Create a class FieldInjection.java implementing Coach interface and write this package demo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class FieldInjection implements Coach { @Autowired private FortuneService fortuneService; public FieldInjection() { System.out.println(">> FieldInjection : Inside Default Constructor"); } @Override public String getDailyWorkout() { return "In FieldInjection : Practice Badminton smash 2 hours a day"; } @Override public String getDailyFortune() { return "In FieldInjection : "+fortuneService.getFortune(); } } 2. In 6_Spring_Annotation_Autowiring retrieve the bean and run the class Coach theCoach = context.getBean("fieldInjection", Coach.class); ----------------------------------------------------------------------------------------- Annotation Autowiring and Qualifiers ============================================== - In case of multiple implementation of FortuneService we could never know which implementation of it will be picked - It will give you a NoUniqueBeanDefinitionException, injection Failed - To resolve this problem another annotation is used called @Qualifier("bean_id") - @Qualifier can be applied to - Constructor Injection - Setter/Method Injection - Field Injection Do this ---------------- 1. Create 7_Spring_Annotation_Qualifier and create these 2. Coach.java interface package demo; public interface Coach { public String getDailyWorkout(); public String getDailyFortune(); } 3. Create BadmintonCoach.java class and implementing Coach interface with Field Depedency Injection 4. Create FortuneService.java interface package demo; public interface FortuneService { public String getFortune(); } 5. Create HappyFortuneService.java class package demo; import org.springframework.stereotype.Component; @Component public class HappyFortuneService implements FortuneService { @Override public String getFortune() { return "In HappyFortuneService : Today is your lucky day"; } } 6. Create RandomFortuneService.java class package demo; import org.springframework.stereotype.Component; @Component public class RandomFortuneService implements FortuneService { @Override public String getFortune() { return "RandomFortuneService : All the very best for your match"; } } 7. Create DatabaseFotruneService.java class package demo; import org.springframework.stereotype.Component; @Component public class DatabaseFortuneService implements FortuneService { @Override public String getFortune() { return "DatabaseFortuneService : I am in the DatabaseFortuneService"; } } 8. Create RESTFortuneService.java class package demo; import org.springframework.stereotype.Component; @Component public class RESTFortuneService implements FortuneService { @Override public String getFortune() { return "RESTFortuneService : I am in RESTFortuneService"; } } 9. Create QualifierDemoApp.java and proceed to run package demo; import org.springframework.context.support.ClassPathXmlApplicationContext; public class QualifierDemoApp { public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); Coach theCoach = context.getBean("badmintonCoach", Coach.class); System.out.println(theCoach.getDailyWorkout()); System.out.println(theCoach.getDailyFortune()); context.close(); } } 10. See the error as multiple classes has implemented the interface FortuneService.java 11. Apply @Qualifier Annotation to BadmintonCoach.java package demo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; @Component public class BadmintonCoach implements Coach { @Autowired // @Qualifier("happyFortuneService") // @Qualifier("randomFortuneService") // @Qualifier("databaseFortuneService") @Qualifier("RESTFortuneService") private FortuneService fortuneService; @Override public String getDailyWorkout() { return "In BadmintonCoach : Practice Badminton smash 2 hours a day"; } @Override public String getDailyFortune() { return "In BadmintonCoach : "+fortuneService.getFortune(); } } 12. Now run QualifierDemoApp.java again and see the output -------------------------------------------------------------------------- Do this -------------------- 1. Changes in RandomFortuneService.java package demo; import java.util.Random; import org.springframework.stereotype.Component; @Component public class RandomFortuneService implements FortuneService { // Create a random array of Strings private String [] fortune = { "Beware of wolf in sheep's clothing", "Diligence is mother of good luck", "The journey is the reward" }; // Create Random number generator private Random random = new Random(); @Override public String getFortune() { // Pick one random fortune from the array int no = random.nextInt(fortune.length); String randomFortune = fortune[no]; return "RandomFortuneService : "+randomFortune; } } ---------------------------------------------------------------------------------------------- Using @Qualifier with Constructors ============================================== @Qualifier is a nice feature, but it is tricky when used with Constructors. The syntax is much different from other examples and not exactly intuitive. Consider this the "deep end of the pool" when it comes to Spring configuration LOL :-) You have to place the @Qualifier annotation inside of the constructor arguments. package com.luv2code.springdemo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; @Component public class TennisCoach implements Coach { private FortuneService fortuneService; // define a default constructor public TennisCoach() { System.out.println(">> TennisCoach: inside default constructor"); } @Autowired public TennisCoach(@Qualifier("randomFortuneService") FortuneService theFortuneService) { System.out.println(">> TennisCoach: inside constructor using @autowired and @qualifier"); fortuneService = theFortuneService; } /* @Autowired public void doSomeCrazyStuff(FortuneService theFortuneService) { System.out.println(">> TennisCoach: inside doSomeCrazyStuff() method"); fortuneService = theFortuneService; } */ /* @Autowired public TennisCoach(FortuneService theFortuneService) { fortuneService = theFortuneService; } */ @Override public String getDailyWorkout() { return "Practice your backhand volley"; } @Override public String getDailyFortune() { return fortuneService.getFortune(); } } ---------------------------------------------------------------------------------- Do this -------------- 1. Changes in BadmintonCoach.java for @Qualifier with annotation package demo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; @Component public class BadmintonCoach implements Coach { // @Autowired // @Qualifier("happyFortuneService") // @Qualifier("randomFortuneService") // @Qualifier("databaseFortuneService") // @Qualifier("RESTFortuneService") private FortuneService fortuneService; /* * If we wanna use the @Qualifier with constructor injection then we have to place @Qualifier annotation inside the constructor argument * Comment the @Autowired and all @Qualifier above */ @Autowired public BadmintonCoach(@Qualifier("happyFortuneService") FortuneService fortuneService) { this.fortuneService = fortuneService; } @Override public String getDailyWorkout() { return "In BadmintonCoach : Practice Badminton smash 2 hours a day"; } @Override public String getDailyFortune() { return "In BadmintonCoach : "+fortuneService.getFortune(); } } ----------------------------------------------------------------------------------------- Important Program ============================== Q. How to inject properties file using Java annotations ? Answer : 1. Create a properties file to hold your properties. It will be a name value pair. New text file: src/sport.properties foo.email=myeasycoach@luv2code.com foo.team=Silly Java Coders Note the location of the properties file is very important. It must be stored in src/sport.properties 2. Load the properties file in the XML config file. File: applicationContext.xml Add the following lines: 3. Inject the properties values into your Swim Coach: SwimCoach.java, No need for Setter methods @Value("${foo.email}") private String email; @Value("${foo.team}") private String team; Do this ------------------ 1. Create 8_Spring_Annotation_Properties_File and add the JARs 2. Create a properties file sport.properties foo.email=debiprasadmishra50@gmail.com foo.player=Lin Dan and Lee Chong Wee #Refer the BadmintonCoach.java 3. Create a applicationContext.xml file 4. Create Coach.java interface package demo; public interface Coach { public String getDailyWorkout(); } 5. Create BadmintonCoach.java class package demo; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @Component public class BadmintonCoach implements Coach { @Value("${foo.email}") private String email; @Value("${foo.player}") private String player; public String getEmail() { return email; } public String getPlayer() { return player; } public BadmintonCoach() { System.out.println("BadmintonCoach : default constructor"); } @Override public String getDailyWorkout() { return "BadmintonCoach : Practice smash for 2 hours"; } } 6. Create PropertiesDemoApp.java and run the class package demo; import org.springframework.context.support.ClassPathXmlApplicationContext; public class PropertiesDemoApp { public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); BadmintonCoach theCoach = context.getBean("badmintonCoach", BadmintonCoach.class); System.out.println(theCoach.getDailyWorkout()); System.out.println(theCoach.getEmail()); System.out.println(theCoach.getPlayer()); context.close(); } } -------------------------------------------------------------------------------- Spring Bean Scopes with Annotation ============================================ - Default scope is singleton : one instance of bean, cached in memory, return a shared reference to the same bean Coach theCoach = context.getBean("badmintonCoach", "Coach.class"); Coach alphaCoach = context.getBean("badmintonCoach", "Coach.class"); both theCoach and alphaCoach are same. alphaCoach will refer to the theCoach reference/location. - Explicitely you can give a bean scope by using @Scope annotation @Scope("singleton") @Scope("prototype") Do this --------------- 1. Create 9_Spring_Annotation_BeanScopes and add the JARs and create a package "demo" 2. Create an applicationContext.xml file and write this 3. Create Coach.java interfacae package demo; public interface Coach { public String getDailyWorkout(); } 4. Create BadmintonCoach.java class package demo; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; @Component //@Scope //@Scope("singleton") //Both the lines are same because default scope is singleton @Scope("prototype") public class BadmintonCoach implements Coach { public BadmintonCoach() { System.out.println("BadmintonCoach : Constructor is called"); } @Override public String getDailyWorkout() { return "In Badminton : Practice Badminton smash 2 hours a day"; } } 5. Create AnnotationBeanScopeDemoApp.java class and run this package demo; import org.springframework.context.support.ClassPathXmlApplicationContext; public class AnnotationBeanScopeDemoApp { public static void main(String[] args) { // Read the spring config file ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); // Retrieve the bean Coach theCoach = context.getBean("badmintonCoach", Coach.class); Coach alphaCoach = context.getBean("badmintonCoach", Coach.class); // check their hashcodes System.out.println(theCoach); System.out.println(alphaCoach); System.out.println(System.identityHashCode(theCoach)); System.out.println(System.identityHashCode(alphaCoach)); System.out.println(theCoach==alphaCoach); // Close the context context.close(); } } ------------------------------------------------------------------------------- Bean Lifecycle Methods with Annotation ================================================ - These codes get executed after the constructor and after the injection of Dependencies Development Process ------------------------ 1. Define your methods init and destroy 2. Add annotations : @PostConstruct and @PreDestroy Special Note about @PostConstruct and @PreDestroy Method Signatures ----------------------------------------------------------------------- Access modifier The method can have any access modifier (public, protected, private) Return type The method can have any return type. However, "void' is most commonly used. If you give a return type just note that you will not be able to capture the return value. As a result, "void" is commonly used. Method name The method can have any method name. Arguments The method can not accept any arguments. The method should be no-arg. **** NOTE: If you are using Java 9 or higher, then you will encounter an error when using @PostConstruct and @PreDestroy in your code. Eclipse is unable to import @PostConstruct or @PreDestroy. This happens because of Java 9 and higher. When using Java 9 and higher, javax.annotation has been removed from its default classpath. That's why Eclipse can't find it. Download the javax.annotation-api-1.3.2.jar from https://search.maven.org/remotecontent?filepath=javax/annotation/javax.annotation-api/1.3.2/javax.annotation-api-1.3.2.jar and build path. **** Do this -------------------- 1. Create the 10_Spring_Annotation_BeanLifeCycle and add the JARs, one annotation jar also add and create demo package 2. create applicationContext.xml 3. Create Coach.java interface package demo; public interface Coach { public String getDailyWorkout(); } 4. Create BadmintonCoach.java class package demo; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; @Component @Scope("singleton") public class BadmintonCoach implements Coach { public BadmintonCoach() { System.out.println(">> BadmintonCoach : Constructor is called"); } @Override public String getDailyWorkout() { return "In Badminton : Practice Badminton smash 2 hours a day"; } // define init method @PostConstruct public void doMyStartupStuff() { System.out.println(">> BadmintonCoach : Inside doMyStartupStuff()"); } // define destroy method @PreDestroy public void doMyCleanupStuff() { System.out.println(">> BadmintonCoach : Inside doMyCleanupStuff()"); } } 5. Create AnnotaionBeanLifeCycleDemoApp.java and run this one package demo; import org.springframework.context.support.ClassPathXmlApplicationContext; public class AnnotationBeanLifeCycleDemoApp { public static void main(String[] args) { // Read the spring config file ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); // Retrieve the bean Coach theCoach = context.getBean("badmintonCoach", Coach.class); System.out.println(theCoach.getDailyWorkout()); // Close the context context.close(); } } ---------------------------------------------------------------------------------------- Special Note about Destroy Lifecycle and Prototype Scope ------------------------------------------------------------ For "prototype" scoped beans, Spring does not call the @PreDestroy method. In contrast to the other scopes, Spring does not manage the complete lifecycle of a prototype bean: the container instantiates, configures, and otherwise assembles a prototype object, and hands it to the client, with no further record of that prototype instance. Thus, although initialization lifecycle callback methods are called on all objects regardless of scope, in the case of prototypes, configured destruction lifecycle callbacks are not called. The client code must clean up prototype-scoped objects and release expensive resources that the prototype bean(s) are holding. To get the Spring container to release resources held by prototype-scoped beans, try using a custom bean post-processor, which holds a reference to beans that need to be cleaned up. How can I create code to call the destroy method on prototype scope beans ? 1. Create a custom bean processor. This bean processor will keep track of prototype scoped beans. During shutdown it will call the destroy() method on the prototype scoped beans. 2. The prototype scoped beans MUST implement the DisposableBean interface. This interface defines a "destory()" method. This method should be used instead of the @PostDestroy annotation. 3. In this app, BeanProcessorDemoApp.java is the main program. TennisCoach.java is the prototype scoped bean. TennisCoach implements the DisposableBean interface and provides the destroy() method. The custom bean processing is handled in the MyCustomBeanProcessor class. Do this ------------ 1. Create 11_Spring_Annotation_BeanLifeCycle_PrototypeScope and add the JARs with annotation jar and create a demo package 2. applicationContext.xml 3. Coach.java interface package demo; public interface Coach { public String getDailyWorkout(); } 4. BadmintonCoach.java class package demo; import javax.annotation.PostConstruct; import org.springframework.beans.factory.DisposableBean; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; @Component @Scope("prototype") public class BadmintonCoach implements Coach, DisposableBean { @PostConstruct public void doMyStartupStuff() { System.out.println(">> BadmintonCoach : inside doMyStartupStuff()"); } @Override public String getDailyWorkout() { return "BadmintonCoach : Practice your backhand smash"; } @Override public void destroy() throws Exception { System.out.println(">> BadmintonCoach : inside destroy()"); } } 5. Create MyCustomBeanProcessor.java package demo; import java.util.LinkedList; import java.util.List; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.stereotype.Component; @Component public class MyCustomBeanProcessor implements BeanPostProcessor, BeanFactoryAware, DisposableBean { private BeanFactory beanFactory; private final List prototypeBeans = new LinkedList<>(); @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { // after start up, keep track of the prototype scoped beans. // we will need to know who they are for later destruction if (beanFactory.isPrototype(beanName)) { synchronized (prototypeBeans) { prototypeBeans.add(bean); } } return bean; } @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { this.beanFactory = beanFactory; } @Override public void destroy() throws Exception { // loop through the prototype beans and call the destroy() method on each one synchronized (prototypeBeans) { for (Object bean : prototypeBeans) { if (bean instanceof DisposableBean) { DisposableBean disposable = (DisposableBean)bean; try { disposable.destroy(); } catch (Exception e) { e.printStackTrace(); } } } prototypeBeans.clear(); } } } 6. BeanProcessorDemoApp.java package demo; import org.springframework.context.support.ClassPathXmlApplicationContext; public class BeanProcessorDemoApp { public static void main(String[] args) { // load spring config file ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); // retrieve bean from spring container Coach theCoach = context.getBean("badmintonCoach", Coach.class); Coach alphaCoach = context.getBean("badmintonCoach", Coach.class); System.out.println(theCoach.getDailyWorkout()); // close the context context.close(); } } ----------------------------------------------------------------------------------------- Spring Configuration without XML ========================================= - Instead of using xml we will use source code to configure the container 3 ways to configure a spring container 1. Full xml config 2. XML component scan or annotation like @Component 3. Java configuration class by using @Configuration, @ComponentScan("package_name") Development Process ---------------------------- 1. Create a java class and annotate it as @Configuraton 2. Add component scanning support : @ComponentScan (Optional) 3. Read Spring java configuration class 4. Retrieve bean from the container Do this ----------------- 1. Create 12_Spring_Config_WithoutXML and add the JARs and create a package demo 2. Coach.java interface created package demo; public interface Coach { public String getDailyWorkout(); } 3. BadmintonCoach.java class created package demo; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; @Component @Scope("singleton") public class BadmintonCoach implements Coach { public BadmintonCoach() { System.out.println(">> BadmintonCoach : Constructor is called"); } @Override public String getDailyWorkout() { return "In Badminton : Practice Badminton smash 2 hours a day"; } } 4. ApplicationConfig.java class created package demo; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan("demo") public class ApplicationConfig { } 5. ApplicationDemoApp.java class created package demo; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class ApplicationDemoApp { public static void main(String[] args) { // read the spring config class AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class); // retrieve the bean from spring container Coach theCoach = context.getBean("badmintonCoach", Coach.class); // call the methods on the bean System.out.println(theCoach.getDailyWorkout()); // close the context context.close(); } } ------------------------------------------------------------------------------------ Defining Beans in Spring using annotation / Spring Depedency Injection without XML ============================================================================================ Development Process ------------------------ 1. Define methods to expose the bean 2. Inject the bean dependencies 3. Read the Spring Java configuration class 4. Retrieve bean from the container 1. Define methods to expose the bean - No component scan, Each bean will be defined within this configuration class - In the configuration class @Configuration public class ApplicationConfig { @Bean public Coach badmintonCoach() { // method name will be the bean id BadmintonCoach theBadmintonCoach = new BadmintonCoach(); return theBadmintonCoach; } } - Method name will be the bean id that will be registered by the spring container 2. Inject the bean dependencies - for depedencies create a method for depedency class(HappyFortuneService) with @Bean annotation and return new instance of HappyFortuneService (return new HappyFortuneService();) - pass the depedency bean id to the Bean @Configuration public class ApplicationConfig { @Bean public FortuneService happyFortuneService() { return new HappyFortuneService(); } @Bean public Coach badmintonCoach() { // method name will be the bean id BadmintonCoach theBadmintonCoach = new BadmintonCoach(happyFortuneService()); return theBadmintonCoach; } } 3. Read the Spring Java configuration class AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class); 4. Retrieve bean from the container Coach theCoach = context.getBean("badmintonCoach", Coach.class); Do this ------------------- 1. Create 13_Spring_WithoutXML_DefineBean and add the JARs and create demo package 2. FortuneService.java interface package demo; public interface FortuneService { public String getFortune(); } 3. SadFortuneService.java class package demo; public class SadFortuneService implements FortuneService { @Override public String getFortune() { return "Today is such a bad day"; } } 4. Coach.java interface package demo; public interface Coach { public String getDailyWorkout(); public String getDailyFortune(); } 5. BadmintonCoach.java class package demo; public class BadmintonCoach implements Coach { private FortuneService fortuneService; public BadmintonCoach(FortuneService fortuneService) { this.fortuneService = fortuneService; } @Override public String getDailyWorkout() { return "In Badminton : Practice Badminton smash 2 hours a day"; } @Override public String getDailyFortune() { return "In BadmintonCoach : "+fortuneService.getFortune(); } } 6. ApplicationConfig.java class package demo; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class ApplicationConfig { // define bean for SadFortuneService @Bean public FortuneService sadFortuneService() //method name is the bean_id { return new SadFortuneService(); } // define bean for BadmintonCoach and inject dependency @Bean public Coach badmintonCoach() { // BadmintonCoach theBadmintonCoach = new BadmintonCoach(sadFortuneService()); // return theBadmintonCoach; // OR return new BadmintonCoach(sadFortuneService()); } } 7. ApplicationDemoApp.java class package demo; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class ApplicationDemoApp { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class); Coach theCoach = context.getBean("badmintonCoach", Coach.class); System.out.println(theCoach.getDailyWorkout()); System.out.println(theCoach.getDailyFortune()); context.close(); } } ---------------------------------------------------------------------------------------------- NOTE : During All Java Configuration, how does the @Bean annotation work in the background? For this code: @Bean public Coach badmintonCoach() { BadmintonCoach myBadmintonCoach = new BadmintonCoach(); return myBadmintonCoach; } At a high-level, Spring creates a bean component manually. By default the scope is singleton. So any request for a "badmintonCoach" bean, will get the same instance of the bean since singleton is the default scope. @Bean The @Bean annotation tells Spring that we are creating a bean component manually. We didn't specify a scope so the default scope is singleton. public Coach badmintonCoach(){ This specifies that the bean will bean id of "badmintonCoach". The method name determines the bean id. The return type is the Coach interface. This is useful for dependency injection. This can help Spring find any dependencies that implement the Coach interface. The @Bean annotation will intercept any requests for "badmintonCoach" bean. Since we didn't specify a scope, the bean scope is singleton. As a result, it will give the same instance of the bean for any requests. BadmintonCoach myBadmintonCoach = new BadmintonCoach(); This code will create a new instance of the BadmintonCoach. return myBadmintonCoach; This code returns an instance of the badmintonCoach. - It is important to note that this method has the @Bean annotation. The annotation will intercept ALL calls to the method "badmintonCoach()". Since no scope is specified the @Bean annotation uses singleton scope. Behind the scenes, during the @Bean interception, it will check in memory of the Spring container (applicationContext) and see if this given bean has already been created. - If this is the first time the bean has been created then it will execute the method as normal. It will also register the bean in the application context. So that is knows that the bean has already been created before. Effectively setting a flag. - The next time this method is called, the @Bean annotation will check in memory of the Spring container (applicationContext) and see if this given bean has already been created. Since the bean has already been created (previous paragraph) then it will immediately return the instance from memory. It will not execute the code inside of the method. Hence this is a singleton bean. BadmintonCoach myBadmintonCoach = new BadmintonCoach(); return myBadmintonCoach; is not executed for subsequent requests to the method public Coach badmintonCoach() . This code is only executed once during the initial bean creation since it is singleton scope. That explains how @Bean annotation works for the badmintonCoach example. return new BadmintonCoach(sadFortuneService()); The code for this question is slightly different. It is injecting a dependency. // define bean for our sad fortune service @Bean public FortuneService sadFortuneService() { return new SadFortuneService(); } // define bean for our swim coach AND inject dependency @Bean public Coach badmintonCoach() { BadmintonCoach myBadmintonCoach = new BadmintonCoach(sadFortuneService()); return myBadmintonCoach; } @Bean public FortuneService sadFortuneService() { return new SadFortuneService(); } In the code above, we define a bean for the sad fortune service. Since the bean scope is not specified, it defaults to singleton. Any calls for sadFortuneService, the @Bean annotation intercepts the call and checks to see if an instance has been created. First time through, no instance is created so the code executes as desired. For subsequent calls, the singleton has been created so @Bean will immediately return with the singleton instance. return new BadmintonCoach(sadFortuneService()) This code creates an instance of BadmintonCoach. Note the call to the method sadFortuneService(). We are calling the annotated method above. The @Bean will intercept and return a singleton instance of sadFortuneService. The sadFortuneService is then injected into the swim coach instance. ------------------------------------------------------------------------------------------------- Injecting Value from Properties File ============================================ Development Process --------------------- 1. Create Propetries file 2. Load properties file in Spring config : @PropertySource("classpath:file_name") 3. Reference the values from properties file Do this ------------------ 1. 14_Spring_WithoutXML_PropertiesFile Created and build path 2. sport.properties created foo.name=Debi Prasad Mishra foo.email=debiprasadmishra50@gmail.com #Refer the BadmintonCoach.java 3. ApplicationConfig.java class created package demo; //import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; @Configuration @PropertySource("classpath:sport.properties") @ComponentScan("demo") public class ApplicationConfig { //Either define the bean explicitely or use @ComponentScan // @Bean // public BadmintonCoach badmintonCoach() { // return new BadmintonCoach(); // } } 4. BadmintonCoach.java class created package demo; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @Component public class BadmintonCoach { @Value("${foo.name}") private String name; @Value("${foo.email}") private String email; public String getName() { return name; } public String getEmail() { return email; } } 5. ApplicationDemoApp.java class created and run this package demo; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class ApplicationDemoApp { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class); BadmintonCoach theBadmintonCoach = context.getBean("badmintonCoach", BadmintonCoach.class); System.out.println(theBadmintonCoach.getName()); System.out.println(theBadmintonCoach.getEmail()); context.close(); } } ---------------------------------------------------------------------------------------- Spring MVC (Model View Controller) =========================================== - It is a framework for building application in java - It leverages the features of core spring framework (like IoC, Depedency Injection) Web -------> Front Controller (Request from browser) Browser | ^ | (Model) | v | Controller (Business Logic) | | | | (Model) | v ---------------------- View Template (html/jsp page) Benefits of Spring MVC ------------------------------ - The Spring way of building web app UIs in Java - Leverage a set of reusable UI components - Help manage application state for web requests - Process from data : validation, conversion ...etc - Flexible configuration for view layer (Not only JSP, but also Thymeleaf, Velocity, Freemaker) www.luv2code.com/spring-mvc-docs Behind the Scenes ---------------------- Components of a spring application ----------------------------------------- 1. A set of web pages to layout UI components 2. A collection of Spring beans (controllers, service, ..etc) 3. Spring configuration (XML, Annotation, Java source code) Front Controller ----------------------- - It is known as DispatcherServlet It is a part of Spring Framework Already developed by Spring Development Team - You will create Model Object (contain data) View Template (JSP page) Controller Classes (service/business logic/processing logic) Controller ----------------------- - Code created by developer - when the front controller has a request it delegates the request to Controller - Contains your busines logic - Handle the request - Store/receive data - place data in model - Send to appropriate view template Model ---------------- - Contains your data - Store/receieve data via backend service DB, web service ...etc Use Spring Bean - Place your data in model Data can be java object/collection View Template ------------------ - Spring MVC is flexible - It supports many view templates - Most common is JSP + JSTL (JSP Standard Tag Library) - Developer creates page and displayes data - Other supported templates are Thymeleaf, Groovy, Velocity, Freemarker ..etc www.luv2code.com/spring-mvc-views Spring MVC Configuration ---------------------------- 1. Configure Spring MVC Dispatcher Servlet 2. Set up url mapping to Spring MVC Dispatcher Servlet 3. Add support for component scanning 4. Add support for conversion, formatting and validation 5. Configure Spring MVC view resolver 1. Configure Spring MVC Dispatcher Servlet 2. Set up url mapping to Spring MVC Dispatcher Servlet ------------------------------------------------ web.xml in WEB-INF folder spring-mvc-demo dispatcher org.springframework.web.servlet.DispatcherServlet contextConfigLocation /WEB-INF/spring-mvc-demo-servlet.xml 1 dispatcher / *** The url pattern / suggests that all the requests will be handled by DispatcherServlet. 3. Add support for component scanning --------------------------------------------- 4. Add support for conversion, formatting and validation -------------------------------------------------------------- 5. Configure Spring MVC view resolver ---------------------------------------------- - When app provides a view name, Spring will automatically look for files to actually render the view for application, then prepend the prefix and append the suffix. - e.g, if we return view name "studentList", spring will prepend /WEB-INF/view/studentList.jsp (append) ***** NOTE : How to configure the Spring Dispatcher Servlet using all Java Code (no xml) ? ===================================================================================== 1. Delete the files: web.xml file and spring-mvc-demo-servlet.xml files 2. Create a new Java package : demo.config 3. Create 2 java classes to the config package DemoAppConfig.java and MySpringMvcDispatcherServletInitializer.java MySpringMvcDispatcherServletInitializer.java -------------------------------------------------- package demo.config; import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; public class MySpringMvcDispatcherServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class[] getRootConfigClasses() { // TODO Auto-generated method stub return null; } @Override protected Class[] getServletConfigClasses() { return new Class[] { DemoAppConfig.class }; } @Override protected String[] getServletMappings() { return new String[] { "/" }; } } DemoAppConfig.java ------------------------- package demo.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.ViewResolver; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.view.InternalResourceViewResolver; @Configuration @EnableWebMvc @ComponentScan(basePackages="demo") public class DemoAppConfig { // define a bean for ViewResolver @Bean public ViewResolver viewResolver() { InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); viewResolver.setPrefix("/WEB-INF/view/"); viewResolver.setSuffix(".jsp"); return viewResolver; } } ***** ---------------------------------------------------------------------------------------------- Development Process ---------------------------- 1. Create the controller class (Add @Controller annotation to controller class) @Controller inherits from @Component 2. Define controller method 3. Add request mapping to the controller method (Add @RequestMapping("/") to the method) 4. Return view name 5. Develop view page @Controller public class HomeController { @RequestMapping("/") public String showMyPage() { return "main-menu"; } } Develop main-menu.jsp page in /WEB-INF/view folder Do This -------------------- 1. Create 15_Spring_MVC_Demo Dynamic Web Project 2. Add all the JAR files that you have to the lib folder in WEB-INF 3. Create a view folder in WEB-INF folder 4. Add starter xml files to WEB-INF folder web.xml ----------------- spring-mvc-demo dispatcher org.springframework.web.servlet.DispatcherServlet contextConfigLocation /WEB-INF/spring-mvc-demo-servlet.xml 1 dispatcher / spring-mvc-demo-servlet.xml ------------------------------------ 5. change the package_name to demo in the xml file component-scan 6. Create a package demo.mvc in src folder and create the controller class there HomeController.java ------------------------- package demo.mvc; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class HomeController { @RequestMapping("/") public String showMyWelcomePage() { return "index"; } } 7. In WEB-INF/view folder create the index.jsp file index.jsp ---------------- <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> Spring MVC Demo Program

Hello! Welcome to Spring MVC First Demo Program

8. Run the project Things to remember --------------------------- 1. Do not run any file in the project, it will give 404 error. Directly click on project, Run on Server 2. All the jsp files will be created at workspace_naem/Project_name/.../.metadata\.plugins\org.eclipse.wst.server.core\tmp0\work\Catalina\localhost\.. In problem/ not running state, debug like this -------------------------------------------------- 1. Make Sure your Package Names is consistent 2. package demo.mvc; Make sure your controller is this package. 3. Reimport Project into Eclipse 4. Check the versions of JDK and Dynamic web module 5. Clear Tomcat Cache 6. Import the Project in a new workspace How Does Component Scan Work with DIfferent package names -------------------------------------------------------------- component-scan : demo Controller is in the package demo.mvc For the Spring attribute: base-package="demo" Spring will recursively scan for components starting at the base package: "demo" When I say "recursive", it means that Spring will start at the base package and scan all sub packages. The package demo.mvc is a sub package because of naming structure, just like folders on a file system. As a result, it will be included in the scan. --------------------------------------------------------------------- Reading HTML From Data with Spring MVC ==================================================== Development Process ------------------------- 1. Create the controller class 2. Show HTML form - Create a controller method to show html form - Create View Page for HTML form 3. Process HTML form - Create a controller method to process HTMl form - Develop view page for Confirmation Do this ------------------- 1. 16_Spring_MVC_ReadFormData Created and all the JARs and XML files 2. Create a package demo.mvc in src folder 3. Create a HelloWorldController.java Controller class package demo.mvc; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class HelloWorldController { // need a controller method t show the initial form @RequestMapping("/") public String index() { return "index"; } @RequestMapping("/showForm") public String showForm() { return "showform"; } // need a controller method to process the HTML form @RequestMapping("/processForm") public String processForm() { return "helloworld"; } } 4. In view create index.jsp <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> Spring MVC Read Form Data

Hello! Welcome to Spring MVC Read Form Data Program



Click Here For ShowForm 5. Create showform.jsp <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> Spring MVC Read Form Data


6. Create helloworld.jsp <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> Spring MVC Read Form Data

Hello World of Spring, Read Data is below



Student Name : ${param.studentName} ------------------------------------------------------------------------- Adding and Reading Data from Model =============================================== - Spring Model is just a container for your application data - In your Controller - You can put anything in the model - Strings, Objects, info from database ...etc - Your view page (JSP) can access data from model. Do this ---------------- 1. 17_Spring_MVC_HandleFormData created and JARs and XMLs were added, package demo.mvc, view folder were created 2. ApplicationController.java class was created package demo.mvc; import javax.servlet.http.HttpServletRequest; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class ApplicationController { @RequestMapping("/") public String index() { return "index"; } @RequestMapping("showForm") public String showForm() { return "showform"; } // add new controller method to read form data // add data to model @RequestMapping("/processFormModel") public String capitalize(HttpServletRequest request, Model model) { // read the request parameter from HTML form String theName = request.getParameter("studentName"); // convert data to capital letters theName = theName.toUpperCase(); // Create the message String result = "Hello "+theName; // add message to the model model.addAttribute("message", result); return "helloworld"; } } 3. index.jsp <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> Add and Manage Model

Hello! Welcome to Spring Model Application



Click Here For ShowForm 4. showform.jsp <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> Add and Manage Model

Hello! Welcome to Spring Model Application





5. helloworld.jsp <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> Add and Manage Model

Hello! Welcome to Spring Model Application



Read Data From Form


Student Name : ${param.studentName}

Read Data From Model


Student Name : ${message} 6. Run the project --------------------------------------------------------------------- **** Note : How to use CSS, JavaScript and Images in Spring MVC Web App ? Answer : 1. Create this folder structure WebContent META-INF resources css test.css images spring-logo.png js test.js WEB-INF 2. Add these lines to spring MVC config file i.e. spring-mvc-demo-servlet.xml 3. In view pages, access static files using this syntax You need to use the JSP expression ${pageContext.request.contextPath} to access the correct root directory for your web application. ----------------------------------------------------------------------------------------------------- Deploying To Tomcat using WAR files ----------------------------------------- If you are using Eclipse, then the best way to visualize it is think of your "WebContent" directory being compressed as a zip file with the .war extension. This includes all of your web pages, images, css etc. It also includes the WEB-INF directory which includes your classes in WEB-INF/classes and supporting JAR files in WEB-INF/lib. The WAR file format is part of the Java EE / Servlet specification. As a result, all Java EE servers support this format (ie jboss, weblogic, websphere, glassfish and tomcat). Do this ------------------ 1. In Eclipse, stop Tomcat 2. Right-click your project and select Export > WAR File 3. In the Destination field, enter: /mycoolapp.war 4. Outside of Eclipse, start Tomcat - If you are using MS Windows, then you should find it on the Start menu 5. Make sure Tomcat is up and running by visiting: http://localhost:8080 6. Deploy your new WAR file by copying it to \webapps Give it about 10-15 seconds to make the deployment. You'll know the deployment is over because you'll see a new folder created in webapps ... with your WAR file name. 7. Visit your new app. If your war file was: mycoolapp.war then you can access it with: http://localhost:8080/mycoolapp/ ------------------------------------------------------------------------------------------------------------ Binding Request Params in Spring/ Read Form data using Annotation ============================================================================= @RequestMapping("/url_pattern") public String method_name(@RequestParam("name_on_form") datatype variable_name, Model model) { // now we can use the variable_name and do our own customization } - In here, there is no requirement of HttpServletRequest request object and getParameter() method. Do this -------------------- 1. Copy and paste and rename 17_Spring_MVC_HandleFormData to 18_Spring_MVC_HandleFormDataAnnotation 2. ApplicationController.java package demo.mvc; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @Controller public class ApplicationController { @RequestMapping("/") public String index() { return "index"; } @RequestMapping("showForm") public String showForm() { return "showform"; } // add new controller method to read form data // add data to model @RequestMapping("/processFormModelAnnotation") public String capitalize(@RequestParam("studentName") String theName, Model model) { // convert data to capital letters theName = theName.toUpperCase(); // Create the message String result = "Hello "+theName; // add message to the model model.addAttribute("message", result); return "helloworld"; } } 3. In showform.jsp change the action of form to processFormModelAnnotation 4. Run the project --------------------------------------------------------------------------------------------- Controller-Level RequestMapping ============================================= - We can define a request mapping at the controller level. - It serves as a parent mapping for the controller - All the request mappings (RequestMapping) on methods in the Controller are relative @RequestMapping("/funny") // Parent Mapping public class FunnyController { @RequestMapping("/showForm") //Method Mapping/relative mapping, it will behave as /funny/showform public String showForm() { return ...; } @RequestMapping("/procesForm") //it will behave as /funny/processForm public String process(HttpServletRequest request, Model model) { return ...; } } Conflicts in request paths/ request mapping ---------------------------------------------------- - 2 controller clases in the same package - It gives a Ambiguous mapping error, IllegalStateException - To overcome this situation, ControllerLevel RequestMapping is used - Now after adding @RequestMapping("/...") to the class, you need to specify the changes in the jsp pages Do this ----------------- 1. 19_Spring_MVC_ControllerLevelRequestMapping created by copy paste and rename 18_Spring_MVC_HandleFormDataAnnotation 2. Create a DemoController.java package demo.mvc; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class DemoController { @RequestMapping("/") public String index() { return "index"; } @RequestMapping("/showForm") public String showForm() { return "showform"; } } ***** Here it will show the Ambiguous mapping error 3. ApplicationController.java package demo.mvc; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @Controller @RequestMapping("/applicationController") public class ApplicationController { @RequestMapping("/") public String index() { return "index"; } @RequestMapping("/showForm") // it will behave as /applicationController/showForm public String showForm() { return "showform"; } // add new controller method to read form data // add data to model @RequestMapping("/processFormModelAnnotation") // it will behave as /applicationController/processFormModelAnnotation public String capitalize(@RequestParam("studentName") String theName, Model model) { // convert data to capital letters theName = theName.toUpperCase(); // Create the message String result = "Hello "+theName; // add message to the model model.addAttribute("message", result); return "helloworld"; } } 4. index.jsp <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> Request Mapping

Hello! Welcome to Spring Request Mapping



Click Here For ShowForm 5. Rest showform.jsp and helloworld.jsp are same or you can change in them too **** Note : You can not call the index.jsp from the ApplicationController.java class, you need to call this from another class **** 6. Run the project -------------------------------------------------------------------------------------------- Spring MVC Form tags ============================== - Spring MVC Form tags are the building blocks for a web page - Form tags are configurable and reusable in a web page - Spring MVC form tags can make use of data binding - This allows, Automatically set data, retrieving data from a java object, bean ...etc Form Tag Description ------------ ------------------ form:form main form container form:input text-field form:textarea multi-line text area form:checkbox check box form:radiobutton radio buttons form:select drop down list ...etc www.luv2code.com/spring-mvc-form-tags How to reference Spring MVC Form Tags At beginning of your jsp file add this reference <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> - In Spring Controller class , Before you show the form, you must add a Model Attribute - This is a Bean that will hold form data for data binding. @RequestMapping("/showform") public String showForm(Model model) { model.addAttribute(attribute_name/message(student), value (new Student())); return showform; } - form will use the attribute_name for data binding First Name :

Last Name :

- path=firstName/lastName - this binds this form field to a property on the Bean - When the form is loaded, Spring MVC will populate the form field It will call student.getFirstName() method It is in the format of attribute_name . get path_name(), path_name is capitalized student : attribute_name getFirstName() : firstName is path name - When form is submitted, Spring MVC will call the setter methods It will call student.setFirstName() method student : attribute_name setFirstName() : firstName is path name @RequestMapping("/processForm") public String processForm(@ModelAttribute(attribute_name(student) Student theStudent)) { //log the input data System.out.println("TheStudent "+theStudent.getFirstName()); return studentconfirmation; } @ModelAttribute : Annotation to retrieve the model data like @RequestParam theStudent : parameter, Spring binds the data to this variable - In confirmation jsp page use the ${} to read the data ${student.firstName} ${attribute_name.path_name} Development Process ------------------------ 1. Create a Student Class 2. Create a StudentController Class 3. Create a HTML form 4. Create the Processing Code 5. Create the confirmation page Do this --------------------- 1. Copy paste and rename 19_Spring_MVC_ControllerLevelRequestMapping to 20_Spring_MVC_FormTags 2. Create Student.java POJO class package demo.mvc; public class Student { private String firstName; private String lastName; public Student() { } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } } 3. Create a StudentController.java class package demo.mvc; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class StudentController { @RequestMapping("/") public String index() { return "index"; } @RequestMapping("/showForm") public String showForm(Model model) { // Create Student Object Student theStudent = new Student(); // add object to model model.addAttribute("student", theStudent); return "showform"; } @RequestMapping("/processForm") public String processForm(@ModelAttribute("student") Student theStudent) { //log the input data System.out.println(theStudent.getFirstName()); System.out.println(theStudent.getLastName()); return "studentconfirmation"; } } 4. index.jsp page <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> Spring MVC Form Tags

Hello! Welcome to Spring MVC Form Tags App



Click Here For ShowForm 5. showform.jsp apge <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> Spring MVC Form Tags

Hello! Welcome to Spring MVC Form Tags App



First Name :
Last Name :
6. studentconfirmation.jsp page created and helloworld.jsp page deleted <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> Spring MVC Form Tags

Hello! Welcome to Spring MVC Form Tags App



Student Confirmation : ${student.firstName} ${student.lastName} 7. Run the project --------------------------------------------------------------------------------------------------- Drop down lists ======================== - In Spring MVC Drop-Down list is represented by //path = property for data binding //label is what you see on the screen // value is the actual code that you will pass over Development Process ------------------------- 1. Update HTML 2. Update Student class - add getter and setter for new property 3. Update Confirmation page Do this ------------------- 1. In 20_Spring_MVC_FormTags, in showform.jsp page do this <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> Spring MVC Form Tags

Hello! Welcome to Spring MVC Form Tags App



First Name :

Last Name :

Country :
2. In Student.java do this package demo.mvc; import java.util.LinkedHashMap; public class Student { private String firstName; private String lastName; private String country; // Instead of Hard coding, if you want to read the country options from Java class, do this // It can read data from a list/database/web service/properties file etc.. private LinkedHashMap countryOptions; public Student() { // populate country options : used ISO country code countryOptions = new LinkedHashMap(); countryOptions.put("Null", "Country"); countryOptions.put("IND", "India"); countryOptions.put("FRN", "France"); countryOptions.put("GER", "Germany"); countryOptions.put("RUS", "Russia"); } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getCountry() { return country; } public void setCountry(String country) { this.country = country; } public LinkedHashMap getCountryOptions() { return countryOptions; } } 3. In studentconfirmation.jsp page do this <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> Spring MVC Form Tags

Hello! Welcome to Spring MVC Form Tags App



Student Confirmation : ${student.firstName} ${student.lastName}

Country : ${student.country}

4. Run the project ----------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------- How to Use properties file to load country options ----------------------------------------------------- 1. Create a properties file to hold the countries. It will be a name value pair. Country code is name. Country name is the value. Place it in WEB-INF folder WEB-INF/countries.properties BR=Brazil FR=France CO=Colombia IN=India 2. Update header section for Spring config file We are going to use a new set of Spring tags for . As a result, you need to update the header information in the Spring config file. In spring-mvc-dmo-servlet.xml file header part write this 3. Load the country options properties file in the Spring config file. Bean id: countryOptions spring-mvc-dmo-servlet.xml 4. In StudentController.java, add the following import statement: import java.util.Map; 5. Inject the properties values into your Spring Controller: StudentController.java @Value("#{countryOptions}") private Map countryOptions; 6. Add the country options to the Spring MVC model. Attribute name: theCountryOptions @RequestMapping("/showForm") public String showForm(Model theModel) { // create a student object Student Student theStudent = new Student(); // add student object to the model theModel.addAttribute("student", theStudent); // add the country options to the model theModel.addAttribute("theCountryOptions", countryOptions); return "showform"; } 7. Update the JSP page, showform.jsp, to use the new model attribute for the drop-down list: theCountryOptions 8. Remove all references to country option from your Student.java. like getter and setter methods, remove it. 9. Run the project ---------------------------------------------------------------------------------------------------------------- Radio Buttons in Spring MVC ======================================== -In Spring MVC it is represented by JAVA C# Spring favouriteLanguage : property that we are binding this radio button to - During submission it will call student.setFavouriteLanguage() method, setter method handles the submission. Do this ----------------- 1. In 20_Spring_MVC_FormTags in showform do this Favourite Language : Java C# PHP .NET

2. In Student.java class create binding parameter and generate getter and setters private String favouriteLanguage; public String getFavouriteLanguage() { return favouriteLanguage; } public void setFavouriteLanguage(String favouriteLanguage) { this.favouriteLanguage = favouriteLanguage; } 3. In studentconfirmation.jsp page write this Favourite Language : ${student.favouriteLanguage}

4. Run the Project --------------------------------------------------------------------------------------------- Instead of hardcoding if you wanna read the radiobutton options from java class then do this -------------------------------------------------------------------------------------------------------- 1. Set up the data in your Student class Add a new field private LinkedHashMap favouriteLanguageOptions; In your constructor, populate the data // populate favorite language options favouriteLanguageOptions = new LinkedHashMap<>(); favouriteLanguageOptions.put("Java", "Java"); favouriteLanguageOptions.put("C#", "C#"); favouriteLanguageOptions.put("PHP", "PHP"); favouriteLanguageOptions.put("Ruby", "Ruby"); 2. Add getter method public LinkedHashMap getFavouriteLanguageOptions() { return favouriteLanguageOptions; } 3. Reference the data in your form Favorite Language: ------------------------------------------------------------------------------------------------------------------- CheckBoxes with Spring MVC ===================================== - It allows you to get multiple items for a single work. - It is represented by Linux Mac OS MS Windows operatingSystem : binding parameter - In java class you will have a collection as multiple value(s) might get selected Do this --------------- 1. 20_Spring_MVC_FormTags 2. index.jsp <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> Spring MVC Form Tags

Hello! Welcome to Spring MVC Form Tags App



Click Here For ShowForm 3. showform.jsp <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> Spring MVC Form Tags

Hello! Welcome to Spring MVC Form Tags App



First Name :

Last Name :

Country :

Favourite Language :

Operating Systems : Linux Mac OS MS Windows

4. StudentController.java package demo.mvc; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class StudentController { @RequestMapping("/") public String index() { return "index"; } @RequestMapping("/showForm") public String showForm(Model model) { // Create Student Object Student theStudent = new Student(); // add object to model model.addAttribute("student", theStudent); return "showform"; } @RequestMapping("/processForm") public String processForm(@ModelAttribute("student") Student theStudent) { //log the input data System.out.println("Name : "+theStudent.getFirstName()+" "+theStudent.getLastName()); System.out.println(theStudent.getCountry()); System.out.println(theStudent.getFavouriteLanguage()); return "studentconfirmation"; } } 5. Student.java package demo.mvc; import java.util.LinkedHashMap; public class Student { private String firstName; private String lastName; private String country; private String favouriteLanguage; private String[] operatingSystem; // Instead of Hard coding, if you want to read the country options from Java class, do this // It can read data from a list/database/web service/properties file etc.. private LinkedHashMap countryOptions; // Instead of hard-coding if you wanna read the radiobutton options from java class then do this private LinkedHashMap favouriteLanguageOptions; public Student() { // populate country options : used ISO country code countryOptions = new LinkedHashMap(); countryOptions.put("Null", "Country"); countryOptions.put("IND", "India"); countryOptions.put("FRN", "France"); countryOptions.put("GER", "Germany"); countryOptions.put("RUS", "Russia"); // populate favorite language options favouriteLanguageOptions = new LinkedHashMap<>(); favouriteLanguageOptions.put("Java", "Java"); favouriteLanguageOptions.put("C#", "C#"); favouriteLanguageOptions.put("PHP", "PHP"); favouriteLanguageOptions.put("Ruby", "Ruby"); } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getCountry() { return country; } public void setCountry(String country) { this.country = country; } public LinkedHashMap getCountryOptions() { return countryOptions; } public String getFavouriteLanguage() { return favouriteLanguage; } public void setFavouriteLanguage(String favouriteLanguage) { this.favouriteLanguage = favouriteLanguage; } public LinkedHashMap getFavouriteLanguageOptions() { return favouriteLanguageOptions; } public String[] getOperatingSystem() { return operatingSystem; } public void setOperatingSystem(String[] operatingSystem) { this.operatingSystem = operatingSystem; } } 6. studentconfirmation.jsp <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> Spring MVC Form Tags

Hello! Welcome to Spring MVC Form Tags App



Student Confirmation : ${student.firstName} ${student.lastName}

Country : ${student.country}

Favourite Language : ${student.favouriteLanguage}

Operating Systems :
  • ${temp}
7. Run the project --------------------------------------------------------------------------------------------------- Spring Form Validation =================================== - Need for validation - validation for required fields - valid numbers in a give range - valid format (postal code) - Own custom business validation - We have Java Standard Bean Validation API - It defines a metadata model and API for entiry validation - It is available for both server side and client side applications (using like JavaFx/Swing Apps) http://www.beanvalidation.org Spring and Validation ---------------------------------- - Spring version 4 and higher supports Bean Validation API - Its actually preferred method for validation when building Spring apps - To use it, simply add Validation JAR to our project, and use the appropriate annotations Bean validation features -------------------------- - You can check if a field is required or not - You can validate a length - You can validate numbers - validate with regular expression - custom validation ...etc Validation Annotations --------------------------------- @NotNull : Checks that annotated value is not null @Min : Must be a number >= value @Max : Must be a number <= value @Size : Size must match a given size @Pattern : Must match a regular expression pattern @Future/@Past : Date must be in future/past of given date ...etc - Java standard bean validation API (JSR-303/309) - Only a specification..vendor independent..portable - But we still need an implementation - We need something that implements the specification - This is where Hibernate comes to play Hibernate ------------------- - It started as an ORM(Object Relational Mapping) project - In recent years thety have expanded it to other areas - They have a fully compliant JSR-303/309 implementation - Not tied to ORM or database work... it is a separate project just for doing validation http://www.hibernate.org/validator - Download Validation JAR files from Hibernate Website - Add those JAR files to your project for maven org.hibernate.validator hibernate-validator 6.1.2.Final - Jar files are in D:\Spring & Hibernate\Spring JARs\JARs\hibernate-validator-6.1.2.Final\dist\lib\required D:\Spring & Hibernate\Spring JARs\JARs\hibernate-validator-6.1.2.Final\dist directory Validation for Required Fields ----------------------------------------- - In many cases, last name/ email is required field, else are optional. In that cases we use this validation Development Process --------------------------- 1. Add validatiion rule to Customer class 2. Display error message in HTML form 3. Perform Validation in controller class 4. Update Confirmation page 1. Add validation rule to Customer class import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; public Class Customer { private String firstName; @NotNull(message="is required") @Size(min=1, message="is required") // min=1 : minimum 1 character is required private String lastName; //getter and setter methods } *** Make sure the imports are from these only 2. Update in showform.jsp Last Name (*) : : displays the error message cssClass : class in CSS file, or define the css class in header section, basic HTML stuff 3. Perform Validation in controller class import javax.validation.Valid; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.ModelAttribute; @RequestMapping("/processForm") public String processForm(@Valid @ModelAttribute("customer") Customer theCustomer, BindingResult theBindingResult){ if (theBindingResult.hasErrors()) return "showform"; else return "customerconfirmation"; } @Valid : Perform Validation rules on Customer Object BindingResult : Result of that validation will be placed in BindingResult *** Check the imports 4. Update Confirmation page Customer is : ${customer.firstName} ${customer.lastName} - When performing Spring MVC validation, the location of the BindingResult parameter is very important. In the method signature, the BindingResult parameter must appear immediately after the model attribute. - If you place it in any other location, Spring MVC validation will not work as desired. In fact, your validation rules will be ignored. ***** Defining @RequestMapping methods @RequestMapping handler methods have a flexible signature and can choose from a range of supported controller method arguments and return values. ***** Catch with @NotNull and @Valid : It will pass the whitespace(e.g, space) if it is entered. ***** Do this ------------------ 1. 21_Spring_MVC_ValidationAPI Created, validation JARs added, to lib folder 2. Customer.java created package demo.mvc; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; public class Customer { private String firstName; @NotNull(message="is required") @Size(min=2, message="is required") private String lastName; public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } } 3. index.jsp created <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> Spring MVC Validation

Hello! Welcome to Spring MVC Validation API App



Click Here For CustomerForm 4. customerform.jsp created <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> Spring MVC Validation

Hello! Welcome to Spring MVC Validation API App



(*) fields are required fields

First Name :

Last Name (*) :

5. CustomerController.java class created package demo.mvc; import javax.validation.Valid; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class CustomerController { @RequestMapping("/") public String index() { return "index"; } @RequestMapping("/customerform") public String showform(Model model) { model.addAttribute("customer", new Customer()); return "customerform"; } @RequestMapping("/processForm") public String processForm(@Valid @ModelAttribute("customer") Customer theCustomer, BindingResult theBindingResult) { System.out.println("Last Name : "+theCustomer.getLastName()); if(theBindingResult.hasErrors()) return "customerform"; else return "customerconfirmation"; } } 6. customerconfirmation.jsp page created <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> Spring MVC Validation

Hello! Welcome to Spring MVC Validation API App



Customer Name : ${customer.firstName} ${customer.lastName}

7. Run the project -------------------------------------------------------------- *** There is catch, in the required field, it will accept the whitespace like space which we do not want. we can overcome it by @InitBinder *** @InitBinder ========================= - it works as a pre-processor - It will pre-process every web request to our controller, This code will execute first - Method annotated with @InitBinder is executed first - We will use it to trim Strings, It will remove leading and trailing white spaces - If string only has white space, trim it to null @InitBinder public void initBinder(WebDataBinder dataBinder) { StringTrimmerEditor stringTrimmerEditor = new StringTrimmerEditor(true); dataBinder.registerCustomEditor(String.class, stringTrimmerEditor); } StringTrimmerEditor : class defined in Spring API, Its purpose to Trim Strings, It removes leading and Trailing WhiteSpaces new StringTrimmerEditor(true) : It will trim the string down to null, if it is entirely all whitespace dataBinder.registerCustomEditor(String.class, stringTrimmerEditor) : For every String class apply the StringTrimmerEditor - What it will do is - pre-process every String form data - Remove leading and trailing white spaces - If string only has white spaces, trim it to null Do this ------------- 1. In CustomerController.java write this package demo.mvc; import javax.validation.Valid; import org.springframework.beans.propertyeditors.StringTrimmerEditor; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.annotation.InitBinder; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class CustomerController { // The form will be passed for whitespace in the required field which should not happen, to resolve this issue // add an @InitBinder to convert trim input strings // remove leading and trailing whitespace // resolve issue for our validation @InitBinder public void initBinder(WebDataBinder dataBinder) { StringTrimmerEditor stringTrimmerEditor = new StringTrimmerEditor(true); dataBinder.registerCustomEditor(String.class, stringTrimmerEditor); } @RequestMapping("/") public String index() { return "index"; } @RequestMapping("/customerform") public String showform(Model model) { model.addAttribute("customer", new Customer()); return "customerform"; } @RequestMapping("/processForm") public String processForm(@Valid @ModelAttribute("customer") Customer theCustomer, BindingResult theBindingResult) { System.out.println("Last Name : |"+theCustomer.getLastName()+"|"); if(theBindingResult.hasErrors()) return "customerform"; else return "customerconfirmation"; } } 2. Run the project ------------------------------------------------------------------------------------------- Validate Number Range (@Min , @Max) ============================================= Development Process -------------------------- 1. Add validation rule to Customer class 2. Display error message in HTML page 3. Perform validation in the Controller class 4. Update Confirmation Page Do this --------------------- 1. In 21_Spring_MVC_ValidationAPI, In Customer.java write this (freePasses field added and getter and setters were generated) package demo.mvc; import javax.validation.constraints.Max; import javax.validation.constraints.Min; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; public class Customer { private String firstName; @NotNull(message="is required") @Size(min=2, message="is required") private String lastName; @Min(value=0, message="must be greater than or equal to 0") @Max(value=10, message="must be less than or equal to 10") private int freePasses; public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public int getFreePasses() { return freePasses; } public void setFreePasses(int freePasses) { this.freePasses = freePasses; } } 2. In customerform.jsp write this Free Passes :

3. In customerconfirmation.jsp page add this Free Passes : ${customer.freePasses}

4. Run the project --------------------------------------------------------------------------- Regular Expressions Validations =============================================== - Regular Expressions is a sequence of characters that define a search pattern - This pattern is used to find or match Strings - Regular Expression is like its own language, its advanced. http://docs.oracle.com/javase/tutorial/essential/regex/ - Apply regex for Pastal Code field in Customer Class Do this ------------------ 1. In 21_Spring_MVC_ValidationAPI, in Customer.java write this and generate getter and setter @Pattern(regexp="^[a-zA-Z0-9]{6}", message="Only 6 digits/chars") private String postalCode; public String getPostalCode() { return postalCode; } public void setPostalCode(String postalCode) { this.postalCode = postalCode; } 2. In customerform.jsp Postal Code :

3. In customerconfirmation.jsp Postal Code : ${customer.postalCode}

4. Run the Project ------------------------------------------------------------------------------------------------- Making an Integer Field as Required (*) ================================================== For this, do this changes in the Customer.java class 1. Add @NotNull annotation to the field 2. Change the datatype of the field from primitive type int to Wrapper type Integer private Integer freePasses; 3. Change the retyrn type in getter method from int to Integer 4. Change the parameter datatype in Setter method from int to Integer package demo.mvc; import javax.validation.constraints.Max; import javax.validation.constraints.Min; import javax.validation.constraints.NotNull; import javax.validation.constraints.Pattern; import javax.validation.constraints.Size; public class Customer { private String firstName; @NotNull(message="is required") @Size(min=2, message="is required") private String lastName; @NotNull(message="is required") @Min(value=0, message="must be greater than or equal to 0") @Max(value=10, message="must be less than or equal to 10") private Integer freePasses; @Pattern(regexp="^[a-zA-Z0-9]{6}", message="Only 6 digits/chars") private String postalCode; public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public Integer getFreePasses() { return freePasses; } public void setFreePasses(Integer freePasses) { this.freePasses = freePasses; } public String getPostalCode() { return postalCode; } public void setPostalCode(String postalCode) { this.postalCode = postalCode; } } ------------------------------------------------------------------------------------------------------ Handle the String input in Integer Field ================================================================================== ******** Here the catch is if we enter any String input in the Integer Field (freePasses), then it will show error(NumberFormatException). To resolve that we need to do this ******** 1. Create a custom error message in properties file placing it in src/resources/message.properties 2. Load the custom message resource in Spring config file spring-mvc-demo-servlet.xml Do this ------------------------ 1. Create a folder resources in src directory 2. Add a messages.properties file into that folder 3. In messages.properties file write this typeMismatch.customer.freePasses=Invalid Number It is in the format Error_Type.Spring_Model_Attribute_Name.Field_Name=Custom_Message_That_You_Wanna_Display 4. In spring-mvc-demo-servlet.xml file in bottom write this 5. Run the Project ---------------------------------------------------------------------------------------- typeMismatch.customer.freePasses=Invalid Number #When we give wrong input to freePasses, as it is Integer and we may give string input so spring says some messages through which we get to know these format #Last Name : |Mishra| #Binding Result : org.springframework.validation.BeanPropertyBindingResult: 1 errors #Field error in object 'customer' on field 'freePasses': rejected value [fdgfgaregr]; codes [typeMismatch.customer.freePasses,typeMismatch.freePasses,typeMismatch.java.lang.Integer,typeMismatch]; #This is what Spring returns in case of Input Mismatch # #To see this just print theBindingResult # System.out.println("Binding Result : "+theBindingResult); # System.out.println("\n\n\n\n"); ------------------------------------------------------------------------------------------------------- Final 21_Spring_MVC_ValidationAPI =============================================== Customer.java --------------------------- package demo.mvc; import javax.validation.constraints.Max; import javax.validation.constraints.Min; import javax.validation.constraints.NotNull; import javax.validation.constraints.Pattern; import javax.validation.constraints.Size; public class Customer { private String firstName; @NotNull(message="is required") @Size(min=2, message="is required") private String lastName; @NotNull(message="is required") @Min(value=0, message="must be greater than or equal to 0") @Max(value=10, message="must be less than or equal to 10") private Integer freePasses; @Pattern(regexp="^[a-zA-Z0-9]{6}", message="Only 6 digits/chars") private String postalCode; public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public Integer getFreePasses() { return freePasses; } public void setFreePasses(Integer freePasses) { this.freePasses = freePasses; } public String getPostalCode() { return postalCode; } public void setPostalCode(String postalCode) { this.postalCode = postalCode; } } CustomerController.java ------------------------------ package demo.mvc; import javax.validation.Valid; import org.springframework.beans.propertyeditors.StringTrimmerEditor; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.annotation.InitBinder; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class CustomerController { // The form will be passed for whitespace in the required field which should not happen, to resolve this issue // add an @InitBinder to convert trim input strings // remove leading and trailing whitespace // resolve issue for our validation @InitBinder public void initBinder(WebDataBinder dataBinder) { StringTrimmerEditor stringTrimmerEditor = new StringTrimmerEditor(true); dataBinder.registerCustomEditor(String.class, stringTrimmerEditor); } @RequestMapping("/") public String index() { return "index"; } @RequestMapping("/customerform") public String showform(Model model) { model.addAttribute("customer", new Customer()); return "customerform"; } @RequestMapping("/processForm") public String processForm(@Valid @ModelAttribute("customer") Customer theCustomer, BindingResult theBindingResult) { System.out.println("Last Name : |"+theCustomer.getLastName()+"|"); System.out.println("Binding Result : "+theBindingResult); System.out.println("\n\n\n\n"); if(theBindingResult.hasErrors()) return "customerform"; else return "customerconfirmation"; } } messages.properties ---------------------------- typeMismatch.customer.freePasses=Invalid Number #When we give wrong input to freePasses, as it is Integer and we may give string input so spring says some messages through which we get to know these format #Last Name : |Mishra| #Binding Result : org.springframework.validation.BeanPropertyBindingResult: 1 errors #Field error in object 'customer' on field 'freePasses': rejected value [fdgfgaregr]; codes [typeMismatch.customer.freePasses,typeMismatch.freePasses,typeMismatch.java.lang.Integer,typeMismatch]; #This is what Spring returns in case of Input Mismatch # #To see this just print theBindingResult # System.out.println("Binding Result : "+theBindingResult); # System.out.println("\n\n\n\n"); index.jsp ---------------- <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> Spring MVC Validation

Hello! Welcome to Spring MVC Validation API App



Click Here For CustomerForm customerform.jsp ---------------------- <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> Spring MVC Validation

Hello! Welcome to Spring MVC Validation API App



(*) fields are required fields

First Name :

Last Name (*) :

Free Passes (*) :

Postal Code :

customerconfirmation.jsp ---------------------------- <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> Spring MVC Validation

Hello! Welcome to Spring MVC Validation API App



Customer Name : ${customer.firstName} ${customer.lastName}

Free Passes : ${customer.freePasses}

Postal Code : ${customer.postalCode}

----------------------------------------------------------------------------------------- Custom Validation with Spring MVC =========================================== - Course Code must start with JAVA error example - To do this we need to create our own custom java annotation : @CourseCode - It can be any name @CourseCode(value="JAVA", message="must start with JAVA") private String courseCode; - To create the @CourseCode you need to do this - To create a custom validation rule 1. Create a @CustomCode Annotation : here @CustomCode is @CourseCode 2. Create CourseCodeConstraintValidator.java class - here you put your own validation/business logic 1. Create a @CustomCode annotation @CourseCode(value="JAVA", message="must start with JAVA") private String courseCode; @Constraint(validatedBy = CourseCodeConstraintValidator.class) : validateBy the helper class which contains your business logic @Target( {ElementType.METHOD, ElementType.FIELD} ) : Where can you apply this annotation @Retention(RetentionPolicy.RUNTIME) : How long should we retain it, here it means keep the annotation in compiled java bytecode so we can use it, introspect on it, and instrument on it during runtime. public @interface CourseCode { // define default course code public String value() default "JAVA"; // define error message public String message() default "must start with JAVA"; } 2. Create CourseCodeConstraintValidator public class CourseCodeConstraintValidator implements ConstraintValidator { // private String coursePrefix; @Override public void initialize(CourseCode theCourseCode) { coursePrefix = theCourseCode.value(); } @Override public boolean isValid(String theCode, ConstraintValidatorContext theConstraintValidatorContext) { // isValid(HTML Form Data entered by user, Helper class for additional error message) boolean result; if (theCode != null) // if entered data != null, check if theCode.startsWith(courseprefix) result = theCode.startsWith(coursePrefix); else // if nothing entered, simply return true result=true; return result; } } Do this ------------------------ 1. Copy paster and rename 21_Spring_MVC_ValidationAPI to 22_Spring_MVC_CustomValidation 2. Create a package demo.mvc.annotation 3. Create an Annotation CourseCode.java and write this package demo.mvc.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import javax.validation.Constraint; import javax.validation.Payload; @Constraint(validatedBy = CourseCodeConstraintValidator.class) // validateBy the helper class which contains your business logic @Target( {ElementType.METHOD, ElementType.FIELD} ) // Where can you apply this annotation @Retention(RetentionPolicy.RUNTIME) // How long should we retain it, here it means keep the annotation in compiled java bytecode so we can use it, introspect on it, and instrument on it during runtime public @interface CourseCode { // define default course code public String value() default "JAVA"; // define default error message public String message() default "must start with JAVA"; // define default groups public Class[] groups() default {}; // can group related constraints // define default payload public Class[] payload() default {}; // Payload provide custom details about validation failure } 4. Create the Helper class CourseCodeConstraintValidator.java and write this package demo.mvc.annotation; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; public class CourseCodeConstraintValidator implements ConstraintValidator { // private String coursePrefix; @Override public void initialize(CourseCode theCourseCode) { coursePrefix = theCourseCode.value(); } @Override public boolean isValid(String theCode, ConstraintValidatorContext theConstraintValidatorContext) { // isValid(HTML Form Data entered by user, Helper class for additional error message) boolean result; if (theCode != null) // if entered data != null, check if theCode.startsWith(courseprefix) result = theCode.startsWith(coursePrefix); else // if nothing entered, simply return true result = true; return result; } } 5. In Customer.java add the field courseCode and generate getter and setter and apply the annotation @CourseCode package demo.mvc; import javax.validation.constraints.Max; import javax.validation.constraints.Min; import javax.validation.constraints.NotNull; import javax.validation.constraints.Pattern; import javax.validation.constraints.Size; import demo.mvc.annotation.CourseCode; public class Customer { private String firstName; @NotNull(message="is required") @Size(min=2, message="is required") private String lastName; @NotNull(message="is required") @Min(value=0, message="must be greater than or equal to 0") @Max(value=10, message="must be less than or equal to 10") private Integer freePasses; @Pattern(regexp="^[a-zA-Z0-9]{6}", message="Only 6 digits/chars") private String postalCode; // @CourseCode // If it is left like this, it will take the default values that you have written in the validation class @CourseCode(value = "TOPS", message = "must start with TOPS") // if you pass the parameters, then it will take these values and perform validation on these values private String courseCode; public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public Integer getFreePasses() { return freePasses; } public void setFreePasses(Integer freePasses) { this.freePasses = freePasses; } public String getPostalCode() { return postalCode; } public void setPostalCode(String postalCode) { this.postalCode = postalCode; } public String getCourseCode() { return courseCode; } public void setCourseCode(String courseCode) { this.courseCode = courseCode; } } 5. In customerform.jsp page write this <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> Spring MVC Validation

Hello! Welcome to Spring MVC Validation API App



(*) fields are required fields

First Name :

Last Name (*) :

Free Passes (*) :

Postal Code :

Course Code :

6. In customerconfirmation.jsp page write this <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> Spring MVC Validation

Hello! Welcome to Spring MVC Validation API App



Customer Name : ${customer.firstName} ${customer.lastName}

Free Passes : ${customer.freePasses}

Postal Code : ${customer.postalCode}

Course Code : ${customer.courseCode}

7. CustomerController.java class package demo.mvc; import javax.validation.Valid; import org.springframework.beans.propertyeditors.StringTrimmerEditor; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.annotation.InitBinder; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class CustomerController { // The form will be passed for whitespace in the required field which should not happen, to resolve this issue // add an @InitBinder to convert trim input strings // remove leading and trailing whitespace // resolve issue for our validation @InitBinder public void initBinder(WebDataBinder dataBinder) { StringTrimmerEditor stringTrimmerEditor = new StringTrimmerEditor(true); dataBinder.registerCustomEditor(String.class, stringTrimmerEditor); } @RequestMapping("/") public String index() { return "index"; } @RequestMapping("/customerform") public String showform(Model model) { model.addAttribute("customer", new Customer()); return "customerform"; } @RequestMapping("/processForm") public String processForm(@Valid @ModelAttribute("customer") Customer theCustomer, BindingResult theBindingResult) { System.out.println("Last Name : |"+theCustomer.getLastName()+"|"); System.out.println("Binding Result : "+theBindingResult); System.out.println("\n\n\n\n"); if(theBindingResult.hasErrors()) return "customerform"; else return "customerconfirmation"; } } 8. index.jsp page <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> Spring MVC Validation

Hello! Welcome to Spring MVC Validation API App



Click Here For CustomerForm 9. Run the Project ------------------------------------------------------------------------------------------------------- ************** Question Spring MVC Custom Validation - Possible to validate with multiple strings ? Answer 1. Update CourseCode.java to use an array of strings 2. Update CourseCodeConstraintValidator.java to validate against array of strings 3. Update Customer.java to validate using array of strings 1. Update CourseCode.java to use an array of strings Change the value entry to an array of Strings: // define default course code public String[] value() default {"JAVA"}; 2. Update CourseCodeConstraintValidator.java to validate against array of strings Change the field for coursePrefixes to an array private String[] coursePrefixes; Update the isValid(...) method to loop through the course prefixes. In the loop, check to see if the code matches any of the course prefixes. @Override public boolean isValid(String theCode, ConstraintValidatorContext theConstraintValidatorContext) { boolean result = false; if (theCode != null) { // loop thru course prefixes // check to see if code matches any of the course prefixes for (String tempPrefix : coursePrefixes) { result = theCode.startsWith(tempPrefix); // if we found a match then break out of the loop if (result) break; } } else result = true; return result; } 3. Update Customer.java to validate using array of strings @CourseCode(value={"TOPS", "LUV"}, message="must start with TOPS or LUV") private String courseCode; Note the use of curley braces. ***************** ========================================================================================================= ***************************************************************************************************************************** ***************************************************************************************************************************** ***************************************************************************************************************************** Spring MVC Database Application ============================================== Customer Relationship Management System (CRM) ====================================================== 1. Setup the Database Table -------------------------------- user name : spring password : spring database name : spring_mvc_CRMapp show databases; create database if not exists spring_mvc_CRMapp; use spring_mvc_crmapp; show tables; drop table if exists customer; CREATE TABLE `customer` ( `id` int(11) NOT NULL AUTO_INCREMENT, `first_name` varchar(45) DEFAULT NULL, `last_name` varchar(45) DEFAULT NULL, `email` varchar(45) DEFAULT NULL, PRIMARY KEY (`id`) ); desc customer; select * from customer; INSERT INTO `customer` VALUES (1,'David','Adams','david@luv2code.com'), (2,'John','Doe','john@luv2code.com'), (3,'Ajay','Rao','ajay@luv2code.com'), (4,'Mary','Public','mary@luv2code.com'), (5,'Maxwell','Dixon','max@luv2code.com'); 2. Setup project and test DB connection ---------------------------------------------- 2.1. Create a Dynamic Web Project named CRM App 2.2. Add Mysql Connector jar 8.0.19.jar 2.3. Create a package demo.testdb 2.4. Create a Servlet TestDBServlet.java package demo.testdb; import java.io.IOException; import java.io.PrintWriter; import java.sql.Connection; import java.sql.DriverManager; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * Servlet implementation class TestDBServlet */ @WebServlet("/testdb") public class TestDBServlet extends HttpServlet { private static final long serialVersionUID = 1L; /** * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response) */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // setup connection variables String usename = "spring"; String password = "spring"; String url = "jdbc:mysql://localhost:3306/spring_mvc_CRMapp?useSSL=false"; String driver = "com.mysql.cj.jdbc.Driver"; // get connection try { PrintWriter out = response.getWriter(); out.println("Connecting to Database : "+url); Class.forName(driver); Connection con = DriverManager.getConnection(url,usename,password); if(con != null) out.print("

Connection Successfull"); con.close(); }catch (Exception e) { e.printStackTrace(); throw new ServletException(e); } } } 2.5. Run the servlet on server 3. Setup Dev Environment -------------------------------- 3.1. Create web.xml and spring-mvc-crud-demo-servlet.xml config files in WEB-INF directory 3.2. Copy and paste the JAR files from Spring jars, Hibernate required and optional/c3p0 to lib folder web.xml ------------ spring-mvc-crud-demo index.jsp index.html dispatcher org.springframework.web.servlet.DispatcherServlet contextConfigLocation /WEB-INF/spring-mvc-crud-demo-servlet.xml 1 dispatcher / spring-mvc-crud-demo-servlet.xml -------------------------------------- org.hibernate.dialect.MySQL5Dialect true true update 4. Test Basic Spring MVC Controller ----------------------------------------- 4.1. Create a package "demo.springmvc.controller" 4.2. Create a class CustomerController.java ----------------------------------------------------- package demo.springmvc.controller; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; @Controller @RequestMapping("/customer") public class CustomerController { @RequestMapping("/list") public String listCustomers(Model model) { return "list-customers"; } } 4.3. Create a folder view in WEB-INF directory and create a jsp file list-customers.jsp ---------------------------------------------------------------------------------------------------- <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> CRM App

This is a CRM App



5. Run the Applciation and Test 5.1. Create Customer.java in demo.springmvc.entity and write this Customer.java ---------------------------- package demo.springmvc.entity; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table(name = "customer") public class Customer { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") private int id; @Column(name = "first_name") private String firstName; @Column(name = "last_name") private String lastName; @Column(name = "email") private String email; public Customer() { } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } @Override public String toString() { return "Customer [id=" + id + ", firstName=" + firstName + ", lastName=" + lastName + ", email=" + email + "]"; } } 6. CRUD Operation ---------------------- - @Repository annotation for DAO Implementation 6.1. CustomerDAO interface in package "demo.springmvc.dao" -------------------------------------------------------------- package demo.springmvc.dao; import java.util.List; import demo.springmvc.entity.Customer; public interface CustomerDAO { public List getCustomers(); } 6.2. CustomerDAOImpl.java -------------------------------- package demo.springmvc.dao; import java.util.List; import javax.transaction.Transactional; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.query.Query; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; import demo.springmvc.entity.Customer; @Repository public class CustomerDAOImpl implements CustomerDAO { // need to inject Hibernate SessionFactory @Autowired private SessionFactory sessionFactory; @Override @Transactional public List getCustomers() { // get the current hibernate session Session session = sessionFactory.getCurrentSession(); // create a query Query query = session.createQuery("from Customer", Customer.class); // get the results from query by executing it and get result list List customers = query.getResultList(); // return results return customers; } } 6.3. CustomerController.java --------------------------------- package demo.springmvc.controller; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import demo.springmvc.dao.CustomerDAO; import demo.springmvc.entity.Customer; @Controller @RequestMapping("/customer") public class CustomerController { // need to inject the CustomerDAO @Autowired private CustomerDAO customerDAO; @RequestMapping("/list") public String listCustomers(Model model) { // get customers from dao List customers = customerDAO.getCustomers(); // add the customers to spring mvc Model model.addAttribute("customers",customers); return "list-customers"; } } 6.4. In WebContent folder create a folder "resources" and a subfolder "css" and in css put 2 css files ------------------------------------------------------------------------------------------------------------ style.css ------------ html, body { margin-left: 15px; margin-right: 15px; padding: 0px; font-family: Verdana, Arial, Helvetica, sans-serif; } table { border-collapse: collapse; border-bottom: 1px solid gray; font-family: Tahoma, Verdana, Segoe, sans-serif; width: 72%; } th { border-bottom: 1px solid gray; background: none repeat scroll 0 0 #09c332; padding: 10px; color: #FFFFFF; } tr { border-top: 1px solid gray; text-align: center; } tr:nth-child(even) { background: #FFFFFF } tr:nth-child(odd) { background: #BBBBBB } #wrapper { width: 100%; margin-top: 0px; } #header { width: 70%; background: #09c332; margin-top: 0px; padding: 15px 0px 15px 15px; } #header h2 { width: 100%; margin: auto; color: #FFFFFF; } #container { width: 100%; margin: auto } #container h3 { color: #000; } #container #content { margin-top: 20px; } .add-button { border: 1px solid #666; border-radius: 5px; padding: 4px; font-size: 12px; font-weight: bold; width: 120px; padding: 5px 10px; margin-bottom: 15px; background: #cccccc; } add-customer-style.css ---------------------------------- form { margin-top: 10px; } label { font-size: 16px; width: 100px; display: block; text-align: right; margin-right: 10px; margin-top: 8px; margin-bottom: 8px; } input { width: 250px; border: 1px solid #666; border-radius: 5px; padding: 4px; font-size: 16px; } .save { font-weight: bold; width: 130px; padding: 5px 10px; margin-top: 30px; background: #cccccc; } table { border-style: none; width: 50%; } tr:nth-child(even) { background: #FFFFFF } tr:nth-child(odd) { background: #FFFFFF } tr { border-style: none; text-align: left; } 6.5. list-customers.jsp ----------------------------------- <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> CRM App
First Name Last Name Email
${customer.firstName} ${customer.lastName} ${customer.email}
6.6. Create a index.jsp file in WebContent directory --------------------------------------------------------- <% response.sendRedirect("customer/list");- %> 6.7. Run and test the app 7. In CustomerController make the RequestMapping to GetMapping 8. Add a Service Layer --------------------------------- - @Service annotation - Use @Transactional in here, not in DAO 8.1. Create a package demo.springmvc.service 8.2. CustomerService.java interface ----------------------------------------- package demo.springmvc.service; import java.util.List; import demo.springmvc.entity.Customer; public interface CustomerService { public List getCustomers(); } 8.3. CustomerServiceImpl.java ---------------------------------------- package demo.springmvc.service; import java.util.List; import javax.transaction.Transactional; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import demo.springmvc.dao.CustomerDAO; import demo.springmvc.entity.Customer; @Service public class CustomerServiceImpl implements CustomerService { // need to inject CustomerDAO @Autowired private CustomerDAO customerDAO; @Override @Transactional public List getCustomers() { return customerDAO.getCustomers(); } } 8.4. Update in CustomerController.java ---------------------------------------------- package demo.springmvc.controller; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import demo.springmvc.entity.Customer; import demo.springmvc.service.CustomerService; @Controller @RequestMapping("/customer") public class CustomerController { // need to inject the CustomerService @Autowired private CustomerService customerService; @GetMapping("/list") public String listCustomers(Model model) { // get customers from service List customers = customerService.getCustomers(); // add the customers to spring mvc Model model.addAttribute("customers",customers); return "list-customers"; } } 8.5. Run and test the app 9. Add Customers ------------------------------ 9.1. Update In list-customers.jsp page ----------------------------------------
9.2. CustomerController.java ---------------------------------- @GetMapping("/showFormForAdd") public String showFormForAdd(Model model) { // create model attribute the bind form data Customer customer = new Customer(); model.addAttribute("customer", customer); return "customer-form"; } 9.3. customer-form.jsp page ------------------------------------- <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %> Save New Customer

Save Customer

Back to List

9.4. CustomerController.java ------------------------------------ @PostMapping("/saveCustomer") public String saveCustomer(@ModelAttribute("customer") Customer customer) { // save the customer using service customerService.saveCustomer(customer); return "redirect:/customer/list"; } 9.5. CustomerServiceImpl.java ------------------------------------ @Override @Transactional public void saveCustomer(Customer customer) { customerDAO.saveCustomer(customer); } 9.6. CustomerDAOImpl.java ---------------------------------- @Override public void saveCustomer(Customer customer) { // get current hibernate session Session session = sessionFactory.getCurrentSession(); // save the customer session.save(customer); } 9.7. Create those method siugnatures in respective interfaces 9.8. Run and test the app 10. Sort the Customer Data --------------------------------------- - Update the Query HQL in CustomerDAOImpl.java // create a query ... sort by lastname Query query = session.createQuery("from Customer order by lastName", Customer.class); 11. Update Customer ------------------------------------ 11.1. Update list-customers.jsp page for update button ---------------------------------------------------------------------
First Name Last Name Email Action
${customer.firstName} ${customer.lastName} ${customer.email} Update
11.2. Create controller code for "/showFormForUpdate" --------------------------------------------------------------- @GetMapping("/showFormForUpdate") public String showFormForUpdate(@RequestParam("customerId") int id, Model model) { // get the customer from the service Customer customer = customerService.getCustomerById(id); // set the customer as a model attribute to pre-populate the customer model.addAttribute("customer", customer); // send over to our form return "customer-form"; } 11.3. Create the method in Service interface and implement it -------------------------------------------------------------------- @Override @Transactional public Customer getCustomerById(int id) { return customerDAO.getCustomerById(id); } 11.4. Create the method in DAO interface and implement it ------------------------------------------------------------------- @Override public Customer getCustomerById(int id) { // get the current hibernate session Session session = sessionFactory.getCurrentSession(); // retrieve the customer from database using the id/primary key Customer customer = session.get(Customer.class, id); return customer; } 11.5. Update in customer-form.jsp --------------------------------------------------------------------------------- 11.6. In CustomerDAOImpl.java do the update --------------------------------------------------- @Override public void saveCustomer(Customer customer) { // get current hibernate session Session session = sessionFactory.getCurrentSession(); // save the customer session.saveOrUpdate(customer); } 11.7. Run the app and test it 12. Delete a customer -------------------------- 12.1. Create the delete link in list-customers.jsp page --------------------------------------------------------------- 12.2. Update in CustomerController.java ----------------------------------------------- @GetMapping("/delete") public String deleteCustomer(@RequestParam("customerId") int id) { // delete the customer customerService.deleteCustomer(id); return "redirect:/customer/list"; } 12.3. Create method in service interface and implement it ------------------------------------------------------------------ @Override @Transactional public void deleteCustomer(int id) { customerDAO.deleteCustomer(id); } 12.4. Create the method in DAO interface and implement it ---------------------------------------------------------------- @Override public void deleteCustomer(int id) { // get the current Hibernate session Session session = sessionFactory.getCurrentSession(); // delete object with primary key @SuppressWarnings("rawtypes") Query query = session.createQuery("delete from Customer where id=:customerId"); query.setParameter("customerId", id); query.executeUpdate(); } 12.5. Run and test the app 13. Add search feature to CRM app ------------------------------------------ 13.1. Create a form in list-customers.jsp ------------------------------------------------- Search customer:
${customer.firstName} ${customer.lastName} ${customer.email} Update / Delete
13.2. Update the CustomerController.java ---------------------------------------------------- @GetMapping("/search") public String searchCustomers(@RequestParam("theSearchName") String theSearchName, Model model) { List customers = customerService.searchCustomers(theSearchName); model.addAttribute("customers", customers); return "list-customers"; } 13.3. Create the method in Service interface and implement it ----------------------------------------------------------------------- @Override @Transactional public List searchCustomers(String theSearchName) { return customerDAO.searchCustomers(theSearchName); } 13.4. Create the method in DAO interface and implement it ------------------------------------------------------------------- @Override public List searchCustomers(String theSearchName) { // get the current session Session session = sessionFactory.getCurrentSession(); @SuppressWarnings("rawtypes") Query query = null; // only search by name if theSearchName is not empty if (theSearchName != null && theSearchName.trim().length() > 0) { // search for firstName or lastName ... case insensitive query = session.createQuery("from Customer where lower(firstName) like :theName or lower(lastName) like :theName", Customer.class); query.setParameter("theName", "%" + theSearchName.toLowerCase() + "%"); } else { // theSearchName is empty ... so just get all customers query =session.createQuery("from Customer", Customer.class); } // execute query and get result list @SuppressWarnings("unchecked") List customers = query.getResultList(); // return the results return customers; } 13.5. Run and test the app 14. How to Add Sorting support ------------------------------------------------ Overview of Development Process 1. Create a Utility class for sort constants 2. In JSP page, add sort links for column headers 3. Update controller to read sort field 4. Update method in the service layer to delegate to DAO 5. Update method in the DAO to get customers sorted by given field 1. Create a Utility class for sort constants -------------------------------------------------------- This utility class will hold constant values for the sort fields. The values can be anything, as long as you stay consistent in the app. File: SortUtils.java package com.luv2code.springdemo.util; public interface SortUtils { public static final int FIRST_NAME = 1; public static final int LAST_NAME = 2; public static final int EMAIL = 3; } 2. In JSP page, add sort links for column headers -------------------------------------------------------- In this page, the user can click on the "First Name" column header and it will sort the data accordingly. The links will have an embedded a sort key. The code below defines a link for the first name. Note the use of SortUtils.FIRST_NAME. File: list-customers.jsp <%@ page import="com.luv2code.springdemo.util.SortUtils" %> ... Then for the column headings, we set up the using the the appropriate link. 3. Update controller to read sort field -------------------------------------------------------- In the CustomerController, we need to update the method to read the sort field. If not sort field is provided, then we just default to SortUtils.LAST_NAME. File: CustomerController.java @GetMapping("/list") public String listCustomers(Model theModel, @RequestParam(required=false) String sort) { // get customers from the service List theCustomers = null; // check for sort field if (sort != null) { int theSortField = Integer.parseInt(sort); theCustomers = customerService.getCustomers(theSortField); } else { // no sort field provided ... default to sorting by last name theCustomers = customerService.getCustomers(SortUtils.LAST_NAME); } // add the customers to the model theModel.addAttribute("customers", theCustomers); return "list-customers"; } 4. Update method in the service layer to delegate to DAO ------------------------------------------------------------------------------ Now, we update the getCustomers(int theSortField) method to accept an int parameter. This is for the service interface and service implementation. File: CustomService.java package com.luv2code.springdemo.service; import java.util.List; import com.luv2code.springdemo.entity.Customer; import com.luv2code.springdemo.util.SortUtils; public interface CustomerService { public List getCustomers(int theSortField); public void saveCustomer(Customer theCustomer); public Customer getCustomer(int theId); public void deleteCustomer(int theId); } File: CustomerServiceImpl.java package com.luv2code.springdemo.service; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.luv2code.springdemo.dao.CustomerDAO; import com.luv2code.springdemo.entity.Customer; import com.luv2code.springdemo.util.SortUtils; @Service public class CustomerServiceImpl implements CustomerService { // need to inject customer dao @Autowired private CustomerDAO customerDAO; @Override @Transactional public List getCustomers(int theSortField) { return customerDAO.getCustomers(theSortField); } ... } 5. Update method in the DAO to get customers sorted by given field -------------------------------------------------------------------------------------- In DAO interface, update the method to accept integer File: CustomerDAO.java package com.luv2code.springdemo.dao; import java.util.List; import com.luv2code.springdemo.entity.Customer; import com.luv2code.springdemo.util.SortUtils; public interface CustomerDAO { public List getCustomers(int theSortField); ... } In the CustomerDAOImpl.java, the getCustomers(...) method has theSortField parameter. It will determine the sort field name based on the parameter. File: CustomerDAOImpl.java package com.luv2code.springdemo.dao; import java.util.List; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.query.Query; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; import com.luv2code.springdemo.entity.Customer; import com.luv2code.springdemo.util.SortUtils; @Repository public class CustomerDAOImpl implements CustomerDAO { // need to inject the session factory @Autowired private SessionFactory sessionFactory; @Override public List getCustomers(int theSortField) { // get the current hibernate session Session currentSession = sessionFactory.getCurrentSession(); // determine sort field String theFieldName = null; switch (theSortField) { case SortUtils.FIRST_NAME: theFieldName = "firstName"; break; case SortUtils.LAST_NAME: theFieldName = "lastName"; break; case SortUtils.EMAIL: theFieldName = "email"; break; default: // if nothing matches the default to sort by lastName theFieldName = "lastName"; } // create a query String queryString = "from Customer order by " + theFieldName; Query theQuery = currentSession.createQuery(queryString, Customer.class); // execute query and get result list List customers = theQuery.getResultList(); // return the results return customers; } ... } ***************************************************************************************************************************** ***************************************************************************************************************************** ***************************************************************************************************************************** Spring AOP (Aspect Oriented Programming) ================================================= - Logging Code - Aspect can be used at multiple locations. - It can be a re-usable class, encapsulate the logic into a class and use it when required Use Cases ------------------- Most Common Logging, Security, Trasanctions Audit Logging who, what, when, where Exception Handling API Management ...etc - Aspect : Module of code for cross-cutting concern (Logging, Security, ...etc) - Advice : what action is taken and when it should be applied Before Advice After Advice or After finally Advice AfterReturning Advice AfterThrowing Advice Around Advice - Join Point : When to apply code during program execution - PointCut : A predicate expression for where advice should be applied - Weaving - Connecting aspects to target objects to create an adviced object - There compile-time and run-time weaving - Spring AOP is run-time AOP @Before and @AfterReturning Advice ========================================== - Applied to method level only Do this ----------------------- 1. Create a project JAVA SE perspective named 1_Spring_AOP_Demo 2. create a lib folder and add all the JAR files and build path 3. create a package demo.spring.aopdemo 4. Download the AspectJ jar file from net https://mvnrepository.com/artifact/org.aspectj/aspectjweaver 5. add the jar to lib forder and build path 6. create a package demo.spring.aopdemo.dao 7. create a class AccountDAO in here ------------------------------------------------- package demo.spring.aopdemo.dao; import org.springframework.stereotype.Component; @Component public class AccountDAO { public void addAccount() { System.out.println(getClass()+" : DOING MY DB WORK : ADDING AN ACCOUNT"); } } 8. in package demo.spring.aopdemo create ApplicationConfig.java ----------------------------- package demo.spring.aopdemo; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; @Configuration @EnableAspectJAutoProxy @ComponentScan("demo.spring.aopdemo") public class ApplicationConfig { } MainApplicationApp.java ---------------------------- package demo.spring.aopdemo; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import demo.spring.aopdemo.dao.AccountDAO; public class MainApplicationApp { public static void main(String[] args) { // read the spring configuration java class AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class); // get the bean from spring container AccountDAO accountDAO = context.getBean("accountDAO", AccountDAO.class); // call the business method accountDAO.addAccount(); // close the context context.close(); } } 9. Run and test if its working or not, Then add the AOP to this 10. create a package demo.spring.aopdemo.aspect 11. create an Aspect class in it ------------------------------------------- package demo.spring.aopdemo.aspect; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; @Aspect @Component public class MyDemoLoggingAspect { // this is where we add all of our related advice for logging // @Before annotation, execute this before executing that @Before("execution(public void addAccount())") // point-cut expression public void beforeAddAccount() { System.out.println("\n=====>>> Executing @Before Advice on addAccount()\n"); } } 12. Update in MainApplicationApp.java -------------------------------------------------- // read the spring configuration java class AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class); // get the bean from spring container AccountDAO accountDAO = context.getBean("accountDAO", AccountDAO.class); // call the business method accountDAO.addAccount(); // calling it again System.out.println("\nCalling the method again\n"); accountDAO.addAccount(); // close the context context.close(); 13. Run and test the app ***************************************************************************************************************** Point Cut Expressions =============================== pointcut : A predicate expression for where advice should be applied @Before("execution(public void addAccount())") : will search for addAccount method in any class @Before("execution(public void demo.spring.aopdemo.dao.AccountDAO.addAccount())") : will search for the addAccount method in AccountDAO class only. @Before("execution(public void add*())") : look for any method that starts with add in any class @Before("execution(* add*())") : look for any modifier, any return type, any mehtod starts with add in any class for method parameters () : match method with no parameter (*) : matches method with One argument of any type (..) : matches a method with 0 or more arguments of any type @Before("execution(* addAccount(demo.spring.aopdemo.entity.Account))") : match addAccount() method with any modifier, any return type, of parameter of Account class only @Before("execution(* addAccount(..))") : match addAccount() method with any modifier, any return type, with any argument of any type matching for package ----------------------------- @Before("execution(* demo.spring.aopdemo.dao.*.*(..))") : match all method with any modifier, with any return type, in any class with any parameter in package demo.spring.aopdemo.dao ***************************************************************************************************************** Ordering Aspects =============================== - If you have more than 1 advices in your Aspect class, You need to set the order in which they will run. Solution : - Place advices in separate Aspects - Control order by using @Order annotation to the Aspects e.g, @Order(1) public class MyDemoLoggingAspect{ } Do this -------------------- 1. Copy paste and rename 1_Spring_AOP_Demo to 2_Spring_AOP_OrderingAspects 2. In package demo.spring.aopdemo.aspect MyDemoLoggingAspect.java -------------------------------- package demo.spring.aopdemo.aspect; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; @Aspect @Component @Order(value = 1) public class MyDemoLoggingAspect { // this is where we add all of our related advice for logging // @Before annotation, execute this before executing that @Before("execution(public void addAccount())") // point-cut expression public void beforeAddAccount() { System.out.println("\n=====>>> Executing @Before Advice |beforeAddAccount| on addAccount()\n"); } } MyApiAnalyticsAspect.java ----------------------------- package demo.spring.aopdemo.aspect; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; @Aspect @Component @Order(2) public class MyApiAnalyticsAspect { @Before("execution(public void demo.spring.aopdemo.dao.AccountDAO.addAccount())") public void performApiAnalysis() { System.out.println("\n====> performing API Analysis \n"); } } MyCloudLogAsyncAspect.java ---------------------------------------- package demo.spring.aopdemo.aspect; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; @Aspect @Component @Order(3) public class MyCloudLogAsyncAspect { @Before("execution(* demo.spring.aopdemo.dao.AccountDAO.add*())") public void performCloudLogAsync() { System.out.println("\n====> performing Cloud Log Async Aspect \n"); } } 3. Run the MainApplicationApp.java and test the app ------------------------------------------------------------------------------------------------------------------------------ AfterReturning Advice ============================== - Run after the method is executed successfully. Do this --------------- 1. Copy and paste and reanme 2_Spring_AOP_OrderingAspects to 3_Spring_AOP_AfterReturning 2. AccountDAO.java ---------------------- package demo.spring.aopdemo.dao; import org.springframework.stereotype.Component; @Component public class AccountDAO { // add a new method public String findAccounts() { System.out.println("\n\nList of Accounts : findAccounts() method in AccountDAO\n"); return "Account 1, Account 2, Account 3"; } public void addAccount() { System.out.println(getClass()+" : DOING MY DB WORK : ADDING AN ACCOUNT"); } } 3. MyDemoLoggingAspect.java ----------------------------------- package demo.spring.aopdemo.aspect; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; @Aspect @Component @Order(value = 1) public class MyDemoLoggingAspect { // this is where we add all of our related advice for logging // add a new advice for @AfterReturning on the findAccounts method @AfterReturning(pointcut = "execution(* demo.spring.aopdemo.dao.AccountDAO.findAccounts())", returning = "result") public void afterReturningFindAccounts(JoinPoint joinpoint, String result) { // print out we are advising on String method = joinpoint.getSignature().toShortString(); System.out.println("\n====>>> Executing @AfterReturning on method : "+method); // print out the results of method call System.out.println("\n====>>> Result is : "+result); // we can modify the data in here also result = result.toUpperCase(); System.out.println("\n====>>> Modified Result is : "+result); } // @Before annotation, execute this before executing that @Before("execution(public void addAccount())") // point-cut expression public void beforeAddAccount() { System.out.println("\n=====>>> Executing @Before Advice |beforeAddAccount| on addAccount()\n"); } } 4. Call the findAccounts() in MainApplicationApp.java accountDAO.findAccounts(); 5. Run and test the app --------------------------------------------------------------------------------------------------------------------------- AfterThrowing Advice ============================== - If an exception is thrown, it will be used Do this -------------- 1. Copy paste and rename 3_Spring_AOP_AfterReturning to 4_Spring_AOP_AfterThrowing 2. MainApplicationApp.java : toggle the boolean tripware value : true or false -------------------------------- package demo.spring.aopdemo; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import demo.spring.aopdemo.dao.AccountDAO; public class MainApplicationApp { public static void main(String[] args) { // read the spring configuration java class AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class); // get the bean from spring container AccountDAO accountDAO = context.getBean("accountDAO", AccountDAO.class); // call the business method accountDAO.addAccount(); // for Before Advice String findAccounts = null; // for either the AfterReturning or AfterThrowing Advice try{ // add a boolean flag to simulate the exception boolean tripware = true; // Toggle this one true or false findAccounts = accountDAO.findAccounts(tripware); // will throw an exception as trip-ware is true } catch (Exception exc) { System.out.println("\n\nIn Main Program Caught Exception : "+exc); } System.out.println("\nIn Main Program : AfterThrowing Advice\n----------------------------\n" + "Accounts are : "+findAccounts); // close the context context.close(); } } 3. AccountDAO.java ----------------------------- package demo.spring.aopdemo.dao; import org.springframework.stereotype.Component; @Component public class AccountDAO { // add a new method public String findAccounts(boolean tripware) { if(tripware) { throw new RuntimeException("Exception Thrown In AccountDAO findAccount as tripware is true"); } System.out.println("\n\nList of Accounts : findAccounts() method in AccountDAO"); return "Account 1, Account 2, Account 3"; } public void addAccount() { System.out.println("\n"+getClass()+" : DOING MY DB WORK : ADDING AN ACCOUNT\n\n\n\n\n"); } } 4. MyDemoLoggingAspect.java : AfterThrowing annotation ------------------------------------ package demo.spring.aopdemo.aspect; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; @Aspect @Component @Order(value = 1) public class MyDemoLoggingAspect { // this is where we add all of our related advice for logging // Execute this if an exception is thrown in application @AfterThrowing( pointcut = "execution(* demo.spring.aopdemo.dao.AccountDAO.findAccounts(..))", throwing = "exception") public void afterThrowingFindAccounts(JoinPoint joinPoint, Throwable exception) { // print out on which method we are advising on String method = joinPoint.getSignature().toShortString(); System.out.println("\n====>>> Executing @AfterThrowing on method : "+method); // log the exception System.out.println("\n====>>> Exception is : "+exception); } // Execute this After execution of that // add a new advice for @AfterReturning on the findAccounts method @AfterReturning(pointcut = "execution(* demo.spring.aopdemo.dao.AccountDAO.findAccounts(..))", returning = "result") public void afterReturningFindAccounts(JoinPoint joinPoint, String result) { // print out on which method we are advising on String method = joinPoint.getSignature().toShortString(); System.out.println("\n====>>> Executing @AfterReturning on method : "+method); // print out the results of method call System.out.println("\n====>>> Result is : "+result); // we can modify the data in here also result = result.toUpperCase(); System.out.println("\n====>>> Modified Result is : "+result); } // @Before annotation, execute this Before executing that @Before("execution(public void addAccount())") // point-cut expression public void beforeAddAccount() { System.out.println("\n=====>>> Executing @Before Advice |beforeAddAccount| on addAccount()"); } } 5. Run and test the app ---------------------------------------------------------------------------------------------------------------------------- After Advice / After finally Advice ================================================ - This aspect will run after a method execution is done. whether it's a success or a failure', regardless of that it will run. - It is like finally block of JAVA SE. - The After advice does not have access to exception, if you need exception, you need AfterThrowing advice - After runs before AfterThrowing and AfterReturning Do this ----------------- 1. Copy paster and reanme 4_Spring_AOP_AfterThrowing to 5_Spring_AOP_After 2. MyDemoLoggingAspect.java ------------------------------- public class MyDemoLoggingAspect { // this is where we add all of our related advice for logging // Execute this after that method execution @After("execution(* demo.spring.aopdemo.dao.AccountDAO.findAccounts(..))") public void afterFinallyFindAccounts(JoinPoint joinPoint) { // print out on which method we are advising on String method = joinPoint.getSignature().toShortString(); System.out.println("\n====>>> Executing @After (finally) on method : "+method); } // Execute this if an exception is thrown in application @AfterThrowing( pointcut = "execution(* demo.spring.aopdemo.dao.AccountDAO.findAccounts(..))", throwing = "exception") public void afterThrowingFindAccounts(JoinPoint joinPoint, Throwable exception) { 3. Run the MainApplicationApp with tripware true and false and see the After runs regardless of success or failure ---------------------------------------------------------------------------------------------------------------------------- Around Advice ========================= - This advice runs Before and After the method execution - Use Cases - Logging, Auditing, Security - Pre and Post processing data - Instrumentation of code - how long does it take to run - Managing Exceptions : Swallow / Handle / Stop Execution - Use of ProceedingJoinPoint : handle to the target method - Our code can use the ProceedingJoinPoint to execute Target Method Do this ------------- 1. Copy paster and reanme 5_Spring_AOP_After to 6_Spring_AOP_Around 2. create a new package "demo.spring.aopdemo.service" 3. Keep MyDemoLoggingAspect.java and delete everything else 4. Delete the DAO package 5. TrafficFortuneService.java in service package ---------------------------------------------------- package demo.spring.aopdemo.service; import java.util.concurrent.TimeUnit; import org.springframework.stereotype.Component; @Component public class TrafficFortuneService { public String getFortune() { //Simulate a delay try { // Thread.sleep(5000); TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } //return a fortune return "Expect heavy traffic this morning"; } } 6. Delete the MainApplicationApp.java and create a new MainApp ---------------------------------------------------------------------- AroundDemoMainApp.java --------------------------- package demo.spring.aopdemo; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import demo.spring.aopdemo.service.TrafficFortuneService; public class AroundDemoMainApp { public static void main(String[] args) { // read the spring configuration java class AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class); // get the bean from spring container TrafficFortuneService fortuneService = context.getBean("trafficFortuneService", TrafficFortuneService.class); System.out.println("Main Program : AroundDemoMainApp"); System.out.println("\nCalling getFortune()"); String fortune = fortuneService.getFortune(); System.out.println("\nMy fortune is : "+fortune); System.out.println("\nFinished"); // close the context context.close(); } } 7. MyDemoLoggingAspect.java -------------------------------------- package demo.spring.aopdemo.aspect; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; @Aspect @Component @Order(value = 1) public class MyDemoLoggingAspect { // this is where we add all of our related advice for logging // Implement Around Advice, Execute this before and after that method @Around("execution(* demo.spring.aopdemo.service.*.get*(..))") public Object aroundGetFortune(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{ // print out the method that we are advising on String method = proceedingJoinPoint.getSignature().toShortString(); System.out.println("\n====>>> Executing @Around on method : "+method); // get the begin time-stamp long begin = System.currentTimeMillis(); // execute the method Object result = proceedingJoinPoint.proceed(); // proceedingJoinPoint : Handle to that method // proceed() : Execute that method // get the end time-stamp long end = System.currentTimeMillis(); // compute the duration and display it long duration = end - begin; System.out.println("\n====>>> Duration : "+duration / 1000.0 +" seconds"); return result; } } 8. ApplicationConfig.java -------------------------------- package demo.spring.aopdemo; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; @Configuration @EnableAspectJAutoProxy @ComponentScan("demo.spring.aopdemo") public class ApplicationConfig { } 9. Run the Main App and test --------------------------------------------------------------------------------------------------------------------------- Handling exceptions with Around Advice ============================================= - The main app will never know about the exception as it will be handled at the Around Advice Do this ------------ 1. Copy paster and reanme 6_Spring_AOP_Around to 7_Spring_AOP_Around_ExceptionHandling 2. AroundHandleExceptionDemoApp.java ------------------------------------------------ package demo.spring.aopdemo; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import demo.spring.aopdemo.service.TrafficFortuneService; public class AroundHandleExceptionDemoApp { public static void main(String[] args) { // read the spring configuration java class AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class); // get the bean from spring container TrafficFortuneService fortuneService = context.getBean("trafficFortuneService", TrafficFortuneService.class); System.out.println("Main Program : AroundDemoMainApp"); System.out.println("\nCalling getFortune()"); boolean tripware = false; // tripware to simulate the application, make it true or false // if true, throw exception, if false, execute successfully String fortune = fortuneService.getFortune(tripware); // For Re-throwing the exception // String fortune = null; // try { // fortune = fortuneService.getFortune(tripware); // } catch (Exception e) { // System.out.println(e); // } System.out.println("\nMy fortune is : \n"+fortune); System.out.println("\nFinished"); // close the context context.close(); } } 3. TrafficFortuneService.java ------------------------------------ package demo.spring.aopdemo.service; import java.util.concurrent.TimeUnit; import org.springframework.stereotype.Component; @Component public class TrafficFortuneService { public String getFortune() { //Simulate a delay try { // Thread.sleep(5000); TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStaackTrace(); } //return a fortune return "Expect heavy traffic this morning"; } public String getFortune(boolean tripware) { if(tripware) throw new RuntimeException("\nException : Accident!, Highway is closed"); return getFortune(); } } 4. MyDemoLoggingAspect.java -------------------------------------- package demo.spring.aopdemo.aspect; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; @Aspect @Component @Order(value = 1) public class MyDemoLoggingAspect { // this is where we add all of our related advice for logging // Implement Around Advice, Execute this before and after that method @Around("execution(* demo.spring.aopdemo.service.*.get*(..))") public Object aroundGetFortune(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{ // print out the method that we are advising on String method = proceedingJoinPoint.getSignature().toShortString(); System.out.println("\n====>>> Executing @Around on method : "+method); // get the begin time-stamp long begin = System.currentTimeMillis(); // execute the method Object result = null; try { result = proceedingJoinPoint.proceed(); } catch (Exception e) { // log the exception and give user a custom message System.out.println(e.getMessage()); //result = "\nHighway is being cleared, Hold your Position"; return e.toString(); // Rethrowing the Exception // throw e; } // get the end time-stamp long end = System.currentTimeMillis(); // compute the duration and display it long duration = end - begin; System.out.println("\n====>>> Duration : "+duration / 1000.0 +" seconds"); return result; } } 5. Run the main app and test it ----------------------------------------------------------------------------------------------------------------------------- Adding AOP to CRM Project ================================ Do this ------------ 1. Bring the CRM project to the AOP workspace 2. Add the servlet-api.jar, aspectjweaver.jar, jsp-api.jar to WEB-INF/lib folder 3. Update in spring-mvc-crud-demo-servlet.xml file ------------------------------------------------------------- 4. Create a package "demo.springmvc.aspect" 5. Create an Aspect class : CRMLoggingAspect.java ---------------------------------------------------------- package demo.springmvc.aspect; import java.util.logging.Logger; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; @Aspect @Component public class CRMLoggingAspect { // setup Logger private Logger myLogger = Logger.getLogger(getClass().getName()); // setup point-cut declarations @Pointcut("execution(* demo.springmvc.controller.*.*(..))") // for any class, for any method, for any arguments private void forControllerPackage() {} // do the same thing for Service and DAO package @Pointcut("execution(* demo.springmvc.dao.*.*(..))") // for any class, for any method, for any arguments private void forDaoPackage() {} @Pointcut("execution(* demo.springmvc.service.*.*(..))") // for any class, for any method, for any arguments private void forServicePackage() {} @Pointcut("forControllerPackage() || forDaoPackage() || forServicePackage()") private void forAppFlow() {} // add Before Advice @Before("forAppFlow()") public void before(JoinPoint joinPoint) { // display the method that we are calling String method = joinPoint.getSignature().toShortString(); myLogger.info("====>>>in @Before : Calling the method : "+method); // display the arguments to the method // get the arguments Object[] arguments = joinPoint.getArgs(); // loop through the arguments for (Object args : arguments) { myLogger.info("====>>>Argument : "+args); } } // add AfterReturning Advice @AfterReturning(pointcut = "forAppFlow()", returning = "result") public void afterReturning(JoinPoint joinPoint, Object result) { // display the method we are returning from String method = joinPoint.getSignature().toShortString(); myLogger.info("====>>>in @AfterReturning : Calling the method : "+method); // display data returned myLogger.info("====>>> Result : "+result); } } 6. Run the App and Test and look the logging message in the console --------------------------------------------------------------------------------------------------------------------- ***************************************************************************************************************************** ***************************************************************************************************************************** ***************************************************************************************************************************** Spring Security ========================= - It defines a framework for security - Implemented using Servlet filters in the background - Two methods of securing a webapp - Declarative - Programmatic Spring Security using Servlet Filters --------------------------------------- - Servlet filters are used to pre-process or post-process web request - Servlet filters can route web requests based on security logic - Spring provides a bulk of Security functionality with servet filters Access Denied ^ | | No | Spring --------> Is Web Resource ---- yes -------------> Is user Authenticated? ------> yes -----> Is user Authorized? Security Protected? ^ | | Filters ^ | | | | | | No | | Authenticate | | | user id & | Yes | password | | | ^ v | | | Send to Login | No | | | --- Show <----- Form | | Login | | Form | | | | v |------------------------------------------------------------------------------ Show resource Authentication ----------------- - Check user id and password with credentials stored in app / db Authorization ----------------- - Check to see if user has an authorized role Declarative Security ------------------------ - We can define application's security constraints in configuration - It can be - All java configuration (@Configuration, no XML) - or Spring XML config file Programmatic Security ----------------------------- - Spring Security provides an API for custom applciation coding - provides greater customization for specific app requirements Different Login Methods ------------------------------- - HTTP Basic Authentication - Default Login Form provided by Spring Security - Custom Login Form : own form Spring Security Demo ----------------------------- - use of @EnableWebSecurity - Add Maven Depedencies for spring security 1. spring-security-web 2. spring-security-config - Spring Security Web App Initializer : special class to register Spring Security Filters className : AbstractSecurityWebApplicationInitializer Development Process ------------------------- 1. Create a maven project add add the depedency 2. Create Spring App Configuration 3. Create Spring Dispatcher Servlet Initializer 4. Develop Spring Controller 5. Develop Jsp View Page Do this ----------------------- 1. Create a maven project add add the depedency ------------------------------------------------------- groupId : demo.spring artifactId : spring-security-demo Rename project to : 1_Spring_Security_Basic_Security Pom.xml --------------- 4.0.0 demo.spring spring-security-demo war 0.0.1-SNAPSHOT spring-security-demo 5.0.2.RELEASE 5.0.0.RELEASE 1.8 1.8 org.springframework.security spring-security-web ${springsecurity.version} org.springframework.security spring-security-config ${springsecurity.version} org.springframework spring-webmvc ${springframework.version} javax.servlet javax.servlet-api 3.1.0 javax.servlet.jsp javax.servlet.jsp-api 2.3.1 javax.servlet jstl 1.2 javax.xml.bind jaxb-api 2.3.0 junit junit 3.8.1 test spring-security-demo org.apache.maven.plugins maven-war-plugin 3.2.3 2. Create Spring App Configuration ---------------------------------------- package demo.spring.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.ViewResolver; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.view.InternalResourceViewResolver; @Configuration @EnableWebMvc @ComponentScan(basePackages = "demo.spring") public class ApplicationConfig { // define a bean for ViewResolver @Bean public ViewResolver viewResolver() { InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); viewResolver.setPrefix("/WEB-INF/view/"); viewResolver.setSuffix(".jsp"); return viewResolver; } } 3. Create Spring Dispatcher Servlet Initializer ----------------------------------------------------- package demo.spring.config; import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; public class MySpringMvcDispatcherServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class[] getRootConfigClasses() { return null; } @Override protected Class[] getServletConfigClasses() { return new Class[] {ApplicationConfig.class}; } @Override protected String[] getServletMappings() { return new String[] { "/" }; } } 4. Develop Spring Controller ---------------------------------- package demo.spring.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; @Controller public class ApplicationController { @GetMapping("/") public String showHome() { return "home"; } } 5. Develop Jsp View Page ----------------------------- <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> Spring Security Demo App

Spring Security Demo App



For Security to the application ----------------------------------------------- 6. SecurityWebApplicationInitializer.java ---------------------------------- package demo.spring.config; import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer; public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer { } 7. ApplicationSecurityConfig.java ---------------------------------------------------- package demo.spring.config; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.User.UserBuilder; @Configuration @EnableWebSecurity public class ApplicationSecurityConfig extends WebSecurityConfigurerAdapter { @Override // Configure users (in memory, database, idap ...etc) protected void configure(AuthenticationManagerBuilder auth) throws Exception { // add our users for in-memory authentication UserBuilder users = User.withDefaultPasswordEncoder(); auth.inMemoryAuthentication() .withUser(users.username("Debi").password("Debi").roles("EMPLOYEE")) .withUser(users.username("Debi Prasad").password("Debi Prasad").roles("MANAGER")) .withUser(users.username("Vicky").password("Vicky").roles("ADMIN")); } } 8. Run and Test --------------------------------------------------------------------------------------------------------------------- Custom Login Form with Spring Security ==================================================== Do this ------------------ 1. Copy paste and rename 1_Spring_Security_Basic_Security to 2_Spring_Security_Custom_Login_Form 2. In ApplicationSecurityConfig.java ------------------------------------------ package demo.spring.config; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.User.UserBuilder; @Configuration @EnableWebSecurity public class ApplicationSecurityConfig extends WebSecurityConfigurerAdapter { @Override // Configure users (in memory, database, idap ...etc) protected void configure(AuthenticationManagerBuilder auth) throws Exception { // add our users for in-memory authentication UserBuilder users = User.withDefaultPasswordEncoder(); auth.inMemoryAuthentication() .withUser(users.username("Debi").password("Debi").roles("EMPLOYEE")) .withUser(users.username("Debi Prasad").password("Debi Prasad").roles("MANAGER")) .withUser(users.username("Vicky").password("Vicky").roles("ADMIN")); } @Override // configure security of Web Paths in application, Login, Logout etc... protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() // Restrict access based on HttpServletRequest .anyRequest().authenticated() // Any request to the app must be authenticated (logged in) .and() .formLogin() // Customize the login process .loginPage("/myLoginPage") // show our custom form at the request mapping .loginProcessingUrl("/authenticateTheUser") // Login form should POST data to this URL for processing .permitAll() // Allow everyone to see the login page .and() .logout().permitAll(); // logout } } 3. Create new Controller : LoginController.java --------------------------------------------------- package demo.spring.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; @Controller public class LoginController { @GetMapping("/myLoginPage") public String myLoginPage() { return "myLoginPage"; } } 4. Create own Login page : myLoginPage.jsp ------------------------------------------------- <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> Spring Security Custom Login Form

Spring Security Custom Login Form App



Invalid Credentials, Please check again

User Name :

Password :

5. Run the app and test ---------------------------------------------------------------- For Logout ------------------- 6. home.jsp page : add the logout button ------------------------------------------------ <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> Spring Security Demo App

Spring Security Demo App Testing



Welcome to Login Logout App

7. myLoginPage.jsp ---------------------------- <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> Spring Security Custom Login Form

Spring Security Custom Login Form App



Invalid Credentials, Please check again You have been logged out

User Name :

Password :

8. bootstrapLogin.jsp -------------------------------- <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> Login Page
Sign In
Invalid username and password.
You have been logged out.
9. Test the App with both pages in LoginController.java ------------------------------------------------------------------------------------------------------------------------ Display User Id and Roles ======================================== Development Process ---------------------- 1. Update POM file for Spring Security JSP Tag Library 2. Add Spring Security JSP Tag library to Jsp page 3. Display User Id and User Roles 1. Update POM file for Spring Security JSP Tag Library ---------------------------------------------------------------- org.springframework.security spring-security-taglibs ${springsecurity.version} 2. Add Spring Security JSP Tag library to Jsp page --------------------------------------------------------- <%@ taglib prefix="security" uri="http://www.springframework.org/security/tags" %> 3. Display User Id and User Roles -------------------------------------- User : Roles(s) : Do this --------------------- 1. Cpoy paste and rename 2_Spring_Security_Custom_Login_Form to 3_Spring_Security_Display_UserRoles 2. In pom.xml add this ---------------------------- org.springframework.security spring-security-taglibs ${springsecurity.version} 3. In home.jsp write this ---------------------------------- <%@ taglib prefix="security" uri="http://www.springframework.org/security/tags" %>

User :

Role(s) :

4. In ApplicationSecurityConfig.java do this changes --------------------------------------------------------- @Override // Configure users (in memory, database, idap ...etc) protected void configure(AuthenticationManagerBuilder auth) throws Exception { // add our users for in-memory authentication UserBuilder users = User.withDefaultPasswordEncoder(); auth.inMemoryAuthentication() .withUser(users.username("Debi").password("Debi").roles("EMPLOYEE")) .withUser(users.username("Debi Prasad").password("Debi Prasad").roles("EMPLOYEE","MANAGER")) .withUser(users.username("Vicky").password("Vicky").roles("EMPLOYEE","ADMIN")); } 5. Run and test in the browser -------------------------------------------------------------------------------------------------------------------------------- Restrict Access Based on Roles ========================================== Do this ------------- 1. Copy paste and rename 3_Spring_Security_Display_UserRoles to 4_Spring_Security_RestrictAccessByRoles 2. In home.jsp add this -------------------------------

Leadership Meeting (Only for Manager People)

System Admin Meeting (Only for Admin People)
3. create a leaders.jsp page -------------------------------------- <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> Leaders Home Page

Leaders Home Page



This is page for Leaders


This has to be accessed by Leaders only and it has to be secret


Back to Home page 4. create a systems.jsp page ----------------------------------------- <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> Systems Home Page

Admin Or Systems Home Page



This is page for Admin


This has to be accessed by System Admin only and it has to be secret


Back to Home page 5. Update the Controller : ApplicationController.java --------------------------------------------------------------- package demo.spring.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; @Controller public class ApplicationController { @GetMapping("/") public String showHome() { return "home"; } // add another request mapping for leaders @GetMapping("/leaders") public String showLeaders() { return "leaders"; } // add another request mapping for systems @GetMapping("/systems") public String showSystems() { return "systems"; } } 6. Update ApplicationSecurityConfig.java --------------------------------------------------- package demo.spring.config; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.User.UserBuilder; @Configuration @EnableWebSecurity public class ApplicationSecurityConfig extends WebSecurityConfigurerAdapter { @Override // Configure users (in memory, database, idap ...etc) protected void configure(AuthenticationManagerBuilder auth) throws Exception { // add our users for in-memory authentication UserBuilder users = User.withDefaultPasswordEncoder(); auth.inMemoryAuthentication() .withUser(users.username("Debi").password("Debi").roles("EMPLOYEE")) .withUser(users.username("Prasad").password("Prasad").roles("EMPLOYEE","MANAGER")) .withUser(users.username("Vicky").password("Vicky").roles("EMPLOYEE","ADMIN")); } @Override // configure security of Web Paths in application, Login, Logout etc... protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() // Restrict access based on HttpServletRequest .antMatchers("/").hasRole("EMPLOYEE") .antMatchers("/leaders/**").hasRole("MANAGER") .antMatchers("/systems/**").hasRole("ADMIN") .and() .formLogin() // Customize the login process .loginPage("/myLoginPage") // show our custom form at the request mapping .loginProcessingUrl("/authenticateTheUser") // Login form should POST data to this URL for processing .permitAll() // Allow everyone to see the login page .and() .logout().permitAll(); // logout } } 7. Run and test the App ----------------------------------------------------------------------------------------------------------------------- Custom Access Denied Page ---------------------------------- 1. ApplicationSecurityConfig.java ------------------------------------------------- @Override // configure security of Web Paths in application, Login, Logout etc... protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() // Restrict access based on HttpServletRequest .antMatchers("/").hasRole("EMPLOYEE") .antMatchers("/leaders/**").hasRole("MANAGER") .antMatchers("/systems/**").hasRole("ADMIN") .and() .formLogin() // Customize the login process .loginPage("/myLoginPage") // show our custom form at the request mapping .loginProcessingUrl("/authenticateTheUser") // Login form should POST data to this URL for processing .permitAll() // Allow everyone to see the login page .and() .logout().permitAll() // logout .and() .exceptionHandling() .accessDeniedPage("/access-denied"); } 2. LoginController.java ------------------------------------ package demo.spring.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; @Controller public class LoginController { @GetMapping("/myLoginPage") public String myLoginPage() { // return "myLoginPage"; return "bootstrapLogin"; } // add request mapping for /access-denied @GetMapping("/access-denied") public String accessDenied() { return "access-denied"; } } 3. access-denied.jsp ------------------------------- <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> Access Denied

Access Denied - You are not authorized to access this page


back to home page 4. Run and test the App ----------------------------------------------------------------------------------------------------------------------------- Display Content Based On Roles ========================================== - security JSP Tags - Employee should not see the links - Respective Role Employee should be able to see those links i.e. related to them Do this ------------------- 1. In 4_Spring_Security_RestrictAccessByRoles home.jsp --------------------------- <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="security" uri="http://www.springframework.org/security/tags" %> Spring Security Demo App

Spring Security Restrict As Role




User :

Role(s) :



Leadership Meeting (Only for Manager People)

System Admin Meeting (Only for Admin People)
----------------------------------------------------------------------------------------------------------------------------- Spring Security User Accounts Stored in the Database ================================================================= - Spring Security already has the JDBC codes Default Spring Security Database Schema ---------------------------------------------- users authorities ----------- ------------------ username Varchar <--------> username Varchar(50) : all Varchar is 50 in size password Varchar authority Varchar : authority means roles enabled TINYINT(1) - Need to use exact names My Database ------------------ username : spring password : spring database : spring_security_demo_plaintext Tables ---------------- Create table users( username Varchar(50) Not Null, password Varchar(50) Not Null, enabled tinyint(1) Not Null, // 1 : Log In, 0 : Can not log in Primary Key(username) ); - In Spring Security 5, passwords get stored using a specific format {id}encodedPassword id Description ----------- ------------------------- noop No Operation, Place text format bcrypt BCrypt password hashing (recommended) ...etc Insert into users values ('Debi', {noop}Debi, 1), ('Prasad', {noop}Prasad, 1), ('Vicky', {noop}Vicky, 1), Create table authorities ( username varchar(50) Not Null, authority varchar(50) Not Null, Unique Key (username, authority), Foreign Key (username) references users(username) ); Insert into authorities values ('Debi', 'ROLE_EMPLOYEE'), ('Prasad', 'ROLE_EMPLOYEE'), ('Prasad', 'ROLE_MANAGER'), ('Vicky', 'ROLE_EMPLOYEE'), ('Vicky', 'ROLE_ADMIN'); For Database use the JDBC Mysql Driver and use C3PO for Connection Pool ------------------------------------------------------------------------------ Connection Pool ----------------------- com.mchange c3p0 0.9.5.2 JDBC Properties file ------------------------------------------ persistance-mysql.properties ------------------------------- # # JDBC Connection properties # jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/spring_security_demo_plaintext?useSSL=false jdbc.user=spring jdbc.password=spring # # Connection Pool Properties # connection.pool.initialPoolSize=5 connection.pool.minPoolSize=5 connection.pool.maxPoolSize=20 connection.pool.maxIdleTime=3000 *********** Autowire Environment class : Environment is a predefined class used for storing value of properties file. Create a DataSource Object *********** Do this ------------------------- 1. In sql create the tables ----------------------------------------- drop database if exists spring_security_demo_plaintext; create database spring_security_demo_plaintext; use spring_security_demo_plaintext; Create table users( username Varchar(50) Not Null, password Varchar(50) Not Null, enabled tinyint(1) Not Null, -- 1 : Log In, 0 : Can not log in Primary Key(username) ); Insert into users values ('Debi', '{noop}Debi', 1), ('Prasad', '{noop}Prasad', 1), ('Vicky', '{noop}Vicky', 1); select * from users; Create table authorities ( username varchar(50) Not Null, authority varchar(50) Not Null, unique key (username, authority), Foreign Key (username) references users(username) ); desc authorities; Insert into authorities values ('Debi', 'ROLE_EMPLOYEE'), ('Prasad', 'ROLE_EMPLOYEE'), ('Prasad', 'ROLE_MANAGER'), ('Vicky', 'ROLE_EMPLOYEE'), ('Vicky', 'ROLE_ADMIN'); select * from authorities; 2. Copy paste and rename 4_Spring_Security_RestrictAccessByRoles to 5_Spring_Security_UsingDatabasePlaintext 2. Add database support to Pom.xml --------------------------------------- mysql mysql-connector-java 8.0.19 com.mchange c3p0 0.9.5.2 3. Creating JDBC Properties file ----------------------------------------- # # JDBC Connection properties # jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/spring_security_demo_plaintext?useSSL=false jdbc.user=spring jdbc.password=spring # # Connection Pool Properties # connection.pool.initialPoolSize=5 connection.pool.minPoolSize=5 connection.pool.maxPoolSize=20 connection.pool.maxIdleTime=3000 4. Define DataSource in Spring Configuration ------------------------------------------------------ package demo.spring.config; import java.beans.PropertyVetoException; import java.util.logging.Logger; import javax.sql.DataSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import org.springframework.core.env.Environment; import org.springframework.web.servlet.ViewResolver; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.view.InternalResourceViewResolver; import com.mchange.v2.c3p0.ComboPooledDataSource; @Configuration @EnableWebMvc @ComponentScan(basePackages = "demo.spring") @PropertySource("classpath:persistance-mysql.properties") public class ApplicationConfig { // set variable to hold the data read from properties file @Autowired private Environment env; // setup a Logger for diagnostics private Logger logger = Logger.getLogger(getClass().getName()); // define a bean for ViewResolver @Bean public ViewResolver viewResolver() { InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); viewResolver.setPrefix("/WEB-INF/view/"); viewResolver.setSuffix(".jsp"); return viewResolver; } // define a Bean for our security data-source @Bean public DataSource securityDataSource() { // create a connection pool ComboPooledDataSource securityDataSource = new ComboPooledDataSource(); // set the JDBC Driver class try { securityDataSource.setDriverClass(env.getProperty("jdbc.Driver")); } catch (PropertyVetoException e) { throw new RuntimeException(e); } // log the connection properties logger.info(">>> jdbc.url="+env.getProperty("jdbc.url")); logger.info(">>> jdbc.user="+env.getProperty("jdbc.user")); // set the Database connection properties securityDataSource.setJdbcUrl(env.getProperty("jdbc.url")); securityDataSource.setUser(env.getProperty("jdbc.user")); securityDataSource.setPassword(env.getProperty("jdbc.password")); // set the connection pool properties securityDataSource.setInitialPoolSize(getIntProperty("connection.pool.initialPoolSize")); securityDataSource.setMinPoolSize(getIntProperty("connection.pool.minPoolSize")); securityDataSource.setMaxPoolSize(getIntProperty("connection.pool.maxPoolSize")); securityDataSource.setMaxIdleTime(getIntProperty("connection.pool.maxIdleTime")); return securityDataSource; } // need a helper method // reading a environment property and convert it to int private int getIntProperty(String propName) { String propValue = env.getProperty(propName); // now convert it to int int intPropVal = Integer.parseInt(propValue); return intPropVal; } } 5. Update Spring Security Configuration to use JDBC --------------------------------------------------------------- package demo.spring.config; import javax.sql.DataSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; @Configuration @EnableWebSecurity public class ApplicationSecurityConfig extends WebSecurityConfigurerAdapter { // Add a reference to our security data source @Autowired private DataSource securityDataSource; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { // use JDBC authentication ... no hard-coded user values auth.jdbcAuthentication().dataSource(securityDataSource); } @Override // configure security of Web Paths in application, Login, Logout etc... protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() // Restrict access based on HttpServletRequest .antMatchers("/").hasRole("EMPLOYEE") .antMatchers("/leaders/**").hasRole("MANAGER") .antMatchers("/systems/**").hasRole("ADMIN") .and() .formLogin() // Customize the login process .loginPage("/myLoginPage") // show our custom form at the request mapping .loginProcessingUrl("/authenticateTheUser") // Login form should POST data to this URL for processing .permitAll() // Allow everyone to see the login page .and() .logout().permitAll() // logout .and() .exceptionHandling() .accessDeniedPage("/access-denied"); } } 6. Run and Test the app ------------------------------------------------------------------------------------------------------------------------- Spring Security Password Encryption ================================================ - It uses bcrypt Algorithm https://www.bcryptcalculator.com/ - make use of {bcrypt}encodedPassword Debi : $2a$10$rf4HJkOXj4aaXq0yfZzgxOe6af1yJhlOg7mm.FXiQ2AZNSclQmFA. Prasad : $2a$10$JqXsmwEchRxkkVuNytyvBOJovvSjbrbbZp.70257vjU6w8tN/9/QS Vicky : $2a$10$csr9n9E4NFAF/eQZE/wpm.06kXSTkAwmupbv.087PCX/WlkffKrB6 Table --------------- Create table users( username Varchar(50) Not Null, password char(70) Not Null, enabled tinyint(1) Not Null, // 1 : Log In, 0 : Can not log in Primary Key(username) ); database : spring_security_demo_bcrypt username : spring password : spring Do this ------------------------------- 1. In SQL do this SQL Codes ===================== drop database if exists spring_security_demo_bcrypt; create database spring_security_demo_bcrypt; use spring_security_demo_bcrypt; Create table users( username Varchar(50) Not Null, password char(70) Not Null, enabled tinyint(1) Not Null, -- 1 : Log In, 0 : Can not log in Primary Key(username) ); Insert into users values ('Debi', '{bcrypt}$2a$10$rf4HJkOXj4aaXq0yfZzgxOe6af1yJhlOg7mm.FXiQ2AZNSclQmFA.', 1), ('Prasad', '{bcrypt}$2a$10$JqXsmwEchRxkkVuNytyvBOJovvSjbrbbZp.70257vjU6w8tN/9/QS', 1), ('Vicky', '{bcrypt}$2a$10$csr9n9E4NFAF/eQZE/wpm.06kXSTkAwmupbv.087PCX/WlkffKrB6', 1); desc users; select * from users; Create table authorities ( username varchar(50) Not Null, authority varchar(50) Not Null, unique key (username, authority), Foreign Key (username) references users(username) ); desc authorities; Insert into authorities values ('Debi', 'ROLE_EMPLOYEE'), ('Prasad', 'ROLE_EMPLOYEE'), ('Prasad', 'ROLE_MANAGER'), ('Vicky', 'ROLE_EMPLOYEE'), ('Vicky', 'ROLE_ADMIN'); select * from authorities; 2. Copy paste and rename 5_Spring_Security_UsingDatabasePlaintext to 6_Spring_Security_UsingDatabaseBCrypt 3. Update properties file ---------------------------------- # # JDBC Connection properties # jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/spring_security_demo_bcrypt?useSSL=false jdbc.user=spring jdbc.password=spring # # Connection Pool Properties # connection.pool.initialPoolSize=5 connection.pool.minPoolSize=5 connection.pool.maxPoolSize=20 connection.pool.maxIdleTime=3000 4. Run the app and test ------------------------------------------------------------------------------------------------------------------------------- ***************************************************************************************************************************** ***************************************************************************************************************************** ***************************************************************************************************************************** Spring REST APIs - REST Web Services ================================================ Problem ------------ - Build a client app that provides the weather report for a city - Need to get weather data from an external service / Database Application Architecture ---------------------------- city My Weather App <---------> Weather Service (Client) report (Server) Get Customers CRM App <---------------> CRM Service : (CRM : Customer Relationship Management) (Client) Customer List (Server) REST : REpresentational State Transfer : Lightweight approach for communicating between applications - REST is language independent, client and server can use any programming language - It can use any data format like XML, JSON ...etc ******************** Where to find the REST APIs ----------------------------------- www.programmableweb.com ******************** Spring REST JSON ------------------------ JSON : JavaScript Object Notation - It is lightweight data format for storing and exchanging data ... plane text - It is language independent { "id" : 14, "firstName" : "Debi", "lastName" : "Mishra", "email" : "debi@gmail.com" "active" : true } - Curley braces define the object - Object members are nemae value pairs, delimited by colon (:) - Every name should be placed in double quote("") - JSON Values : Numbers : no quotes String : always in double quote ("") Boolean : true/false Nested JSON Object Array null : reference to nothing Nested JSON Object -------------------- { "id" : 14, "firstName" : "Debi", "lastName" : "Mishra", "email" : "debi@gmail.com" "active" : true, "address" : { // Nested JSON Object "at" : "Hafimelak", "post" : "chatrachakada", "city" : "kendrapara", "dist" : "Kendrapara", "zip" : 754250 } } JSON Arrays ---------------------- { "id" : 14, "firstName" : "Debi", "lastName" : "Mishra", "email" : "debi@gmail.com" "active" : true, "language" : ["JAVA", "C", "SQL", "JS", "Spring"] // Array } Java JSON Data Binding ----------------------------- - Data binding is the process of converting JSON data to Java POJO JSON -------------> Java <------------- POJO (Plane Old Java Object) Data Binding - Also known as Mapping, Serialization/De-Serialization, Marshalling/Un-Marshalling - Spring uses Jackson Project behind the scenes - Jackson handles data binding between JSON and POJO - Jackson Data Biding API found at : com.fasterxml.jackson.databind Maven Dependency ----------------------- com.fasterxml.jackson.core jackson-databind 2.9.0 - By default jackson will call the appropriate getter and setter methods JSON to Java POJO ------------------------- Development Process --------------------- 1. Download and import Maven starter files 2. Add Maven depedency for Jackson Project 3. Create Student POJO Class 4. Create main Driver App Do this ---------------- 1. Create a workspace Spring REST create a Maven Project 2. Archetype is quickstart, groupId : demo.json, artifactId : jsontopojo, create, after creation, rename it to 1_SpringREST_JSON-POJO 3. Create a folder data in 1_SpringREST_JSON-POJO and create 2 json files sample-lite.json ---------------------- { "id": 14, "firstName": "Debi", "lastName": "Mishra", "email" : "debi@mishra.com", "active": true } sample-full.json ------------------- { "id": 14, "firstName": "Debi", "lastName": "Mishra", "email" : "debi@mishra.com", "active": true, "address": { "street": "100 Main St", "city": "Philadelphia", "state": "Pennsylvania", "zip": "19103", "country": "USA" }, "languages" : ["Java", "C#", "Python", "Javascript"], "company" : "Hexaware" } 4. In pom.xml file write this ----------------------------------- 4.0.0 demo.json JSON-POJO-SpringREST 1.0.0 jar jackson-demo 1.8 1.8 com.fasterxml.jackson.core jackson-databind 2.9.10.4 5. In package "demo.json.jsontopojo" create a Student.java class ------------------------------------------------------------------ package demo.json.jsontopojo; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @JsonIgnoreProperties(ignoreUnknown = true) public class Student { private int id; private String firstName; private String lastName; private String email; private boolean active; public boolean isActive() { return active; } public void setActive(boolean active) { this.active = active; } // ======================================== private Address address; public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } // =================================================== private String[] languages; public String[] getLanguages() { return languages; } public void setLanguages(String[] languages) { this.languages = languages; } // =========================================================== public Student() { } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } } 6. Create Driver_SampleLite.java ------------------------------------- package demo.json.jsontopojo; import java.io.File; import com.fasterxml.jackson.databind.ObjectMapper; public class Driver_SampleLite { public static void main(String[] args) { try { // create a ObjectMapper ObjectMapper mapper = new ObjectMapper(); // Read a JSON file and map/convert to Java POJO: data/sample-lite.json Student theStudent = mapper.readValue(new File("data/sample-lite.json"), Student.class); // print the details of the Student System.out.println("Student ID is : "+theStudent.getId()); System.out.println("Student First Name is : "+theStudent.getFirstName()); System.out.println("Student Last Name is : "+theStudent.getLastName()); System.out.println("Student Email is : "+theStudent.getEmail()); System.out.println("Student Status is : "+theStudent.isActive()); }catch (Exception e) { e.printStackTrace(); } } } 7. To read the sample-full.json file, create Driver_SampleFull.java ----------------------------------------------------------------------------- 7.1. Change the file_name 7.2. Create Pojo class Address.java and create the fileds in Student.java as private Address address; and for language private String[] languages; and generate getters and setters 7.3. Address.java, generate getters and setters and toString() Address.java --------------------- package demo.json.jsontopojo; public class Address { private String street; private String city; private String state; private String zip; private String country; public Address() { } public String getStreet() { return street; } public void setStreet(String street) { this.street = street; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } public String getState() { return state; } public void setState(String state) { this.state = state; } public String getZip() { return zip; } public void setZip(String zip) { this.zip = zip; } public String getCountry() { return country; } public void setCountry(String country) { this.country = country; } @Override public String toString() { return "Address [street=" + street + ", city=" + city + ", state=" + state + ", zip=" + zip + ", country=" + country + "]"; } } Driver_SampleFull.java ---------------------------------- package demo.json.jsontopojo; import java.io.File; import com.fasterxml.jackson.databind.ObjectMapper; public class Driver_SampleFull { public static void main(String[] args) { try { // create a ObjectMapper ObjectMapper mapper = new ObjectMapper(); // Read a JSON file and map/convert to Java POJO: data/sample-lite.json Student theStudent = mapper.readValue(new File("data/sample-full.json"), Student.class); // print the details of the Student System.out.println("Student ID is : "+theStudent.getId()); System.out.println("Student First Name is : "+theStudent.getFirstName()); System.out.println("Student Last Name is : "+theStudent.getLastName()); System.out.println("Student Email is : "+theStudent.getEmail()); System.out.println("Student Status is : "+theStudent.isActive()); System.out.println("Student Address is : "+theStudent.getAddress()); System.out.println("Student Address Street is : "+theStudent.getAddress().getStreet()); String[] studentLanguages = theStudent.getLanguages(); for (String languages : studentLanguages) { System.out.println("Student Languages are : "+languages); } }catch (Exception e) { e.printStackTrace(); } } } 8. For company property in JSON it will give you an exception of UnrecognizedPropertyException: Unrecognized field "company" - To avoid that or to ignore a property of JSON document, use @JsonIgnoreProperties(ignoreUnknown = true) in the Student.java POJO class 9. Run the app again and see the output ----------------------------------------------------------------------------------------------------------------------- Spring REST HTTP ===================== - Most common use of REST is over HTTP HTTP Method CRUD Operation -------------- --------------------- POST Create a new Entity GET Read a list of entities or single entity PUT Update an entity DELETE Delete an existing entity HTTP Messages ---------------- My HTTP Request Message CRM CRM <---------------------------> REST App HTTP Response Message Service (client) (server) HTTP Request Message -------------------------- - It has 3 parts Request line : the HTTP Command (Methods) Header variable : request metadata Message Body : contents of a message as JSON HTTP Response Message ------------------------------ - it is also of 3 parts Response line : server protocol and status code like 200, 404, 500 ... Header Variable : response metadata Message Body : Contents of the message as XML or JSON HTTP Response status code ------------------------------- 100 - 199 : Informational 200 - 299 : Successfull 300 - 399 : Redirection 400 - 499 : Client Error 500 - 599 : Server Error Spring REST Controller Demo ================================== - Spring Web MVC provides support for Spring REST - New annotation : @RestController - Extension of @Controller and it handles the REST requests and responses - Spring REST automatically convert JAVA POJOs to JSON Do this --------------- 1. Create a new Maven Project with Archetype : maven-archetype-webapp groupId : demo.springrest artifactId : restdemo and rename project to 2_SpringREST_Demo 2. In 2_SpringREST_Demo, Rename src/main/resources to src/main/java 3. In pom.xml, write this ------------------------------- 4.0.0 demo.springrest spring-rest-demo 1.0 war 1.8 1.8 org.springframework spring-webmvc 5.0.5.RELEASE com.fasterxml.jackson.core jackson-databind 2.9.5 javax.servlet javax.servlet-api 3.1.0 javax.servlet.jsp javax.servlet.jsp-api 2.3.1 javax.annotation javax.annotation-api 1.3.2 spring-rest-demo org.apache.maven.plugins maven-war-plugin 3.2.0 4. In src/main/java, create a package for configuratiuon of the application "demo.springrest.config" 5. create a config class ApplicationConfig.java and write this ----------------------------------------------------------------------- package demo.springrest.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.EnableWebMvc; @Configuration @EnableWebMvc @ComponentScan(basePackages = "demo.springrest") public class ApplicationConfig { } 6. Create a class to handle the AbstractAnnotationConfigDispatcherServletInitializer --------------------------------------------------------------------------------------------- package demo.springrest.config; import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; public class SpringMvcDispatcherServlerInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class[] getRootConfigClasses() { return null; } @Override protected Class[] getServletConfigClasses() { return new Class[] {ApplicationConfig.class}; } @Override protected String[] getServletMappings() { return new String[] { "/" }; } } 7. Create a package DemoRESTController.java in package "demo.springrest.rest", change the package name while creating ------------------------------------------------------------------------------------------------------------------------ package demo.springrest.rest; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/test") public class DemoRESTController { // add code for "/hello" end-point @GetMapping("/hello") public String sayHello() { return "Hello World!"; } } 8. Run the project in Server, Before that, in "src/main/webapp/index.jsp" file write this for a better access of the project ------------------------------------------------------------------------------------------------------------------------------

Spring REST Demo


Click here to go to the App

Hello, Click Here 9. Run the project on server and click the links --------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------- Spring REST Service ----------------------------- - Displaying a List of Students Do this ------------------ 1. In 2_SpringREST_Demo Create a package "demo.springrest.entity" 2. Create a POJO class Student.java ------------------------------------------------ package demo.springrest.entity; public class Student { private String firstName; private String lastName; public Student() { } public Student(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } } 3. Create a StudentRESTController.java in "demo.springrest.rest" package ---------------------------------------------------------------------------- package demo.springrest.rest; import java.util.ArrayList; import java.util.List; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import demo.springrest.entity.Student; @RestController @RequestMapping("/api") public class StudentRESTController { // define endpoint for a "/studnets" - return list of students @GetMapping("/students") public List getStudents(){ List students = new ArrayList(); students.add(new Student("Debi", "Mishra")); students.add(new Student("Debi", "Prasad")); students.add(new Student("Vicky", "Mishra")); students.add(new Student("Rog", "Debi")); return students; } } 4. Run the application and check the link in Postman http://localhost:9090/spring-rest-demo/api/students ---------------------------------------------------------------------------------------------------------- Spring REST Path Varibles ================================== - Retrieve single student by id /api/students/{studentId} - Add request mapping to Spring REST Service, Bind @PathVarible to method parameter - Add a new depedency for Annotaion to use @PostConstruct Annotation javax.annotation javax.annotation-api 1.3.2 Do this --------------- 1. In 2_SpringREST_Demo In StudentRESTController.java do this --------------------------------------------------------------------- package demo.springrest.rest; import java.util.ArrayList; import java.util.List; import javax.annotation.PostConstruct; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import demo.springrest.entity.Student; @RestController @RequestMapping("/api") public class StudentRESTController { private List students; // define @PostConstruct to load student data.. execute only once after bean creation @PostConstruct public void loadData() { students = new ArrayList(); students.add(new Student("Debi", "Mishra")); students.add(new Student("Debi", "Prasad")); students.add(new Student("Vicky", "Mishra")); students.add(new Student("Rog", "Debi")); } // define end-point for a "/students" - return list of students @GetMapping("/students") public List getStudents(){ return students; } // define end-point for "/students/{studentId}" - return student index in ArrayList @GetMapping("/students/{studentId}") public Student getStudent(@PathVariable int studentId) { // just index it into the list return students.get(studentId); } } 2. Check in the postman http://localhost:9090/spring-rest-demo/api/students/1 -------------------------------------------------------------------------------------------------------- ********************** Exception Handling for Spring REST Controller ------------------------------------------------------ - For bad value like 9999, it will give you an 500:internal server error - To overcome that we add an @ExceptionHandler to our code - @ExceptionHandler - Used over methdos - Exception handler will return ResponseEntity - ResponseEntity is just a wrapper for HTTP response Object Development Process ------------------------ 1. Create a custom error response class 2. Create a custom Exception class 3. Update REST service to throw an exception if student is not found 4. Add an exception handler using @ExceptionHandler Do this ---------------------- 1. Create a new package demo.springrest.exception 2.1. Create a custom error response class : StudentErrorResponse.java -------------------------------------------------------------------------------- package demo.springrest.exception; public class StudentErrorResponse { private int status; private String message; private long timeStamp; public StudentErrorResponse() { } public StudentErrorResponse(int status, String message, long timeStamp) { this.status = status; this.message = message; this.timeStamp = timeStamp; } public int getStatus() { return status; } public void setStatus(int status) { this.status = status; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public long getTimeStamp() { return timeStamp; } public void setTimeStamp(long timeStamp) { this.timeStamp = timeStamp; } } 2.2. Create a custom Exception class : StudentNotFoundException.java -------------------------------------------------------------------- package demo.springrest.exception; @SuppressWarnings("serial") public class StudentNotFoundException extends RuntimeException { public StudentNotFoundException(String message, Throwable cause) { super(message, cause); } public StudentNotFoundException(String message) { super(message); } public StudentNotFoundException(Throwable cause) { super(cause); } } 2.3. Update REST service to throw an exception if student is not found : StudentRESTController.java -------------------------------------------------------------------------------------------------------------- @GetMapping("/students/{studentId}") public Student getStudent(@PathVariable int studentId) { // just index it into the list // check the studentId against the list.size() if((studentId >= students.size()) || (studentId<0)) throw new StudentNotFoundException("Student Not Found with id "+studentId); return students.get(studentId); } 2.4. Add an exception handler using @ExceptionHandler : in StudentRESTController.java ------------------------------------------------------------------------------------------------ @ExceptionHandler public ResponseEntity handleException(StudentNotFoundException e) { return null; } @ExceptionHandler : This method is an Exception handler public ResponseEntity handleException(Exception Type to handle/catch obj_reference_of_Exception_Type) @ExceptionHandler public ResponseEntity handleException(StudentNotFoundException e) { // Create a new StudentErrorResponse StudentErrorResponse error = new StudentErrorResponse(); error.setStatus(HttpStatus.NOT_FOUND.value()); error.setMessage(e.getMessage()); error.setTimeStamp(System.currentTimeMillis()); //return ResponseEntity return new ResponseEntity<> (error, HttpStatus.NOT_FOUND); // (body, Status Code) } ----------------------------------------------------------------------------------------------------------------------------- ************************************************** ************************************************** ************************************************** - If we enter the String at that, It will also fail - To catch that, we need to modufy the code // Add another exception Handler.. to catch any type of exception that is thrown (catch all) @ExceptionHandler public ResponseEntity handleException(Exception e) { // Create a new StudentErrorResponse StudentErrorResponse error = new StudentErrorResponse(); error.setStatus(HttpStatus.BAD_REQUEST.value()); // error.setMessage(e.getMessage()); // Default Message of Java error.setMessage("Do not enter anything unrelated"); // Custom MEssage that will be displayed error.setTimeStamp(System.currentTimeMillis()); //return ResponseEntity return new ResponseEntity<> (error, HttpStatus.BAD_REQUEST); // (body, Status Code) } ************************************************** ************************************************** ************************************************** Complete StudentRESTController.java ================================================= package demo.springrest.rest; import java.util.ArrayList; import java.util.List; import javax.annotation.PostConstruct; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import demo.springrest.entity.Student; import demo.springrest.exception.StudentErrorResponse; import demo.springrest.exception.StudentNotFoundException; @RestController @RequestMapping("/api") public class StudentRESTController { private List students; // define @PostConstruct to load student data.. execute only once after bean creation @PostConstruct public void loadData() { students = new ArrayList(); students.add(new Student("Debi", "Mishra")); students.add(new Student("Debi", "Prasad")); students.add(new Student("Vicky", "Mishra")); students.add(new Student("Rog", "Debi")); } // define end-point for a "/students" - return list of students @GetMapping("/students") public List getStudents(){ return students; } // define end-point for "/students/{studentId}" - return student index in ArrayList @GetMapping("/students/{studentId}") public Student getStudent(@PathVariable int studentId) { // just index it into the list // check the studentId against the list.size() if((studentId >= students.size()) || (studentId<0)) throw new StudentNotFoundException("Student Not Found with id "+studentId); return students.get(studentId); } // Add an exception Handler @ExceptionHandler public ResponseEntity handleException(StudentNotFoundException e) { // Create a new StudentErrorResponse StudentErrorResponse error = new StudentErrorResponse(); error.setStatus(HttpStatus.NOT_FOUND.value()); error.setMessage(e.getMessage()); error.setTimeStamp(System.currentTimeMillis()); //return ResponseEntity return new ResponseEntity<> (error, HttpStatus.NOT_FOUND); // (body, Status Code) } // Add another exception Handler.. to catch any type of exception that is thrown (catch all) @ExceptionHandler public ResponseEntity handleException(Exception e) { // Create a new StudentErrorResponse StudentErrorResponse error = new StudentErrorResponse(); error.setStatus(HttpStatus.BAD_REQUEST.value()); // error.setMessage(e.getMessage()); // Default Message of Java error.setMessage("Do not enter anything unrelated"); // Custom MEssage that will be displayed error.setTimeStamp(System.currentTimeMillis()); //return ResponseEntity return new ResponseEntity<> (error, HttpStatus.BAD_REQUEST); // (body, Status Code) } } ------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------- Spring REST : Global Exception Handling =================================================== - The exception that we handled are only gonna be handled by that specific REST controller - We need global Exception Handlers - It promotes the idea of re-use - It centralizes Eception Handling - Use of @ControllerAdvice - It is similar to interceptor/filter - It can be used with pre-processed requests, Post-process response to handle exception - Real time of AOP Do this --------------- 1. Copy Paste and Rename 2_SpringREST_Demo to 3_SpringREST_GlobalExceptionHandling 2. In "demo.springrest.exception" package create a @ControllerAdvice class whill will handle the exception globally StudentRESTExceptionHandler.java ------------------------------------------- package demo.springrest.exception; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; @ControllerAdvice public class StudentRESTExceptionHandler { // Add exception handling code // Add an exception Handler @ExceptionHandler public ResponseEntity handleException(StudentNotFoundException e) { // Create a new StudentErrorResponse StudentErrorResponse error = new StudentErrorResponse(); error.setStatus(HttpStatus.NOT_FOUND.value()); error.setMessage(e.getMessage()); error.setTimeStamp(System.currentTimeMillis()); //return ResponseEntity return new ResponseEntity<> (error, HttpStatus.NOT_FOUND); // (body, Status Code) } // Add another exception Handler.. to catch any type of exception that is thrown (catch all) @ExceptionHandler public ResponseEntity handleException(Exception e) { // Create a new StudentErrorResponse StudentErrorResponse error = new StudentErrorResponse(); error.setStatus(HttpStatus.BAD_REQUEST.value()); // error.setMessage(e.getMessage()); // Default Message of Java error.setMessage("Do not enter anything unrelated"); // Custom MEssage that will be displayed error.setTimeStamp(System.currentTimeMillis()); //return ResponseEntity return new ResponseEntity<> (error, HttpStatus.BAD_REQUEST); // (body, Status Code) } } 3. Remove all the exception codes from the RestController and put them in here 4. Run the applciation in server and test in postman, and see the working ------------------------------------------------------------------------------------------------------------------------------- Spring REST API Design ============================= Design Process ------------------- 1. Review API Requirements 2. Identify main resource/entity 3. Use HTTP methods to assign action on resource Never use the url as /deletecustomers or /addcustomers, Instead it will be just /customers, but use the HTTP methods like DELETE and post Spring REST API ======================= Application Architecture ----------------------------- CRM Customer Customer Service <-----------> Service <---------> DAO <-------> Database (spring-rest) Do this -------------- 1. Create a maven Project groupId : demo.springdemo artifactId : spring-crm-rest Rename it to 4_SpringREST_CRM 2. Pom.xml --------------------------- 4.0.0 demo.springdemo spring-crm-rest 1.0.0 war 5.0.6.RELEASE 5.4.1.Final 5.1.45 0.9.5.2 1.8 1.8 org.springframework spring-webmvc ${springframework.version} org.springframework spring-tx ${springframework.version} org.springframework spring-orm ${springframework.version} com.fasterxml.jackson.core jackson-databind 2.9.5 org.hibernate hibernate-core ${hibernate.version} mysql mysql-connector-java ${mysql.connector.version} com.mchange c3p0 ${c3po.version} javax.servlet javax.servlet-api 3.1.0 javax.servlet.jsp javax.servlet.jsp-api 2.3.1 javax.servlet jstl 1.2 javax.xml.bind jaxb-api 2.3.0 spring-crm-rest org.apache.maven.plugins maven-war-plugin 3.2.0 3. Setup Database Table --------------------------------- username : spring password : spring CREATE DATABASE IF NOT EXISTS web_customer_tracker; USE `web_customer_tracker`; DROP TABLE IF EXISTS `customer`; CREATE TABLE `customer` ( `id` int(11) NOT NULL AUTO_INCREMENT, `first_name` varchar(45) DEFAULT NULL, `last_name` varchar(45) DEFAULT NULL, `email` varchar(45) DEFAULT NULL, PRIMARY KEY (`id`) ) AUTO_INCREMENT=6 ; INSERT INTO `customer` VALUES (1,'David','Adams','david@luv2code.com'), (2,'John','Doe','john@luv2code.com'), (3,'Ajay','Rao','ajay@luv2code.com'), (4,'Mary','Public','mary@luv2code.com'), (5,'Maxwell','Dixon','max@luv2code.com'); select * from customer; 4. Create 4 packages in src/main/java ---------------------------------------------- demo.spring.config demo.spring.dao demo.spring.entity demo.spring.service 5. In demo.spring.config -------------------------------------- DemoAppConfig.java ---------------------------------- package demo.spring.config; import java.beans.PropertyVetoException; import java.util.Properties; import java.util.logging.Logger; import javax.sql.DataSource; import org.hibernate.SessionFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import org.springframework.core.env.Environment; import org.springframework.orm.hibernate5.HibernateTransactionManager; import org.springframework.orm.hibernate5.LocalSessionFactoryBean; import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.web.servlet.ViewResolver; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.view.InternalResourceViewResolver; import com.mchange.v2.c3p0.ComboPooledDataSource; @Configuration @EnableWebMvc @EnableTransactionManagement @ComponentScan("demo.spring") @PropertySource({ "classpath:persistence-mysql.properties" }) public class DemoAppConfig implements WebMvcConfigurer { @Autowired private Environment env; private Logger logger = Logger.getLogger(getClass().getName()); @Bean public DataSource myDataSource() { // create connection pool ComboPooledDataSource myDataSource = new ComboPooledDataSource(); // set the jdbc driver try { myDataSource.setDriverClass("com.mysql.jdbc.Driver"); } catch (PropertyVetoException exc) { throw new RuntimeException(exc); } // for sanity's sake, let's log url and user ... just to make sure we are reading the data logger.info("jdbc.url=" + env.getProperty("jdbc.url")); logger.info("jdbc.user=" + env.getProperty("jdbc.user")); // set database connection props myDataSource.setJdbcUrl(env.getProperty("jdbc.url")); myDataSource.setUser(env.getProperty("jdbc.user")); myDataSource.setPassword(env.getProperty("jdbc.password")); // set connection pool props myDataSource.setInitialPoolSize(getIntProperty("connection.pool.initialPoolSize")); myDataSource.setMinPoolSize(getIntProperty("connection.pool.minPoolSize")); myDataSource.setMaxPoolSize(getIntProperty("connection.pool.maxPoolSize")); myDataSource.setMaxIdleTime(getIntProperty("connection.pool.maxIdleTime")); return myDataSource; } private Properties getHibernateProperties() { // set hibernate properties Properties props = new Properties(); props.setProperty("hibernate.dialect", env.getProperty("hibernate.dialect")); props.setProperty("hibernate.show_sql", env.getProperty("hibernate.show_sql")); return props; } // need a helper method // read environment property and convert to int private int getIntProperty(String propName) { String propVal = env.getProperty(propName); // now convert to int int intPropVal = Integer.parseInt(propVal); return intPropVal; } @Bean public LocalSessionFactoryBean sessionFactory(){ // create session factorys LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean(); // set the properties sessionFactory.setDataSource(myDataSource()); sessionFactory.setPackagesToScan(env.getProperty("hibernate.packagesToScan")); sessionFactory.setHibernateProperties(getHibernateProperties()); return sessionFactory; } @Bean @Autowired public HibernateTransactionManager transactionManager(SessionFactory sessionFactory) { // setup transaction manager based on session factory HibernateTransactionManager txManager = new HibernateTransactionManager(); txManager.setSessionFactory(sessionFactory); return txManager; } } MySpringMvcDispatcherServletInitializer.java ------------------------------------------------ package demo.spring.config; import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; public class MySpringMvcDispatcherServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class[] getRootConfigClasses() { // TODO Auto-generated method stub return null; } @Override protected Class[] getServletConfigClasses() { return new Class[] { DemoAppConfig.class }; } @Override protected String[] getServletMappings() { return new String[] { "/" }; } } 6. In demo.spring.entity -------------------------------------- Customer.java ------------------ package demo.spring.entity; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table(name="customer") public class Customer { @Id @GeneratedValue(strategy=GenerationType.IDENTITY) @Column(name="id") private int id; @Column(name="first_name") private String firstName; @Column(name="last_name") private String lastName; @Column(name="email") private String email; public Customer() { } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } @Override public String toString() { return "Customer [id=" + id + ", firstName=" + firstName + ", lastName=" + lastName + ", email=" + email + "]"; } } 7. In demo.spring.dao ------------------------ interface CustomerDAO.java ------------------------------ package demo.spring.dao; import java.util.List; import demo.spring.entity.Customer; public interface CustomerDAO { public List getCustomers(); public void saveCustomer(Customer theCustomer); public Customer getCustomer(int theId); public void deleteCustomer(int theId); } Implemented class CustomerDAOImpl.java -------------------------------------------- package demo.spring.dao; import java.util.List; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.query.Query; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; import demo.spring.entity.Customer; @Repository public class CustomerDAOImpl implements CustomerDAO { // need to inject the session factory @Autowired private SessionFactory sessionFactory; @Override public List getCustomers() { // get the current hibernate session Session currentSession = sessionFactory.getCurrentSession(); // create a query ... sort by last name Query theQuery = currentSession.createQuery("from Customer order by lastName", Customer.class); // execute query and get result list List customers = theQuery.getResultList(); // return the results return customers; } @Override public void saveCustomer(Customer theCustomer) { // get current hibernate session Session currentSession = sessionFactory.getCurrentSession(); // save/upate the customer ... finally LOL currentSession.saveOrUpdate(theCustomer); } @Override public Customer getCustomer(int theId) { // get the current hibernate session Session currentSession = sessionFactory.getCurrentSession(); // now retrieve/read from database using the primary key Customer theCustomer = currentSession.get(Customer.class, theId); return theCustomer; } @Override public void deleteCustomer(int theId) { // get the current hibernate session Session currentSession = sessionFactory.getCurrentSession(); // delete object with primary key @SuppressWarnings("rawtypes") Query theQuery = currentSession.createQuery("delete from Customer where id=:customerId"); theQuery.setParameter("customerId", theId); theQuery.executeUpdate(); } } 8. In demo.spring.service ---------------------------- interface CustomerService.java ------------------------------------- package demo.spring.service; import java.util.List; import demo.spring.entity.Customer; public interface CustomerService { public List getCustomers(); public void saveCustomer(Customer theCustomer); public Customer getCustomer(int theId); public void deleteCustomer(int theId); } Implemented Class CustomerServiceImpl.java ---------------------------------------------------- package demo.spring.service; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import demo.spring.dao.CustomerDAO; import demo.spring.entity.Customer; @Service public class CustomerServiceImpl implements CustomerService { // need to inject customer dao @Autowired private CustomerDAO customerDAO; @Override @Transactional public List getCustomers() { return customerDAO.getCustomers(); } @Override @Transactional public void saveCustomer(Customer theCustomer) { customerDAO.saveCustomer(theCustomer); } @Override @Transactional public Customer getCustomer(int theId) { return customerDAO.getCustomer(theId); } @Override @Transactional public void deleteCustomer(int theId) { customerDAO.deleteCustomer(theId); } } 9. In src/main/resources folder In "persistence-mysql.properties" file ----------------------------------------------------------------------------- # # JDBC connection properties # jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/web_customer_tracker?useSSL=false jdbc.user=spring jdbc.password=spring # # Connection pool properties # connection.pool.initialPoolSize=5 connection.pool.minPoolSize=5 connection.pool.maxPoolSize=20 connection.pool.maxIdleTime=3000 # # Hibernate properties # hibernate.dialect=org.hibernate.dialect.MySQLDialect hibernate.show_sql=true hibernate.packagesToScan=demo.spring.entity Paste this in web.xml file to upgrade the descriptor version ----------------------------------------------------------------------- Archetype Created Web Application 10. In Deployed Resources/webapp/index.jsp page wrote this and Run the application ------------------------------------------------------------------------------------------------

Spring CRM REST Demo


11. If this page appears, then we are good to go ------------------------------------------------------------------------------------------------------------------- Spring REST API Application Design =============================================== 12. We already have one project with DAO and SERVICE and POJO Layer, we are gonna create the REST controller 13. Create a CustomerRESTController.java in "demo.spring.rest" package and Autowire the CustomerService.java and add mappings for Insert, Read, Update, Delete operation 14. To Read data, In CustomerRESTController.java create this -------------------------------------------------------------- package demo.spring.rest; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import demo.spring.entity.Customer; import demo.spring.service.CustomerService; @RestController @RequestMapping("/api") public class CustomerRESTController { // Autowire the CustomerService @Autowired private CustomerService customerService; // add mapping for GET /customers @GetMapping("/customers") public List getCustomers() { return customerService.getCustomers(); } } In Postman, check it as : http://localhost:9090/spring-crm-rest/api/customers 15. To get one customer, do this in CustomerRESTController.java ----------------------------------------------------------------------------- // add mapping for GET /customer/{customerId} : get customer by Id @GetMapping("/customers/{customerId}") public Customer getCustomer(@PathVariable int customerId) { Customer customer = customerService.getCustomer(customerId); if(customer == null) throw new CustomerNotFoundException("Customer Not found with Id : "+customerId); return customer; } check in postman : http://localhost:9090/spring-crm-rest/api/customers/1 http://localhost:9090/spring-crm-rest/api/customers/2 For bad customer Id, or for string input, there will be exception generated, to handle the exception do this --------------------------------------------------------------------------------------------------------------- Create CustomerErrorResponse.java class in "demo.spring.exception" package (POJO class for JSON Body) ------------------------------------------------------------------------------------------------------------- package demo.spring.exception; public class CustomerErrorResponse { private int status; private String message; private long timeStamp; public CustomerErrorResponse() { } public CustomerErrorResponse(int status, String message, long timeStamp) { this.status = status; this.message = message; this.timeStamp = timeStamp; } public int getStatus() { return status; } public void setStatus(int status) { this.status = status; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public long getTimeStamp() { return timeStamp; } public void setTimeStamp(long timeStamp) { this.timeStamp = timeStamp; } } CustomerNotFoundException.java : To handle the exception -------------------------------------------------------------------- package demo.spring.exception; @SuppressWarnings("serial") public class CustomerNotFoundException extends RuntimeException { public CustomerNotFoundException() { super(); } public CustomerNotFoundException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); } public CustomerNotFoundException(String message, Throwable cause) { super(message, cause); } public CustomerNotFoundException(String message) { super(message); } public CustomerNotFoundException(Throwable cause) { super(cause); } } CustomerRESTExceptionHandler.java : Global Exception Handler ---------------------------------------------------------------------- package demo.spring.exception; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; @ControllerAdvice public class CustomerRESTExceptionHandler { // Add an exception handler for CustomerNotfoundException @ExceptionHandler // : states the method is exception handler method // ResponseEntity handleException(Exception_Type Exception_Type_Object_Reference) public ResponseEntity handleException(CustomerNotFoundException c) { // create Customer Error Response CustomerErrorResponse error = new CustomerErrorResponse(HttpStatus.NOT_FOUND.value(), c.getMessage(), System.currentTimeMillis());; // Return ResponseEntity return new ResponseEntity<>(error, HttpStatus.NOT_FOUND); } // Add another Exception for any exception ... to catch any exception @ExceptionHandler public ResponseEntity handleException(Exception e) { // create Customer Error Response CustomerErrorResponse error = new CustomerErrorResponse(HttpStatus.BAD_REQUEST.value(), e.getMessage(), System.currentTimeMillis());; // Own Custom Message to be displayed error.setMessage("Do Not Enter Anything Un-Necessary, Enter Appropriate Values"); // Return ResponseEntity return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST); } } Now update your REST Code 16. For all CRUD Operation, The REST Controller -------------------------------------------------------- package demo.spring.rest; import java.util.List; 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.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import demo.spring.entity.Customer; import demo.spring.exception.CustomerNotFoundException; import demo.spring.service.CustomerService; @RestController @RequestMapping("/api") public class CustomerRESTController { // Autowire the CustomerService @Autowired private CustomerService customerService; // add mapping for GET /customers : get all customers ordered by last-name @GetMapping("/customers") public List getCustomers() { return customerService.getCustomers(); } // add mapping for GET /customer/{customerId} : get customer by Id @GetMapping("/customers/{customerId}") public Customer getCustomer(@PathVariable int customerId) { Customer customer = customerService.getCustomer(customerId); if(customer == null) throw new CustomerNotFoundException("Customer Not found with Id : "+customerId); return customer; } // add mapping for POST /customers : Add a new Customer @PostMapping("/customers") public Customer addCustomer(@RequestBody Customer customer) { // just in case they passed the id in JSON, force set it to 0, so that it will perform an Insert customer.setId(0); customerService.saveCustomer(customer); return customer; } // add mapping for PUT /customers : Update a customer data @PutMapping("/customers") public Customer updateCustomer(@RequestBody Customer customer) { customerService.saveCustomer(customer); return customer; } // add mapping for DELETE /customers/{customerId} : Delete a customer @DeleteMapping("/customers/{customerId}") public String deleteCustomer(@PathVariable int customerId) { // check if the customer with the id exists or not, if not, throw an exception Customer customer = customerService.getCustomer(customerId); if(customer == null) throw new CustomerNotFoundException("Customer Not found with Id : "+customerId); // delete the customer customerService.deleteCustomer(customerId); return "Deleted Customer Id : "+customerId; } } Now Run the application ================================================================================================================================ ================================================================================================================================ Check the ClientWebApp project and Security project ***************************************************************************************************************************** ***************************************************************************************************************************** ***************************************************************************************************************************** Spring Boot =========================== - Makes it easier to get started with Spring development, less writing of configuration file or with configuration class - Performs auto-configuration via properties file and JAR classpath - It provids embeded HTTP server - It helps to resolve depedency conflicts - It provides the Spring Initializer - It is a website for quickly creating a starter Spring project http://start.spring.io - select your depedencies and create a Maven/Gradle project from there - Download and import into IDE - It provides an embedded HTTP server, it has support for Tomcat, Jetty, Undertow ...etc - So no need to install a server separately - You will have a jar file like "myapplication.jar" and it will have the application code and embedded server - Spring Boot apps can run standalone as it includes Embedded server with it. - To run in the command line "java -jar myapplication.jar" Spring Boot Initializer ================================ Development Process --------------------------- 1. Configure project in the Spring Intializer Website : http://start.spring.io 2. Download the zip file and unzip it 3. Import the Maven project into IDE Do this --------------- 1. Go the the start.spring.io website and Give the group Id : demo.springboot 2. Artifact Id as : firstbootapp : It will be the project name 3. Select Depedencies : Spring Web 4. Gererate the zip file, extract it, and move the project to the workspace 5. Import the Project into your workspace (Project name is firstboot) 6. To run the application, run it as Java Application as it has a embeded Tomcat Server ************************************************************************************************************* Note : Error : localhost:8080 port is already in use error https://www.youtube.com/watch?v=KdH6MvnGNpU netstat -a -o -n find the PID of port 8080 taskkill /F /PID (that id value) taskkill is to kill a process /F is force /PID is id of that process ************************************************************************************************************* @SpringBootApplication : It is the collection of @Configuration @ComponentScan @EnableAutoConfiguration static folder : put all the HTML, CSS, JS, JSP, PDFs inside that folder templates : this folder deals with Template like FreeMarker, Thymeleaf(Popular), Mustache : template engines to select the right depedencies : www.luv2code.com/spring-boot-starters *************************************************************************************************************** Create a REST Controller =================================== - use @RestController annotation Do this ---------------- 1. In firstboot create a new class FunRestController and change the package name to "demo.springboot.firstbootapp.rest" 2. FunRestController.java package demo.springboot.firstbootapp.rest; import java.time.LocalDateTime; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class FunRestController { // expose "/" that return Hello World @GetMapping("/") public String sayHello() { return "Hello World!, Time on the Server is "+LocalDateTime.now(); } } 3. Run the FirstbootappApplication.java 4. Refresh the browser, http://localhost:8080/ -------------------------------------------------------------------------------------------------------------------- Spring Dev Tools ========================= - For each time update, you need to restart your application. - For this dev-tools to the rescue - You need to add this "spring-boot-devtools" depedency to the pom.xml org.springframework.boot spring-boot-devtools www.luv2code.com/devtools-docs Do this --------------- 1. Copy paste and rename 1_firstbootapp to 2_SpringBoot_Devtools 2. Add the depedency in the pom.xml 4.0.0 org.springframework.boot spring-boot-starter-parent 2.2.6.RELEASE demo.springboot firstbootapp 0.0.1-SNAPSHOT firstbootapp Demo project for Spring Boot 1.8 org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-test test org.junit.vintage junit-vintage-engine org.springframework.boot spring-boot-devtools org.springframework.boot spring-boot-maven-plugin 3. Run the SpringBootApplication and do the changes in the FunRestController package demo.springboot.firstbootapp.rest; import java.time.LocalDateTime; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class FunRestController { // expose "/" that return Hello World @GetMapping("/") public String sayHello() { return "Hello World!, Time on the Server is "+LocalDateTime.now(); } // expose a new endpoint for "workout" @GetMapping("/workout") public String getDailyWorkout() { return "Run a hard 5K"; } @GetMapping("/fortune") public String getDailyFortune() { return "Today is your lucky day"; } } 4. Check out by writting http://localhost:8080/fortune http://localhost:8080/workout in the browser and check if the reloading of application happens automatically or not ------------------------------------------------------------------------------------------------------------ Spring Boot Actuator =============================== - To monitor and manage, checking the application matrix, application health use this - They Automatically exposes endpoints to monitor and manage your application - easily get DevOps functionality - Just add the depedency to pom.xml file org.springframework.boot spring-boot-starter-actuator - Endpoints are prefixed with : /actuator write this like ...8080/actuator/health endpoints description ------------------ -------------------- /health Health information about application /info Information about your given project /auditevents Audit events of your application /beans List all the beans registered in the spring application context /mapping List of all @RequestMapping paths ...etc www.luv2code.com/actuator-endpoints - By default /info and /health are exposed - To expose all the actuator endpoints over HTTP in "application.properties" write this management.endpoints.web.exposure.include=* * : exposes all the endpoints info,health ...etc : selected endpoints, separated by a "," Do this --------------------- 1. Copy paste and rename 2_SpringBoot_Devtools to 3_SpringBoot_Actuator 2. In pom.xml add the depedency org.springframework.boot spring-boot-starter-actuator 3. Run the SpringBootMainApplication and check it in browser by typing http://localhost:8080/actuator/health http://localhost:8080/actuator/threaddump http://localhost:8080/actuator/beans http://localhost:8080/actuator/info for /info it will show {} To show info in "application.properties" write this # all the prefix info. will be used by ../actuator/info info.app.main=My First Spring Boot App info.app.description=It is a nice Application info.app.verison=1.0.0 # use wildcard * to expose all the endpoints # To expose individual separate them with a , management.endpoints.web.exposure.include=* 4. Check the endpoints again in the browser --------------------------------------------------------------------------------------------------------------------- ******************************************************************* Adding Security to Actuators ------------------------------ - We do not wanna expose everything to everyone - To add the security, add this depedency to the pom.xml file org.springframework.boot spring-boot-starter-security - To check for beans http://localhost:8080/actuator/beans it will ask you for login info. - Default user name : user - Password is : look in the console window foe generated password - To override those, and give your own custom username and password in appliction.properties file write this spring.security.user.name=debi spring.security.user.password=debi - To exclude some of the endpoints, in application.properties write this management.endpoints.web.exposure.exclude=health,info Do this --------------------- 1. In 3_SpringBoot_Actuator in pom.xml write this org.springframework.boot spring-boot-starter-security 2. run the Main application again, and see the things 3. check for the user = user and password is in the console 4. In application.properties write this # all the prefix info. will be used by ../actuator/info info.app.main=My First Spring Boot App info.app.description=It is a nice Application info.app.verison=1.0.0 # use wild-card * to expose all the end-points # To expose individual separate them with a , management.endpoints.web.exposure.include=* # Exclude individual end-points with a , delimiter #management.endpoints.web.exposure.exclude=health,info # Provide your own custom userId and password #spring.security.user.name=debi #spring.security.user.password=debi 5. Write each and test in browser ------------------------------------------------------------------------------------------------------------------------- Injecting properties from properties file ================================================ Do this ---------------- 1. Copy paste and rename 3_SpringBoot_Actuator to 4_SpringBoot_Properties 2. In application.properties # Define the custom properties coach.name=Micky Mouse team.name=The Mouse Club 3. In FunRestController.java package demo.springboot.firstbootapp.rest; import java.time.LocalDateTime; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class FunRestController { // injecting properties for : coach.name and team.name @Value("${coach.name}") private String coachName; @Value("${team.name}") private String teamName; // expose new endpoint for "team info" @GetMapping("/teaminfo") public String teamInfo() { return "Coach : "+coachName+" , Team Name : "+teamName; } // expose "/" that return Hello World @GetMapping("/") public String sayHello() { return "Hello World!, Time on the Server is "+LocalDateTime.now(); } // expose a new endpoint for "workout" @GetMapping("/workout") public String getDailyWorkout() { return "Run a hard 5K"; } @GetMapping("/fortune") public String getDailyFortune() { return "Today is your lucky day"; } } 4. Run the main application and check in the browser as localhost:8080/teaminfo ------------------------------------------------------------------------------------------------------------ Spring Boot Properties ================================== - 1000+ properties are there www.luv2code.com/spring-boot-props - There are several groupings for the properties Core Web Security Data Actuator Integration DevTools Testing Core properties ------------------------- - Log levels severity mapping Levels are : TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF logging.level.org.springframework=DEBUG logging.level.org.hibernate=TRACE logging.level.com.luv2code=INFO - Log file name logging.file=mylogfile.log www.luv2code.com/spring-boot-logging WEB Properties --------------------------- - HTTP Server port server.port = 7070 - Context path of applciation (default is /) server.servlet.context-path=/myapp --> Need of this is, you will access it now as localhost:7070/myapp/.... - Default HTTp session timeout (default is 30 minutes) server.servlet.session.timeout=15m (15 minutes) Actuator Properties ------------------------------ - Endpoints to include/exclude by name or wild-card management.endpoints.web.exposure.include/exclude=* - Basepath for actuator end points management.endpoints.web.base-path=/actuator Security Properties --------------------------- - Default User name and password spring.security.user.name=admin spring.security.user.password=password Data Properties --------------------- - JDBC Url of the Database spring.datasource.url=jdbc:mysql://locahost:3306/db_name - Login username and password of the database spring.datasource.username=username spring.datasource.password=password Spring Boot REST API - CRUD ========================================== Create a RESR API for Employee Directory REST Clients should be able to - Get a list of employee - Get a single employee by empId - Add a new Employee - Update an employee - Delete an employee Employee Employee Employee REST <---> Service <---> DAO (Hibernate API/ JPA API/ Spring Data JPA) <---> Database Controller - Spring Boot, No Java config, No XML - New User Created in MySQL - username : spring - password : spring 1. Setup Database Table ----------------------------------- CREATE DATABASE IF NOT EXISTS `employee_directory`; USE `employee_directory`; -- -- Table structure for table `employee` -- DROP TABLE IF EXISTS `employee`; CREATE TABLE `employee` ( `id` int(11) NOT NULL AUTO_INCREMENT, `first_name` varchar(45) DEFAULT NULL, `last_name` varchar(45) DEFAULT NULL, `email` varchar(45) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=myISAM AUTO_INCREMENT=1 DEFAULT CHARSET=latin1; -- -- Data for table `employee` -- INSERT INTO `employee` VALUES (1,'Leslie','Andrews','leslie@luv2code.com'), (2,'Emma','Baumgarten','emma@luv2code.com'), (3,'Avani','Gupta','avani@luv2code.com'), (4,'Yuri','Petrov','yuri@luv2code.com'), (5,'Juan','Vega','juan@luv2code.com'); 2. From www.start.spring.io create new project with depedencies, name was crudapp, rename it to 5_EmployeeManagement_Hibernate i. WEB ii. DevTools iii. Lombok iv. MySQL Driver v. Data JPA : Hibernate *********************************************************************************** Lombok ==================== package demo.springcrud.crudapp.entity; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import lombok.AllArgsConstructor; import lombok.Cleanup; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.NonNull; import lombok.RequiredArgsConstructor; import lombok.Setter; import lombok.ToString; @AllArgsConstructor @NoArgsConstructor(forced = true) @RequiredArgsConstructor // For selected type of constructor, to select, make them not null or it only considers the fields that are non-static and final @Getter @Setter @ToString @EqualsAndHashCode @NonNull public @Data class Test { private int id; // @NonNull private String final firstName; // @NonNull private String final lastName; private String email; public static void main(String[] args) throws IOException { @Cleanup InputStream in = new FileInputStream(args[0]); @Cleanup OutputStream out = new FileOutputStream(args[1]); } } Annotation Will Generate ---------------- -------------------- @Getter/@Setter Getters and Setters @ToString toString() @EqualsAndHashCode equals() and hashCode() @Data Generate all of the above and @AllArgsConstructor Constructor with all the arguments @NoArgsConstructor Default Constructor @RequiredArgsConstructor For selected type of constructor, to select, make them not null or it only considers the fields that are non-static and final @Cleanup Automatic resource management: Call your close() and finally block ...etc *********************************************************************************** 3. DAO Layer - Here EntityManager will serve the purpose instead of SessionFactory - Spring Boot will automatically create beans for Datasource and EntityManager - Version1 : Use EntityManager but leverage native Hibernate API - Version2 : Use EntityManager and standard JPA API - Version3 : Use Spring Data JPA Development Process ------------------------- 3.1. Update the db configs in application.properties 3.2. Create Employee entity 3.3. Create DAO interface 3.4. Create the Implementation 3.5. Create a REST Controller to use DAO 3.1. Update the db configs in application.properties ------------------------------------------------------------------ # # JDBC Properties # spring.datasource.url=jdbc:mysql://localhost:3306/employee_directory?useSSL=false&serverTimezone=UTC spring.datasource.username=spring spring.datasource.password=spring 3.2. Create Employee entity -------------------------------- package demo.springcrud.crudapp.entity; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; //import lombok.Data; @Entity @Table(name = "employee") //@Data public class Employee { //define Fields @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") private int id; @Column(name = "first_name") private String firstName; @Column(name = "last_name") private String lastName; @Column(name = "email") private String email; //define constructors public Employee() { } public Employee(String firstName, String lastName, String email) { this.firstName = firstName; this.lastName = lastName; this.email = email; } //define getters and setters public int getId() { return id; } public void setId(int id) { this.id = id; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } //define toString @Override public String toString() { return "Employee [id=" + id + ", firstName=" + firstName + ", lastName=" + lastName + ", email=" + email + "]"; } } 3.3. Create DAO interface -------------------------------- package demo.springcrud.crudapp.dao; import java.util.List; import demo.springcrud.crudapp.entity.Employee; public interface EmployeeDAO { public List findAll(); } 3.4. Create the Implementation (*** Make sure the imports are correct) ------------------------------------- package demo.springcrud.crudapp.dao; import java.util.List; import javax.persistence.EntityManager; import org.hibernate.Session; import org.hibernate.query.Query; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; import demo.springcrud.crudapp.entity.Employee; @Repository public class EmployeeDAOHibernateImpl implements EmployeeDAO { // define fields for EntityManager private EntityManager entityManager; // set up constructor injection @Autowired public EmployeeDAOHibernateImpl(EntityManager entityManager) { this.entityManager = entityManager; } @Override @Transactional // org.springframework.transaction.annotation.Transactional public List findAll() { // get the current hibernate session Session session = entityManager.unwrap(Session.class); //org.hibernate.Session // create a query Query query = session.createQuery("from Employee", Employee.class); //org.hibernate.query.Query; // execute the query List employees = query.getResultList(); // return the results return employees; } } 3.5. Create a REST Controller to use DAO ------------------------------------------------------- package demo.springcrud.crudapp.rest; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import demo.springcrud.crudapp.dao.EmployeeDAO; import demo.springcrud.crudapp.entity.Employee; @RestController @RequestMapping("/api") public class EmployeeRestController { @Autowired private EmployeeDAO employeeDAO; // quick and dirty : inject EmployeeDAO (constructor injection / Field Injection) // @Autowired // public EmployeeRestController(EmployeeDAO employeeDAO) { // this.employeeDAO = employeeDAO; // } // expose the "/employees" : return all the employees @GetMapping("/employees") public List findAll(){ return employeeDAO.findAll(); } } ***** Search in the web as http://localhost:8080/api/employees Sending JSON Data to Spring REST Controller ----------------------------------------------- - for the controller to process JSON data need to set a HTTP request header Content-type : application/json - So need to configure REST client to send the correct HTTP request header Version1 : Use EntityManager but leverage native Hibernate API ============================================================================ In 5_EmployeeManagement_Hibernate --------------------------------------- The Whole Project ======================== 1. application.properties -------------------------------- # # JDBC Properties # spring.datasource.url=jdbc:mysql://localhost:3306/employee_directory?useSSL=false&serverTimezone=UTC spring.datasource.username=spring spring.datasource.password=spring 2. Entity remains the same, Employee.java ---------------------------------------------- package demo.springcrud.crudapp.entity; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; //import lombok.Data; @Entity @Table(name = "employee") //@Data public class Employee { //define Fields @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") private int id; @Column(name = "first_name") private String firstName; @Column(name = "last_name") private String lastName; @Column(name = "email") private String email; //define constructors public Employee() { } public Employee(String firstName, String lastName, String email) { this.firstName = firstName; this.lastName = lastName; this.email = email; } //define getters and setters public int getId() { return id; } public void setId(int id) { this.id = id; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } //define toString @Override public String toString() { return "Employee [id=" + id + ", firstName=" + firstName + ", lastName=" + lastName + ", email=" + email + "]"; } } 3. EmployeeDAO.java interface --------------------------------- package demo.springcrud.crudapp.dao; import java.util.List; import demo.springcrud.crudapp.entity.Employee; public interface EmployeeDAO { public List findAll(); public Employee findById(int id); public void save(Employee employee); public void deleteById(int id); } 4. EmployeeDAOHibernateImpl.java class ----------------------------------------------- package demo.springcrud.crudapp.dao; import java.util.List; import javax.persistence.EntityManager; import org.hibernate.Session; import org.hibernate.query.Query; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; //import org.springframework.transaction.annotation.Transactional; import demo.springcrud.crudapp.entity.Employee; @Repository public class EmployeeDAOHibernateImpl implements EmployeeDAO { // define fields for EntityManager @Autowired private EntityManager entityManager; // set up constructor injection or use the field injection // @Autowired // public EmployeeDAOHibernateImpl(EntityManager entityManager) { // this.entityManager = entityManager; // } @Override // @Transactional // org.springframework.transaction.annotation.Transactional // Remove, because we have a service layer to do the transaction public List findAll() { // get the current hibernate session Session session = entityManager.unwrap(Session.class); //org.hibernate.Session // create a query Query query = session.createQuery("from Employee", Employee.class); //org.hibernate.query.Query; // execute the query List employees = query.getResultList(); // return the results return employees; } @Override public Employee findById(int id) { // get the Hibernate session Session session = entityManager.unwrap(Session.class); // get the employee Employee employee = session.get(Employee.class, id); // return the desired employee return employee; } @Override public void save(Employee employee) { // get the current session Session session = entityManager.unwrap(Session.class); // save the employee session.saveOrUpdate(employee); // If the primary key of ID is 0, it will do an insert, else it will do an update } @Override public void deleteById(int id) { // get the current session Session session = entityManager.unwrap(Session.class); Query query = session.createQuery("delete from Employee where id=:employeeId"); query.setParameter("employeeId", id); query.executeUpdate(); } } ******************* For Transaction purpose it shpuld be done in Service layer, not in DAO layer ******************* 5. EmployeeService.java interface created ------------------------------------------------- package demo.springcrud.crudapp.service; import java.util.List; import demo.springcrud.crudapp.entity.Employee; public interface EmployeeService { public List findAll(); public Employee findById(int id); public void save(Employee employee); public void deleteById(int id); } 6. EmployeeServiceImpl.java class created ---------------------------------------------- package demo.springcrud.crudapp.service; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import demo.springcrud.crudapp.dao.EmployeeDAO; import demo.springcrud.crudapp.entity.Employee; @Service public class EmployeeServiceImpl implements EmployeeService { @Autowired public EmployeeDAO employeeDAO; // Either Field injection or Constructor injection // @Autowired // public EmployeeServiceImpl(EmployeeDAO employeeDAO) { // this.employeeDAO = employeeDAO; // } @Override @Transactional //org.springframework.transaction.annotation.Transactional public List findAll() { return employeeDAO.findAll(); } @Override @Transactional //org.springframework.transaction.annotation.Transactional public Employee findById(int id) { return employeeDAO.findById(id); } @Override @Transactional //org.springframework.transaction.annotation.Transactional public void save(Employee employee) { employeeDAO.save(employee); } @Override @Transactional //org.springframework.transaction.annotation.Transactional public void deleteById(int id) { employeeDAO.deleteById(id); } } 7. EmployeeRestController.java : The controller class ------------------------------------------------------------------ ********************** For each method in here, do check in web-browser or in Postman. - Download Postman, a setup file will be there, give username, password, email and create account - Select new tab, just like browser, and paste the link from the browser http://localhost:8080/api/employees ... etc For Select : Method is GET For Insert : Method is POST : For this, select raw, then JSON to write the details { "firstName" : "Hector", "lastName" : "Perez", "email" : "hector@perez.com" } For Update : Method is PUT { "id": 1, "firstName": "Debi", "lastName": "Prasad", "email": "debi@mishra.com" } For Delete : Method is DELETE ********************** package demo.springcrud.crudapp.rest; import java.util.List; 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.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import demo.springcrud.crudapp.entity.Employee; import demo.springcrud.crudapp.service.EmployeeService; @RestController @RequestMapping("/api") public class EmployeeRestController { @Autowired private EmployeeService employeeService; // either use field injection or constructor injection // @Autowired // public EmployeeRestController(EmployeeService employeeService) { // this.employeeService = employeeService; // } // expose the "/employees" : return all the employees, method : GET @GetMapping("/employees") public List findAll(){ return employeeService.findAll(); } // add mapping for GET /employees/{employeeId} @GetMapping("/employees/{employeeId}") public Employee getEmployee(@PathVariable int employeeId) { Employee theEmployee = employeeService.findById(employeeId); if(theEmployee == null){ throw new RuntimeException("Employee id not found : "+employeeId); } return theEmployee; } // add mapping for POST /employees - add new employee @PostMapping("/employees") public Employee addEmployee(@RequestBody Employee employee) { // also just in case they pass id in JSON... set id to 0 // this is to force save of a new item... insert instead of update employee.setId(0); employeeService.save(employee); return employee; } // add mapping for PUT /employees - @PutMapping("/employees") public Employee updateEmployee(@RequestBody Employee employee) { employeeService.save(employee); return employee; } // add mapping for DELETE /employees/{employeeId} - Delete an existing employee @DeleteMapping("/employees/{employeeId}") public String deleteEmployee(@PathVariable int employeeId) { Employee theEmployee = employeeService.findById(employeeId); // throw Exception if employee is null or doesn't exist if(theEmployee == null) { throw new RuntimeException("Employee Doesn't exist with id "+employeeId); } employeeService.deleteById(employeeId); return "Deleted Employee with id : "+employeeId; } } ---------------------------------------------------------------------------------------------------------------------- Version2 : Use EntityManager and standard JPA API ============================================================== Employee Employee Employee REST <---> Service <---> DAO (Hibernate API/ JPA API/ Spring Data JPA) <---> Database Controller - Benefit : you are not locked to vendor's implementation' : portable and flexible - Standard JPA API methods are similar to Native Hibernate API - It supports JPQL (JPA Query Language) Action Native Hibernate Method JPA Method ----------- ---------------------------- --------------- Create/save new entity session.save(obj) entityManager.persist(obj) Retrieve entity by id session.get() / load() entityManager.find(...) Retrieve a list of entities session.createQuery() entityManager.createQuery() Save or Update entity session.saveOrUpdate() entityManager.merge() Delete entity session.delete() entityManager.remove() Do this ------------ 1. Copy paste and rename 5_EmployeeManagement_Hibernate to 6_EmployeeManagement_JPA 2. In 6_EmployeeManagement_JPA in "demo.springcrud.crudapp.dao" package EmployeeDAOJPAImpl.java ------------------------------ package demo.springcrud.crudapp.dao; import java.util.List; import javax.persistence.EntityManager; import javax.persistence.Query; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; import demo.springcrud.crudapp.entity.Employee; @Repository public class EmployeeDAOJPAImpl implements EmployeeDAO { @Autowired private EntityManager entityManager; // Either use this field injection or make it constructor injection // @Autowired // public EmployeeDAOJPAImpl(EntityManager entityManager) { // this.entityManager = entityManager; // } @Override public List findAll() { // create a query Query query = entityManager.createQuery("from Employee"); // execute the query and get a Result List List employees = query.getResultList(); // return the list return employees; } @Override public Employee findById(int id) { // get Employee Employee employee = entityManager.find(Employee.class, id); // return the employee return employee; } @Override public void save(Employee employee) { // save or Update the Employee Employee theEmployee = entityManager.merge(employee); // if id is 0, they will insert, if not, it will do an update // updated id from db... so we can get generated id for save/insert employee.setId(theEmployee.getId()); } @Override public void deleteById(int id) { //delete the object with primary key Query query = entityManager.createQuery("delete from Employee where id=:employeeId"); query.setParameter("employeeId", id); query.executeUpdate(); } } 3. Update the EmployeeServiceImpl.java ---------------------------------------------- package demo.springcrud.crudapp.service; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import demo.springcrud.crudapp.dao.EmployeeDAO; import demo.springcrud.crudapp.entity.Employee; @Service public class EmployeeServiceImpl implements EmployeeService { @Autowired @Qualifier("employeeDAOJPAImpl") private EmployeeDAO employeeDAO; // Either Field injection or Constructor injection // @Autowired // public EmployeeServiceImpl(@Qualifier("employeeDAOJPAImpl") EmployeeDAO employeeDAO) { // this.employeeDAO = employeeDAO; // } @Override @Transactional //org.springframework.transaction.annotation.Transactional public List findAll() { return employeeDAO.findAll(); } @Override @Transactional //org.springframework.transaction.annotation.Transactional public Employee findById(int id) { return employeeDAO.findById(id); } @Override @Transactional //org.springframework.transaction.annotation.Transactional public void save(Employee employee) { employeeDAO.save(employee); } @Override @Transactional //org.springframework.transaction.annotation.Transactional public void deleteById(int id) { employeeDAO.deleteById(id); } } 4. Rest is the same, now run the main app and test -------------------------------------------------------------------------------------------------------------------- Spring Data JPA ========================== Version3 : Use Spring Data JPA in Spring Boot ========================================================== Employee Employee Employee REST <---> Service <---> DAO (Hibernate API/ JPA API/ Spring Data JPA) <---> Database Controller Problem --------------- - To get/save/findById... the codes are written and the entity type is given. If we have to map more than one entity, it is resource consuming and inappripriate to write implement things for each entity class. Wish --------------- - Spring will create a DAO for me, plug in my entity type and primary key, Give me all basic CRUD features for free. Solution --------------- - Spring Data JPA - It Creates a DAO and plugin your entity type and primary key - Spring will give you a CRUD implementation for free - It provides an interface called JpaRepository - It exposes methods (some by inheritance from parents) findAll(), findById(), save(), deleteById(), ...etc Development Process ---------------------------- 1. Extend JpaRepository interface public interface EmployeeRepository extends JpaRepository { // } 2. Use your Repository in your app, no implementation class for DAO needed, directly use methods in implemented Service class JpaRepository Advanced features ---------------------------------- - Extending and adding custom queries with JPQL - Query Domain Specific Language (Query DSL) - Defining custom methods (low-level-coding), you can do that too as per your requirement https://docs.spring.io/spring-data/jpa/docs/current/api/org/springframework/data/jpa/repository/JpaRepository.html Do this ---------------- 1. Copy paste and rename 6_EmployeeManagement_JPA to 7_EmployeeManagement_SpringDataJPA 2. Delete everything in the "demo.springcrud.crudapp.dao" package 3. Create EmployeeRepository.java in dao package --------------------------------------------------------- package demo.springcrud.crudapp.dao; import org.springframework.data.jpa.repository.JpaRepository; import demo.springcrud.crudapp.entity.Employee; public interface EmployeeRepository extends JpaRepository { // No need for any code } 4. Changes in EmployeeServiceImpl.java --------------------------------------------- package demo.springcrud.crudapp.service; import java.util.List; import java.util.Optional; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import demo.springcrud.crudapp.dao.EmployeeRepository; import demo.springcrud.crudapp.entity.Employee; @Service public class EmployeeServiceImpl implements EmployeeService { @Autowired private EmployeeRepository employeeRepository; // Either use the Field injection or Constructor injection // @Autowired // public EmployeeServiceImpl(EmployeeRepository employeeRepository) { // this.employeeRepository = employeeRepository; // } @Override public List findAll() { return employeeRepository.findAll(); } @Override public Employee findById(int id) { Optional result = employeeRepository.findById(id); Employee theEmployee = null; if(result.isPresent()) theEmployee = result.get(); else // In case we didn't find the employee by that id throw new RuntimeException("Employee Not Found with Id : "+id); return theEmployee; } @Override public void save(Employee employee) { employeeRepository.save(employee); } @Override public void deleteById(int id) { employeeRepository.deleteById(id); } } 5. Run the main application and test ---------------------------------------------------------------------------------------------------------------- Spring Data REST in Spring Boot ======================================= Problem ----------- - For REST API, we created a REST Controller for Employee, what if need another REST Controller for another entity, we would have to repea the same code over and over again. Wish ------------ - Create REST API for me, Use my existing JpaRepository (entity, primary key), and Give me all basic REST API CRUD features for free Solution ---------------- - Spring Data REST is the Solution https://spring.io/projects/spring-data-rest - Leverages existing JpaRepository, and give you REST CRUD implementation for free - exposes these endpoints for free HTTP Method URL CRUD Action ----------- ----------- ----------------------- POST /employees Create/Insert a new employee GET /employees Read a list of employees GET /employees{empoyeeId} Read a single employee PUT /employees{empoyeeId} Update an existing employee DELETE /employees{empoyeeId} Delete an existing employee - Spring Data REST will scan your project for JpaRepository - Expose REST APIs for each entity type for your JpaRepository public interface EmployeeRepository extends JpaRepository { } - It will expose Employee's endpoints' - By default, it will create endpoints based on entity type, simple pluralized form - Entity type is "Employee" , Endpoint will be "/employees" - To give application a base path, in application.properties add properties # #Spring Data REST Properties # spring.data.rest.base-path=/magic-api Development Process ------------------------ 1. Add the Spring Data REST to your Maven POM file, no coding required org.springframework.boot spring-boot-starter-data-rest - In a Nutshell, you need only 3 items 1. Your Entity Class : Employee 2. JpaRepository : EmployeeRepository extends JpaRepository 3. Maven POM depedency Application Architecture --------------------------- Spring Data REST <---> EmployeeRepository <---> Database /employees Spring Data JPA - No need of RESTControler, and Service Layer - Spring Data REST endpoints are HATEOAS compliant - HATEOAS : Hypermedia As The Engine Of Application State - Hypermedia-driven sites provide information to access REST Interfaces HATEOAS (Hypermedia As The Engine Of Application State) =================================================================== - Spring Data REST response using HATEOAS - For example REST response from : GET /employees/3 : Get a single employee It will give response in JOSN form like this { "firstName" : "Debi" "lastName" : "Mishra" "email" : "debi@mishra.com" // Upto this is the employee data "-links" : { // From this, Response Metadata links to entry or data "self" : { "href" : http://localhost:8080/employees/3 }, "employee" : { "href" : http://localhost:8080/employees/3 } } } - HATEOAS uses Hypertext Application Language (HAL) data format Advanced Features ------------------------- - Pagination, sorting and searching - Extending and adding custom queries with JPQL - Query Domain Specific Language (Query DSL) Do this ----------------- 1. Copy paste and rename 7_EmployeeManagement_SpringDataJPA to 8_EmployeeManagement_SpringDataREST 2. In 8_EmployeeManagement_SpringDataREST delete the "service" and "rest" package 3. In Pom.xml add this org.springframework.boot spring-boot-starter-data-rest 4. In application.properties add this # #Spring Data REST Properties # spring.data.rest.base-path=/magic-api 5. Run the main app and test in the browser as http://localhost:8080/magic-api/employees http://localhost:8080/magic-api/employees/1 ... etc For Update or PUT Mehtod, do not give id in the JSON, give the id in the URL url : http://localhost:8080/magic-api/employees/1 JSON Body : { "firstName" : "Debi", "lastName" : "Prasad", "email" : "debi@mishra.com" } ------------------------------------------------------------------------------------------------------------------------- Spring Data Configuration, Pagination, Sorting =========================================================== Configuration --------------- Problem ---------- - Spring Data REST doesnot handle complex pluralized forms - You need to manually specify a plural name or expose a different resource name Solution ----------- - Specify plural name / path name with annotation In Repository class use @RepositoryRestResource(path="members") : instead of /emploees it will be reflected as /members public interface EmployeeRepository extends JpaRepository { // No need for any code } http://localhost:8080/members Pagination ---------------- - By default, Spring Data REST will return the first 20 elements page size is 20 http://localhost:8080/employees?page=0 http://localhost:8080/employees?page=1 http://localhost:8080/employees?page=2 Spring Data REST properties ----------------------------------------- Name Description ------------- --------------------- spring.data.rest.base-path Base path used to expose repository resources spring.data.rest.default-page-size Default size of pages, return how many elements per page spring.data.rest.max-page-size Maximum page size ...etc www.luv2code.com/spring-boot-props Sorting --------------- - Sort by property names of your entity - In employee we have firstName, lastName, email sort by lastName(ascending is default) : http://localhost:8080/employees?sort=lastName sort by firstName(descending) : http://localhost:8080/employees?firstName,desc sort by firstName, lastName (ascending) : http://localhost:8080/employees?firstName,lastName,asc Do this ----------------- 1. In 8_EmployeeManagement_SpringDataREST in EmployeeRepository add this annotation package demo.springcrud.crudapp.dao; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.rest.core.annotation.RepositoryRestResource; import demo.springcrud.crudapp.entity.Employee; @RepositoryRestResource(path = "members") public interface EmployeeRepository extends JpaRepository { // No need for any code } 2. Check in Postman as http://localhost:8080/magic-api/members 3. Comment the RepositoryRestResource and make the default-page-size to 3 and check in postman http://localhost:8080/magic-api/employees?page=1&size=3 4. Make the default-page-size back to 20 and check in postman as http://localhost:8080/magic-api/employees 5. Check in the postman for sorting as http://localhost:8080/magic-api/employees?sort=lastName ------------------------------------------------------------------------------------------------------------------ Thymeleaf with Spring Boot ================================== - It is a Java Templating Engine - Commonly used to henerate HTML views for Web Apps - www.thymeleaf.org - It is a separate project, un-related to Spring.io - Just like jsp, It has its own engine, tags, processed on server - Difference is : JSP can only be used in a web-environment, whereas Thymeleaf can be used in web or non-web environment. - Email Tamplate, CSV Template, PDF Template are non-web environment Maven ------------ org.thymeleaf thymeleaf 3.0.11.RELEASE OR org.springframework.boot spring-boot-starter-thymeleaf - Put the templates in folder src/main/resources/templates - For webapps, Thymeleaf templates have a .html extension Thymeleaf Helloworld : Write some code =========================================== Do this ------------ 1. Open start.spring.io 2. Create a project with group : demo.springboot artifact : thymeleaf Name : 9_SpringBoot_ThymeleafIntro Depedencies : Web, Thymeleaf, Lombok, DevTools 3. Import that project to workspace 4. Create a package demo.springboot.thymeleaf.controller 5. Create a Controller in this package ThymeleafApplicationController.java ----------------------------------------- package demo.springboot.thymeleaf.controller; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; @Controller public class ThymeleafApplicationController { // create a mapping for "/hello" @GetMapping("/hello") public String sayHello(Model model) { model.addAttribute("theDate", new java.util.Date()); return "helloworld"; // It will go to helloworld.html by default } } 6. In src/main/resources/templates folder create a helloworld.html page helloworld.html ----------------------- Thymeleaf Application

7. Run the main app and test in browser as "http://localhost:8080/hello" ------------------------------------------------------------------------------------------------------------------- Adding CSS to Thymeleaf Template ---------------------------------------- 1. Create a folder named "css" in resources/static path 2. Create a css file in there demo.css ------------ .funny{ font-style: italic; color: blue; font-size: 40px; } 3. helloworld.html ---------------------------- Thymeleaf Application

4. Run and test the app -------------------------------------------------------------------------------------------------------------------------- Build HTML Tables ========================= Do this -------------- 1. Create a package "demo.springboot.thymeleaf.model" 2. Create a Employee class in it Employee.java ------------------- package demo.springboot.thymeleaf.model; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @NoArgsConstructor @AllArgsConstructor public @Data class Employee { private String firstName; private String lastName; private String email; } 3. In controller package Create a EmployeeController EmployeeController.java ------------------------------ package demo.springboot.thymeleaf.controller; import java.util.ArrayList; import java.util.List; import javax.annotation.PostConstruct; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import demo.springboot.thymeleaf.model.Employee; @Controller @RequestMapping("/employees") public class EmployeeController { // load the Employee Data private List employees; @PostConstruct private void loadData() { // create employees Employee emp1 = new Employee("Debi", "Prasad", "debi@mishra.com"); Employee emp2 = new Employee("Debi", "Mishra", "prasad@mishra.com"); Employee emp3 = new Employee("Vicky", "Prasad", "debi@vicky.com"); Employee emp4 = new Employee("Mishra", "Prasad", "prasad@mishra.com"); // create the list employees = new ArrayList(); // add to the list employees.add(emp1); employees.add(emp2); employees.add(emp3); employees.add(emp4); } // add mapping for "/list" @GetMapping("/list") public String listEmployees(Model model) { model.addAttribute("employees", employees); return "list-employees"; } } 4. In templates folder create list-employees.html list-employees.html -------------------------- Employee Directory

Employee Directory


First Name Last Name Email Action
First Name Last Name Email Action
First Name Last Name Email
5. In browser write "http://localhost:8080/employees/list" and test the app --------------------------------------------------------------------------------------------------------------------------- BootStraping the EmployeeList --------------------------------------- list-employees.html --------------------------- Employee Directory

Employee Directory


First Name Last Name Email
-------------------------------------------------------------------------------------------------------------------------------- Thymeleaf CRUD : Real Time Application ================================================= Employee Management ---------------------- 1. copy and paste and rename 9_SpringBoot_ThymeleafIntro to 10_SpringBoot_EmployeeManagement_Thymeleaf 2. Add spring data-jpa support and mysql to pom.xml org.springframework.boot spring-boot-starter-data-jpa mysql mysql-connector-java 3. Add the entity, service, dao package from 7_EmployeeManagement_SpringDataJPA to this project and rename the packages accordingly. 4. Copy paste the application.properties file and overwrite it. 5. EmployeeController.java ------------------------------------- package demo.springboot.thymeleaf.controller; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import demo.springboot.thymeleaf.entity.Employee; import demo.springboot.thymeleaf.service.EmployeeService; @Controller @RequestMapping("/employees") public class EmployeeController { @Autowired private EmployeeService employeeService; // add mapping for "/list" @GetMapping("/list") public String listEmployees(Model model) { List employees = employeeService.findAll(); model.addAttribute("employees", employees); return "list-employees"; } } 6. in static folder create a index.html to redirect it to http://localhost:8080/employees/list index.html --------------------- 7. Run the main app and test it as http://localhost:8080 -------------------------------------------------------------------------------------------------------------------------- For Add Employees ---------------------- Thymeleaf Expressions to build Form ----------------------------------------- th:action Location to send form dats th:object Reference to Model attribute th:field Bind input field to a property on model attribute ...etc 8. list-employees.html ----------------------------------

Employee Directory


Add Employee 9. EmployeeController.java -------------------------------- // add mapping for /showFormForAdd @GetMapping("/showFormForAdd") public String showFormForAdd(Model model) { // create model attribute to bind the data Employee employee = new Employee(); model.addAttribute("employee", employee); return "employee-form"; } 10. Create employee-form.html in templates folder employee-form.html ----------------------------- Add Employee

Employee Directory


Save Employee


Back to Employees list
11. EmployeeController.java ------------------------------------ // add mapping for saving the employee : "/save" @PostMapping("/save") public String saveEmployee(@ModelAttribute("employee") Employee employee) { // for employee : refer th:object in employee-form.html // save the employee employeeService.save(employee); // use redirect to prevent duplicate submission return "redirect:/employees/list"; } 12.Sorting Employee List By last name ------------------------------------------- EmployeeRepository.java ------------------------------ package demo.springboot.thymeleaf.dao; import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; import demo.springboot.thymeleaf.entity.Employee; public interface EmployeeRepository extends JpaRepository { // No need for any code // add method to sort by lastname public List findAllByOrderByLastNameAsc(); // https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.query-methods.details // To look for the method name patterns } EmployeeServiceImpl.java ---------------------------------- @Autowired private EmployeeRepository employeeRepository; // Either use the Field injection or Constructor injection @Override public List findAll() { return employeeRepository.findAllByOrderByLastNameAsc(); } 13. Run and test the application ------------------------------------------------------------------------------------------------------------------------- Update an Employee ============================ 14. list-employees.html ---------------------------
First Name Last Name Email Action
Update
15. EmployeeController.java ------------------------------- // add apping for "/showFormForUpdate" @GetMapping("/showFormForUpdate") public String showFormforUpdate(@RequestParam("employeeId") int id, Model model) { // get the employee from EmployeeService Employee employee = employeeService.findById(id); // set employee as a model attribute to pre-populate the form model.addAttribute("employee", employee); // send over to our forms return "employee-form"; } 16. employee-form.html ---------------------------- - Add a hidden form field to tell which employee you are updating
17. Run and test the app ---------------------------------------------------------------------------------------------------------------- Delete an Employee --------------------------- 18. list-employees.html ---------------------------- Update Delete 19. EmployeeController.java ------------------------------- // add mapping for "/deleteEmployee" @GetMapping("/deleteEmployee") public String deleteEmployee(@RequestParam("employeeId") int id) { // delete the employee employeeService.deleteById(id); // redirect to /employees/list return "redirect:/employees/list"; } 20. Run and test the app --------------------------------------------------------------------------------------------------------------------