springAOP的概述及使用

Spring AOP

SpringAOP是Spring中非常重要的功能模块之一,该模块提供了面向切面编程,在事务处理,日志记录,安全控制等操作中广泛使用。

SpringAOP的基本概念

AOP的概念

AOP(Aspect-Oriented Programming)即面向切面编程,它与OOP(Object-Oriented Programming,面向对象编程)相辅相成,提供了与OOP不同的抽象软件结构的视角。在OOP中,以类作为程序的基本单元,而AOP的基本单元是Aspect(切面)。Struts2的拦截器设计就是基于AOP的思想,是个比较经典的应用。

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

AOP常用术语

在Spring框架中涉及的主要常用术语。

  1. 切面:指封装横切到系统功能(如事务管理)的类。

  2. 连接点:是指程序运行中的一些时间点,例如方法的调用或异常的抛出。

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

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

  5. 引入:运行在现有的实现类中添加自定义的属性和方法。

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

  7. 代理:是通知应用到目标对象之后被动态创建的对象。

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

动态代理

在Java有很多动态代理,例如JDK、CGLIB、Javassit、ASM,其中最常用的动态代理技术是JDK和CGLIB,目前在SpringAOP中常用JDK和CGLIB两种动态代理技术。

JDK动态代理

JDK动态代理是java.lang.reflect.*包提供的功能,它必须要借助一个接口才可以产生代理对象,因此对于使用业务接口的类,Spring默认使用JDK动态代理实现AOP。下面通过实例演示JDK动态代理实现SpringAOP。

  1. 创建接口及其实现类(TestDao)
package dynamic.jdk;
public interface TestDao {
    public void save();
    public void modify();
    public void delete();
}
package dynamic.jdk;
public class TestDaoImpl implements TestDao{
    @Override
    public void save() {
        System.out.println("保存");
    }

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

    @Override
    public void delete() {
        System.out.println("删除");
    }
}
  1. 创建切面类MyAspect,定义了多个通知(增强处理的功能方法)。
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("性能检测");
    }
}
  1. 创建代理类(JDDKDynamicProxy),在JDK动态代理时必须要实现InvocationHandler接口,并编写代理方法,在代理方法中需要Proxy实现动态代理
package dynamic.jdk;
import aspect.MyAspect;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
 * 代理对象的产生,在JDK动态代理中代理类必须实现InvocationHandler接口,并且实现方法。
 */
public class JDKDynamicProxy implements InvocationHandler {
    //声明目标类对象(真实对象)
    private TestDao testDao;
    /**创建代理的方法,建立代理对象与真实对象的代理关系,并返回代理对象**/
    public Object createProxy(TestDao testDao){
        this.testDao=testDao;
        //使用类加载器加载被代理对象实现的所有接口
        ClassLoader classLoader = JDKDynamicProxy.class.getClassLoader();
        Class[] clazz=testDao.getClass().getInterfaces();
        //使用代理类进行增强,返回代理后的对象
        return Proxy.newProxyInstance(classLoader,clazz,this);
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //创建一个切面
        MyAspect myAspect = new MyAspect();
        //前增强
        myAspect.check();
        myAspect.except();
        //在目标类上调用方法并传入参数,相当于调用testDao的方法
        Object object = method.invoke(testDao,args);
        //后增强
        myAspect.log();
        myAspect.monitor();
        return object;
    }
}
  1. 创建测试类,创建代理对象和目标对象,然后从代理对象获取增强后的对象,最后调用对象的方法。
package Test;
import dynamic.jdk.JDKDynamicProxy;
import dynamic.jdk.TestDao;
import dynamic.jdk.TestDaoImpl;
public class JDKDynamicTest {
    public static void main(String[] args) {
        //创建代理对象
        JDKDynamicProxy jdkDynamicProxy = new JDKDynamicProxy();
        //创建目标对象
        TestDao testDao = new TestDaoImpl();
        //从代理对象获取增强后的对象。该对象是一个被代理的对象,它会进入invoke方法中。
        TestDao testDaoAdvice=(TestDao)jdkDynamicProxy.createProxy(testDao);
        testDaoAdvice.save();
        System.out.println("===============");
        testDaoAdvice.modify();
        System.out.println("===============");
        testDaoAdvice.delete();
    }
}

CGLIB动态代理

JDK动态代理必须提供接口才可以实现,对于没有提供接口的类,只能使用CGLB动态代理。

CGLIB是一个高性能的开源代码生成包,采用非常底层的字节码技术,对指定的目标类生成一个子类,并且对子类进行增强,在Spring Core包中已经集成了所需要的jar包,不需要导入jar包,下面通过实例演示CGLIB动态代理的实现过程。

  1. 创建目标类TestDao
package dynamic.cglib;

public class TestDao {
    public void save(){
        System.out.println("保存");
    }
    public void modify(){
        System.out.println("修改");
    }
    public void delete(){
        System.out.println("删除");
    }
}
  1. 创建代理类CglibDynamicProxy,该类实现MethodInterceptor接口。
package dynamic.cglib;

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

import java.lang.reflect.Method;

public class CglibDynamicProxy implements MethodInterceptor {
    public Object createProxy(Object target){
        //创建一个动态类对象,即增强类对象
        Enhancer enhancer = new Enhancer();
        //确定需要增强的类,设置为父类
        enhancer.setSuperclass(target.getClass());
        //确定代理逻辑类为当前对象,要求当前对象实现MethodInterceptor的方法
        enhancer.setCallback(this);
        //返回创建的代理对象
        return enhancer.create();
    }
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        //创建一个切面类
        MyAspect myAspect = new MyAspect();
        //前增强
        myAspect.check();
        myAspect.except();
        //目标方法执行,返回代理结果
        Object object=methodProxy.invokeSuper(o,objects);
        //后增强
        myAspect.monitor();
        myAspect.log();
        return object;
    }
}
  1. 创建测试类CglinDynamicTest
package Test;

import dynamic.cglib.CglibDynamicProxy;
import dynamic.cglib.TestDao;

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

基于代理类的AOP实现

从上面的内容,在Spring中默认使用JDK动态代理实现AOP编程,使用aop.framework.ProxyFactory创建代理是SpringAOP实现的最基本方式。

通知类型:根据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:是在目标类中添加一些新的方法和属性,可应用于修改目标类(增强类)。

ProxyFactoryBean:是org.springframework.beans.factory.FactoryBean接口的实现类,FactoryBean负责实例化一个Bean实例,ProxyFactoryBean负责为其他Bean实例创建代理实例,ProxyFactoryBean类的常用属性如下表所示

属性 描述
target 代理的目标对象
proxyInterfaces 代理需要实现的接口对象,如果是多个接口采用list进行赋值
interceptorNames 需要织入的目标Advice
proxyTargetClass 是否对类代理而不是接口,默认是false,使用JDK动态代理,当为true时,使用CGLIB代理。
singleton 返回的实例是否为单例,默认为true
optimize 当设置为true时强制使用CGLIB代理

list方式赋值:

<list>
	<value></value>
	<value></value>
</list>

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

  1. 导入所需要的jar包。在核心jar包的基础上,还需要导入spring-aopaopalliance两个包。
 <dependency>
      <groupId>aopalliance</groupId>
      <artifactId>aopalliance</artifactId>
      <version>1.0</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>5.0.2.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-expression</artifactId>
      <version>5.0.2.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.0.2.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
      <version>5.0.2.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>5.0.2.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>commons-logging</groupId>
      <artifactId>commons-logging</artifactId>
      <version>1.2</version>
    </dependency>
  1. 创建切面类MyAspect:由于该实例实现的是环绕通知,所以切面类需要实现MethodInterceptor接口。
package spring.proxyfactorybean;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class MyAspect implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        //前置方法
        check();
        except();
        //执行目标方法
        Object object = invocation.proceed();
        //增强方法
        log();
        monitor();
        return object;
    }
    public void check(){
        System.out.println("模拟权限控制");
    }
    public void except(){
        System.out.println("模拟异常处理");
    }
    public void log(){
        System.out.println("模拟日志记录");
    }
    public void monitor(){
        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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="testDao" class="dynamic.jdk.TestDaoImpl"/>
    <!--创建一个切面-->
    <bean id="myAspect" class="spring.proxyfactorybean.MyAspect"/>
    <!--使用Spring代理工厂定义一个testDaoProxy代理对象-->
    <bean id="testDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!--指定代理实现的接口-->
        <property name="proxyInterfaces" value="dynamic.jdk.TestDao"/>
        <!--指定目标对象-->
        <property name="target" ref="testDao"/>
        <!--指定切面,,织入环绕通知-->
        <property name="interceptorNames" value="myAspect"/>
        <!--指定代理方式,true指定CGLIB动态代理,默认为false,指定jdk动态代理-->
        <property name="proxyTargetClass" value="true"/>
    </bean>
</beans>
  1. 创建测试类
package Test;
import dynamic.jdk.TestDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ProxyFactoryBeanTest {
    public static void main(String[] args) {
        ApplicationContext appCo=new ClassPathXmlApplicationContext("applicationContext.xml");
        TestDao testDaoAdvice=(TestDao)appCo.getBean("testDaoProxy");
        testDaoAdvice.save();
        System.out.println("===========");
        testDaoAdvice.modify();
        System.out.println("===========");
        testDaoAdvice.delete();
    }
}

基于XML配置开发的AspectJ

AspectJ是一个基于Java的AOP框架,从Spring2.0以后引入AspectJ的支持,对于目前的Spring框架,建议开发者使用AspectJ实现SpringAOP,使用Aspect实现SpringAOP的方式有2种,一是基于XML配置开发AspectJ,二是基于注解开发AspectJ。

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

元素名称 用途
<aop:config> 开发AspectJ的顶层配置元素,在配置文件<beans>下可以包含多个该元素。
<aop:aspect> 配置一个切面,<aop:config>的子元素,属性ref指定切面的定义。
<aop:pointcut> 配置切入点,<aop:aspect>的子元素,属性expression指定通知增强哪些方法。
<aop:before> 配置前置通知。<aop:aspect>的子元素,属性method指定前置通知方法,属性pointcut-ref指定关联的切入点。
<aop:after-returning> 配置后置返回通知,<aop:aspect>的子元素,属性method指定后置返回通知方法,属性pointcut-ref指定关联的切入点。
<aop:around> 配置环绕通知,<aop:aspect>的子元素,属性method指定环绕通知方法,属性pointcut-ref指定关联的切入点。
<aop:after-throwing> 配置异常通知,<aop:aspect>的子元素,属性method指定异常通知的方法,属性pointcut-ref指定关联的切入点。
<aop:after> 配置后置(最终)通知,<aop:aspect>的子元素,属性method指定后置通知的方法,属性pointcut-ref指定关联的切入点。
<aop:declare-parents> 给通知引入新的额外接口,增强功能,不要求掌握该类型的通知。

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

  1. 导入jar包:另外还要加入基本包
<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>
  1. 创建切面类MyAspect
package aspectj.xml;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
public class MyAspect {
    /**
     *前置通知,使用JoinPoint接口作为参数获得目标对象信息
     */
    public void before(JoinPoint joinpoint){
        System.out.print("前置通知,模拟权限控制");
        System.out.println(",目标对象:"+joinpoint.getTarget()+",被增强的方法"+joinpoint.getSignature().getName());
    }
    /**
     *后置返回通知
     */
    public void afterReturning(JoinPoint joinPoint){
        System.out.print("后置返回通知:"+"模拟删除临时文件");
        System.out.println(",被增强处理的方法为:"+joinPoint.getSignature().getName());
    }
    /**
     * 环绕通知
     */
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
        //开始
        System.out.println("环绕开始,执行目标方法前,模拟开启事务");
        //执行目标方法
        Object object= proceedingJoinPoint.proceed();
        //结束
        System.out.println("环绕结束,执行目标方法后,模拟关闭事务");
        return object;
    }
    /**
     * 异常通知
     */
    public void except(Throwable e){
        System.out.println("异常通知:"+"程序执行异常"+e.getMessage());
    }
    /**
     * 后置通知
     */
    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">
    <!--定义目标对象-->
    <bean id="testDao" class="dynamic.jdk.TestDaoImpl"/>
    <!--定义切面-->
    <bean id="myAspect" class="aspectj.xml.MyAspect"/>
    <!--AOP配置-->
    <aop:config>
        <!--配置切面-->
        <aop:aspect ref="myAspect">
            <!--配置切入点,通知增强哪些方法,使用execution表达式声明位置-->
            <aop:pointcut id="myPointCut" expression="execution(* dynamic.jdk.*.*(..))"/>
            <!--将通知与切入点关联-->
            <!--关联前置通知-->
            <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 Test;

import dynamic.jdk.TestDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class XMLAspectJTest {
    public static void main(String[] args) {
        ApplicationContext appCo = new ClassPathXmlApplicationContext("aop_bean.xml");
        TestDao testDaoAdvice=(TestDao)appCo.getBean("testDao");
        testDaoAdvice.save();
    }
}

基于注解开发AspectJ

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

注解名称 描述
@Aspect 用于定义一个切面,注解在切面类上 。
@Pointcut 用于定义切入点表达式,在使用时需要定义一个切入点方法, 该方法是一个返回值void且方法体为空的普通方法。
@Before 用于定义前置通知,在使用 时通常为其指定value属性值,该值可以是已有的切入点,也可以直接定义切入点表达式。
@AfterReturning 用于定义后置返回通知,在使用时通常为其指定value属性值,该值可以是已有的切入点,也可以直接定义切入点表达式
@Around 用于定义环绕通知,在使用时通常为其指定value属性值,该值可以是已有的切入点,也可以直接定义切入点表达式
@AfterThrowing 用于定义异常通知,在使用时通常为其指定value属性值,该值可以是已有的切入点,也可以直接定义切入点表达式,另外,还有一个throwing属性用于访问的目标方法抛出的异常,该属性值与异常通知方法中同名的形参一致。
@After 用于定义后置(最终)通知,在使用时通常为其指定value属性值,该值可以是已有的切入点,也可以直接定义切入点表达式。

下面通过一个演示注解开发的过程

  1. 创建切面类MyAspect,并进行注解
package aspectj.annoation;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
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 id="myPointCut" expression="execution(* dynamic.jdk.*.*(..))"/>
    }

    /**
     * 前置方法
     * @param joinPoint
     */
    @Before("myPointCut()") //对应<aop:before method="before" pointcut-ref="myPointCut"/>
    public void before(JoinPoint joinPoint){
        System.out.print("前置通知,模拟权限控制");
        System.out.println(",目标类对象:"+joinPoint.getTarget()+",被增强处理的方法:"+joinPoint.getSignature().getName());
    }

    /**
     * 后置返回通知
     * @param joinPoint
     */
    @AfterReturning("myPointCut()")//对应<aop:after-returning method="afterReturning" pointcut-ref="myPointCut"/>
    public void afterReturning(JoinPoint joinPoint){
        System.out.print("后置返回通知:"+"模拟删除临时文件");
        System.out.println(",被增强处理的方法:"+joinPoint.getSignature().getName());
    }

    /**
     * 环绕通知
     * ProceedingJoinPoint是JoinPoint子接口,代表可以执行的目标方法
     * 返回值的类型是Object
     * 必须一个参数为ProceedingJoinPoint类型
     * 必须throws Throwable
     * @param proceedingJoinPoint
     * @return
     * @throws Throwable
     */
    @Around("myPointCut()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint)throws Throwable{
        //开始
        System.out.println("环绕开始:执行目标方法前,模拟开启事务");
        //执行当前目标方法
        Object object=proceedingJoinPoint.proceed();
        //结束
        System.out.println("环绕结束:执行目标方法后,模拟关闭事务");
        return object;
    }

    /**
     * 异常通知
     * @param e
     */
    @AfterThrowing(value = "myPointCut()",throwing = "e")
    public void except(Throwable e){
        System.out.println("异常通知:"+"程序异常执行"+e.getMessage());
    }

    /**
     * 后置通知
     */
    @After("myPointCut()")
    public void after(){
        System.out.println("最终通知,模拟释放资源");
    }
}
  1. 注解目标类:使用注解@Repository将目标类注为目标对象。

  2. 创建配置文件

<?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.annoation"/>
    <context:component-scan base-package="dynamic.jdk"/>
    <!--启动注解配置-->
    <aop:aspectj-autoproxy/>
</beans>
  1. 创建测试类AnnoationAspectTest
package Test;

import dynamic.jdk.TestDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AnnoationAspectTest {
    public static void main(String[] args) {
        ApplicationContext appCo = new ClassPathXmlApplicationContext("aop_anntoation.xml");
        TestDao testDao=(TestDao) appCo.getBean("testDao");
        testDao.save();
    }
}
posted @ 2021-01-20 14:11  笑忘书丶  阅读(195)  评论(0)    收藏  举报