AOP

AOP的核心:代理模式

代理模式的分类

1.静态代理
2.动态代理

代理模式的优点以及代理模式的使用实例

使用代理模式的一个很大的优点。最直白的就是在Spring中的面向切面编程(AOP),我们能在一个切点之前执行一些操作,在一个切点之后执行一些操作,这个切点就是一个个方法。这些方法所在类肯定就是被代理了,在代理过程中切入了一些其他操作。

静态代理

在这里插入图片描述
静态代理中的角色

  1. 抽象角色,一般会使用接口或者抽象类来解决(租房)
  2. 真实角色:被代理的角色(房东)
  3. 代理角色:代理真实角色(中介)
  4. 客户:访问代理对象的人(租房的人)

在这里插入图片描述
3.代理角色
**加粗样式
**
在这里插入图片描述

public class StaticProxyTest {
    public static void main(String[] args) {
        //被代理的学生张三,他的班费上交有代理对象monitor(班长)完成
        Person zhangsan = new Student("张三");
        
        //生成代理对象,并将张三传给代理对象
        Person monitor = new StudentsProxy(zhangsan);
        
        //班长代理上交班费
        monitor.giveMoney();
    }
}

先创建一个被代理对象,然后将被代理对象作为属性传入代理对象,最后代理对象执行方法

代理模式的好处:
1.可以使真实角色的操作更加纯粹,不用去关注一些公共的业务
2.公共业务都交给代理来完成,实现了业务的分工
3.公共业务需要拓展的时候方便集中管理

缺点:
一个真实角色就会产生一个代理角色,代码量会增加,开发效率变低

动态代理

静态代理每次代理一个角色就要增加很多代码量,利用反射可以动态得加载一些类,动态代理得底层就是反射
动态代理得代理类是动态生成的,不是我们直接写好的
动态代理也可以分为两大类
1.基于接口的动态代理
2.基于类的动态代理。

动态代理,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。代理类就是Proxy

动态代理与静态代理的区别。
(1)Proxy类的代码被固定下来,不会因为业务的逐渐庞大而庞大;
(2)可以实现AOP编程,这是静态代理无法实现的;
(3)解耦,如果用在web业务下,可以实现数据层和业务层的分离。
(4)动态代理的优势就是实现无侵入式的代码扩展。
静态代理这个模式本身有个大问题,如果类方法数量越来越多的时候,代理类的代码量是十分庞大的。所以引入动态代理来解决此类问题

动态代理实例

interface Human{
	String getBelied();
	void eat(String food) ;
}
class SuperMan implements Human{//被代理类
	@Override
	public String getBelied() {
		return "I believe T can fly!";
	}
	@Override
	public void eat(String food) {
		System.out.println("like eat :"+food);
	}
}
/*
 * 要实现动态代理需要解决的问题:
 * 1.怎么根据加载到内存中的被代理类 ,动态的创建一个代理类以及代理类对象
 * 2.当通过代理类的对象时 如何动态的去调用被代理类中的同名方法
 */
class ProxyFactory{
	public static Object getProxyInstance(Object obj) {//obj:被代理类对象
		MyInvocationHandler handler=new MyInvocationHandler();
		handler.bind(obj);//这里就创建被被代理类对象
		//返回一个代理类对象  Object是一个代理类的对象
		return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(),handler );
	}
}
class MyInvocationHandler implements InvocationHandler{
	private Object obj;//被代理类对象  赋值时使用被代理类对象复制
	public void bind(Object obj) {
		this.obj=obj;
	}
	//通过代理类对象,调用方法时,会自动的调用invoke()
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//proxy :代理类对象    method:代理类对象调用的方法 
		//当通过代理类的对象时 如何动态的去调用被代理类中的同名方法
		//将被代理类要执行的方法声明在这里 invoke()
			//加入事务,权限,日志代码
		Object result= method.invoke(obj, args);//method:即为代理类对象,此方法就作为被代理独享要调用的方法
		//加入事务,权限,日志代码
		
        return result;
	}
}
public class ProxyTest {
	public static void main(String[] args) {
		SuperMan superMan=new SuperMan();//被代理类对象
		Human proxyInstance=(Human)ProxyFactory.getProxyInstance(superMan);
		//当通过调用代理类对象调用方法时 会自动调用被代理类中 同名的方法
		String belief=proxyInstance.getBelied();
		System.out.println(belief);
		proxyInstance.eat("川菜");
	}
}

AOP的一些概念

想象下面的场景,开发中在多个模块间有某段重复的代码,我们通常是怎么处理的?显然,没有人会靠“复制粘贴”吧。在传统的面向过程编程中,我们也会将这段代码,抽象成一个方法,然后在需要的地方分别调用这个方法,这样当这段代码需要修改时,我们只需要改变这个方法就可以了。然而需求总是变化的,有一天,新增了一个需求,需要再多出做修改,我们需要再抽象出一个方法,然后再在需要的地方分别调用这个方法,又或者我们不需要这个方法了,我们还是得删除掉每一处调用该方法的地方。实际上涉及到多个地方具有相同的修改的问题我们都可以通过 AOP 来解决。

在这里插入图片描述
Target是一个接口或者方法
代理就是我们生成的代理类

AOP切面

1.首先创建一个接口 IBuy.java

package com.sharpcj.aopdemo.test1;

public interface IBuy {
    String buy();
}

实现了这个接口

package com.sharpcj.aopdemo.test1;

import org.springframework.stereotype.Component;

@Component
public class Boy implements IBuy {
    @Override
    public String buy() {
        System.out.println("男孩买了一个游戏机");
        return "游戏机";
    }
}

我们需要在男孩和女孩的 buy 方法之前,需要打印出“男孩女孩都买了自己喜欢的东西”
2、 定义一个切面类,BuyAspectJ.java
下面展示一些 内联代码片

package com.sharpcj.aopdemo.test1;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class BuyAspectJ {
    @Before("execution(* com.sharpcj.aopdemo.test1.IBuy.buy(..))")
    public void haha(){
        System.out.println("男孩女孩都买自己喜欢的东西");
    }
}

这个类,我们使用了注解 @Component 表明它将作为一个Spring Bean 被装配,使用注解 @Aspect 表示它是一个切面。
类中只有一个方法 haha 我们使用 @Before 这个注解,表示他将在方法执行之前执行。关于这个注解后文再作解释。
参数(“execution(* com.sharpcj.aopdemo.test1.IBuy.buy(…))”) 声明了切点,表明在该切面的切点是com.sharpcj.aopdemo.test1.Ibuy这个接口中的buy方法。至于为什么这么写,下文再解释。

3、 在配置文件中启用AOP切面功能

package com.sharpcj.aopdemo;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@ComponentScan(basePackageClasses = {com.sharpcj.aopdemo.test1.IBuy.class})
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AppConfig {
}

我们在配置文件类增加了@EnableAspectJAutoProxy注解,启用了 AOP 功能,参数proxyTargetClass的值设为了 true 。默认值是 false,两者的区别下文再解释。

Spring AOP 所支持的 AspectJ 切点指示器

execution指示器是我们在编写切点定义时最主要使用的指示器,在此基础上,我们使用其他指示器来限制所匹配的切点。
下图的切点表达式表示当Instrument的play方法执行时会触发通知。
在这里插入图片描述
我们使用execution指示器选择Instrument的play方法,方法表达式以 * 号开始,标识我们不关心方法的返回值类型。然后我们指定了全限定类名和方法名。对于方法参数列表,我们使用 … 标识切点选择任意的play方法,无论该方法的入参是什么。
多个匹配之间我们可以使用链接符 &&、||、!来表示 “且”、“或”、“非”的关系。但是在使用 XML 文件配置时,这些符号有特殊的含义,所以我们使用 “and”、“or”、“not”来表示。

举例:
限定该切点仅匹配的包是 com.sharpcj.aopdemo.test1,可以使用
execution(* com.sharpcj.aopdemo.test1.IBuy.buy(…)) && within(com.sharpcj.aopdemo.test1.)
在切点中选择 bean,可以使用
execution(
com.sharpcj.aopdemo.test1.IBuy.buy(…)) && bean(girl)

五种通知类型

在这里插入图片描述

package com.sharpcj.aopdemo.test1;

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

@Aspect
@Component
public class BuyAspectJ {
    @Before("execution(* com.sharpcj.aopdemo.test1.IBuy.buy(..))")
    public void hehe() {
        System.out.println("before ...");
    }

    @After("execution(* com.sharpcj.aopdemo.test1.IBuy.buy(..))")
    public void haha() {
        System.out.println("After ...");
    }

    @AfterReturning("execution(* com.sharpcj.aopdemo.test1.IBuy.buy(..))")
    public void xixi() {
        System.out.println("AfterReturning ...");
    }

    @Around("execution(* com.sharpcj.aopdemo.test1.IBuy.buy(..))")
    public void xxx(ProceedingJoinPoint pj) {
        try {
            System.out.println("Around aaa ...");
            pj.proceed();
            System.out.println("Around bbb ...");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }

}

为了方便看效果,我们测试类中,只要 Boy 类:

package com.sharpcj.aopdemo;

import com.sharpcj.aopdemo.test1.Boy;
import com.sharpcj.aopdemo.test1.Girl;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class AppTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        Boy boy = context.getBean("boy",Boy.class);
        Girl girl = (Girl) context.getBean("girl");
        boy.buy();
        // girl.buy();
    }
}

在这里插入图片描述

4.3 通过注解声明切点表达式

如你看到的,上面我们写的多个通知使用了相同的切点表达式,对于像这样频繁出现的相同的表达式,我们可以使用 @Pointcut注解声明切点表达式,然后使用表达式,修改代码如下:
BuyAspectJ.java

package com.sharpcj.aopdemo.test1;

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

@Aspect
@Component
public class BuyAspectJ {

    @Pointcut("execution(* com.sharpcj.aopdemo.test1.IBuy.buy(..))")
    public void point(){}

    @Before("point()")
    public void hehe() {
        System.out.println("before ...");
    }

    @After("point()")
    public void haha() {
        System.out.println("After ...");
    }

    @AfterReturning("point()")
    public void xixi() {
        System.out.println("AfterReturning ...");
    }

    @Around("point()")
    public void xxx(ProceedingJoinPoint pj) {
        try {
            System.out.println("Around aaa ...");
            pj.proceed();
            System.out.println("Around bbb ...");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
AOP(Aspect-Oriented Programming)是面向切面编程的一种编程范式,通过在程序的不同阶段植入通用的切面逻辑,使得这些逻辑可重用并可以在不同的模块中引用。其中,aop returning是AOP的一个关键方面,用于在方法执行功返回之后执行的操作。 aop returning是在被通知的方法功返回结果之后,执行一些附加操作的一种机制。通常,这些附加操作包括日志记录、性能监测、事务管理等。通过aop returning,我们可以将这些通用操作与原始方法逻辑解耦,并将它们封装在切面中。 在使用aop returning时,我们首先需要定义一个切面(Aspect),其中包含切入点(Pointcut)和通知(Advice)。切入点决定了哪些方法会被植入切面逻辑,通知则定义了方法执行前、后以及返回时的具体操作。 在aop returning中,通知型为返回通知(AfterReturning Advice)。当被通知的方法功执行并返回结果时,返回通知即会触发。我们可以在返回通知中编写所需的逻辑,比如记录返回结果、日志输出或是检查业务逻辑等。 通过使用aop returning,我们可以集中处理各个方法执行功后的附加操作,避免了在每个方法中显式编写这些逻辑的重复性工作。这样可以提高代码的可维护性、可读性和代码复用程度。 总而言之,aop returning是AOP编程中用于在方法执行功返回之后执行的一种机制。通过使用aop returning,我们可以在方法返回后执行一些附加操作,提高代码的可维护性和代码复用程度,并有效地解耦原始方法和通用操作。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值