Archive
Spring Securty 3.2 with Custom Service
user table
CREATE TABLE 'user' ( 'user_id' int(11) NOT NULL AUTO_INCREMENT, 'user_name' varchar(250) NOT NULL, 'password' varchar(250) DEFAULT NULL, PRIMARY KEY ('user_id','user_name') ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1
role table
CREATE TABLE 'role' ( 'role_id' int(11) NOT NULL AUTO_INCREMENT, 'role_name' varchar(250) NOT NULL, PRIMARY KEY ('role_id','role_name') ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1
user_role join table
CREATE TABLE 'user_role' ( 'user_id' int(11) NOT NULL, 'role_id' int(11) NOT NULL, PRIMARY KEY ('user_id','role_id') ) ENGINE=InnoDB DEFAULT CHARSET=latin1
user and role has many to many relationship, and joining table is user_role
User.java
package com.java2practice.springsecurity.model; import java.util.HashSet; import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.Table; @Entity @Table(name="user") public class User { @Id @GeneratedValue @Column(name="user_id") private Integer userId; @Column(name="user_name") private String userName; @Column(name="password") private String password; @ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL) @JoinTable(name = "user_role", joinColumns = { @JoinColumn(name = "USER_ID", nullable = false, updatable = false) }, inverseJoinColumns = { @JoinColumn(name = "ROLE_ID", nullable = false, updatable = false) }) private Set<Role> userRoles = new HashSet<Role>(0); public Integer getUserId() { return userId; } public void setUserId(Integer userId) { this.userId = userId; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public Set<Role> getUserRoles() { return userRoles; } public void setUserRoles(Set<Role> userRoles) { this.userRoles = userRoles; } }
Role.java
package com.java2practice.springsecurity.model; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table(name="role") public class Role { @Id @GeneratedValue @Column(name="role_id") private Integer roleId; @Column(name="role_name") private String roleName; public Integer getRoleId() { return roleId; } public void setRoleId(Integer roleId) { this.roleId = roleId; } public String getRoleName() { return roleName; } public void setRoleName(String roleName) { this.roleName = roleName; } }
UserDao.java
package com.java2practice.springsecurity.dao; import javax.annotation.Resource; import org.apache.log4j.Logger; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.criterion.Restrictions; import com.java2practice.springsecurity.model.User; public class UserDao { Logger logger = Logger.getLogger(this.getClass()); @Resource SessionFactory sessionFactory; public User getUser(String userName){ logger.info("getUser called"); Session session = null; try{ session = sessionFactory.openSession(); User user = (User) session.createCriteria(User.class). add(Restrictions.eq("userName", userName)).uniqueResult(); return user; }catch (Exception e) { e.printStackTrace(); return null; } } }
Our service has to implement org.springframework.security.core.userdetails.UserDetailsService
it has one method
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
this method returns org.springframework.security.core.userdetails.UserDetails
UserDetails user = new User(“username”, “password”,true, true, true, true, authorities);
like this we can convert our User to Spring UserDetails.
UserDetailsServiceImpl.java
package com.java2practice.springsecurity.service; import java.util.ArrayList; import java.util.List; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.authority.GrantedAuthorityImpl; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import com.java2practice.springsecurity.dao.UserDao; import com.java2practice.springsecurity.model.Role; @SuppressWarnings("deprecation") public class UserDetailServiceImpl implements UserDetailsService{ Logger logger = Logger.getLogger(getClass()); @Autowired UserDao userDao; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { try{ logger.info("loadUserByUsername "); com.java2practice.springsecurity.model.User userFromDB = userDao.getUser(username); logger.info("got user from database "+userFromDB); List<GrantedAuthorityImpl> authorities = new ArrayList<GrantedAuthorityImpl>(); for(Role role : userFromDB.getUserRoles()){ authorities.add(new GrantedAuthorityImpl(role.getRoleName())); } UserDetails user = new User(userFromDB.getUserName(), userFromDB.getPassword(), true, true, true, true, authorities); return user; }catch(Exception e){ logger.error("error "+e); return null; } } }
WebController.java
package com.java2practice.springsecurity.controller; import org.apache.log4j.Logger; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class WebController { Logger logger = Logger.getLogger(this.getClass()); @RequestMapping("/home") public String home(){ logger.info("webcontroller"); return "home"; } }
applicationContext.xml
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xmlns:security="http://www.springframework.org/schema/security" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd"> <context:annotation-config /> <context:property-placeholder location="classpath:db.properties" /> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="${jdbc.driverClassName}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.user}" /> <property name="password" value="${jdbc.password}" /> </bean> <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="packagesToScan" value="com.java2practice.springsecurity.model" /> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.format_sql">true</prop> </props> </property> </bean> <!-- A custom service where Spring will retrieve users and their corresponding access levels --> <bean id="userDetailServiceImpl" class="com.java2practice.springsecurity.service.UserDetailServiceImpl" autowire="byName" /> <bean id="userDao" class="com.java2practice.springsecurity.dao.UserDao" autowire="byName" /> <!-- This is where we configure Spring-Security --> <security:http auto-config="true" use-expressions="true"> <security:form-login login-page="/login.jsp" authentication-failure-url="/login.jsp?error=true" default-target-url="/home.html" /> </security:http> <!-- Declare an authentication-manager to use a custom userDetailsService --> <security:authentication-manager> <security:authentication-provider user-service-ref="userDetailServiceImpl"> </security:authentication-provider> </security:authentication-manager> </beans>
dispatcher-servlet.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <context:component-scan base-package="com.java2practice.springsecurity.controller" /> <context:annotation-config /> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix"> <value>/WEB-INF/jsp/</value> </property> <property name="suffix"> <value>.jsp</value> </property> </bean> </beans>
web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"> <display-name>Spring4MVC</display-name> <welcome-file-list> <welcome-file>login.jsp</welcome-file> </welcome-file-list> <servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>*.html</url-pattern> </servlet-mapping> <filter> <filter-name>springSecurityFilterChain</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>springSecurityFilterChain</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <listener> <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class> </listener> </web-app>
login.jsp
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"%> <%@ taglib uri="http://www.springframework.org/tags" prefix="spring"%> <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body> <h1> <c:if test="${param.error}"> Error occurred ${param.error } </c:if> </h1> Login <form action="j_spring_security_check" method="post"> <p> <label for="j_username">Username</label> <input id="j_username" name="j_username" type="text" /> </p> <p> <label for="j_password">Password</label> <input id="j_password" name="j_password" type="password" /> </p> <input type="submit" value="Login" /> </form> </body> </html>
home.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Home</title> </head> <body> <h1>Welcome to Spring Security</h1> </body> </html>
Project Structure
Spring @Cacheable and @CacheEvict explained in simple terms
In Spring 3.1 we have a feature called Caching, It caches the data based on java method execution, if the java method executed before with same parameters it returns the cached data, other wise it will execute the method and puts the data into cache.
To enable caching :
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cache="http://www.springframework.org/schema/cache" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd"> <cache:annotation-driven /> //your beans </beans>
<cache:annotation-driven /> Will recognize the spring cache annotations @Cacheable and @CacheEvict
Spring cache provides
@Cacheable annotation and @CacheEvict annotation.
Put @Cacheable on the method which you want to cache.
Ex:
@Cacheable(value="messagecache", key="#id", condition="id < 10") public String getMessage(int id){ return "hello"+id; }
Here getMessage() method is marked with @Cacheable, whenever getMessage() is called it will check the messagecache, if the data is already in messagecache it will return that otherwise it will executes the getMessage() and returns data.
@Cacheable annotations has 3 attributes
- Value : is the cache name and it is mandatory, in example it is “messagecache”
- Key: based on this data will be cached and it is optional
- Condition: based on the condition data will be cached. In example if the id < 10 then only data will be cached otherwise won’t. it is optional
@CacheEvict annotation will be used to delete the data from existing cache.
@CacheEvict(“employees”)
public void saveEmployee(Employee e){
}
Here whenever a saveEmployee() is called cache will be deleted.
@CacheEvict has 5 attributes:
- Value, 2 Key, 3 condition are similar to @Cacheable, apart from these 3 we have another 2 attributes
- allEntries : is a Boolean type and delete entire cache
- beforeInvocation: is Boolean type and will delete the cache before the method execution
Will go through one small example:
In this example I have used ehcache, refer www.ehcache.org for more information
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:cache="http://www.springframework.org/schema/cache" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- Scans within the base package of the application for @Components to configure as beans --> <context:component-scan base-package="com" /> <!-- Process cache annotations --> <cache:annotation-driven /> <!-- Configuration for using Ehcache as the cache manager--> <bean id="cacheManager" p:cache-manager-ref="ehcache"/> <bean id="ehcache" p:config-location="classpath:ehcache.xml"/> <bean id="employee" class="com.java2practice.model.Employee"/> </beans>
ehcache.xml
<ehcache> <diskStore path="java.io.tmpdir"/> <cache name="employeeCache" maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true" maxElementsOnDisk="10000000" diskPersistent="false" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU"/> </ehcache>
Employee pojo:
package com.java2practice.model; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cache.annotation.Cacheable; public class Employee { Logger logger = LoggerFactory.getLogger(getClass()); @Cacheable(value="employeeCache", key = "#id") public String getEmployee(Integer id){ logger.info("get employee called"); return "employee"+id; } }
This Employee class is injected into controller:
Controller :
package com.java2practice.web; import java.util.HashMap; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import com.java2practice.model.Employee; @Controller public class WebController { @Autowired Employee employee; @RequestMapping("/index.htm") public String homePage(@RequestParam(required= false) Integer id, HashMap<String, String> map){ map.put("message", employee.getEmployee(id)); return "index"; } }
And finally here is my JSP code:
<%@ 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" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Cache Example</title> </head> <body> <h1>This is ${message }</h1> </body> </html>
First time for the URL : http://localhost:8080/springcache/index.htm?id=1
Employee class getEmployee() method will be executed and data will be placed in cache. second time for the same request with id=1 getEmployee() method wont be executed, this can be seen by using the log message “get employee called” prints only for the first time.
And for the http://localhost:8080/springcache/index.htm?id=2
getEmployee() will be called and data will be placed in cache.
Here id is the Key, if id changes method will be executed and data will be placed in cache with the key.
When ever the requests comes for the @Cacheable annotated method, spring will check the key in corresponding cache if the cache has key in it. data will be returned from the cache other wise method will be executed.
Instead of ehcache we can use Spring SimpleCacheManager also:
<!-- Configuration for using SimpleCacheManager as the cache manager--> <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager" > <property name="caches"> <set> <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="employeeCache"/> </set> </property> </bean>
How to pass JSON Object string or JSONArray string from javascript to spring controller
We usually send primitive data to spring controller by using @RequestParam annotation. But how to pass whole JSONObject string or JSONArray string to spring controller directly.
For that we have to include below jar files in buildpath
- jackson-core-asl-1.7.3.jar
- jackson-mapper-asl-1.7.3.jar
I have created Person pojo which will be mapped with javascript JSONObject exactly, Whatever the identifiers are there in this POJO should be there in Javascript JSON.
public class Person implements Serializable{ private String id; private String firstName; private String lastName; //setters and getters }
Pojo should implement Serializable interface, as Jackson will serialize and deserialize to send data between server and client.
Our json data is : {"persons": [ { "firstName": "Ramesh", "id": "id1", "lastName": "Kotha" }, { "firstName": "Sathish", "id": "id2", "lastName": "Kotha" } ] }
Below is an ajax request from jsp.
$.ajax({ type: 'POST', dataType: 'json', contentType:'application/json', url: "create_persons.htm", data:JSON.stringify(arr), success: function(data, textStatus ){ console.log(data); //alert("success"); }, error: function(xhr, textStatus, errorThrown){ //alert('request failed'+errorThrown); } });
Now will see Spring controller Code:
@RequestMapping(value="/create_persons.htm") public @ResponseBody createPerson(@RequestBody Person[] persons){ //here you can persons array as normal for(Person person : persons){ System.out.println(person.getId()); } }
By seeing @RequestBody annontation json data will be converted into Java Person[] and passed to persons array.
Add this in your Configuration file :
<bean id="jacksonMessageConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"></bean> <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> <property name="messageConverters"> <list> <ref bean="jacksonMessageConverter"/> </list> </property>
That’s it, now if you pass json string to the spring controller, it will be converted into java POJO.
Spring AOP explained in simple terms
Aspect Oriented Programming:
Developer has to concentrate on business logic, Cross cutting functionalities like Transactions, Logging, Data Access Logic should be taken care by container.
This principle is called AOP.
Until I have used AOP, I didn’t recognize its worth:
1. Let’s explore the non AOP architecture
package com.java2practice.aop; import java.math.BigDecimal; public class Account { BigDecimal currentAmount = new BigDecimal("5000"); public BigDecimal withdraw(BigDecimal withdrawlAmount){ if(withdrawlAmount.compareTo(currentAmount) < 0 ) return currentAmount.subtract(withdrawlAmount); else throw new FundsNotSufficientException(); } }
Do we have any problem with above withdraw method, if we observe closely?
First we should use logging. Otherwise resolving the errors would be a big problem.
For that we will change the above method accordingly
import java.math.BigDecimal; import org.apache.log4j.Logger; public class Account { Logger logger = Logger.getLogger(Account.class); BigDecimal currentAmount = new BigDecimal("5000"); public BigDecimal withdraw(BigDecimal withdrawlAmount){ logger.info("withdraw method started"); if(withdrawlAmount.compareTo(currentAmount) < 0 ){ return currentAmount.subtract(withdrawlAmount); } else{ throw new FundsNotSufficientException(); } logger.info("withdraw method successfully completed"); } }
Second there is no Transaction started, As withdraw method going update the currentAmount in Database it should be within Transaction so will add transaction.
import java.math.BigDecimal; import org.apache.log4j.Logger; import org.hibernate.Session; import org.hibernate.Transaction; public class Account { Logger logger = Logger.getLogger(Account.class); BigDecimal currentAmount = new BigDecimal("5000"); public BigDecimal withdraw(BigDecimal withdrawlAmount){ Transaction tx = null; Session session = getCurrentSession(); try{ logger.info("withdraw method started"); if(withdrawlAmount.compareTo(currentAmount) < 0 ){ tx = session.beginTransaction(); currentAmount = currentAmount.subtract(withdrawlAmount); tx.commit(); return currentAmount; } else{ tx.rollback(); logger.debug("FundsNotSufficientException :: withdraw method failed"); throw new FundsNotSufficientException(); } logger.info("withdraw method successfully completed"); }catch(Exception ex){ tx.rollback() logger.debug("withdraw method failed"); } } }
Now we implemented Transactions and logging successfully but what we are doing if any new requirement is coming we are changing the BankAccount class which is against Object Oriented principle.
If we think again there should be data access logic for the Bank Account class then we have to change once again the class to like this:
package com.java2practice.aop; import java.math.BigDecimal; import org.apache.log4j.Logger; import org.hibernate.Session; import org.hibernate.Transaction; public class Account { Logger logger = Logger.getLogger(Account.class); BigDecimal currentAmount = new BigDecimal("5000"); public BigDecimal withdraw(BigDecimal withdrawlAmount){ logger.info("withdraw method started"); Transaction tx = null; Session session = getCurrentSession(); try{ if(currentUser.isWIthdrawEnabled()){ if(withdrawlAmount.compareTo(currentAmount) < 0 ){ tx = session.beginTransaction(); currentAmount = currentAmount.subtract(withdrawlAmount); tx.commit(); return currentAmount; } else{ tx.rollback(); logger.debug("FundsNotSufficientException :: withdraw method failed"); throw new FundsNotSufficientException(); } logger.info("withdraw method successfully completed"); }else { logger.info("withdraw method Failed withdrawal is disabled for this user"); } }catch(Exception ex){ tx.rollback(); logger.debug("withdraw method failed"); } } }
Now it looks fine, but in future any new cross cutting functionality comes, we need to change the Bank Account class once again. What if we separate the cross cutting functionalities like Logging, Security, Transaction to outside of the business logic then we can add new cross cutting functionality without touching BankAccount class.
Imagine if the Data access logic changes, we may need to change lot of files. Same with the entire cross cutting functionalities, They should not be coupled with the business logic and should be defined separately. This we can achieve through Spring AOP.
2. Will see the AOP architecture:
For Cross cutting functionality Logging will see the AOP architecture
- CustomerDAO.java
package com.java2practice.dao; public interface CustomerDAO { String addCustomer(); String addCustomerReturnValue() void addCustomerThrowException() throws Exception; void addCustomerAround(String name); }
- CustomerDAOImpl.java
package com.java2practice.dao.impl; import com.java2practice.dao.CustomerDAO; public class CustomerDAOImpl implements CustomerDAO { public String addCustomer(){ System.out.println("addCustomer() is running "); //throw new ArithmeticException(); return "ramesh"; } public String addCustomerReturnValue(){ System.out.println("addCustomerReturnValue() is running "); return "abc"; } public void addCustomerThrowException() throws Exception { System.out.println("addCustomerThrowException() is running "); throw new Exception("Generic Error"); } public void addCustomerAround(String name){ System.out.println("addCustomerAround() is running, args : " + name); } }
Here logging will be implemented by AOP, for that we need to write an Aspect.
- LoggingAspect.java
package com.java2practice.aspect; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; @Aspect public class LoggingAspect { @Before("execution(* com.java2practice.dao.*.*(..))") public void logBefore(JoinPoint joinPoint) { System.out.println(joinPoint.getSignature().getName()+" is started! from logBefore"); } @After("execution(* com.java2practice.dao.*.*(..))") public void logAfter(JoinPoint joinPoint){ System.out.println(joinPoint.getSignature().getName()+" is completed! from logAfter"); } @AfterThrowing(pointcut = "execution(* com.java2practice.dao.CustomerDAO.*(..))", throwing= "error") public void logAround(JoinPoint joinPoint, Object error){ System.out.println(joinPoint.getSignature().getName()+" after throwing called! from after throwing "+error); } @AfterReturning( pointcut = "execution(* com.java2practice.dao.CustomerDAO.addCustomerReturnValue(..))", returning= "result") public void afterReturning(JoinPoint joinPoint, Object result){ System.out.println("after returning called result: "+result); } @Around("execution(* com.java2practice.dao.CustomerDAO.addCustomerAround(..))") public void logAround(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("Around before is running!"); if(joinPoint.getArgs()[0].equals("ramesh")){ joinPoint.proceed(); //continue on the intercepted method System.out.println("Around after is running!"); } else{ System.out.println("you dont have permission to call this method"); } } }
- @Aspect annotation will tells the container it is an Aspect
- @Before(“execution(* com.java2practice.dao.*.*(..))”) — it will be executed before any method executes in com.java2.practice.dao package
- @After(“execution(* com.java2practice.dao.*.*(..))”) — it will be executed after any method execution in com.java2.practice.dao package
- @AfterThrowing(pointcut = “execution(* com.java2practice.dao.CustomerDAO.*(..))”, throwing= “error”) — It will be executed if any method throws any exception in com.java2practice.dao.CustomerDAO class and the error will be captured in “error”.
- @AfterReturning( pointcut = “execution(* com.java2practice.dao.CustomerDAO.addCustomerReturnValue(..))”, returning= “result”) – it will be executed if com.java2practice.dao.CustomerDAO.addCustomerReturnValue() method returns any value and the returned value will be stored in “result”.
- @Around(“execution(* com.java2practice.dao.CustomerDAO.addCustomerAround(..))”) – This is very powerful l aspect, it will be executed before the method execution and after the method execution. Even we can control the method execution by joinPoint.proceed()
So now will write spring-config.xml
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd "> <aop:aspectj-autoproxy /> <bean id="customerDAO" /> <!-- Aspect --> <bean id="logAspect" /> </beans>
Finally will write Main program to see the power of AOP.
package com.java2practice.aspect; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.java2practice.dao.CustomerDAO; public class Main { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml"); CustomerDAO customer = (CustomerDAO) context.getBean("customerDAO"); try { //customer.addCustomer(); customer.addCustomerAround("ramesh"); //customer.addCustomerReturnValue(); //customer.addCustomerThrowException(); } catch (Exception e) { //e.printStackTrace(); } } }
Have fun with AOP.
How MVC belongs to only Presentation Layer?
Spring MVC in simple terms:
It is a presentation framework, you may have doubt “How MVC belongs to only presentation layer”.
MVC Framework:
View and Controller belongs to Presentation Layer as Controller takes the URL and directs to appropriate View based on the given URL.
MVC Flow:
- Request comes to the controller, controller decides is there any business logic to be implemented for this request or not.
- If no business logic required it will redirect to the appropriate View. Ex: if we are requesting a login form no business logic required here so controller directly redirect login.jsp.
- If business logic required to the particular request it will call the appropriate Service class to process the request. Based on the service object response, controller redirects to the appropriate view.
- In the view if there is any data is to be shown, then the data will be taken from appropriate model.
With the above diagram View and Controller directly interacting with Presentation Layer.
And model is used by View to show the data.
So MVC belongs to Presentation Layer
MVC in Three tier architecture:
In three tier architecture we have Presentation Tier, Middle Tier, Data Access Tier, simply we can call Presentation layer, Service layer and DAO Layer.
Presentation layer interacts with the Service layer and Service layer interacts with DAO layer.
The Flow will be like this:
- Request comes to controller and controller decides whether this request needs any business logic to be implemented or not
- If no it simply returns a view.
- If yes it interacts with Service Layer and Service Layer interacts with DAO Layer
- Later the based on the response from Service Layer appropriate View will be shown by the Controller.
Spring recommends the same model can be used for presentation layer, In Struts we have form beans as presentation layer model. But in Spring we can use the same model across all the Three Layers.
Inversion of Control – Dependency Injection explained in simple terms
Inversion of Control – Dependency Injection:
Instead of Object creating its dependencies, let container create and inject into Object. Control is being inverted from Object to Container.
Encapsulation says “The Caller should know its dependencies”
IOC says “The Caller should know its dependency interfaces”
If the caller doesn’t know its dependencies and knows only interfaces, we can give any of the class to the Object. Will explain with an example
Let’s Say we have a
package com.java2practice; public class UsersListFromDB{ public void printUsers(){ System.out.println(“printing users from database”); } }
And UsersView to use the UsersListFromDB,
package com.java2practice; public class UsersView{ private UsersListFromDB userListFromDB = new UsersListFromDB(); public void printUsers(){ userListFromDB.printUsers(); } }
Now create a client program to access the UsersView.java
package com.java2practice; public class UsersViewTest{ public static void main(String[] args){ UsersView usersView = new UsersView(); usersView.printUsers(); } }
If we use like this, the object UsersListFromDB is tightly coupled with UsersView. If we want to show UsersListFromExcel it won’t be possible.
For this we need to create an interface called
public interface UsersList{ public void printUsers(); }
And make the UsersListFromDB, UsersListFromExcel implement the UsersList like below
public class UsersListFromDB implements UsersList{ public void printUsers(){ System.out.println(“users list printing from database”); } } public class UsersListFromExcel implements UsersList{ public void printUsers(){ System.out.println(“users list printing from excel”); } }
Now we can change our UsersView class to
public class UsersView{ private UsersList userList = new UsersListFromDB(); public void printUsers(){ userList.printUsers(); } }
This concept is called interaface referencing. One of the object oriented principle Program to interface not program to class”
But still our problem is not solved, with out changing the UsersView we can not add UsersListFromExcel
Now We will try with Factory Design pattern
public class UsersFactory{ public UsersList getUsersList(){ return new UsersListFromDB(); } }
In the UsersView.java we need to call this UsersFactory to get the appropriate object.
public UsersView{ UsersList usersList = new UsersFactory().getUsersList(); public void printUsers(){ usersList.printUsers(); } }
Now the client program will look like this
public class UsersViewTest{ public static void main(String[] args){ UsersView usersView = new UsersView(); usersView.printUsers(); } }
This is more elegant and with out touching UsersView.java file we can switch to UsersListFromExcel or UsersListFromDB by changing UsersFactory.
But still we need to change the UsersFactory.
With spring IOC our UsersView.java will look like this
public class UsersView{ private UsersList usersList; public void setUsersList(UsersList usersList){ this.usersList = usersList; } public void printUsers(){ usersList.printUsers(); } }
That’s it, we don’t need to bother about instantiating the UsersList interface with its implemented classes.
To tell the container about our Java classes we need to write configuration file.
Bean-config.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="usersListFromDB" class="com.java2practice.UsersListFromDB"/> <bean id="usersListFromExcel" class="com.java2practice.UsersListFromExcel/"> <bean id="userView"> <property ref=” usersListFromDB” /> </bean> </beans>
We have to write our client program to use all these:
public class UesrsViewTest{ public static void main(String[] args){ ApplicationContext context = new ClassPathXmlApplicationContext("bean-config.xml"); UsersView usersView = (UsersView)context.getBean(“userView”); usersView.printUsers(); } }
Here ApplicationContext will read the bean configuration file and accordingly it will inject the dependencies into the UsersView.java
When we are calling context.getBean(“userView”) it will return UsersView with dependencies injected into it.
If we want to use UsersListFromExcel, just we need to change in configuration file
Then the out put will be “printing users from excel file”