- ISOLATION_DEFAULT (Spring)
This is a PlatformTransactionManager Default isolation level, using Default Database transaction level. The other 4 are correspond to JDBC isolation level - ISOLATION_READ_UNCOMMITTED
Lowest isolation level. Allow other transaction to see the data from this transaction. This level can cause dirty read, non-repeatable read, and illusion read - ISOLATION_READ_COMMITTED (normally used)
Promise other transaction can read data until this transaction committed. - ISOLATION_REPEATABLE_READ
Prevent dirty read, non-repeatable read. But can illusion read. - ISOLATION_SERIALIZABLE
This is most expensive level, but most reliable. It promise Sequence. Prevent dirty read, non-repeatable read, and illusion read
I'm a software engineer, interested learning the best technologies, and contribute to the industry.
Showing posts with label Spring AOP. Show all posts
Showing posts with label Spring AOP. Show all posts
Wednesday, April 27, 2016
Spring Transaction Isolation Level
Spring Transaction Isolation Level
The standard levels are 2,3,4,5, the higher level, the poorer concurrency/effi
Transaction Definition
Transaction Definition
- PROPAGATION_REQUIRED (normally used)
If transaction existed, use it, otherwise open a new transaction - PROPAGATION_SUPPORTS
If transaction existed, use it, otherwise run as non-transaction - PROPAGATION_MANDATORY
If transaction existed, use it, otherwise throw Exception - PROPAGATION_REQUIRES_NEW
Always open a new transaction, if transaction existed, hang up the existed transaction - PROPAGATION_NOT_SUPPORTED
Always run without transaction, if transaction existed, hang up the existed transaction - PROPAGATION_NEVER
Always run without transaction, if transaction existed, throw Exception - PROPAGATION_NESTED (in Spring)
If transaction existed, run nested transaction inside the existed transaction.
If no transaction existed, run as PROPAGATION_REQUIRED
| Propagation | T1 None | T1 Existed |
|---|---|---|
| REQUIRED | T2=>new | T1 |
| SUPPORTS | T2 None | T1 |
| MANDATORY | T2 Exception | T1 |
| REQUIRES_NEW | T2=>new | T2=>new |
| NOT_SUPPORTED | None | Hang up T1 |
| NEVER | None | T2 Exception |
| NESTED | As REQUIRED | Nested |
Dynamic Proxy: JDK VS CGLIB
If target object implements interface, by default, spring will use Dynamic Proxy from JDK for AOP.
If target object implements interface, we can force to use CGLIB to make Dynamic Proxy for AOP.
If target object does not implements interface, we MUST use CGLIB to make Dynamic Proxy for AOP.
In conclusion:
JDK can only apply to class which implements interface
CGLIB can apply to class with/without implementing interface
Spring will dynamically choose to use JDK or CGLIB for each target object.
JDK proxy example:
userManager $Proxy2 (id=42)
CGLIB example:
UserManagerImpl$$EnhancerBySpringCGLIB$$7f895f28 (id=46)
If target object implements interface, we can force to use CGLIB to make Dynamic Proxy for AOP.
If target object does not implements interface, we MUST use CGLIB to make Dynamic Proxy for AOP.
In conclusion:
JDK can only apply to class which implements interface
CGLIB can apply to class with/without implementing interface
Spring will dynamically choose to use JDK or CGLIB for each target object.
JDK proxy example:
userManager $Proxy2 (id=42)
h JdkDynamicAopProxy (id=54)
UserManagerImpl$$EnhancerBySpringCGLIB$$7f895f28 (id=46)
You can see CGLIB extends the target class to generate proxy, JDK did not
Because CGLIB do extends, our target class should NOT be final
Because CGLIB do extends, our target class should NOT be final
to Enable CGLIB:
dependency:
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.2.2</version> </dependency>
applicationContext.xml config
<!-- proxy-target-class="true" means force to use CGLIB --> <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
But if we want spring to choose for us, we don't have to force to do the config, just add dependency.
Tuesday, April 26, 2016
Spring AOP AspectJ Advice
An advice is really simple,
If config it in xml:
If with Annotation
Add a JoinPoint argument to get target class method information
If config it in xml:
package com.gvace.aop;
/*
* To define an Aspect
*
*/
public class SecurityHandler {
private void checkSecurity() {
System.out.println("Runned checkSecurity");
}
}
If with Annotation
package com.gvace.aop;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
/*
* To define an Aspect
*
*/
@Aspect
public class SecurityHandler {
//@Pointcut Describe the target functions
//So this function is ONLY JUST A FLAG, it will NEVER be called anyway
//(* add*(..)), means return any type, name likes "add*", parameter can be anything
// the addAddMethod function does not want parameter and return type
@Pointcut("execution(* add*(..))")
private void addAddMethod(){}
//define this Advice is run before target function
//And it applied to some @Pointcut described functions
@Before(value = "addAddMethod()")
//@After(value = "addAddMethod()")
private void checkSecurity() {
System.out.println("Runned checkSecurity");
}
}
Add a JoinPoint argument to get target class method information
package com.gvace.aop;
import org.aspectj.lang.JoinPoint;
/*
* To define an Aspect
*
*/
public class SecurityHandler {
//add a JoinPoint argument to get target class method information
private void checkSecurity(JoinPoint joinPoint){
System.out.println("Method name: "+joinPoint.getSignature().getName());
for(int i=0; i<joinPoint.getArgs().length; i++){
System.out.println(joinPoint.getArgs()[i]);
}
System.out.println("Runned checkSecurity");
}
}
Spring AOP AspectJ Pointcut Expression
any public functions
any return type
any function name
any/no arguments
execution(public * *(..))
Any public functions which name start with "set"
execution(* set*(..))
All ABCService interface's function
execution(* com.abc.ABCService.*(..))
All functions classes in package abc
execution(* com.abc.*.*(..))
All functions classes in package abc or sub-package of abc
execution(* com.abc..*.*(..))
can use "||" "&&" like
execution(* set*(..)) && execution(* com.abc.*.*(..))
More See this
http://howtodoinjava.com/spring/spring-aop/writing-spring-aop-aspectj-pointcut-expressions-with-examples/
Spring AOP with AspectJ no Annotation config
Concepts
Cross Cutting Concern: A isolated service, across all procedure in system
Aspect: The model pattern of Cross Cutting Point
Advice: The implementation of Aspect
Pointcut: Defines which JoinPoint(s) applied by Advice. For Spring, it's function invoke.
JoinPoint: the time point when Advice run, Spring only support JoinPoint on function
Weave: Apply Advice to Target Object called Weave. Spring support dynamic Weave.
Target Object: The Target Object that Advice applied to.
Proxy: Spring AOP default uses dynamic proxy from JDK, it's proxy is created when running. It can also use CGLIB proxy(when not using Interface).
Introduction: Add function for Class dynamically
See this first: Spring AOP
http://gvace.blogspot.com/2016/03/aop.html
To skip this and see: Spring AOP with AspectJ and Annotation
http://gvace.blogspot.com/2016/04/spring-aop-with-annotation.html
Example
Using Spring Version 4.2.5
Add dependency AspectJ
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>spring_static_proxy</groupId>
<artifactId>spring_static_proxy</artifactId>
<version>0.0.1-SNAPSHOT</version>
<build>
<sourceDirectory>src</sourceDirectory>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.build.mainClass>com.gvace.main</project.build.mainClass>
<java.version>1.7</java.version>
<spring.version>4.2.5.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.9</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
</dependencies>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/libs-milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>
Create a class as Aspect
SecurityHandler.java
package com.gvace.aop;
/*
* To define an Aspect
*
*/
public class SecurityHandler {
private void checkSecurity() {
System.out.println("Runned checkSecurity");
}
}
An example interface(Target Object)
package com.gvace.inter;
public interface UserManager {
public void addUser(String username,String password);
public void delUser(int userId);
public String findUserById(int userId);
public void modifyUser(int userId, String username,String password);
}
The implementation of the interface(Target Object)
UserManagerImpl.java
package com.gvace.impl;
import com.gvace.inter.UserManager;
// each method have to call checkSecurity()
// and you don't want to change it based on requirement changes(like the codes being comment out)
public class UserManagerImpl implements UserManager {
@Override
public void addUser(String username, String password) {
//checkSecurity();
System.out.println("addUser:"+username+" "+password);
}
@Override
public void delUser(int userId) {
//checkSecurity();
System.out.println("delUser:"+userId+" ");
}
@Override
public String findUserById(int userId) {
//checkSecurity();
System.out.println("findUserById:"+userId+" ");
return "findUserById"+userId;
}
@Override
public void modifyUser(int userId, String username, String password) {
//checkSecurity();
System.out.println("modifyUser:"+userId+" "+username+" "+password);
}
/*
public void checkSecurity(){
System.out.println("Runned checkSecurity");
}
*/
}
Register beans and aspect annotation
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:util="http://www.springframework.org/schema/util"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/aop/spring-tx.xsd">
<!-- Data Source -->
<bean id="userManager" class="com.gvace.impl.UserManagerImpl"></bean>
<!-- Aspect Class-->
<bean id="securityHandler" class="com.gvace.aop.SecurityHandler"></bean>
<aop:config>
<!-- Define Aspect, link to Aspect Class -->
<aop:aspect id="securityAspect" ref="securityHandler">
<!-- Define Pointcut -->
<aop:pointcut id="addAddMethod" expression="execution(* add*(..))"/>
<!-- Link the Advice method to Pointcut -->
<aop:before method="checkSecurity" pointcut-ref="addAddMethod"/>
</aop:aspect>
</aop:config>
</beans>
Test to run
Client.java
package com.gvace.test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.gvace.inter.UserManager;
public class Client {
public static void main(String[] args){
BeanFactory factory = new ClassPathXmlApplicationContext("applicationContext.xml");
UserManager userManager = (UserManager)factory.getBean("userManager");
//Since this UserManager interface contains function like execution(* add*(..))
//this userManager reference will not be the implementation class, it will be a Proxy
userManager.addUser("a", "b");
}
}
Spring AOP with AspectJ and Annotation
Concepts
Cross Cutting Concern: A isolated service, across all procedure in system
Aspect: The model pattern of Cross Cutting Point
Advice: The implementation of Aspect
Pointcut: Defines which JoinPoint(s) applied by Advice. For Spring, it's function invoke.
JoinPoint: the time point when Advice run, Spring only support JoinPoint on function
Weave: Apply Advice to Target Object called Weave. Spring support dynamic Weave.
Target Object: The Target Object that Advice applied to.
Proxy: Spring AOP default uses dynamic proxy from JDK, it's proxy is created when running. It can also use CGLIB proxy(when not using Interface).
Introduction: Add function for Class dynamically
See this first: Spring AOP
http://gvace.blogspot.com/2016/03/aop.html
Then see this Spring AOP with AspectJ with no Annotation Config
http://gvace.blogspot.com/2016/04/spring-aop-with-aspectj-no-annotation.html
Example
Using Spring Version 4.2.5
Add dependency AspectJ
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>spring_static_proxy</groupId>
<artifactId>spring_static_proxy</artifactId>
<version>0.0.1-SNAPSHOT</version>
<build>
<sourceDirectory>src</sourceDirectory>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.build.mainClass>com.gvace.main</project.build.mainClass>
<java.version>1.7</java.version>
<spring.version>4.2.5.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.9</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
</dependencies>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/libs-milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>
Create a class as Aspect
SecurityHandler.java
package com.gvace.aop;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
/*
* To define an Aspect
*
*/
@Aspect
public class SecurityHandler {
//@Pointcut Describe the target functions
//So this function is ONLY JUST A FLAG, it will NEVER be called anyway
//(* add*(..)), means return any type, name likes "add*", parameter can be anything
// the addAddMethod function does not want parameter and return type
@Pointcut("execution(* add*(..))")
private void addAddMethod(){}
//define this Advice is run before target function
//And it applied to some @Pointcut described functions
@Before(value = "addAddMethod()")
//@After(value = "addAddMethod()")
private void checkSecurity() {
System.out.println("Runned checkSecurity");
}
}
An example interface(Target Object)
package com.gvace.inter;
public interface UserManager {
public void addUser(String username,String password);
public void delUser(int userId);
public String findUserById(int userId);
public void modifyUser(int userId, String username,String password);
}
The implementation of the interface(Target Object)
UserManagerImpl.java
package com.gvace.impl;
import com.gvace.inter.UserManager;
// each method have to call checkSecurity()
// and you don't want to change it based on requirement changes(like the codes being comment out)
public class UserManagerImpl implements UserManager {
@Override
public void addUser(String username, String password) {
//checkSecurity();
System.out.println("addUser:"+username+" "+password);
}
@Override
public void delUser(int userId) {
//checkSecurity();
System.out.println("delUser:"+userId+" ");
}
@Override
public String findUserById(int userId) {
//checkSecurity();
System.out.println("findUserById:"+userId+" ");
return "findUserById"+userId;
}
@Override
public void modifyUser(int userId, String username, String password) {
//checkSecurity();
System.out.println("modifyUser:"+userId+" "+username+" "+password);
}
/*
public void checkSecurity(){
System.out.println("Runned checkSecurity");
}
*/
}
Register beans and aspect annotation
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:util="http://www.springframework.org/schema/util"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/aop/spring-tx.xsd">
<!-- Enable AspectJ Annotation -->
<aop:aspectj-autoproxy/>
<!-- Data Source -->
<bean id="userManager" class="com.gvace.impl.UserManagerImpl"></bean>
<!-- Aspect -->
<bean id="securityHandler" class="com.gvace.aop.SecurityHandler"></bean>
</beans>
Test to run
Client.java
package com.gvace.test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.gvace.inter.UserManager;
public class Client {
public static void main(String[] args){
BeanFactory factory = new ClassPathXmlApplicationContext("applicationContext.xml");
UserManager userManager = (UserManager)factory.getBean("userManager");
//Since this UserManager interface contains function like execution(* add*(..))
//this userManager reference will not be the implementation class, it will be a Proxy
userManager.addUser("a", "b");
}
}
Monday, April 25, 2016
Static Proxy and Dynamic Proxy
Normally like security check, transaction, log, these commonly used functions are not related to business logic, we can use proxy to separate these code from business codes
Think an interface has some functions
And before running each function in this interface, you need to call checkSecurity() first
To make the implementation cleaner
And which does not change when requirement for checkSecurity() changes
What solutions we have?
Example Interface:
Clear Implementation example:
To simplify target implementation class, we build this proxy
We do checkSecurity() in proxy
So target implementation will not changed when requirement change
But we still have to call checkSecurity() in every method in this proxy
And we still have to change this proxy when requirement change
Thinking:
Actually checkSecurity() is separate logic from the target functions.
We need to find a way to separate it, so we have Solution 2
Solution 2: Dynamic Proxy
And to run the target through this Handler
You can also use factory pattern to hide the procedure of creating handler and proxy instance
Static Proxy and Dynamic Proxy
Since dynamic proxy is dynamic registering the function, it's not efficient as static proxy.
Solution 3: Spring AOP
http://gvace.blogspot.com/2016/03/aop.html
Think an interface has some functions
And before running each function in this interface, you need to call checkSecurity() first
To make the implementation cleaner
And which does not change when requirement for checkSecurity() changes
What solutions we have?
Example Interface:
package com.gvace.inter;
public interface UserManager {
public void addUser(String username,String password);
public void delUser(int userId);
public String findUserById(int userId);
public void modifyUser(int userId, String username,String password);
}
Clear Implementation example:
package com.gvace.impl;
import com.gvace.inter.UserManager;
// each method have to call checkSecurity()
// and you don't want to change it based on requirement changes(like the codes being comment out)
public class UserManagerImpl implements UserManager {
@Override
public void addUser(String username, String password) {
//checkSecurity();
System.out.println("addUser:"+username+" "+password);
}
@Override
public void delUser(int userId) {
//checkSecurity();
System.out.println("delUser:"+userId+" ");
}
@Override
public String findUserById(int userId) {
//checkSecurity();
System.out.println("findUserById:"+userId+" ");
return "findUserById"+userId;
}
@Override
public void modifyUser(int userId, String username, String password) {
//checkSecurity();
System.out.println("modifyUser:"+userId+" "+username+" "+password);
}
/*
public void checkSecurity(){
System.out.println("Runned checkSecurity");
}
*/
}
Solution 1: Static ProxyTo simplify target implementation class, we build this proxy
We do checkSecurity() in proxy
So target implementation will not changed when requirement change
But we still have to call checkSecurity() in every method in this proxy
And we still have to change this proxy when requirement change
package com.gvace.proxy.static_;
import com.gvace.inter.UserManager;
/*
* To simplify target implementation class, we build this proxy
* We do checkSecurity() in proxy
* So target implementation will not changed when requirement change
* But we still have to call checkSecurity() in every method in this proxy
* And we still have to change this proxy when requirement change
*
* Thinking:
* Actually checkSecurity() is separate logic from the target functions.
* We need to find a way to separate it
*/
public class UserManagerImplProxy implements UserManager {
UserManager userManager;
public UserManagerImplProxy(UserManager userManager){
this.userManager = userManager;
}
@Override
public void addUser(String username, String password) {
checkSecurity();
userManager.addUser(username, password);
}
@Override
public void delUser(int userId) {
checkSecurity();
userManager.delUser(userId);
}
@Override
public String findUserById(int userId) {
checkSecurity();
return userManager.findUserById(userId);
}
@Override
public void modifyUser(int userId, String username, String password) {
checkSecurity();
userManager.modifyUser(userId, username, password);
}
private void checkSecurity(){
System.out.println("Runned checkSecurity");
}
}
Thinking:
Actually checkSecurity() is separate logic from the target functions.
We need to find a way to separate it, so we have Solution 2
Solution 2: Dynamic Proxy
Create a Handler which implements InvocationHandler
This handler will register all functions from the target interfaces
So every-time the registered functions runs, it needs to go through the invoke() function in this handler
In this way, we can control the registered functions
package com.gvace.proxy.dynamic;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//To use Dynamic Proxy
//We build a InvocationHandler only for the function checkSecurity()
public class SecurityHandler implements InvocationHandler {
private Object targetObject;
public Object createProxyInstance(Object targetObject){
//Save the targetObject as a reference
this.targetObject = targetObject;
// By creating this proxy
// all interfaces' functions(targetObject.getClass().getInterfaces())
// will be registered to this handler(filter)
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
targetObject.getClass().getInterfaces(),
this);
}
// see java.lang.reflect.InvocationHandler
// invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
//
// invoke() like a filter of the target function
// This will run every-time when the registered interfaces' functions runs
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//do the checkSecurity()
checkSecurity();
//after checkSecurity() the filter is finished
//we can invoke targetObject's function now.(like a filter chain)
//or we can choose to return null, if it's not pass the checkSecurity() requirement
return method.invoke(targetObject, args);
}
/*
* checkSecurity() runs like invisible
*/
private void checkSecurity() {
System.out.println("Runned checkSecurity");
}
}
And to run the target through this Handler
You can also use factory pattern to hide the procedure of creating handler and proxy instance
package com.gvace.test;
import com.gvace.impl.UserManagerImpl;
import com.gvace.inter.UserManager;
import com.gvace.proxy.dynamic.SecurityHandler;
public class Client {
public static void main(String[] args){
//Create a proxy instance
//You can also hidden this by using a factory pattern
SecurityHandler handler = new SecurityHandler();
UserManager userManager = (UserManager)handler.createProxyInstance(new UserManagerImpl());
userManager.addUser("aaa", "123");
}
}
Since dynamic proxy is dynamic registering the function, it's not efficient as static proxy.
Solution 3: Spring AOP
http://gvace.blogspot.com/2016/03/aop.html
Thursday, March 31, 2016
Spring AOP
AOP aspect oriented programming
Normally like security check, transaction, log, these commonly used functions are not related to business logic, we can use proxy to separate these code from business codes
Dynamic Proxy
Dynamic Proxy
AOP is done in theory of Dynamic Proxy
To understand how and why Dynamic Proxy works:
Advice implements Aspect
Pointcut(add*): only apply on function name of add*
To see this Spring AOP with AspectJ with no Annotation Config(newer Version)
http://gvace.blogspot.com/2016/04/spring-aop-with-aspectj-no-annotation.html
To see Spring AOP with annotation(newer Version):
http://gvace.blogspot.com/2016/04/spring-aop-with-annotation.html
Target Objects
Advice class
Proxy
Procedure
- define interface
- target bean class
- Advice class
- beans xml config
- config target bean
- config advice
- config proxy
Advice types
| Advice type | Interface | Description |
|---|---|---|
| Around | org.aopalliance.intercept.MethodInterceptor | Intercept target method |
| Before | org.springframework.aop.MethodBeforeAdvice | Call before target method |
| After | org.springframework.aop.AfterReturningAdvice | Call after target method returns |
| Throws | org.springframework.aop.ThrowsAdvice | Call when target method throws exception |
| Function Filter by Name | org.springframework.aop.support.NameMatchMethodPointcutAdvisor | Apply only on function with the matched name |
Advice order:
- MethodBeforeAdvice
- MethodInterceptor
- target method
- MethodInterceptor
- AfterReturningAdvice
ProxyFactoryBean:
Only need to setup, does not need code, use dynamic proxy instead
each ProxyFactoryBean only targets to one existed bean
- proxyInterfaces: register interceptor on all functions of each interface
- interceptorNames: register advice to run before/after all functions in proxyInterfaces
- target: has to be only one existed bean
MethodInterceptor
Remember to get method return value, and return the value.
Example:
public class MyMethodInterceptor implements MethodInterceptor{
/**
* @methodInvocation
* @return return what methodInvocation returns
*/
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("Before method invote "+methodInvocation.getMethod().getName());
Object returnValue = methodInvocation.proceed();
System.out.println("After method invote "+methodInvocation.getMethod().getName());
return returnValue;
}
}
ThrowsAdvice
Run when there is an error
ThrowsAdvice is just a flag, no function required to implement
Create either one of the following functions by yourself(See from example)
If created both, it always just run the one with multiple arguments
Example:
public class MyThrowsAdvice implements ThrowsAdvice{
/*
* ThrowsAdvice is just a flag, no function required to implement
* Create either one of the following functions by yourself
*
* If created both, it always just run the one with multiple arguments
*/
public void afterThrowing(Throwable throwable){
System.out.println("******Something WRONG!!!!!"+throwable.getMessage());
}
public void afterThrowing(Method m, Object[] os, Object target, Exception throwable){
System.out.println("Something WRONG!!!!!"+throwable.getMessage());
}
}
NameMatchMethodPointcutAdvisor
Like a Filter: apply only on function with the matched name(support regular expression)
Referenced on another Advice, and filter its function by matched name
So the referenced Advice does not need to register in the proxy, use this Filter instead
Just config, do not need implementation
Example:
<bean id="myMethodBeforeAdviceFilter" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor"> <property name="advice" ref="MyMethodBeforeAdvice" /> <property name="mappedNames"> <list> <value>sayBye</value> </list> </property> </bean>
Whole Process Example
- define interface
public interface TestServiceInterface { public void sayHello(); } public interface TestServiceInterface2 { public void sayBye(); } - target bean class
public class Test1Service implements TestServiceInterface, TestServiceInterface2{ private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public void sayHello() { System.out.println("Hello "+name); } @Override public void sayBye() { System.out.println("Bye "+name); } } - Advice class
public class MyMethodBeforeAdvice implements MethodBeforeAdvice{ /** * @method target method * @args parameters of target method * @target target object */ @Override public void before(Method method, Object[] args, Object target) throws Throwable { //method.invoke(target, args); System.out.println("Log"+method.getName()); } } - beans xml config
- config target bean
- config advice
- config proxy
<!-- target objects --> <bean id="test1Service" class="com.gvace.aop.Test1Service"> <property name="name" value="service1"></property> </bean> <!-- Method Before Advice --> <bean id="MyMethodBeforeAdvice" class="com.gvace.aop.MyMethodBeforeAdvice"></bean> <!-- Proxy Object --> <bean id="ProxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean"> <!-- config Target Object --> <property name="target" ref="test1Service" /> <!-- Proxy Interface collection --> <property name="proxyInterfaces"> <list> <value>com.gvace.aop.TestServiceInterface</value> <value>com.gvace.aop.TestServiceInterface2</value> </list> </property> <!-- Insert Advice into proxy object --> <property name="interceptorNames"> <!-- link Advices with ProxyFactoryBean --> <list> <value>MyMethodBeforeAdvice</value> </list> </property> </bean>
Subscribe to:
Posts (Atom)