前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java EE之SSM框架整合开发 -- (4) Spring AOP

Java EE之SSM框架整合开发 -- (4) Spring AOP

作者头像
浩Coding
发布2019-07-03 11:59:54
9360
发布2019-07-03 11:59:54
举报
文章被收录于专栏:浩Coding

前言 -- 本章知识点

1.什么是AOP?有哪些术语?为什么要学习AOP编程?

答:AOPAspect-Oriented Programming),即面向切面编程

在Spring AOP框架中,涉及以下常用术语:切面、连接点、切入点、通知(增强处理)、引入、目标对象、代理、组入。

AOP采取横向抽取机制,即将分散在各个方法中的重复代码提取出来,然后在程序编译或运行阶段,再将这些抽取出来的代码应用到需要执行的地方。这种横向抽取机制,采用传统的OOP是无法办到的,因为OOP实现的是父子关系的纵向重用。但是AOP不是OOP的替代品,而是OOP的补充,它们相辅相成

2.在Java中,有哪些常用的动态代理技术?

答:在Java中,有多种动态代理技术,如JDK、CGLIB、Javassist、ASM,其中最常用的动态代理技术有JDK和CGLIB

3.AspectJ框架的AOP开发方式有哪几种?

答:使用AspectJ实现Spring AOP的方式有两种:一是基于XML配置开发AspectJ,一是基于注解开发AspectJ

4.1 Spring AOP的基本概念

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

AOP中类与切面的关系图

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 动态代理

在Java中有多种动态代理技术,例如JDK、CGLIB、Javassist、ASM,目前,Spring AOP中常用JDK和CGLIB两种动态代理技术

4.2.1 JDK动态代理

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

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

1.创建应用

创建一个名为ch4的Web应用,并导入所需的JAR包。

2.创建接口及实现类

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

接口TestDao:

代码语言:javascript
复制
package dynamic.jdk;
public interface TestDao {
  public void save();
  public void modify();
  public void delete();
}

实现类(目标类):

代码语言:javascript
复制
package dynamic.jdk;

import org.springframework.stereotype.Repository;

@Repository("testDao")
//Dao层的Bean注解
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.创建切面类

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

切面类MyAspect:

代码语言:javascript
复制
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实现动态代理。

代理类JDKDynamicProxy(代理类的原理理解比较困难,建议大家有条件的话可以去看下源码):

代码语言:javascript
复制
package dynamic.jdk;

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

import aspect.MyAspect;
import dynamic.jdk.TestDao;

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代理结果
   */
  @Override
  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。在主方法中创建代理对象和目标对象,然后从代理对象中获取对目标对象增强后的对象,最后调用该对象的添加、修改和删除方法。

测试类JDKDynamicTest:

代码语言:javascript
复制
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();
  }
}

在下面的截图中可以看到,testDAO实例中的方法已经被调用,并且在方法调用前后做了处理。

4.2.2 CGLIB动态代理

上面我们已经讲解过,JDK动态代理必须提供接口才能使用,对于没有提供接口的类,我们只能才用CGLIB动态代理。

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

下面同样是一个实例讲解CGLIB动态代理:

1.创建目标类

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

代码语言:javascript
复制
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接口。

代码语言:javascript
复制
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方法的代理对象,用于执行父类的方法
   * 返回代理结果
   */
  @Override
  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。在主方法中创建代理对象和目标对象,然后从代理对象中获取对目标对象增强后的对象,最后调用该对象的添加、修改和删除方法。

代码语言:javascript
复制
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();
  }
}

最后的结果和JDK动态代理例子一样,这里不再赘述。

4.3 基于代理类的AOP实现

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

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

2.ProxyFactoryBean

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

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

(1)导入相关JAR包

在核心JAR包基础上,需要再向ch4应用的/WEB-INF/lib目录下导入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”下载。

(2)创建切面类

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

代码语言:javascript
复制
package spring.proxyfactorybean;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
/**
 * 切面类
 */
public class MyAspect implements MethodInterceptor{
  @Override
  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容器才能识别为切面对象。在spring.proxyfactorybean包中,创建配置文件applicationContext.xml,并在文件中配置切面和指定代理对象

代码语言:javascript
复制
<?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>

(4)创建测试类

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

代码语言:javascript
复制
package spring.proxyfactorybean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import dynamic.jdk.TestDao;
public class ProxyFactoryBeanTest {
  public static void main(String[] args) {
    //注意配置文件的相对路径哦
    ApplicationContext appCon = new ClassPathXmlApplicationContext("/spring/proxyfactorybean/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>元素内。下面是详细配置表:

AOP 配置元素

描述

<aop:config>‍

顶层配置元素,<beans>元素的子元素

<aop : after>

定义 AOP 后置通知(不管被通知方法是否执行成功)

<aop : after-returing>

定义 AOP after-returing 通知

<aop : after-throwing>

定义 AOP after-throwing 通知

<aop : around>

定义 AOP 环绕通知

<aop : aspect>

定义切面

<aop : aspectj-autoproxy>

启动 @AspectJ 注解驱动的切面

<aop : before>

定义 AOP 前置通知

<aop : config>

顶层的 AOP 配置元素,大多数 aop : * 元素必须包含在 元素内

<aop : declare-parents>

为被通知的对象引入额外接口,并透明的实现

<aop : pointcut>

定义切点

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

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

需要再向ch4应用的/WEB-INF/lib目录下导入JAR包spring-aspects-5.0.2.RELEASE.jar和aspectjweaver-1.8.13.jar。

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

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

2.创建切面类

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

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

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

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

代码语言:javascript
复制
<?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">
    <!-- 定义目标对象,使用4.2.1节的实现类 -->
    <bean id="testDao" class="dynamic.jdk.TestDaoImpl"/>
    <!-- 定义切面 -->
    <bean id="myAspect" class="aspectj.xml.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>

4.创建测试类

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

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

结果和我们前三个例子是一样的,只不过切面类定义的通知略有不同:

我们这里再测试一下异常通知,在dynamic.jdk.TestDaoImpl目标类中的delete方法中,加上这条语句:

代码语言:javascript
复制
public void delete() {
    int num = 1/0;
    System.out.println("删除");
  }

这个时候我们重新运行测试类XMLAspectJTest,看看结果:

由此我们总结出各类型的通知与目标方法的执行过程图

4.5 基于注解开发AspectJ

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

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

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

在ch4应用的src目录下,创建aspectj.annotation包,在该包中创建切面类MyAspect。在该类中,首先使用@Aspect注解定义一个切面类,由于该类在Spring中是作为组件使用的,所以还需要使用@Component注解。然后,使用@Pointcut注解切入点表达式,并通过定义方法来表示切入点名称(方法名字就是切入点名称,用于和通知关联)。最后在每个通知方法上添加相应的注解,并将切入点名称作为参数传递给需要执行增强的通知方法

代码语言:javascript
复制
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 {
  /**
   * 定义切入点 : 就是通知什么时候执行,在哪执行  myPointCut()方法名字就是切入点名字,和通知关联时候用
   */
  @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.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;
  }
  /**
   * 异常通知 value=切入点  throwing=异常通知方法中的形参,用于切入点向通知传递参数?
   */
  @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注解为目标对象,注解代码如下:

代码语言:javascript
复制
@Repository("testDao")

3.创建配置文件

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

代码语言:javascript
复制
<?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>

4.创建测试类

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

关于Spring AOP我的个人理解:

AOP:面向切面编程,实现一些OOP不容易实现的功能,例如事务、日志、异常、权限等等。

AOP原理:代码织入,类似于过滤器,用代理对象增强目标类,实现将用户所需代码在合适时间和位置运行。

AOP技术实现方法:JDK和CGLIB动态代理、基于代理类实现(ProxyFactoryBean)、AspectJ框架。

AOP术语:

切面Aspect:定义通知。

切入点PointCut:通知的位置。

通知Advice:用户真正想要执行/增强的代码。

目标类Target:被增强的对象。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-03-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 浩Coding 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档