Java EE SSM框架整合开发(四)Spring AOP

268 阅读14分钟

参考博客

AOP(Aspect Oriented Programming,面向切面编程)采取横向抽取机制,将分散在各个方法中的重复代码提取出来,然后在程序编译(静态代理)或运行阶段(动态代理),再将这些抽取出来的代码应用到需要执行的地方。

1111.png

代理模式

为了理解切面织入的代理实现,需要对代理模式有一定的了解;代理模式即给某对象提供一个代理,由代理对象控制对原对象的引用。

1673a262814cef3e.png

1673a2653b02d775.png

subject:抽象主题角色,是一个接口。该接口是对象和它的代理共用的接口;

RealSubject:真实主题角色,是实现抽象主题接口的类;

Proxy:代理角色,内部含有对真实对象RealSubject的引用,从而可以操作真实对象。代理对象提供与真实对象相同的接口,以便代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。

1.1 代理模式示例

举个例子就是现在一个班上的学生需要交作业,现在由班长代理交作业,那么班长就是代理,学生就是被代理的对象。

1.1.1 静态代理

首先,创建一个Person接口。这个接口就是学生(被代理类),和班长(代理类)的公共接口,他们都有交作业的行为。这样,学生交作业就可以让班长来代理执行。

public interface Person {
    void giveTask();//交作业
}

Student类实现Person接口,实现交作业这个行为。

public class Student implements Person {
    private String name;
    public Student(String name) {
        this.name = name;
    }
    public void giveTask() {
        System.out.println(name + "交语文作业");
    }
}

StudentsProxy类也实现了Person接口,但是还另外持有一个学生类对象,那么他可以代理学生类对象执行交作业的行为。

public class StudentsProxy implements Person{
    Student stu; //被代理的学生
 
    public StudentsProxy(Person stu) {
        // 只代理学生对象
        if(stu.getClass() == Student.class) {
            this.stu = (Student)stu;
        }
    }
 
    //代理交作业,调用被代理学生的交作业的行为
    public void giveTask() {
        stu.giveTask();
    }
}

代理模式使用方法

public class StaticProxyTest {
    public static void main(String[] args) {
        //被代理的学生林浅,他的作业上交有代理对象monitor完成
        Person linqian = new Student("林浅");
 
        //生成代理对象,并将林浅传给代理对象
        Person monitor = new StudentsProxy(linqian);
 
        //班长代理交作业
        monitor.giveTask();
    }
}

这里并没有直接通过林浅(被代理对象)来执行交作业的行为,而是通过班长(代理对象)来代理执行了。这就是代理模式。代理模式就是在访问实际对象时引入一定程度的间接性,即不直接调用实际对象的方法,那么我们在代理过程中就可以加上一些其他用途。比如班长在帮林浅交作业的时候想告诉老师最近林浅的进步很大,在代理类的交作业之前加入方法即可。

这个优点就可以运用在Spring AOP中,我们可以在切入点之前或者之后执行一些操作,切入点就是一个个方法。这些方法所在类肯定就是被代理了,在代理过程中切入了一些其他操作。

1.1.2 JDK动态代理

动态代理和静态代理的区别是,静态代理对于每一个方法,在代理类中都需要相对应的代理方法,且代理类代理的类也是确定的。如果学生增加了记录成绩的方法,那么在静态代理中就要新增对应的代理方法,如果想在记录成绩和交作业两个方法之前添加统一的动作,就需要在两个代理方法中分别写代码。

静态代理在程序运行之前就已经变异完成,但是动态代理的代理类是在程序运行时创建的(观察下文中的StuInvocationHandler中被代理类不写为具体类而写为Object,及被代理方法写为Method)。相比于静态代理,动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法(如在每个代理方法之前加一个处理方法)。

java 有三种代理方式,静态代理,JDK动态代理,CGLIB动态代理,JDK动态代理实现方法如下。

首先还是定义一个Person接口,创建需要被代理的实际类,也就是学生类,这些和静态代理相同。 java动态代理这里用InvocationHandler(接口)和Proxy(类)实现

InvocationHandler

每一个proxy代理实例都有一个关联的调用处理程序invoke,由InvocationHandler接口的继承类实现该方法,并关联到代理类上,在代理实例调用方法时,方法调用被编码分派到调用处理程序的invoke方法。

    /**
    * proxy:代理类代理的真实代理对象com.sun.proxy.$Proxy0
    * method:我们所要调用某个对象真实的方法的Method对象
    * args:指代代理对象方法传递的参数
    */
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;

Proxy

Proxy就是用来创建一个代理对象的类,它提供了很多方法,但是我们最常用的是newProxyInstance方法。

/*
loader:一个classloader对象,定义了由哪个classloader对象对生成的代理类进行加载
interfaces:一个interface对象数组,表示我们将要给我们的代理对象提供一组什么样的接口,即声明代理类实现了这些接口,代理类就可以调用接口中声明的所有方法。
h:一个InvocationHandler对象,表示当动态代理对象调用方法的时候会关联到哪一个InvocationHandler对象上,并最终由其调用invoke。
*/
public static Object newProxyInstance(ClassLoader loader, 
                                        Class<?>[] interfaces, 
                                        InvocationHandler h)

创建InvocationHandler接口实现类StuInvocationHandler,这个类中持有一个被代理对象的实例target。InvocationHandler中有一个invoke方法,所有执行代理对象的方法都会被替换成执行invoke方法。

public class StuInvocationHandler<T> implements InvocationHandler {
    //invocationHandler持有的被代理对象
    T target;
 
    public StuInvocationHandler(T target) {
        this.target = target;
    }
 
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理执行" +method.getName() + "方法");
        Object result = method.invoke(target, args);
        return result;
    }
}

测试类

public class ProxyTest {
    public static void main(String[] args) {
 
        //创建一个实例对象,这个对象是被代理的对象
        Person linqian = new Student("林浅");
 
        //创建一个与代理对象相关联的InvocationHandler
        InvocationHandler stuHandler = new StuInvocationHandler<Person>(linqian);
 
        //通过Proxy创建一个代理对象stuProxy来代理linqian,代理对象的每个执行方法都会替换执行Invocation中的invoke方法
        Person stuProxy = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, stuHandler);
 
        //代理执行交作业的方法
        stuProxy.giveTask();
    }
}

接下来继续了解AOP的基本概念及Spring中AOP是如何实现的。

Spring AOP

3.1 基本概念

0823.png

  • 切面(Aspect):提供辅助功能的模块,如安全管理,日志信息等。

  • 连接点(JoinPoint):切面代码可以通过连接点切入到正常业务之中,图中每个方法的每个点都是连接点。

  • 切入点(PointCut):是指那些需要处理的连接点。一个切面不需要通知所有的连接点,而在连接点的基础之上增加切入的规则,选择需要增强的点,最终真正通知的点就是切入点。

  • 通知(增强处理Advice):通知是切面的具体实现,是切入点处所要执行的程序代码。主要有五种通知:before,after,afterReturning,afterThrowing,around。(翻译成通知好怪啊。)

  • 引入(introduction):在不修改代码的前提下,引入可以在运行期为类动态地添加方法或字段。

  • 织入(Weaving):将切面代码插入到目标对象上,从而生成代理对象的过程。

  • 目标对象(Target Object):被增强的对象。如果AOP框架使用运行时代理的方式(动态的AOP)来实现切面,那么通知对象是一个代理对象。

  • 代理(Proxy)是通知应用到目标对象之后,被动态创建的对象。

关于织入:

根据不同的实现技术,AOP织入有三种方式:编译器织入,需要有特殊的Java编译器;类装载期织入,需要有特殊的类装载器;动态代理织入,在运行期为目标类添加通知生成子类的方式。

关于Spring Aop 通知类型:

image.png

通知类型描述
环绕通知org.aopalliance.intercept.MethodInterceptor在目标方法执行前和执行后实施增强,可以应用于日志记录、事务处理等功能。
前置通知org.springframework.aop.MethodBeforeAdvice在目标方法执行前实施增强,可应用于权限管理等功能。
后置返回通知org.springframework.aop.AfterReturningAdvice在目标方法成功执行后实施增强,可应用于关闭流、删除临时文件等功能。
后置(最终)通知org.springframework.aop.AfterAdvice在目标方法执行后实施增强,与后置返回通知不同的是,不管是否发生异常都要执行该通知,可应用于释放资源。
异常通知org.springframework.aop.ThrowsAdvice在方法抛出异常后实施增强,可以应用于处理异常、记录日志等功能。
引入通知org.springframework.aop.IntroductionInterceptor在目标类中添加一些新的方法和属性,可以应用于修改目标类(增强类)。

Spring AOP框架默认采用JDK动态代理实现AOP编程,AspectJ(基于Java语言的AOP框架)采用编译器织入和类装载期织入。

Spring 也提供了 AspectJ 的支持,包括基于XML开发和基于注解开发两种

3.2 动态代理

3.1.1 JDK

JDK动态代理是java.lang.reflect.*包提供的方式,它必须借助一个接口才能产生代理对象。因此,对于使用业务接口的类,Spring默认使用JDK动态代理实现AOP。

实例如下:(其实和交作业的实例一样啦)

被代理类与代理类共同的接口

package com.tmp;

public interface TestDao {
	public void work();
}

被代理类

package com.tmp;

import org.springframework.stereotype.Repository;

@Repository("testDao")
public class TestDaoImpl implements TestDao{
	@Override
	public void work() {
		System.out.println("对数据库进行一些操作");
	}

}

接口实现类,代理类的创建也可以由他的函数完成

package com.tmp;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class TestInvocationHandler implements InvocationHandler {
	private TestDao testDao;
	
	public Object getProxy(TestDao testDao) {
		//创建动态代理类
		this.testDao = testDao;
		return Proxy.newProxyInstance(TestInvocationHandler.class.getClassLoader(), testDao.getClass().getInterfaces(), this);
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println("前置通知");
		Object result = method.invoke(testDao, args);
		System.out.println("后置通知");
		return result;
	}
	
}

测试类

package com.tmp;

public class TestProxy {
	public static void main(String args[]) {
		TestDao testdao = new TestDaoImpl(); //被代理类
		TestInvocationHandler testhandler = new TestInvocationHandler(); //InvocationHandler接口实现类
		TestDao testproxy = (TestDao) testhandler.getProxy(testdao); //代理类
		testproxy.work();
	}
	
}

3.1.2 CGLB

CGLIB(Code Generation Library)是一个高性能开源的代码生成包,采用非常底层的字节码技术,对指定的目标类生成一个子类,并对子类进行增强

主要借助Enhancer(类)和MethodInterceptor(接口)实现

MethodInterceptor

public class CglibMethodInterceptor implements MethodInterceptor {    
    /**
    * 生成 Cglib 动态代理类的方法
    * @param target 被代理类的 Class 对象
    */
   public Object CglibProxyGeneratory(Class target) {        
       /** 创建cglib 代理类 start */
       Enhancer enhancer = new Enhancer();        // 创建加强器,用来创建动态代理类
       enhancer.setSuperclass(target);   // 为代理类指定需要代理的类,也即是父类        
       enhancer.setCallback(this);        // 设置方法拦截器回调引用,对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept() 方法进行拦截 
       return enhancer.create();       // 获取动态代理类对象并返回 
   }    
   /**
    * @param proxy     CGLIB根据指定父类生成的代理对象
    * @param method    代理类中被拦截的方法
    * @param args      接口方法参数
    * @param methodProxy  方法的代理对象,用于执行父类的方法
    */
   @Override
   public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
       System.out.println("before");
       Object result = methodProxy.invokeSuper(proxy, args);       
       System.out.println("after");
       return result;
   }
}

示例如下:

创建被代理类,该类不需要事先任何接口

package com.tmp;
public class TestDao {
	public void work() {
		System.out.println("对数据库进行一些操作");
	}
}

创建CglibProxyGeneratory接口实现类,该类需要实现intercept方法,通过Enhancer类得到动态代理类,并与方法拦截器关联。

package com.tmp;
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

public class CglibDynamicProxy implements MethodInterceptor {
	public Object getProxy(Object target) {
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(target.getClass()); //指定被代理类为父类,生曾其子类
		enhancer.setCallback(this);   //关联方法拦截器,需要对应的类实现intercept
		return enhancer.create();    // 获取动态代理类对象并返回 
	   }    
	
	@Override
	public Object intercept(Object proxy, Method metod, Object[] args, MethodProxy methodProxy) throws Throwable {
		System.out.println("前置通知");
		Object result = methodProxy.invokeSuper(proxy, args);
		System.out.println("后置通知");
		return result;
	}
}

创建测试类检测

package com.tmp;
public class TestProxy {
	public static void main(String args[]) {
		TestDao testDao = new TestDao(); //被代理对象
		CglibDynamicProxy cdp = new CglibDynamicProxy();
		TestDao proxy = (TestDao)cdp.getProxy(testDao); //获取动态代理对象
		proxy.work();
	}
}

3.3 基于代理类的AOP实现

ProxyFactoryBean是org.springframework.beans.factory.FactoryBean接口的实现类,与工厂产生Bean作用类似,可以在XML中配置代理类的工厂。

注意和CGLIB使用的是不同的import

  1. 建立被代理类接口TestDao和接口实现类TestDaoImpl
  2. 继承MethodInterceptor创建切面类MyAspect,注意使用invoke导的包和使用interceptMethodInterceptor导的不是同一个包
package com.tmp;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class MyAspect implements MethodInterceptor{
	@Override
	public Object invoke(MethodInvocation invocation) throws Throwable {
		//MethodInvocation 方法调用者,代表被代理类
		check();
		Object result = invocation.proceed();  //执行目标方法
		log();
		return result;
	}
	public void check() {
		System.out.println("权限控制");
	}
	public void log() {
		System.out.println("日志记录");
	}

}
  1. 在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:context="http://www.springframework.org/schema/context"
	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/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/tx
		http://www.springframework.org/schema/tx/spring-tx.xsd">
       
	
	<!-- 创建被代理对象 -->
	<bean id="testDao" class="com.tmp.TestDaoImpl" />
	 
	<!-- 创建切面类 --> 
	<bean id = "myAspect" class="com.tmp.MyAspect"/>
	
	<!-- 使用代理工厂创建代理类 -->
	<bean id="testDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
		<!-- 指定代理类要代理的接口 -->
		<property name="proxyInterfaces" value="com.tmp.TestDao"/>
		<!-- 被代理对象  -->
		<property name="target" ref="testDao"/>
		<!-- 指定方法拦截器 -->
		<property name="interceptorNames" value="myAspect"/>
		<!-- 指定代理方法,true指定CGLIB动态代理;默认false,指定JDK动态代理  -->
		<property name="proxyTargetClass" value="true"/>
	</bean>
    
</beans>
  1. 创建测试类
package com.tmp;

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

public class TestProxyFactory {
	public static void main(String args[]) {
		@SuppressWarnings("resource")
		ApplicationContext appCon = new ClassPathXmlApplicationContext("applicationContext.xml");
		TestDao testDaoProxy = (TestDao)appCon.getBean("testDaoProxy");
		testDaoProxy.work();
	}
}

3.4 AspectJ

AspectJ是一个基于Java语言的AOP框架。从Spring 2.0以后引入了AspectJ的支持。 目前的Spring框架,建议开发者使用AspectJ实现Spring AOP。使用AspectJ实现Spring AOP的方式有两种。

3.4.1 基于XML

基于XML配置开发AspectJ是指通过XML配置文件定义切面、切入点及通知,所有这些定义都必须在<aop:config>元素内。

目录结构: 345435.png

  1. 建立TestDao及TestDaoImpl
package dynamic.jdk;
public interface TestDao {
	public void save();
	public void modify();
	public void delete();
}
package dynamic.jdk;
import org.springframework.stereotype.Repository;
@Repository("testDao")
public class TestDaoImpl implements TestDao{
	@Override
	public void save() {
		System.out.println("save");
	}
	@Override
	public void modify() {
		System.out.println("modify");
	}
	@Override
	public void delete() {
		System.out.println("delete");
	}
}
  1. 创建切面类
package com.tmp;

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

public class MyAspect {
	/**
	 * 前置通知,使用Joinpoint接口作为参数获得目标对象信息
	 */
	public void before(JoinPoint jp) {
		System.out.print("前置通知:模拟权限控制");
		System.out.println(",目标类对象:" + jp.getTarget() + ",被增强处理的方法:" + jp.getSignature().getName());
	} 
	/**
	 * 后置返回通知
	 */
	public void afterReturning(JoinPoint jp) {
		System.out.print("后置返回通知:模拟删除临时文件");
		System.out.println(",目标类对象:" + jp.getTarget() + ",被增强处理的方法:" + jp.getSignature().getName());
	}
	/**
	 * 环绕通知
	 * ProceedingJoinPoint是JoinPoint子接口,代表可以执行的目标方法
	 * 返回值类型必须是Object
	 * 必须一个参数是ProceedingJoinPoint类型
	 * 必须throws Throwable
	 */
	public Object around(ProceedingJoinPoint pjp) throws Throwable{
		//开始
		System.out.println("环绕开始:执行目标方法前,模拟开启事务");
		//执行当前目标方法
		Object obj = pjp.proceed();
		//结束
		System.out.println("环绕结束:执行目标方法后,模拟关闭事务");
		return obj;
	}
	/**
	 * 异常通知
	 */
	public void except(Throwable e) {
		System.out.println("异常通知:" + "程序执行异常" + e.getMessage());
	}
	
	/**
	 * 后置(最终)通知
	 */
	public void after() {
		System.out.println("最终通知:模拟释放资源");
	}
}
  1. 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"
	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.xsd">
    <!-- 定义目标对象 -->
    <bean id="testDao" class="dynamic.jdk.TestDaoImpl"/>
    <!-- 定义切面 -->
    <bean id="myAspect" class="com.tmp.MyAspect"/>
    <!-- AOP配置 -->
    <aop:config>
    	<!-- 配置切面 -->
    	<aop:aspect ref="myAspect">
    		
    		<!-- 配置切入点,通知增强哪些方法 -->
    		<aop:pointcut expression="execution(* dynamic.jdk.*.*(..))" id="myPointCut"/>
    		
    		<!-- 将通知与切入点关联 -->
    		<!-- 关联前置通知 -->
    		<aop:before method="before" pointcut-ref="myPointCut"/>
    		<!-- 关联后置返回通知,在目标方法成功执行后执行 -->
    		<aop:after-returning method="afterReturning" pointcut-ref="myPointCut"/>
    		<!-- 关联环绕通知 -->
    		<aop:around method="around" pointcut-ref="myPointCut"/>
    		<!-- 关联异常通知,没有异常发生时,将不会执行增强,throwing属性设置通知的第二个参数名称 -->
    		<aop:after-throwing method="except" pointcut-ref="myPointCut" throwing="e"/>
    		<!-- 关联后置(最终)通知,不管目标方法是否成功,都要执行 -->
    		<aop:after method="after" pointcut-ref="myPointCut"/>
    	</aop:aspect>
    </aop:config>
</beans>
  1. 测试类
package com.tmp;

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

import dynamic.jdk.TestDao;

public class TestXMLAspectJ {
	public static void main(String[] args) {
		@SuppressWarnings("resource")
		ApplicationContext appCon = new ClassPathXmlApplicationContext("applicationContext.xml");
		//从容器中,获取增强后的目标对象
		TestDao testDaoAdvice = (TestDao)appCon.getBean("testDao");
		//执行方法
		testDaoAdvice.save();
	}
}

3.4.1 基于注解

image.png

和基于XML类似,但是基于注解更加简洁

  1. 建立TestDao及TestDaoImpl

这里要为TestDaoImpl加上注解@Repository("testDao")

  1. 创建切面类,并进行注解
  • 首先使用@Aspect注解定义一个切面类,由于该类在Spring中是作为组件使用的,所以还需要使用@Component注解。
  • 然后,使用@Pointcut注解切入点表达式,并通过定义方法来表示切入点名称。
  • 最后,在每个通知方法上添加相应的注解,并将切入点名称作为参数传递给需要执行增强的通知方法。
package aspectj.annotation;
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.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
 * 切面类,在此类中编写各种类型通知
 */
@Aspect//对应<aop:aspect ref="myAspect">
@Component//对应<bean id="myAspect" class="aspectj.xml.MyAspect"/>
public class MyAspect {
	/**
	 * 定义切入点
	 */
	@Pointcut("execution(* dynamic.jdk.*.*(..))")
	private void myPointCut() {
		//对应<aop:pointcut expression="execution(* dynamic.jdk.*.*(..))" id="myPointCut"/>
	}
	/**
	 * 前置通知,使用Joinpoint接口作为参数获得目标对象信息
	 */
	@Before("myPointCut()")//对应<aop:before method="before" pointcut-ref="myPointCut"/>
	public void before(JoinPoint jp) {
		System.out.print("前置通知:模拟权限控制");
		System.out.println(",目标类对象:" + jp.getTarget() + ",被增强处理的方法:" + jp.getSignature().getName());
	} 
	/**
	 * 后置返回通知
	 */
	@AfterReturning("myPointCut()")
	public void afterReturning(JoinPoint jp) {
		System.out.print("后置返回通知:" + "模拟删除临时文件");
		System.out.println(",目标类对象:" + jp.getTarget() + ",被增强处理的方法:" + jp.getSignature().getName());
	}
	/**
	 * 环绕通知
	 * ProceedingJoinPoint是JoinPoint子接口,代表可以执行的目标方法
	 * 返回值类型必须是Object
	 * 必须一个参数是ProceedingJoinPoint类型
	 * 必须throws Throwable
	 */
	@Around("myPointCut()")
	public Object around(ProceedingJoinPoint pjp) throws Throwable{
		//开始
		System.out.println("环绕开始:执行目标方法前,模拟开启事务");
		//执行当前目标方法
		Object obj = pjp.proceed();
		//结束
		System.out.println("环绕结束:执行目标方法后,模拟关闭事务");
		return obj;
	}
	/**
	 * 异常通知
	 */
	@AfterThrowing(value="myPointCut()",throwing="e")
	public void except(Throwable e) {
		System.out.println("异常通知:" + "程序执行异常" + e.getMessage());
	}
	/**
	 * 后置(最终)通知
	 */
	@After("myPointCut()")
	public void after() {
		System.out.println("最终通知:模拟释放资源");
	}	
}
  1. 注解扫描
<?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.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
   <!-- 指定需要扫描的包,使注解生效 -->
   <context:component-scan base-package="aspectj.annotation"/>
   <context:component-scan base-package="dynamic.jdk"/>
   <!-- 启动基于注解的AspectJ支持 -->
   <aop:aspectj-autoproxy/>
</beans>
  1. 测试
package aspectj.annotation;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import dynamic.jdk.TestDao;
public class AnnotationAspectJTest {
	public static void main(String[] args) {
		ApplicationContext appCon = new ClassPathXmlApplicationContext("applicationContext.xml");
		//从容器中,获取增强后的目标对象
		TestDao testDaoAdvice = (TestDao)appCon.getBean("testDao");
		//执行方法
		testDaoAdvice.save();
	}
}