Spring_AOP

1 什么是 AOP。
     AOP(Aspect Orient Programming),作为面向对象编程的一种补充,广泛应用于处理一些具有横切性质的系统级服务,如事务管理、安全检查、缓存、日志对象池管理等。AOP 实现的关键就在于 AOP 框架自动创建的 AOP 代理,AOP 代理则可分为静态代理和动态代理两大类,其中静态代理是指使用 AOP 框架提供的命令进行编译,从而在编译阶段就可生成 AOP 代理类,因此也称为编译时增强;而动态代理则在运行时借助于 JDK 动态代理、CGLIB 等在内存中“临时”生成 AOP 动态代理类,因此也被称为运行时增强。

2 代理设计模式 :解决程序扩展问题的。对目标程序访问进行控制的。
A(客户端)   ->   D(目标程序(包含:业务代码,非业务代码))
A(客户端)   ->   B(代理对象(包含:非业务代码))  ->  D(目标程序(包含:业务代码))
A(客户端)   ->   B(动态代理对象)  -> C(调用处理器-包含:非业务代码)  ->  D(目标程序(包含:业务代码))
(1) JDK动态代理:
Object proxyObject = Proxy.newProxyInstance(..,);
InvocationHandler
JDK动态代理基于接口实现的,代理类和目标类都实现共同接口。
(2) Cglib动态代理:
(3) Javassist动态代理:
     Cglib和Javassist动态代理基于继承实现的。代理类是目标类的子类。
               
3 Spring框架的AOP
       AOP (Aspect Oriented Programming) 面向切面编程,是一种编程思想,是 OOP的一种补充,目的就是将应用程序中的代码解耦, 完成功能的扩展,将目标中业务代码和非业务代码分开,实现低耦合的方式开发。
AOP相关术语
(1) 切面 (Aspect ):是一个抽象的概念。aspect=PointCut(切入点)+advice(通知)                
          具体表现形式:事务扩展,异常扩展,日志扩展,权限扩展。
          一般将需要解决问题,进行代码的模块化,形成具体的切面类。这就是所谓面向切面编程。
(2) 横切关注点 :从每个方法中抽取出来的同一类非核心业务。
(3) 通知(Advice):表示切面程序中具体代码所在的方法,用于执行扩展。
          前置通知:@Before 用于执行目标方法前进行的功能扩展
          方法返回通知:@AfterReturning 用于执行目标方法后进行的功能扩展
          异常通知:@AfterThrowing 用于执行目标方法时,出现异常了,进行的功能扩展
          后置通知:@After 用于执行目标方法后,不管是否出现异常,都要进行执行的功能扩展。
          环绕通知:@Around 可以看做以上四种通知的组合,更像一个拦截器,可以在执行目标方法前,后,异常,最终时进行功能扩展。
(4) 连接点(JoinPoint ):需要执行扩展代码的地点,称为连接点。
          例如:目标程序中的方法可以被看做连接点。
(5) 切入点(PointCut)
          就是通过切入点表达式匹配到的连接点。是连接点的集合。
(6) 目标对象 (Target ):
          具体的需要进行扩展的业务类所生成的对象;
(7) Proxy (代理对象)
          对目标对象进行代理,去进行功能扩展。代理中的代码,应该是去执行切面类操作。
官方文档中文解释:

advice

4.Spring中AOP的简单使用
     ① 首先拷贝IOC和AOP相关Jar包
IOC :
spring-beans-4.0.0.RELEASE.jar
spring-context-4.0.0.RELEASE.jar
spring-core-4.0.0.RELEASE.jar
spring-expression-4.0.0.RELEASE.jar
commons-logging-1.1.3.jar

AOP :
com.springsource.net.sf.cglib-2.2.0.jar
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
spring-aop-4.0.0.RELEASE.jar
spring-aspects-4.0.0.RELEASE.jar
     ② 增加aop名称空间,增加相应配置
<?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/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
        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-4.0.xsd">

    <!-- 设置扫描的包 -->
    <context:component-scan base-package="com.atguigu.spring.aop.*"/>

    <!-- 启用AOP:设置spring的动态代理模式,说白了就是采取一种什么样子的方式去初始化对象。
         <aop:aspectj-autoproxy />有一个proxy-target-class属性,默认为false,表示使用jdk动态代理织入增强;
         当配为<aop:aspectj-autoproxy  poxy-target-class="true"/>时,表示使用CGLib动态代理技术织入增强;
         不过即使proxy-target-class设置为false,如果目标类没有声明接口,则spring将自动使用CGLib动态代理。
     -->
    <aop:aspectj-autoproxy />

</beans>
     ③ 定义目标接口和实现类
package com.atguigu.spring.aop.bean;
//目标对象存在接口,那么,会产生JDK动态代理;
//目标对象没有接口,那么,会产生Cglib动态代理;
public interface MathCalculator {
    int add(int i,int j);
    int sub(int i,int j);
    int mul(int i, int j);
    int div(int i,int j);
}
package com.atguigu.spring.aop.bean;
import org.springframework.stereotype.Component;
/**
 * 目标类:目标对象
 *   目标类中的方法:连接点
 *  通过切入点表达式匹配到的连接点,统称切入点
 */
@Component
public class EazyImpl {//implements MathCalculator{
     public int add(int i, int j) {
          int result = i + j;
          System.out.println("方法内部打印:result="+result);
          return result;
     }
     public int sub(int i, int j) {
          int result = i - j;
          System.out.println("方法内部打印:result="+result);
          return result;
     }
     public int mul(int i, int j) {
          int result = i * j;
          System.out.println("方法内部打印:result="+result);
          return result;
     }
     public int div(int i, int j) {
          int result = i / j;
          System.out.println("方法内部打印:result="+result);
          return result;
     }
}
     ④ 定义切面类1
package com.atguigu.spring.aop.aspect;
import java.util.Arrays;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
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):解决日志功能扩展。
@Component //声明当前类对象为系统的组件类对象
@Aspect //声明当前类为一个切面类

public class LogAspect {
    //重用切入点表达式
    @Pointcut(value="execution(* *.*(..))")
    public void pointcut(){}

    @Before("pointcut()")
    public void startLog(JoinPoint joinPoint){
        Signature signature = joinPoint.getSignature();
        String targetObjectMethod = signature.getName();
        Object[] args = joinPoint.getArgs();
        System.out.println(targetObjectMethod + " - 方法开始执行了 - ages = " + Arrays.asList(args)); //横切关注点代码
    }

    //后置(最终)通知:
    //@After(value="execution(public int com.atguigu.spring.aop.bean.EazyImpl.*(int, int))")
    @After("pointcut()")
    public void endLog(JoinPoint joinPoint){
        Signature signature = joinPoint.getSignature();
        String targetObjectMethod = signature.getName();
        Object[] args = joinPoint.getArgs();
        System.out.println(targetObjectMethod + " - 方法执行结束了 - ages = " + Arrays.asList(args));
    }

    //异常通知:
    //可以获取异常对象:throwing属性值,与异常通知修改的方法参数名称一致。
    //@AfterThrowing(value="execution(public int com.atguigu.spring.aop.bean.EazyImpl.*(int, int))",throwing="e")
    @AfterThrowing(pointcut="pointcut()",throwing="e")
    public void throwExceptionLog(JoinPoint joinPoint,Throwable e){
        Signature signature = joinPoint.getSignature();
        String targetObjectMethod = signature.getName();
        Object[] args = joinPoint.getArgs();
        System.out.println(targetObjectMethod + " 方法执行出异常了 - 异常是:"+e.getMessage());
    }

    //方法返回通知:
    //可以获取目标方法的返回结果:要求returning的值与通知方法参数的变量名称一致
    //@AfterReturning(value="execution(public int com.atguigu.spring.aop.bean.EazyImpl.*(int, int))",returning="result")
    @AfterReturning(pointcut="pointcut()",returning="result")
    public void methodReturningLog(JoinPoint joinPoint,Object result){
        Signature signature = joinPoint.getSignature();
        String targetObjectMethod = signature.getName();
        Object[] args = joinPoint.getArgs();
        System.out.println(targetObjectMethod + " - 方法执行完成返回结果了 Args="+Arrays.asList(args)+",result = " + result);
    }

    @Around(value="execution(public int com.atguigu.spring.aop.bean.EazyImpl.*(int, int))")
    public Object aroundLog(ProceedingJoinPoint pjp) {
        Signature signature = pjp.getSignature();
        String methodName = signature.getName();
        Object[] args = pjp.getArgs();
        Object result = null ;
        try {
            System.out.println("@Around - "+methodName + "方法开始执行了 - Args = " + Arrays.asList(args));
            result = pjp.proceed();//执行目标方法
            System.out.println("@Around - "+methodName + "方法执行完成了 - Args = " + Arrays.asList(args) + ",result=" + result);
        } catch (Throwable e) {
            System.out.println("@Around - "+methodName + "方法执行出现异常了 - " + e.getMessage());
            e.printStackTrace();
            throw new RuntimeException(e) ;
        } finally{
            System.out.println("@Around - "+methodName + "方法最终执行(释放资源)了 - Args = " + Arrays.asList(args) + ",result=" + result);
        }
        return result ;
    }

}
      ⑤ 定义切面类2
package com.atguigu.spring.aop.aspect;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Component
@Aspect
@Order(1)
public class TxAspect {

    @Before(value="execution(* com.atguigu.spring.aop.bean.EazyImpl.*(..))")
    public void startTx(){
        System.out.println("con.setAutoCommit(false);");
    }

    @AfterReturning(value="execution(* com.atguigu.spring.aop.bean.EazyImpl.*(..))")
    public void commitTx(){
        System.out.println("con.commit();");
    }

    @AfterThrowing(value="execution(* com.atguigu.spring.aop.bean.EazyImpl.*(..))")
    public void rollbackTx(){
        System.out.println("con.rollback();");
    }

    @After(value="execution(* com.atguigu.spring.aop.bean.EazyImpl.*(..))")
    public void closeCon(){
        System.out.println("con.close();");
    }
}
   ⑥编写测试类
package junit.test;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.atguigu.spring.aop.bean.EazyImpl;
import com.atguigu.spring.aop.bean.MathCalculator;
public class TestAOP {
     ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
     @Test
     public void testAOP(){
          MathCalculator proxyObject = (MathCalculator)ioc.getBean(MathCalculator.class);
          //class com.sun.proxy.$Proxy8
          System.out.println(proxyObject.getClass());
          //调用代理对象  ->  调用处理器 -> 调用切面中的通知方法   -> 目标对象中方法
          proxyObject.add(10, 5);
          
     }
     
     @Test
     public void testAOP2(){
          EazyImpl proxyObject = (EazyImpl)ioc.getBean(EazyImpl.class);
          //class com.atguigu.spring.aop.bean.EazyImpl$$EnhancerByCGLIB$$297e0270
          System.out.println(proxyObject.getClass());
          //调用代理对象  ->  调用处理器 -> 调用切面中的通知方法   -> 目标对象中方法
          proxyObject.add(10, 5);
          System.out.println("---------------");
          //proxyObject.div(10, 0);
     }
}
5.Spring如何实现功能扩展的?
     通过代理机制实现的,如果目标对象有接口,会采用JDK动态代理实现扩展。如果目标对象没有接口,会采用Cglib动态代理实现扩展。

6.切入点表达式
最详细的切入点表达式
execution(public int com.pers.spring.aop.EazyImpl.add(int, int))
最模糊的切入点表达式
execution(* *.*(..))
说明:第一个*代表public int
第二个*代表com.pers.spring.aop
  第三个*代表EazyImpl.add
最后的两个 . .分别代表两个int 类型,其中..代表任意的参数个数和任意的类型。
例如:
/**
* 切面:实现日志功能扩展的切面
*/
@Aspect  //声明当前类为一个切面类
@Component //声明一个Bean对象
public class LogAspect {
     @Before(value="execution(public int com.per.spring.aop.EazyImpl.*(int, int)) ")
     public void startLog(JoinPoint joinPoint){
          Signature signature = joinPoint.getSignature();
          String methodName = signature.getName();
          Object[] args = joinPoint.getArgs();
          List<Object> asList = Arrays.asList(args);
          System.out.println("[☆日志]["+methodName+"方法开始][参数值:"+asList+"]");
     }
}
说明:切入点表达式的语法格式
     execution([权限修饰符] [返回值类型] [简单类名/全类名] [方法名]([参数列表]))

7.多个切面执行的优先级
可以通过@Order注解设置value属性值;value属性值越小,优先级越高。
@Order(0)
public class TxAspect {}
@Order(1)
public class LogAspect {}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员学习圈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值