慢慢渗透Spring AOP概念(一)

慢慢渗透Spring AOP概念(一)
冷静 小白书生 今天
我们在理解Spring AOP概念的时候我们首先要知道什么是面向切面编程?

1:什么是面向切面编程(AOP)呢?

在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。  --来自《百度百科》

个人理解: 将那些与业务无关,却为业务模块所共同调用的逻辑进行的区分,比如在项目中我们用到的权限认证,日志记录,事务处理等等

AOP 是一种编程范式,不是编程语言,AOP只能解决特定问题不是解决所有问题,他是OOPd补充而不是替代。

AOP 初衷:1:DRY原则(代码重复性问题)2:Soc原则(关注点分离): 水平分离(展示层->服务层->持久层)/垂直分离(订单-库存的分离)/切面分离(分离功能性需求和非功能性需求)

好了废话不多说开始进行我们今天对于Spring AOP的理解吧!

2 :Spring AOP 的使用方式

本文主要讲解关于注解方式的使用,首先我们需要先了解一下关于springAOP有哪些可以使用的注解以及如何使用?

@Aspect: 标记当前类可以作为一个切面供容器进行读取

@Pointcut: 切入点,就是对那些Joinpoint进行拦截的定义,切点的功能是指出切面的通知应该从哪里织入应用的执行流

@Advice: 通知

1:@Pointcut 切点定义的实例

    切点定义匹配的方式有5种分别为一下种:

    匹配方法:execution() -常用

    匹配注解:@target(),@args() , @within() , @annotation()

   匹配包/类型:@within()

    匹配对象:@this , @bean, @target()

    匹配参数: @args()

    接下来我们介绍关于这几个匹配表达式使用的方式,

    匹配包/类型  

// 匹配ProduceService下面所有的方法
@Pointcut(“within(com.example.contianer.testservice.PrduceService)”)
// 匹配com.example.contianer包以及子包下所有类的方法
@Pointcut(“within(com.example.contianer…*)”)
匹配对象

//匹配AOP对象的目标对象为指定类型的方法,即D
@Pointcut(“com.example.contianer.DemoDao”)
//匹配实现DemoDao接口的目标对象(而不是aop代理后的对象)的方法,这里即DemoDao的方法
@Pointcut(“target(com.example.contianer.IDao)”)
// 匹配所有以Service结尾的bean里面的方法
@Pointcut(“bean(*Service)”)

参数匹配   

// 匹配任何以find开头而且只有一个Long参数的方法
@Pointcut(“execution(”**…find*(Long)")")
// 匹配任何一个只有Long参数的方法
@Pointcut(“args(Long)”)
// 匹配任何以find开头的而且第一个参数为Long的方法
@Pointcut(“execution(* …find(Long…)”)
// 匹配第一个参数为Long的方法
@PointCut(“agrs(Long…)”)
匹配注解

// 匹配方法标注有自定义注解的方法@inteface
@Pointcaut("@annotation(注解的全路径名称)")
// 匹配标注有Beta的类底下的放啊,要求的annotation的RetentionPolicy级别为CLASS
@Pointcut(“within(Beta的全路径名称)”)
// 匹配标注有Repository的类地下的方法,要求的annotation的RetentationPolicy级别为Runtime
@Pointcut("@target(注解的全路径名称)")
// 匹配传入的参数类标注有Repository注解的方法
@Pointcut("@args(注解的全路径名称)")

@Retention(RetentionPolicy.RUNTIME) // 在运行时的注解 RetentionPolicy.CLASS表示这是在编译级别的注解
@Target(ElementType.TYPE) // 这表示在类方法上的注解
@Inherited // 表示此注解可以被继承
public @interface NeedSecured {
}

上述的匹配原则如何测试呢?这时候我们需要在IDEA上进行对于这些原则的测试。

1:首先我们要建立一个maven工程(使用IDEA上Spring Initializr创建一个简单的springboot的工程项目)

2:然后我们要引入aop要使用的依赖

 <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
 </dependency>

3:创建config包放置我们接下来要测试的aop配置的类,创建service包以及实现类的包

4:创建测试类然后进行测试即可

2:Advice通知的5种注解实例

    @Before   前置通知

    @After 后置通知,方法执行完之后

    @AfterReturning 返回通知,成功执行之后 该注解可以获取方法的返回值

// @AfterReturnning(value=“切入点@Pointcut定义的切入点”, returning=“返回值”)
@AfterReturnning(value=“切入点”, returning=“name”)
public void test(String name){}

    @AfterThrowing 异常通知,抛出异常之后

    @Around 环绕通知

@Around(“切点”)
public void test(ProceedingJoinPoint point) throws Throwable{
System.out.println("…Before(此处执行的代码相当于-前置通知)…");
try{
point.proceed();//有此代码,被切入的方法体才会执行,如果被切入的方法有返回值,则返回值为null,见3
System.out.println("…AfterReturning(此处执行的代码相当于-返回通知)…");
}catch(Exception e){
System.out.println("…AfterThrowing(此处执行的代码相当于-异常通知)…");
}finally{
System.out.println("…After1(此处执行的代码相当于-后置通知)…");
}
System.out.println("…After2(此处执行的代码相当于-后置通知)…");
}

特:SpringAOP之Proceedingjoinpoint和JoinPoint的区别以及含义

    首先看一下关于JoinPoint的含义

public interface JoinPoint {
String toString(); //连接点所在位置的相关信息
String toShortString(); //连接点所在位置的简短相关信息
String toLongString(); //连接点所在位置的全部相关信息
Object getThis(); //返回AOP代理对象,也就是com.sun.proxy.$Proxy18
Object getTarget(); //返回目标对象,一般我们都需要它或者(也就是定义方法的接口或类,为什么会是接口呢?这主要是在目标对象本身是动态代理的情况下,例如Mapper。所以返回的是定义方法的对象如aoptest.daoimpl.GoodDaoImpl或com.b.base.BaseMapper<T, E, PK>)
Object[] getArgs(); //返回被通知方法参数列表
Signature getSignature(); //返回当前连接点签名 其getName()方法返回方法的FQN,如void aoptest.dao.GoodDao.delete()或com.b.base.BaseMapper.insert(T)(需要注意的是,很多时候我们定义了子类继承父类的时候,我们希望拿到基于子类的FQN,这直接可拿不到,要依赖于AopUtils.getTargetClass(point.getTarget())获取原始代理对象,下面会详细讲解)
SourceLocation getSourceLocation();//返回连接点方法所在类文件中的位置
String getKind(); //连接点类型
StaticPart getStaticPart(); //返回连接点静态部分
}

我们学习SrpingAop我们当然是需要去了解代理模式和责任链模式两大设计模式的原理:

接下来我们用代码来解释一下什么是代理模式和责任链模式

首先:

代理模式:

定义:代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用,通俗的的来讲就是生活中的中介。

代理模式的结构:

1:抽象主题类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。

 2:真实主题(Real Subject)类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。

 3: 代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能

为什么我们要用代理模式呢?

在某些情况下,一个客户代不想或者不能直接引用一个委托对象,而代理类对象可以在客户和委托起到中介的作用,其特征是代理类和委托类实现相同的接口。

代理类除了是客户类和委托类中介之外,我们还可以通过代理类增加额外的功能来扩展委托类的功能,这样做我们只需要修改代理类而不需要子修改委托类,代理类主要负责为委托类预处理消息,通过过滤消息把消息转发给委托类,代理类本身不需要真正的实现服务,而是通过调用委托类的相关方法,来提供特定的服务,真正的业务功能还是由委托类来实现,但是可以在业务功能执行的前后加入一些公共的服务。

代理模式基本分为两大类:静态代理和动态代理

1:静态代理

第一步:创建服务类

package com.imooc.aop;

/**

  • liuhongwei
  • 服务类
    */
    public interface Heartjing {
    void jinger();
    }
    第二步:实现服务接口

package com.imooc.aop;

/**

  • 创建服务类 liuhongwei
    */
    public class GirleFirendClass implements Heartjing {
    @Override
    public void jinger() {
    System.out.println(“转告你。。”);
    }
    }
    第三步:创建代理类

/**

  • 创建代理类 liuhongwei
    */
    public class OtherProxy implements Heartjing {

    private GirleFirendClass girleFirendClass;

    // 构造方法注入GirleFirendClass类
    public OtherProxy(final GirleFirendClass girleFirendClass) {
    this.girleFirendClass = girleFirendClass;
    }

    @Override
    public void jinger() {
    System.out.println(“转告之前”);
    girleFirendClass.jinger();
    System.out.println(“喜欢这事还需要自己说才有诚意!”);
    }
    }

第四步:编写测试类

package com.imooc.aop;

public class Proxy {

public static void main(String[] args) {
    // GirleFirendClass实现了Heartjing的接口
    Heartjing heartjing = new GirleFirendClass();

    heartjing.jinger();
    // otherProxy 代理了Heartjing 
    OtherProxy otherProxy = new OtherProxy(heartjing);
    // 这样调用jinger() 就相当于代理了委托类GirleFirendClass
    otherProxy.jinger();
}

}

优点:可以做到在符合开闭原则的情况下对目标对象进行功能扩展。

缺点:我们得为每一个服务都得创建代理类,工作量太大,不易管理。同时接口一旦发生改变,代理类也得相应修改

2:动态代理
所谓的动态代理就是我们不需要在手动创建代理类,我们只需要编写一个动态处理器就可以了

第一步:编写动态处理器

package com.imooc.aop;

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

/**

  • 编写动态处理器 jdk动态代理
    */
    public class LoveQuiter implements InvocationHandler {

    // 创建对象
    private Object object;
    // 构造强势注入 对象
    public LoveQuiter(final Object object) {
    this.object = object;
    }

    @Override
    public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
    System.out.println(“表白转告之前。。。”);
    Object invoke = method.invoke(object, objects);

     System.out.println("表白之后。。。");
     return invoke;
    

    }
    }
    第二步:编写测试类

package com.imooc.aop;

import java.lang.reflect.Proxy;

public class quiterMain {
public static void main(String[] args) {
Heartjing heartjing = new GirleFirendClass();
// 采用jdk代理模式来实现 动态代理

    Heartjing proxyInstance = (Heartjing) Proxy.newProxyInstance(Heartjing.class.getClassLoader(), new Class[]{Heartjing.class}, new LoveQuiter(heartjing));
    // 动态代理了 Heartjing这个接口类
    proxyInstance.jinger();


}

}
Proxy.newProxyInstance:

在这个方法中有三个参数:

1:ClassLoader loader 指定当前目标对象使用的类加载器,获取加载器的方法是固定

2:Class<?>[] interfaces 指定目标对象实现的接口类型,使用泛型方式确认类型

3:InvocationHandler 指定动态处理器(就是我们自定义是实现的动态处理器),在执行目标对象方法时,会触发事件处理器的方法

总结: 动态代理相对于静态代理,大大减少了我们的开发任务,野同时减少了对业务接口的依赖,降低了耦合度。 在我们动态生成的代理类里面,他们有一个共同的父类叫Proxy

Java的继承机制注定了这些动态代理类们无法实现对class的动态代理,原因是多继承在Java中本质上就行不通。有很多条理由,人们可以否定对 class代理的必要性,但是同样有一些理由,相信支持class动态代理会更美好。接口和类的划分,本就不是很明显,只是到了Java中才变得如此的细化。如果只从方法的声明及是否被定义来考量,有一种两者的混合体,它的名字叫抽象类。实现对抽象类的动态代理,相信也有其内在的价值。此外,还有一些历史遗留的类,它们将因为没有实现任何接口而从此与动态代理永世无缘。如此种种,不得不说是一个小小的遗憾。但是,不完美并不等于不伟大,伟大是一种本质,Java动态代理就是佐例

3: CGLIB 代理类

JDK实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类,如何实现动态代理呢,这就需要CGLib了。CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。但因为采用的是继承,所以不能对final修饰的类进行代理。JDK动态代理与CGLib动态代理均是实现Spring AOP的基础。

第一步: 创建CGLIB代理类

package com.imooc.aop;

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 CglibPoxy implements MethodInterceptor {

private Object object;

public Object getInstance(final Object target) {
    this.object = target;
    //Enhancer是一个非常重要的类,它允许为非接口类型创建一个JAVA代理,Enhancer动态的创建给定类的子类并且拦截代理类的所有的方法
    Enhancer enhancer = new Enhancer();
    // 设置超类
    enhancer.setSuperclass(this.object.getClass());
    // 设置回调对象
    enhancer.setCallback(this);
    //创建 Object
    return enhancer.create();
}

@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    System.out.println("转告表白之前");
    Object invoke = methodProxy.invoke(o, objects);
    System.out.println("就是喜欢Quiter");
    return invoke;
}

}
第二步:创建测试类

package com.imooc.aop;

public class CglibProxyTest {
public static void main(String[] args) {
Heartjing heartjing = new GirleFirendClass();
CglibPoxy cglibPoxy = new CglibPoxy();
// 使用自定创建的cglib代理类 去代理 Heartjing的接口类
GirleFirendClass girleFirendClass = (GirleFirendClass) cglibPoxy.getInstance(heartjing);

    girleFirendClass.jinger();
}

}
CGLIB代理总结: CGIB创建的动态代理对象比JDK创建的动态代理对象的性能更高,但是CGLIB创建代理对象时所花费的时间却比JDK多得多。所以对于单例的对象,因为无需频繁创建对象,用CGLIB合适,反之使用JDK方式要更为合适一些。同时由于CGLib由于是采用动态创建子类的方法,对于final修饰的方法无法进行代理。

责任链模式:

参考:http://c.biancheng.net/view/1383.html

在现实生活中,常常会出现这样的事例:一个请求有多个对象可以处理,但每个对象的处理条件或权限不同。例如,公司员工请假,可批假的领导有部门负责人、副总经理、总经理等,但每个领导能批准的天数不同,员工必须根据自己要请假的天数去找不同的领导签名,也就是说员工必须记住每个领导的姓名、电话和地址等信息,这增加了难度。这样的例子还有很多,如找领导出差报销、生活中的“击鼓传花”游戏等。

在计算机软硬件中也有相关例子,如总线网中数据报传送,每台计算机根据目标地址是否同自己的地址相同来决定是否接收;还有异常处理中,处理程序根据异常的类型决定自己是否处理该异常;还有 Struts2 的拦截器、JSP 和 Servlet 的 Filter 等,所有这些,如果用责任链模式都能很好解决。

责任链(Chain of Responsibility)模式的定义:为了避免请求发送者与多个请求处理者耦合在一起,将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。

在责任链模式中,客户只需要将请求发送到责任链上即可,无须关心请求的处理细节和请求的传递过程,所以责任链将请求的发送者和请求的处理者解耦了。

职责链模式主要包含以下角色。

抽象处理者(Handler)角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接。

具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。

客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。

/**

  • 抽象处理者角色
    */
    public abstract class Handler {

    private Handler next;

    public Handler getNext() {
    return next;
    }

    public void setNext(Handler next) {
    this.next = next;
    }

    // 处理请求方法
    public abstract void handleRequest(String Request);
    }

/**
*具体处理者角色21
*/
public class ConcreteHandler1 extends Handler {
@Override
public void handleRequest(String request) {
if (request.equals(“one”)) {
System.out.println(“具体处理者处理该请求!”);
} else {
if (getNext() != null) {
getNext().handleRequest(request);
} else {
System.out.println(“没有人处理该请求”);
}
}
}
}

package com.imooc.ChainOfResponsibilityPattern;
// 具体处理者角色2
public class ConcreteHandler2 extends Handler{
@Override
public void handleRequest(String request) {
if(request.equals(“two”))
{
System.out.println(“具体处理者2负责处理该请求!”);
}
else
{
if(getNext()!=null)
{
getNext().handleRequest(request);
}
else
{
System.out.println(“没有人处理该请求!”);
}
}

}

}
编写测试类

package chainOfResponsibility;
public class ChainOfResponsibilityPattern
{
public static void main(String[] args)
{
//组装责任链
Handler handler1=new ConcreteHandler1();
Handler handler2=new ConcreteHandler2();
handler1.setNext(handler2);
//提交请求
handler1.handleRequest(“two”);
}
}

package com.imooc.ChainOfResponsibilityPattern;

public abstract class HandlerProcess {
// 定义successer 来判断是否还有方法可以调用
private HandlerProcess successer;

public HandlerProcess getSuccesser() {
    return successer;
}

public void setSuccesser(HandlerProcess successer) {
    this.successer = successer;
}

// 定义executer
public void executer() {
    HandlerProcesses();
    // 如果有successer就调用他自己的定义executer
    if (successer != null) {
        successer.executer();
    }
}

protected abstract void HandlerProcesses();

}

客户端: 链式调用

public class Client {

static class HandlerA extends HandlerProcess{
    @Override
    protected void HandlerProcesses() {
        System.out.println("handler by 1");
    }
}
static class HandlerB extends HandlerProcess{
    @Override
    protected void HandlerProcesses() {
        System.out.println("handler by 1");
    }

}
static class HandlerC extends HandlerProcess{
    @Override
    protected void HandlerProcesses() {
        System.out.println("handler by 1");
    }
}

public static void main(String[] args) {
    HandlerA handlerA = new HandlerA();
    HandlerB handlerB = new HandlerB();
    HandlerC handlerC = new HandlerC();
    handlerA.setSuccesser(handlerB);
    handlerB.setSuccesser(handlerC);
    handlerA.executer();
}

}
优化上述的链式调用重新在给main方法内进行封装,更加简洁的进行链式的调用

第一步:创建一个新的抽象类

package com.imooc.ChainOfResponsibilityPattern;

/**

  • 创建一个新的chainHandler也是有处理方法的
    */
    public abstract class ChainHandler {
    // 每次都来调用者
    public void execute(Chain chain) {
    handlerProcess();// 递归调用这个handler的处理方法
    chain.proceed(); // 递归的将chain里面的集合数据handlers全部走完,
    }

    protected abstract void handlerProcess();
    }

第二步:

package com.imooc.ChainOfResponsibilityPattern;

import java.util.List;

public class Chain {

private List<ChainHandler> handlers;

public Chain(List<ChainHandler> handlers) {
    this.handlers = handlers;
}

// 定义类似游标的index
private int index = 0;
//调用这个proceed的方法 去调用execute的方法  handler时可能存在多方法
public void proceed() {
    if (index >= handlers.size()) {
        return;
    }
    handlers.get(index++).execute(this);
}

}
第三步:编写测试类

package com.imooc.ChainOfResponsibilityPattern;

import com.sun.tools.javac.code.Attribute;

import java.util.Arrays;
import java.util.List;

public class ChainHandleTest {

static class HandlerA extends ChainHandler{

    @Override
    protected void handlerProcess() {
        System.out.println("1");
    }
}
static class HandlerB extends ChainHandler{

    @Override
    protected void handlerProcess() {
        System.out.println("2");
    }
}
static class HandlerC extends ChainHandler{

    @Override
    protected void handlerProcess() {
        System.out.println("3");
    }
}

public static void main(String[] args) {
    List<ChainHandler> handlers  = Arrays.asList(
            new HandlerA(),
            new HandlerB(),
            new HandlerC()
    );
    Chain chain = new Chain(handlers);
    chain.proceed();//递归调用和完毕
}

}

这样使用这个责任链模式我们就不需要一个个的创建的对象,现在我们就可以使用数据集合来简要的解决这个情况

上述时关于本次SpringAOP概念的理解,理解可能有错误欢迎大家留言指正,谢谢!

有些时候遇见了就忘不掉,希望你够勇敢,让这遇见不仅仅是回忆和后悔!

下期:项目实战理解aop的使用

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring AOP(Aspect-Oriented Programming,面向切面编程)是Spring框架中的一个重要组件,它是一种编程范式,用于将横切关注点(Cross-Cutting Concerns)与核心业务逻辑进行分离。横切关注点是指那些与业务逻辑无关,但是对多个模块或层产生影响的代码,例如日志记录、性能统计、安全控制、事务处理等等。通过将这些横切关注点抽象成切面(Aspect),并将切面织入到应用程序中,可以实现对这些横切关注点的统一处理,从而提高应用程序的可维护性和可扩展性,减少重复代码的编写。 Spring AOP框架主要由以下几个概念组成: 1. 切面(Aspect):由切点和通知组成的一个模块,用于实现一个或多个横切关注点。 2. 切点(Pointcut):用于定义在哪些方法上进行拦截。 3. 通知(Advice):定义了拦截后要执行的操作,包括前置通知、后置通知、返回通知、异常通知和环绕通知等。 4. 连接点(Join Point):在应用程序中可以被拦截的点,例如方法调用、异常抛出等。 5. 切入点(Join Point):指定连接点的一组集合,用于定义切点。 6. 织入(Weaving):将切面应用到目标对象并创建新的代理对象的过程。 Spring AOP框架的实现方式主要是通过动态代理技术和字节码增强技术。在使用Spring AOP框架时,开发人员只需要定义切面、切点和通知,框架会自动将切面织入到应用程序中,从而实现对横切关注点的统一处理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值