Spring AOP的面向切面编程

1. 代理模式

我们以前有学过面向对象,今天所要说的面向切面 。这两个的区别其实不大

面向切面的目的是什么? 增强对象 比如说 我们写了一个对象 或者 一个类 ,我想让这个对象里的方法在之前的功能基础之上在干点其他的事情。

在这里 一定要提一下 开闭原则

对添加放开 对修改关闭 ,如果说:我写了一个类 ,类里有一对方法,我现在想让这个方法进行增强,但是原方法不变,这咋办呢?

我这里有一个class A :

class A{
   
    a();
    
}

在这个方法基础之上进行增强 我们可以搞一个 B 去继承 A ,重写A的方法

那么 我们进行一个小总结

面向对象 进行 增强 - - 》 继承方式 和 代理(设计模式)

​ 设计模式 - - 》 单例 :这个对象只能被new一次,这个对象在当前JVM中有且仅有一个实例叫做单例

​ 工厂 :屏蔽某个通向生产的细节,直接从工厂里面拿到就行了

​ 模板方法(IO) :在IO流里面去继承的时候继承的都是类而不是实现的方法

​ 策略模式(if-else switch) 改造的是if-else 和 switch

代理模式 如何实现面向对象的增强呢?我们来看一下代理的几个概念

代理对象: 对代理对象被代理之后生成的对象

目标对象(被代理对象):

增强:目标对象被增强的部分就是增强

举个小例子:

StuService.java

//使用代理的方式 开闭原则 此代码不能修改(被代理对象) 目标对象
public class StuService {
    public void del(){
        System.out.println("StuService·····直接删除·····");
    }
}

StuServiceProxy.java

//代理对象要持有一个代理对象的实例  代理对象
public class StuServiceProxy {

    private StuService target;
    //限制构造方法  来构造的时候必须给我一个被代理对象
    public StuServiceProxy(StuService stuService){
        this.target=stuService;
    }
    //调方法的时候调用我这个方法
    public void proxyDel(){
        System.out.println("权限校验"); 增强
        target.del();
    }
}

但是我们要手动的要对每一个目标对象生成一个代理对象 这就是静态代理 可扩展性差

所以说 我们就有了动态代理 :给我一个对象 我就生成一个他的代理对象

而在动态代理里分为两种形式 :JDK动态代理(面向接口) 和 cglib动态代理(asm:手动更改字节码的框架)

1.1 动态代理

必须要有一个接口:

public interface StuServiceI {
    public void del();
}

接口实现类

//(被代理对象)
public class StuService implements StuServiceI {
    public void del(){
        System.out.println("StuService·····直接删除·····");
    }
}

JdkProxy

package proxy;

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

/*
* jsk动态代理类的实现
* */
public class JdkProxy {
    //声明目标对象
    private Object target;
    //我要代理那个类  你只要把实例给我一个就可以了
    public JdkProxy(Object target){
        this.target=target;
    }
    //通过newProxyInstance 生成代理对象
    public Object getProxy(){
        //1.类加载器 (双亲委派)
        //2.目标对象的接口 ()
        //3.InvocationHandler
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), new InvocationHandler() {
                  // 1. proxy :代理类
                  // 2. method : 目标对象中的方法
                  // 3. arg:目标对象中要执行方法的参数
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("方法校验");
                        //调用目标对象中的方法
                        //参数 :当前在那个对象上执行 ,目标对象中要执行方法的参数
                        Object result = method.invoke(target, args);
                        return result;
                    }
                });
    }

    public static void main(String[] args) {
        StuService stuService = new StuService();
        //强转成他的接口类型
        StuServiceI jdkProxy = (StuServiceI)new JdkProxy(stuService).getProxy();
        jdkProxy.del();
    }
}

1.2 cglib动态代理

加入目标没有接口 那我们就使用cglib动态代理 :你给我一个目标对象 我会给你的目标对象生成一个子类

//目标对象

//使用代理的方式 开闭原则 此代码不能修改(被代理对象)
public class StuService {
    public void del(){
        System.out.println("StuService·····直接删除·····");
    }
}

//代理对象

package proxy;

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 CglibProxy {
    //声明目标对象
    private Enhancer enhancer=new Enhancer();

    public Object getProxy(Class clazz){
        //设置父类
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(new MethodInterceptor() {
            //1 .o
            //2 .method 目标类要执行的方法
            //3 .objects 目标类要执行的方法参数
            //4 .代理的方法
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                Object result = methodProxy.invokeSuper(o, objects);
                return result;
            }
        });
        return enhancer.create();
    }

    public static void main(String[] args) {
        StuService proxy = (StuService) new CglibProxy().getProxy(StuService.class);
        proxy.del();
    }
}

1.3 两种代理的总结

在这里插入图片描述

2. AOP面向切面

2.1 什么是AOP

AOP Aspect Oriented Programing 面向切面编程

AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码(性能监视、事务管理、安全检查、缓存)

Spring AOP使用纯Java实现,不需要专门的编译过程和类加载器,在运行期通过代理方式向目标类织入增强代码

AspecJ是一个基于Java语言的AOP框架,Spring2.0开始,Spring AOP引入对Aspect的支持,AspectJ扩展了Java 语言,提供了一个专门的编译器,在编译时提供横向代码的织入

2.2 AOP相关术语

Joinpoint(连接点): 我要对某个类名的某个方法进行增强 ,所有的方法就是连接点

Pointcut(切入点) :在这个基础之上 缩小一层 ,比如 一个类名里面有10个方法 这10个方法都可以称之为连接点,但是 我只对里面的3个方法进行增强 ,这3个方法就是切入点

Advice(通知/增强) :切入点增强的功能

Introduction(引介): 引介是一种特殊的通知,在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field.

Target(目标对象): 代理的目标对象

**Weaving(织入): ** 是指把增强应用到目标对象来创建新的代理对象的过程.

spring采用动态代理织入,而AspectJ采用编译期织入和类装在期织入

**Proxy(代理): ** 一个类被AOP织入增强后,就产生一个结果代理类

Aspect(切面) : 是切入点和通知(引介)的结合

2.3 AOP增强类型

Spring按照通知Advice在目标类方法的连接点位置,可以分为5类

前置通知 org.springframework.aop.MethodBeforeAdvice

​ 在目标方法执行前实施增强

后置通知 org.springframework.aop.AfterReturningAdvice

​ 在目标方法执行后实施增强

环绕通知 org.aopalliance.intercept.MethodInterceptor

​ 在目标方法执行前后实施增强

异常抛出通知 org.springframework.aop.ThrowsAdvice

​ 在方法抛出异常后实施增强

引介通知 org.springframework.aop.IntroductionInterceptor

​ 在目标类中添加一些新的方法和属性

2.4 使用AspectJ实现AOP

通过配置启用@AspectJ切面
 <aop:aspectj-autoproxy />
<context:component-scan base-package="aop"></context:component-scan>
代码演示

定义一个接口

public interface PersonService {
    int add(String name);
    void del(int id);
}

接口实现类

package aop;

import org.springframework.stereotype.Component;

@Component
public class PersonServiceI implements PersonService {
    @Override
    public int add(String name) {
        System.out.println("add·····"+name);
        return 250;
    }

    @Override
    public void del(int id) {
        System.out.println("del"+id);
    }
}

切面类:

package aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

/*
*  切面类 : 增强+切入点
*
* */
@Component
@Aspect
public class MyAspectJ {

    // 定义切入点信息  execution表达式
    @Before(value = "execution(* aop.PersonServiceI.*(..))")
    public void myBefore(){
        System.out.println("前置通知");
    }

    // 获取目标方法的返回值  returning :拿到的返回值的名称
    @AfterReturning(value = "execution(* aop.PersonServiceI.add(..))",returning = "re")
    public void myBeforeAfterR(Object re){
        System.out.println("前置通知");
        System.out.println("返回值是"+re);
    }

    // 环绕通知 可以控制目标方向的运行
    @Around(value = "execution(* aop.PersonServiceI.add(..))")
    public Object myBeforeAround(ProceedingJoinPoint joinPoint){
        System.out.println("环绕通知前");
        try {
            Object o = joinPoint.proceed();
            System.out.println("返回值是"+o);
            System.out.println("环绕通知后");
            return o;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            return null;
        }
    }
    //异常通知: 只有目标异常 抛出异常  才会执行
    @AfterThrowing(value = "execution(* aop.PersonServiceI.add(..))",throwing = "ex")
    public void myAfterThrowing(JoinPoint joinPoint,Throwable ex){
        System.out.println("异常通知");
        System.out.println("ex.getMessage() = " + ex.getMessage());
    }

    //最终通知 :不管有无异常 最终都会通知
    @After(value = "execution(* aop.PersonServiceI.add(..))")
    public void myAfter(JoinPoint joinPoint){
        System.out.println("最终通知 ");

    }
}

测试类:

public class TestAop {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext applicationContext =
                new ClassPathXmlApplicationContext("bean.xml");
        PersonService personService = applicationContext.getBean(PersonService.class);
        personService.add("admin");
    }
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值