您的位置:首页 > 编程语言 > Java开发

Spring AOP

2015-09-14 20:51 239 查看

AOP

AOP 的定义

AOP(Aspect-Oriented Programming, 面向切面编程): 是一种新的方法论, 是对传统 OOP(Object-Oriented Programming, 面向对象编程) 的补充.

AOP 的主要编程对象是切面(aspect), 而切面模块化横切关注点.

在应用 AOP 编程时, 仍然需要定义公共功能, 但可以明确的定义这个功能在哪里, 以什么方式应用, 并且不必修改受影响的类. 这样一来横切关注点就被模块化到特殊的对象(切面)里.

AOP 的好处

每个事物逻辑位于一个位置, 代码不分散, 便于维护和升级

业务模块更简洁, 只包含核心业务代码.

切面(Aspect) 横切关注点(跨越应用程序多个模块的功能)被模块化的特殊对象

通知(Advice)

切面必须要完成的工作

目标(Target)

被通知的对象

代理(Proxy)

向目标对象应用通知之后创建的对象

连接点(Joinpoint)

程序执行的某个特定位置:如类某个方法调用前、调用后、方法抛出异常后等。连接点由两个信息确定:方法表示的程序执行点;相对点表示的方位。例如 ArithmethicCalculator#add() 方法执行前的连接点,执行点为 ArithmethicCalculator#add(); 方位为该方法执行前的位置

切点(pointcut)

每个类都拥有多个连接点:例如 ArithmethicCalculator 的所有方法实际上都是连接点,即连接点是程序类中客观存在的事务。AOP 通过切点定位到特定的连接点。类比:连接点相当于数据库中的记录,切点相当于查询条件。切点和连接点不是一对一的关系,一个切点匹配多个连接点,切点通过 org.springframework.aop.Pointcut 接口进行描述,它使用类和方法作为连接点的查询条件。

原理

动态代理

代理设计模式

代理设计模式的原理: 使用一个代理将对象包装起来, 然后用该代理对象取代原始对象. 任何对原始对象的调用都要通过代理. 代理对象决定是否以及何时将方法调用转到原始对象上.

示例代码(使用动态代理自己实现的日志代理类)

package com.weixuan.springaop;

public interface Calculator {

int add(int i, int j);

int sub(int i, int j);

int mul(int i, int j);

int div(int i, int j);
}
package com.weixuan.springaop;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;

public class CalculatorLoggingProxy {

/**
* 要代理的对象
*/
private Calculator calculator;

public CalculatorLoggingProxy(Calculator calculator) {
this.calculator = calculator;
}

public Calculator getLoggingProxy() {
Calculator proxy = null;
/**
* 代理对象有哪一个类加载器来进行加载
*/
ClassLoader classLoader = calculator.getClass().getClassLoader();

/**
* 代码对象有哪些方法
*/
Class[] interfaces = new Class[] { Calculator.class };

/**
* 当调用代理对象的方法时,执行的代码
*/
InvocationHandler invocationHandler = new InvocationHandler() {

/**
* @param proxy
*            正在返回的对象,一般情况下,不使用
* @param method
*            正在被调用的方法
* @param args
*            调用方法时传入的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/**
* 日志
*/
System.out.println("The mathod " + method.getName() + " begins with " + Arrays.asList(args));
/**
* 执行
*/
Object result = method.invoke(calculator, args);
/**
* 日志
*/
System.out.println("The mathod " + method.getName() + " ends with " + result);
return result;
}
};
return proxy = (Calculator) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
}
}

package com.weixuan.springaop;

public class Client {

public static void main(String[] args) {
Calculator calculator = new CalculatorImpl();
//System.out.println("----> " + calculator.div(4, 2));
Calculator proxy = new CalculatorLoggingProxy(calculator).getLoggingProxy();
proxy.add(2, 3);
}
}


使用spring aop的注解

package com.weixuan.springsop.aspect;

import java.util.Arrays;
import java.util.List;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

/**
* 把这个类声明为一个切面
*
* @author Nicholas
* 1. 把该类放入IOC容器 @compont
* 2. 添加注解 @aspect
*/

@Component
@Aspect
public class LoggingAspect {
/**
* 声明该方法是一个前置通知 joinpoint 切入点
*/
@Before("execution(public int com.weixuan.springsop.inter.Calculator.*(int , int ))")
public void beforeMessage(JoinPoint joinpoint) {
String methodName = joinpoint.getSignature().getName();
List<Object> args = Arrays.asList(joinpoint.getArgs());
System.out.println("The method " + methodName + " begin with " + args);
}
}

package com.weixuan.springsop.impl;
import org.springframework.stereotype.Component;
import com.weixuan.springsop.inter.Calculator;
@Component
public class CalculatorImpl implements Calculator {
@Override
public int add(int i, int j) {
return i + j;
}
@Override
public int sub(int i, int j) {
return i - j;
}
@Override
public int mul(int i, int j) {
return i * j;
}

@Override
public int div(int i, int j) {
if (j != 0)
return i / j;
return 0;
}
}

package com.weixuan.springsop.client;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.weixuan.springsop.inter.Calculator;

public class Main {

public static void main(String[] args) {

// 1. 创建ioc容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("com/weixuan/springsop/config/application-context.xml");
// 2. 获取实例
Calculator calculator = applicationContext.getBean(Calculator.class);
// 3. 使用
System.out.println(calculator.add(2, 4));
}
}


<?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:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd"> <!-- 配置自动扫描的包 -->
<context:component-scan base-package="com.weixuan.springsop"></context:component-scan>

<!-- 使aspect注解起作用  ,自动为匹配的类生成代理对象-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>


各种通知

package com.weixuan.springsop.aspect;

import java.util.Arrays;
import java.util.List;

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;
import org.springframework.stereotype.Component;

/**
* 把这个类声明为一个切面
*
* @author Nicholas 1. 把该类放入IOC容器 @compont 2. 添加注解 @aspect
*/

@Component
@Aspect
public class LoggingAspect {
/**
* 声明该方法是一个前置通知 joinpoint 切入点
*/
@Before("execution(public int com.weixuan.springsop.inter.Calculator.*(int , int ))")
public void beforeMessage(JoinPoint joinpoint) {
String methodName = joinpoint.getSignature().getName();
List<Object> args = Arrays.asList(joinpoint.getArgs());
System.out.println("The method " + methodName + " begin with " + args);
}

/**
* 无论是否有异常,都执行 还不能访问目标方法的返回结果,返回结果要在返回通知中才能访问
*
* @param joinpoint
*/
@After("execution(public int com.weixuan.springsop.inter.Calculator.*(int , int ))")
public void afterMessage(JoinPoint joinpoint) {
String methodName = joinpoint.getSignature().getName();
List<Object> args = Arrays.asList(joinpoint.getArgs());
System.out.println("The method " + methodName + " end with " + args);
}

/**
* 返回通知
*
* @param joinpoint
*            在方法正常返回的通知
*/
@AfterReturning(value = "execution(public int com.weixuan.springsop.inter.Calculator.*(int , int ))", returning = "result")
public void returnMessage(JoinPoint joinpoint, Object result) {
String methodName = joinpoint.getSignature().getName();
System.out.println("The method " + methodName + " return with " + result);
}

/**
* 返回通知
*
* @param joinpoint
*            在方法正常返回的通知
*/
@AfterThrowing(value = "execution(public int com.weixuan.springsop.inter.Calculator.*(int , int ))", throwing = "exception")
public void throwingMessage(JoinPoint joinpoint, Exception exception) {
String methodName = joinpoint.getSignature().getName();
System.out.println("The method " + methodName + " occurs exception " + exception);
}

/**
* 环绕通知 类似于 动态代理的全过程 这个类型参数可以决定是否执行目标方法,必须有返回值。
*
* @param ProceedingJoinPoint
*            在方法正常返回的通知s
*/
@Around(value = "execution(public int com.weixuan.springsop.inter.Calculator.*(int , int ))")
public Object aroundMessage(ProceedingJoinPoint proceedingJoinPoint) {
System.out.println("The method ");
Object result = null;
try {
result = proceedingJoinPoint.proceed();
} catch (Throwable e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return result;
}
}


切面的优先级

多个切面,那个先执行?

使用@order(1..n)指定优先级,值越小,优先级越高

@Component
@Aspect
@Order(1)
public class LoggingAspect {}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  spring