Tuesday, April 26, 2016

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");
 }
}













No comments:

Post a Comment