4.Spring AOP

4.1 Spring AOP****的基本概念

4.1.1 AOP****的概念

在业务处理代码中,通常都有日志记录、性能统计、安全控制、事务处理、异常处理等操作。尽管使用OOP可以通过封装或继承的方式达到代码的重用,但仍然存在同样的代码分散到各个方法中。因此,采用OOP处理日志记录等操作,不仅增加了开发者的工作量,而且提高了升级维护的困难。为了解决此类问题,AOP思想应运而生。AOP **采取横向抽取机制,即将分散在各个方法中的重复代码提取出来,然后在程序编译或运行阶段,再将这些抽取出来的代码应用到需要执行的地方。**这种横向抽取机制,采用传统的OOP是无法办到的,因为OOP实现的是父子关系的纵向重用。但是****AOP不是OOP的替代品,而是OOP的补充,它们相辅相成。

4.1.2 AOP的术语

1.切面

切面(Aspect)是指封装横切到系统功能(如事务处理)的类。

2.连接点

连接点(Joinpoint)是指程序运行中的一些时间点,如方法的调用或异常的抛出。

3.切入点

切入点(Pointcut)是指那些需要处理的连接点。在Spring AOP 中,所有的方法执行都是连接点,而切入点是一个描述信息,它修饰的是连接点,通过切入点确定哪些连接点需要被处理。

4.通知(增强处理)

由切面添加到特定的连接点(满足切入点规则)的一段代码,即在定义好的切入点处所要执行的程序代码。可以将其理解为切面开启后,切面的方法。因此,通知是切面的具体实现。

5.引入

引入(Introduction)允许在现有的实现类中添加自定义的方法和属性。

6.目标对象

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

7.代理

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

8.组入

组入(Weaving)是将切面代码插入到目标对象上,从而生成代理对象的过程。根据不同的实现技术,AOP织入有三种方式:编译器织入,需要有特殊的Java编译器;类装载期织入,需要有特殊的类装载器;动态代理织入,在运行期为目标类添加通知生成子类的方式。Spring AOP框架默认采用动态代理织入,而AspectJ(基于Java语言的AOP框架)采用编译器织入和类装载期织入。

4.2****动态代理

目前,Spring AOP中常用JDK和CGLIB两种动态代理技术。

4.2.1 JDK****动态代理

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

下面通过一个实例演示使用JDK动态代理实现Spring AOP

1.创建应用

创建一个名为SpringAOP的Web应用。

2.创建接口及实现类

在SpringAOP的java目录下,创建一个dynamic.jdk包,在该包中创建接口TestDao和接口实现类TestDaoImpl。该实现类作为目标类,在代理类中对其方法进行增强处理。

java 复制代码
package dynamic.jdk;

public interface TestDao {
    public void save();

    public void modify();

    public void delete();
}
java 复制代码
package dynamic.jdk;

public class TestDaoImpl implements TestDao {
    
    public void save() {
        System.out.println("保存");
    }


    public void modify() {
        System.out.println("修改");
    }


    public void delete() {
        System.out.println("删除");
    }
}

3.创建切面类

在SpringAOP的java目录下,创建一个aspect包,在该包中创建切面类MyAspect,在该类中可以定义多个通知(增强处理的功能方法)。

java 复制代码
package aspect;

/**
 * 切面类,可以定义多个通知,即增强处理的方法
 */
public class MyAspect {
    public void check() {
        System.out.println("模拟权限控制");
    }

    public void except() {
        System.out.println("模拟异常处理");
    }

    public void log() {
        System.out.println("模拟日志记录");
    }

    public void monitor() {
        System.out.println("性能监测");
    }
}

4.创建代理类

在dynamic.jdk包中,创建代理类JDKDynamicProxy。在JDK动态代理中,代理类必须实现java.lang.reflect.InvocationHandler接口,并编写代理方法。在代理方法中,需要通过Proxy实现动态代理。

java 复制代码
package dynamic.jdk;

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

import aspect.MyAspect;

public class JDKDynamicProxy implements InvocationHandler {
    //声明目标类接口对象(真实对象)
    private TestDao testDao;

    /**
     * 创建代理的方法,建立代理对象和真实对象的代理关系,并返回代理对象
     **/
    public Object createProxy(TestDao testDao) {
        this.testDao = testDao;
        //1.类加载器
        ClassLoader cld = JDKDynamicProxy.class.getClassLoader();
        //2.被代理对象实现的所有接口
        Class[] clazz = testDao.getClass().getInterfaces();
        //3.使用代理类进行增强,返回代理后的对象
        return Proxy.newProxyInstance(cld, clazz, this);
    }

    /**
     * 代理的逻辑方法,所有动态代理类的方法调用,都交给该方法处理
     * proxy被代理对象
     * method将要被执行的方法信息
     * args执行方法时需要的参数
     * return代理结果
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //创建一个切面
        MyAspect myAspect = new MyAspect();
        //前增强
        myAspect.check();
        myAspect.except();
        //在目标类上调用方法,并传入参数,相当于调用testDao里的方法
        Object obj = method.invoke(testDao, args);
        //后增强
        myAspect.log();
        myAspect.monitor();
        return obj;
    }
}

5.创建测试类

在dynamic.jdk包中,创建测试类JDKDynamicTest。在主方法中创建代理对象和目标对象,然后从代理对象中获取对目标对象增强后的对象,最后调用该对象的添加、修改和删除方法。

java 复制代码
package dynamic.jdk;

public class JDKDynamicTest {
    public static void main(String[] args) {
        //创建代理对象
        JDKDynamicProxy jdkProxy = new JDKDynamicProxy();
        //创建目标对象
        TestDao testDao = new TestDaoImpl();
        /**从代理对象中获取增强后的目标对象,该对象是一个被代理的对象,它会进入代理的逻辑方法invoke里**/
        TestDao testDaoAdvice = (TestDao) jdkProxy.createProxy(testDao);
        //执行方法
        testDaoAdvice.save();
        System.out.println("==============");
        testDaoAdvice.modify();
        System.out.println("==============");
        testDaoAdvice.delete();
    }
}

4.2.2 CGLIB****动态代理

CGLIB(Code Generation Library)是一个高性能开源的代码生成包,采用非常底层的字节码技术,对指定的目标类生成一个子类,并对子类进行增强。在Spring Core包中已经集成了CGLIB所需要的JAR包,不需要另外导入JAR包。

1.创建目标类

在SpringAOP的java目录下,创建一个dynamic.cglib包,在该包中创建目标类TestDao,该类不需要实现任何接口。

java 复制代码
package dynamic.cglib;

public class TestDao {
    public void save() {
        System.out.println("保存");
    }

    public void modify() {
        System.out.println("修改");
    }

    public void delete() {
        System.out.println("删除");
    }
}

2.创建代理类

在dynamic.cglib包中,创建代理类CglibDynamicProxy,该类实现MethodInterceptor接口。

java 复制代码
package dynamic.cglib;

import java.lang.reflect.Method;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import aspect.MyAspect;

public class CglibDynamicProxy implements MethodInterceptor {
    /**
     * 创建代理的方法,生成CGLIB代理对象
     * target目标对象,需要增强的对象
     * 返回目标对象的CGLIB代理对象
     */
    public Object createProxy(Object target) {
        //创建一个动态类对象,即增强类对象
        Enhancer enhancer = new Enhancer();
        //确定需要增强的类,设置其父类
        enhancer.setSuperclass(target.getClass());
        //确定代理逻辑对象为当前对象,要求当前对象实现MethodInterceptor的方法
        enhancer.setCallback(this);
        //返回创建的代理对象
        return enhancer.create();
    }

    /**
     * intercept方法会在程序执行目标方法时被调用
     * proxy CGLIB根据指定父类生成的代理对象
     * method拦截方法
     * args拦截方法的参数数组
     * methodProxy方法的代理对象,用于执行父类的方法
     * 返回代理结果
     */

    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        //创建一个切面
        MyAspect myAspect = new MyAspect();
        //前增强
        myAspect.check();
        myAspect.except();
        //目标方法执行,返回代理结果
        Object obj = methodProxy.invokeSuper(proxy, args);
        //后增强
        myAspect.log();
        myAspect.monitor();
        return obj;
    }
}

3.创建测试类

在dynamic.cglib包中,创建测试类CglibDynamicTest。在主方法中创建代理对象和目标对象,然后从代理对象中获取对目标对象增强后的对象,最后调用该对象的添加、修改和删除方法。

java 复制代码
package dynamic.cglib;

public class CglibDynamicTest {
    public static void main(String[] args) {
        //创建代理对象
        CglibDynamicProxy cdp = new CglibDynamicProxy();
        //创建目标对象
        TestDao testDao = new TestDao();
        //获取增强后的目标对象
        TestDao testDaoAdvice = (TestDao) cdp.createProxy(testDao);
        //执行方法
        testDaoAdvice.save();
        System.out.println("==============");
        testDaoAdvice.modify();
        System.out.println("==============");
        testDaoAdvice.delete();
    }
}

4.3基于代理类的AOP****实现

从4.2节可知,在Spring中默认使用JDK动态代理实现AOP编程。使用org.springframework.aop.framework.ProxyFactoryBean创建代理是Spring AOP实现的最基本方式。

4.3.1**.通知类型**

在讲解ProxyFactoryBean之前,先了解一下Spring的通知类型。根据Spring中通知在目标类方法的连接点位置,可以分为6种如下类型:

(1)环绕通知

环绕通知(org.aopalliance.intercept.MethodInterceptor)是在目标方法执行前和执行后实施增强,可以应用于日志记录、事务处理等功能。

(2)前置通知

前置通知(org.springframework.aop.MethodBeforeAdvice)是在目标方法执行前实施增强,可应用于权限管理等功能。

(3)后置返回通知

后置返回通知(org.springframework.aop.AfterReturningAdvice)是在目标方法成功执行后实施增强,可应用于关闭流、删除临时文件等功能。

(4)后置(最终)通知

后置通知(org.springframework.aop.AfterAdvice)是在目标方法执行后实施增强,与后置返回通知不同的是,不管是否发生异常都要执行该通知,可应用于释放资源。

(5)异常通知

异常通知(org.springframework.aop.ThrowsAdvice)是在方法抛出异常后实施增强,可以应用于处理异常、记录日志等功能。

(6)引入通知

引入通知(org.springframework.aop.IntroductionInterceptor)是在目标类中添加一些新的方法和属性,可以应用于修改目标类(增强类)。

4.3.2**.**ProxyFactoryBean

ProxyFactoryBeanorg.springframework.beans.factory.FactoryBean接口的实现类,FactoryBean负责实例化一个Bean实例,ProxyFactoryBean负责为其他Bean****实例创建代理实例。

下面通过一个实现环绕通知的实例演示Spring使用ProxyFactoryBean创建AOP代理的过程。

1)导入相关JAR

在核心JAR包基础上,需要再向SpringAOP的外部库目录下导入JAR包spring-aop-5.0.2.RELEASE.jar和aopalliance-1.0.jar。aopalliance-1.0.jar是AOP联盟提供的规范包,可以通过地址"http://mvnrepository.com/artifact/aopalliance/aopalliance/1.0"下载。

导入aopalliance-1.0.jar方法:

在 pom.xml 中的依赖中添加以下代码:

复制代码
<dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>5.2.10.RELEASE</version><!--在项目中使用的 Spring 版本-->
    </dependency>
    <dependency>
      <groupId>aopalliance</groupId>
      <artifactId>aopalliance</artifactId>
      <version>1.0</version>
    </dependency>

2)创建切面类

由于该实例实现环绕通知,所以切面类需要实现org.aopalliance.intercept.MethodInterceptor接口。在java目录下,创建一个spring.proxyfactorybean包,并在该包中创建切面类MyAspect。

复制代码
package spring.proxyfactorybean;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
/**
 * 切面类
 */
public class MyAspect implements MethodInterceptor{
    public Object invoke(MethodInvocation arg0) throws Throwable {
        //增强方法
        check();
        except();
        //执行目标方法
        Object obj = arg0.proceed();
        //增强方法
        log();
        monitor();
        return obj;
    }
    public void check() {
        System.out.println("模拟权限控制");
    }
    public void except() {
        System.out.println("模拟异常处理");
    }
    public void log() {
        System.out.println("模拟日志记录");
    }
    public void monitor() {
        System.out.println("性能监测");
    }
}

3)配置切面并指定代理

切面类需要配置为Bean实例,Spring容器才能识别为切面对象。在resources包中,创建配置文件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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!-- 定义目标对象 -->
    <bean id="testDao" class="dao.TestDaoImpl"/>
    <!-- 创建一个切面 -->
    <bean id="myAspect" class="spring.proxyfactorybean.MyAspect"/>
    <!-- 使用Spring代理工厂定义一个名为testDaoProxy的代理对象 -->
    <bean id="testDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!-- 指定代理实现的接口 -->
        <property name="proxyInterfaces" value="dao.TestDao"/>
        <!-- 指定目标对象 -->
        <property name="target" ref="testDao"/>
        <!-- 指定切面,植入环绕通知 -->
        <property name="interceptorNames" value="myAspect"/>
        <!-- 指定代理方式,true指定CGLIB动态代理;默认false,指定JDK动态代理 -->
        <property name="proxyTargetClass" value="true"/>
    </bean>
</beans>

4)创建测试类

在spring.proxyfactorybean包中,创建测试类ProxyFactoryBeanTest,在主方法中使用Spring容器获取代理对象,并执行目标方法。

复制代码
package spring.proxyfactorybean;

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

public class ProxyFactoryBeanTest {
    private static ApplicationContext appCon;

    public static void main(String[] args) {
        appCon = new ClassPathXmlApplicationContext("resources/applicationContext.xml");
        //从容器中,获取增强后的目标对象
        TestDao testDaoAdvice = (TestDao) appCon.getBean("testDaoProxy");
        //执行方法
        testDaoAdvice.save();
        System.out.println("================");
        testDaoAdvice.modify();
        System.out.println("================");
        testDaoAdvice.delete();
    }
}

4.4基于XML配置开发AspectJ

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

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

下面通过一个实例演示基于XML配置开发AspectJ的过程。

1**.导入AspectJ框架相关的JAR包**

需要再向SpringAOP应用导入JAR包spring-aspects-5.0.2.RELEASE.jar和aspectjweaver-1.8.13.jar。

spring-aspects-5.0.2.RELEASE.jar是Spring为AspectJ提供的实现。

aspectjweaver-1.8.13.jar是AspectJ框架所提供的规范包,可以通过地址"http://mvnrepository.com/artifact/org.aspectj/aspectjweaver/1.8.13"下载。

pom文件中增加如下代码:

复制代码
<dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>5.0.2.RELEASE</version>
    </dependency>


    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.8.13</version>
    </dependency>

2**.创建切面类**

在SpringAOP应用的java目录下,创建aspectj.xml包,在该包中创建切面类MyAspect,并在类中编写各种类型通知。

复制代码
package aspectj.xml;
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.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("最终通知:模拟释放资源");
	}
}

3**.创建配置文件,并编写相关配置**

在java包中,创建配置文件applicationContext.xml,并<aop:config>元素及其子元素编写相关配置。

复制代码
<?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="dao.TestDaoImpl"/>
    <!-- 定义切面 -->
    <bean id="myAspect" class="aspectj.xml.MyAspect"/>
    <!-- AOP配置 -->
    <aop:config>
    	<!-- 配置切面 -->
    	<aop:aspect ref="myAspect">
    		<!-- 配置切入点,通知增强哪些方法 -->
    		<aop:pointcut expression="execution(* dao.*.*(..))" 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>

4**.创建测试类**

在aspectj.xml包中,创建测试类XMLAspectJTest,在主方法中使用Spring容器获取代理对象,并执行目标方法。

复制代码
package aspectj.xml;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import dao.TestDao;
public class XMLAspectJTest {
	private static ApplicationContext appCon;
	public static void main(String[] args) {
		appCon = new ClassPathXmlApplicationContext("/aspectj/xml/applicationContext.xml");
		//从容器中,获取增强后的目标对象
		TestDao testDaoAdvice = (TestDao)appCon.getBean("testDao");
		//执行方法
		testDaoAdvice.save();
	}
}

4.5基于注解开发AspectJ

基于注解开发AspectJ要比基于XML配置开发AspectJ便捷许多,所以在实际开发中推荐使用注解方式。

下面通过一个实例讲解基于注解开发AspectJ的过程。

1**.创建切面类,并进行注解**

在SpringAOP应用的java目录下,创建aspectj.annotation包,在该包中创建切面类MyAspect。在该类中,首先使用@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(* dao.*.*(..))")//也可以直接注解到通知方法上
	private void myPointCut() {
		//对应<aop:pointcut expression="execution(* dao.*.*(..))" id="myPointCut"/>
	}
	/**
	 * 前置通知,使用Joinpoint接口作为参数获得目标对象信息
	 */
	@Before("myPointCut()")//对应<aop:before method="before" pointcut-ref="myPointCut"/>
	//@Pointcut("execution(* dao.*.*(..))")
	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.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("最终通知:模拟释放资源");
	}
}

2**.注解目标类**

使用注解@Repository将目标类dynamic.jdk.TestDaoImpl注解为目标对象,注解代码如下:

@Repository("testDao")

复制代码
package dao;
import org.springframework.stereotype.Repository;
@Repository("testDao")
public class TestDaoImpl implements TestDao{
	@Override
	public void save() {
		//int n = 100/0;
		System.out.println("保存");
	}
	@Override
	public void modify() {
		System.out.println("修改");
	}
	@Override
	public void delete() {
		System.out.println("删除");
	}
}

3**.创建配置文件**

在resources包中,创建配置文件applicationContext.xml,并在配置文件中指定需要扫描的包,使注解生效。同时,需要启动基于注解的AspectJ支持。

复制代码
<?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="dao"/>
   <!-- 启动基于注解的AspectJ支持 -->
   <aop:aspectj-autoproxy/>
</beans>

4**.创建测试类**

测试类与运行结果与4.4相同,不再赘述。

相关推荐
一只叫煤球的猫8 小时前
写代码很6,面试秒变菜鸟?不卖课,面试官视角走心探讨
前端·后端·面试
bobz9658 小时前
tcp/ip 中的多路复用
后端
bobz9658 小时前
tls ingress 简单记录
后端
皮皮林5519 小时前
IDEA 源码阅读利器,你居然还不会?
java·intellij idea
你的人类朋友9 小时前
什么是OpenSSL
后端·安全·程序员
bobz96510 小时前
mcp 直接操作浏览器
后端
前端小张同学12 小时前
服务器部署 gitlab 占用空间太大怎么办,优化思路。
后端
databook12 小时前
Manim实现闪光轨迹特效
后端·python·动效
武子康13 小时前
大数据-98 Spark 从 DStream 到 Structured Streaming:Spark 实时计算的演进
大数据·后端·spark
该用户已不存在13 小时前
6个值得收藏的.NET ORM 框架
前端·后端·.net