Spring AOP

AOP(Aspect Orient Programming),我们一般称为面向方面(切面)编程,作为面向对象的一种补充,用于处理系统中分布于各个模块的横切关注点,比如事务管理、日志、缓存等等。AOP实现的关键在于AOP框架自动创建的AOP代理,AOP代理主要分为静态代理和动态代理,静态代理的代表为AspectJ;而动态代理则以Spring AOP为代表。

先以静态代理实现,静态代理关键是在代理对象和目标对象实现共同的接口,并且代理对象持有目标对象的引用。

package com.soecode.lyf.service;

public interface ISayHello {
	void sayHello();
}
package com.soecode.lyf.service.impl;

import com.soecode.lyf.service.ISayHello;

public class ISayHelloImpl implements ISayHello {

	@Override
	public void sayHello() {
		System.out.println("hello spring AOP");
		
	}

}



静态代理类代码:

package com.soecode.lyf.web;

import com.soecode.lyf.service.ISayHello;
import com.soecode.lyf.service.impl.ISayHelloImpl;

public class ProxySayHello implements ISayHello{

	private ISayHelloImpl impl;
	
	public ProxySayHello(ISayHelloImpl im){
		super();
		this.impl=im;
	}
	
	@Override
	public void sayHello() {
		System.out.println("静态代理前");
		impl.sayHello();
		System.out.println("静态代理后");
	}
	
	public static void main(String[] args) {
		ProxySayHello p = new ProxySayHello(new ISayHelloImpl());
		p.sayHello();
	}
	
}

日志打印:

静态代理前
hello spring AOP
静态代理后
以动态代理实现:

package com.soecode.lyf.web;

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

import com.soecode.lyf.service.ISayHello;
import com.soecode.lyf.service.impl.ISayHelloImpl;


public class DynaProxySayHello implements InvocationHandler{
	
	private Object proxy;
	public Object setProxy(Object object){
		this.proxy=object;
		return Proxy.newProxyInstance(this.proxy.getClass().getClassLoader(), proxy.getClass().getInterfaces(), this);
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		
		Class clazz = this.proxy.getClass();
		//有参数用new Class[]{Method.class}
		Method sayHello = clazz.getDeclaredMethod("sayHello", null);
		System.out.println("动态代理就是用反射");
		method.invoke(this.proxy, args);
		System.out.println("代理调用结束");
		return null;
	}

	public static void main(String[] args) {
		ISayHello sayHello = (ISayHello)new DynaProxySayHello().setProxy(new ISayHelloImpl());
		sayHello.sayHello();
		
	}

}
动态代理就是用反射
hello spring AOP
代理调用结束

反射就是把java类中的各种成分映射成一个个的Java对象

例如:一个类有:成员变量、方法、构造方法、包等等信息,利用反射技术可以对一个类进行解剖,把个个组成部分映射成一个个对象


maven找引入的jar包http://mvnrepository.com/,这个比较方便。

Joinpoint(连接点):程序执行时的某个特定的点,在Spring中就是某一个方法的执行 。
Pointcut(切点):说的通俗点,spring中AOP的切点就是指一些方法的集合,而这些方法是需要被增强、被代理的。一般都是按照一定的约定规则来表示的,如正则表达式等。切点是由一类连接点组成。
Advice(通知):还是说的通俗点,就是在指定切点上要干些什么。
Advisor(通知器):其实就是切点和通知的结合 。

一、基于spring XML配置的实现

ISayHelloImpl的代码:

package com.soecode.lyf.service.impl;

import com.soecode.lyf.service.ISayHello;

public class ISayHelloImpl implements ISayHello {

	@Override
	public void sayHello() {
		System.out.println("hello spring AOP");
		try {
			Thread.sleep(100);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		//int[] a = {1}; 
		//int r = a[2];
	}

}

SayhelloInterceptor的代码:

package com.soecode.lyf.web;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;



public class SayhelloInterceptor {
	 /**
     * 前置通知:目标方法调用之前执行的代码
      * @param jp
     */
    public void doBefore(JoinPoint jp){
        System.out.println("===========执行前置通知============"+System.currentTimeMillis());
    }
    
    /**
     * 后置返回通知:目标方法正常结束后执行的代码
      * 返回通知是可以访问到目标方法的返回值的
      * @param jp
     * @param result
     */
    public void doAfterReturning(JoinPoint jp,String result){
        System.out.println("===========执行后置通知============");
        System.out.println("返回值result==================="+result);
    }
    
    /**
     * 最终通知:目标方法调用之后执行的代码(无论目标方法是否出现异常均执行)
      * 因为方法可能会出现异常,所以不能返回方法的返回值
      * @param jp
     */
    public void doAfter(JoinPoint jp){
        System.out.println("===========执行最终通知============"+System.currentTimeMillis());
    }
    
    /**
     * 
     * 异常通知:目标方法抛出异常时执行的代码
      * 可以访问到异常对象
      * @param jp
     * @param ex
     */
    public void doAfterThrowing(JoinPoint jp,Exception ex){
        System.out.println("===========执行异常通知============");
    }
    
    /**
     * 环绕通知:目标方法调用前后执行的代码,可以在方法调用前后完成自定义的行为。
      * 包围一个连接点(join point)的通知。它会在切入点方法执行前执行同时方法结束也会执行对应的部分。
      * 主要是调用proceed()方法来执行切入点方法,来作为环绕通知前后方法的分水岭。
      * 
     * 环绕通知类似于动态代理的全过程:ProceedingJoinPoint类型的参数可以决定是否执行目标方法。
      * 而且环绕通知必须有返回值,返回值即为目标方法的返回值
      * @param pjp
     * @return
     * @throws Throwable
     */
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable{
        System.out.println("======执行环绕通知开始========="+System.currentTimeMillis());
         // 调用方法的参数
        Object[] args = pjp.getArgs();
        // 调用的方法名
        String method = pjp.getSignature().getName();
        // 获取目标对象
        Object target = pjp.getTarget();
        // 执行完方法的返回值
        // 调用proceed()方法,就会触发切入点方法执行
        Object result=pjp.proceed();
        System.out.println("输出,方法名:" + method + ";目标对象:" + target + ";返回值:" + result);
        System.out.println("======执行环绕通知结束========="+System.currentTimeMillis());
        return result;
    }
}

spring-aop.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:aop="http://www.springframework.org/schema/aop"
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/tx 
    http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
    http://www.springframework.org/schema/aop 
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
    
    <!-- /myProject/src/main/java/com/soecode/lyf/service/impl/ISayHelloImpl.java -->
    <bean id="iSayHelloImpl" class="com.soecode.lyf.service.impl.ISayHelloImpl" >
    	<!-- <property name=""></property> -->
    </bean>
    
    <bean id="sayhelloInterceptor" class="com.soecode.lyf.web.SayhelloInterceptor" ></bean>
    
    <aop:config>
    	<aop:aspect ref="sayhelloInterceptor">
    	<!-- Pointcut(切点):spring中AOP的切点就是指一些方法的集合,而这些方法是需要被增强、被代理的。一般都是按照一定的约定规则来表示的,如正则表达式等。切点是由一类连接点组成。  -->
    	<aop:pointcut expression="execution(* com.soecode.lyf.service.impl.ISayHelloImpl..*(..))" id="pointcut"/>
    	<!-- 前置通知 -->
    	<aop:before method="doBefore" pointcut-ref="pointcut" />
    	<!-- 后置返回通知 -->
    	<aop:after-returning method="doAfterReturning" pointcut-ref="pointcut" returning="result" />
    	<!-- 最终通知 -->
    	<aop:after method="doAfter" pointcut-ref="pointcut"/>
    	<!-- 环绕通知 -->
    	<aop:around method="doAround" pointcut-ref="pointcut"/>
    	<!-- 异常通知 -->
    	<aop:after-throwing method="doAfterThrowing" pointcut-ref="pointcut" throwing="ex" />
    	</aop:aspect>
    </aop:config>
    
</beans>

AOPTest的代码如下:

package com.soecode.lyf.web;

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

import com.soecode.lyf.service.ISayHello;

public class AOPTest {
	public static void main(String[] args) {
		ApplicationContext act =  new ClassPathXmlApplicationContext("spring/spring-aop.xml");
		ISayHello iSayHello = (ISayHello)act.getBean("iSayHelloImpl");
		iSayHello.sayHello();
	}
}

打印的日志1:

[org.springframework.context.support.ClassPathXmlApplicationContext] - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@3d2f40eb: startup date [Wed Jan 31 11:00:52 CST 2018]; root of context hierarchy
  [org.springframework.beans.factory.xml.XmlBeanDefinitionReader] - Loading XML bean definitions from class path resource [spring/spring-aop.xml]
  ===========执行前置通知============1517367652653
======执行环绕通知开始=========1517367652653
hello spring AOP
输出,方法名:sayHello;目标对象:com.soecode.lyf.service.impl.ISayHelloImpl@196b16f2;返回值:null
======执行环绕通知结束=========1517367652762
===========执行最终通知============1517367652762
打印的日志2:

[org.springframework.context.support.ClassPathXmlApplicationContext] - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@694f23ae: startup date [Wed Jan 31 11:07:16 CST 2018]; root of context hierarchy
  [org.springframework.beans.factory.xml.XmlBeanDefinitionReader] - Loading XML bean definitions from class path resource [spring/spring-aop.xml]
  ===========执行前置通知============1517368036735
======执行环绕通知开始=========1517368036735
hello spring AOP
===========执行异常通知============
===========执行最终通知============1517368036844
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 2
	at com.soecode.lyf.service.impl.ISayHelloImpl.sayHello(ISayHelloImpl.java:17)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:606)
	at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
	at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:58)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:85)
	at com.soecode.lyf.web.SayhelloInterceptor.doAround(SayhelloInterceptor.java:69)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:606)
	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:621)
	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:610)
	at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:68)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.aspectj.AspectJAfterAdvice.invoke(AspectJAfterAdvice.java:43)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor.invoke(AfterReturningAdviceInterceptor.java:52)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:52)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
	at com.sun.proxy.$Proxy2.sayHello(Unknown Source)
	at com.soecode.lyf.web.AOPTest.main(AOPTest.java:12)


二、基于注解的实现

注解类AopAspectJ的代码:

package com.soecode.lyf.web;

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 AopAspectJ {
	
	public static final String EDP="execution(* com.soecode.lyf.service.impl.ISayHelloImpl..*(..))";
	
	/**
     * 切面的前置方法 即方法执行前拦截到的方法
      * 在目标方法执行之前的通知
      * @param jp
     */
    @Before(EDP)
    public void doBefore(JoinPoint jp){
        
        System.out.println("=========执行前置通知==========");
    }
    
    
    /**
     * 在方法正常执行通过之后执行的通知叫做返回通知
      * 可以返回到方法的返回值 在注解后加入returning 
     * @param jp
     * @param result
     */
    @AfterReturning(value=EDP,returning="result")
    public void doAfterReturning(JoinPoint jp,String result){
        System.out.println("===========执行后置通知============");
    }
    
    /**
     * 最终通知:目标方法调用之后执行的通知(无论目标方法是否出现异常均执行)
      * @param jp
     */
    @After(value=EDP)
    public void doAfter(JoinPoint jp){
        System.out.println("===========执行最终通知============");
    }
    
    /**
     * 环绕通知:目标方法调用前后执行的通知,可以在方法调用前后完成自定义的行为。
      * @param pjp
     * @return
     * @throws Throwable
     */
    @Around(EDP)
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable{

        System.out.println("======执行环绕通知开始=========");
        // 调用方法的参数
        Object[] args = pjp.getArgs();
        // 调用的方法名
        String method = pjp.getSignature().getName();
        // 获取目标对象
        Object target = pjp.getTarget();
        // 执行完方法的返回值
        // 调用proceed()方法,就会触发切入点方法执行
        Object result=pjp.proceed();
        System.out.println("输出,方法名:" + method + ";目标对象:" + target + ";返回值:" + result);
        System.out.println("======执行环绕通知结束=========");
        return result;
    }
    
    /**
     * 在目标方法非正常执行完成, 抛出异常的时候会走此方法
      * @param jp
     * @param ex
     */
    @AfterThrowing(value=EDP,throwing="ex")
    public void doAfterThrowing(JoinPoint jp,Exception ex) {
        System.out.println("===========执行异常通知============");
    }
	
}

spring-asp-aop.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:aop="http://www.springframework.org/schema/aop"
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/tx 
    http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
    http://www.springframework.org/schema/aop 
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
    
    <!-- 声明spring对@AspectJ的支持 -->
	<aop:aspectj-autoproxy/>
	<!-- 声明一个业务类 -->
	<bean id="iSayHelloImpl" class="com.soecode.lyf.service.impl.ISayHelloImpl" ></bean>
	<!-- 声明通知类 -->
	<bean id="aspectBean" class="com.soecode.lyf.web.AopAspectJ" />
	
</beans>

测试类的代码:

package com.soecode.lyf.web;

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

import com.soecode.lyf.service.ISayHello;

public class AOPTest {
	public static void main(String[] args) {
		//ApplicationContext act =  new ClassPathXmlApplicationContext("spring/spring-aop.xml");
		ApplicationContext act =  new ClassPathXmlApplicationContext("spring-asp-aop.xml");
		ISayHello iSayHello = (ISayHello)act.getBean("iSayHelloImpl");
		iSayHello.sayHello();
		
	}
}

打印的日志1:

[org.springframework.context.support.ClassPathXmlApplicationContext] - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@694f23ae: startup date [Wed Jan 31 11:24:33 CST 2018]; root of context hierarchy
  [org.springframework.beans.factory.xml.XmlBeanDefinitionReader] - Loading XML bean definitions from class path resource [spring-asp-aop.xml]
  ======执行环绕通知开始=========
=========执行前置通知==========
hello spring AOP
===========执行最终通知============
===========执行异常通知============
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 2
	at com.soecode.lyf.service.impl.ISayHelloImpl.sayHello(ISayHelloImpl.java:17)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:606)
	at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
	at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:52)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:85)
	at com.soecode.lyf.web.AopAspectJ.doAround(AopAspectJ.java:67)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:606)
	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:621)
	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:610)
	at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:68)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.aspectj.AspectJAfterAdvice.invoke(AspectJAfterAdvice.java:43)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor.invoke(AfterReturningAdviceInterceptor.java:52)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:58)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
	at com.sun.proxy.$Proxy10.sayHello(Unknown Source)
	at com.soecode.lyf.web.AOPTest.main(AOPTest.java:13)

打印的日志2:

[org.springframework.context.support.ClassPathXmlApplicationContext] - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@3d2f40eb: startup date [Wed Jan 31 11:28:43 CST 2018]; root of context hierarchy
  [org.springframework.beans.factory.xml.XmlBeanDefinitionReader] - Loading XML bean definitions from class path resource [spring-asp-aop.xml]
  ======执行环绕通知开始=========
=========执行前置通知==========
hello spring AOP
输出,方法名:sayHello;目标对象:com.soecode.lyf.service.impl.ISayHelloImpl@1bd141bb;返回值:null
======执行环绕通知结束=========
===========执行最终通知============




  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值