Spring中AOP的实现和代理模式

代理模式

在许多项目上为了不修改核心底层代码,又希望可以添加更多功能,就可以让一个类去代理核心类,去给他添加更多功能,这也就是代理模式

代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式

在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口

静态代理模式

  • 抽象角色(Image) : 一般使用接口或者抽象类来实现
  • 真实角色(Reallmage) : 被代理的角色
  • 代理角色(Proxylmage) : 代理真实角色 ; 代理真实角色后 , 添加一些附属的操作 .
  • 用户(ProxyPatternDemo) : 使用代理角色来进行一些操作

在这里插入图片描述
代码体现:

抽象角色(Image):

public interface Image {
   public void commodity1();
   public void commodity2();
}

真实角色(Reallmage):

public class Factory implements Image{
   public void commodity1() {
       System.out.println("商品出售1");
  }

   public void commodity2() {
       System.out.println("商品出售2");
  }
}

代理角色(Proxylmage):

public class Shop implements Image {

   private Factory factory;
   public Shop() { }
   public Shop(Factory factory) {
       this.factory = factory;
  }

   //商品出售
   public void commodity1(){
       wrap();
       Factory.commodity1();
      
  }
   public void commodity2(){
       wrap();
       Factory.commodity2();
   //包装
   public void wrap(){
       System.out.println("包装商品");
  }
  
}

客户(ProxyPatternDemo):

public class Client {
   public static void main(String[] args) {
       
       Factory factory = new Factory();
      
       Shop shop = new Shop(factory);

       
       shop.commodity1();
       shop.commodity2();
  }
}

动态代理模式

动态代理中的角色和静态代理一样,只是动态代理中的代理类也就是代理角色(Proxylmage)是动态生成的

动态代理有三类:

  • JDK动态代理:基于接口的动态代理
  • cglib:基于类的动态代理
  • javasist: java字节码生成 (主流)

JDK动态代理来讲述下动态代理

以下JDK动态代理 简称 动态代理

动态代理需要理解掌握两个类:InvocationHandler类Proxy类

InvocationHandler类(调用处理程序)

InvocationHandler是由代理实例的调用处理程序实现的接口 代理类都必须继承这个接口且实现invoke方法

invoke方法:

Object invoke(Object proxy, Method method, Object[] args);

proxy:调用该方法的代理实例
method:所述方法对应于调用代理实例上的接口方法的实例
args:包含的方法调用传递代理实例的参数值的对象的阵列,或null(例如java.lang.Integer)
Proxy类( )(代理)

Proxy类提供了创建动态代理类和实例 的静态方法

每个代理实例都有一个关联的调用处理程序对象,且实现了InvocationHandler接口

 Proxy.newProxyInstance
(this.getClass().getClassLoader(),image.getClass().getInterfaces(),this);
            获得该类的类加载器        获得该接口的的接口类          该类  
实例

抽象角色(Image):

public interface Image {
   public void commodity1();
   public void commodity2();
}

真实角色(Reallmage):

public class Factory implements Image{
   public void commodity1() {
       System.out.println("商品出售1");
  }

   public void commodity2() {
       System.out.println("商品出售2");
  }
}

代理角色(Proxylmage) ProxyInvocationHandler:

public class ProxyInvocationHandler implements InvocationHandler {
   private Image image;

   public void setImage(Image image) {
       this.image = image;
  }

   //生成代理类,重点是第二个参数,获取要代理的抽象角色!静态代理类为代理一个角色,现在可以代理一类角色
   public Object getProxy(){
       return Proxy.newProxyInstance(this.getClass().getClassLoader(),
               image.getClass().getInterfaces(),this);
  }

   
   
   // 处理代理实例上的方法调用并返回结果
   @Override
   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
       
       //核心:本质利用反射实现!
       Object result = method.invoke(image, args);
       wrap();
       return result;
  }

   public void wrap(){
       System.out.println("包装商品");
  }
  
}

客户(ProxyPatternDemo):

public class Client {
   public static void main(String[] args) {
       
       Factory factory = new Factory();
       ProxyInvocationHandler shop = new ProxyInvocationHandler();
       shop.setImage(factory); 
       Image proxy = (Image)shop.getProxy();      
       proxy.commodity1();
       proxy.commodity2();
  }
}
动态代理通用

我们可以把代理角色中

private Image image; 改为 private Object target

把类中所有的image都由target代替,这样我们这个代理角色就可以代理多种抽象类,而不需要去频繁创建和改动他们

Spring中的AOP实现

spring中的AOP(面向切面编程) 就是依托 代理模式来实现的。

利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低提高程序的可重用性同时提高了开发的效率

AOP中存在多个名词来帮助理解面向切面编程的原理:

  • 横切关注点: 跨越应用程序多个模块的方法或功能。
  • 切面(ASPECT): 横切关注点 被模块化 的特殊对象。即,它是一个类。
  • 通知(Advice): 切面必须要完成的工作。即,它是类中的一个方法。
  • 目标(Target): 被通知对象。
  • 代理(Proxy): 向目标对象应用通知之后创建的对象。
  • 切入点(PointCut): 切面通知 执行的 “地点”的定义。
  • 连接点(JointPoint): 与切入点匹配的执行点。

在这里插入图片描述
要想使用AOP的话,需要导入相应的jar包或者maven依赖

maven依赖:

<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.6</version>
</dependency>

Spring中AOP的实现方式有三种:

  • 第一种是通过Spring的API接口来实现
  • 第二种是通过自定义类来实现,
  • 第三种是通过注解来实现。

抽象类:

public interface Image {
   public void commodity1();
   public void commodity2();
}

真实角色(Reallmage):

public class Factory implements Image{
   public void commodity1() {
       System.out.println("商品出售1");
  }

   public void commodity2() {
       System.out.println("商品出售2");
  }
}

1:通过Spring的API接口实现

通过Spring的API接口实现的话,得需要了解MethodBeforeAdvice(前置增强)AfterReturningAdvice(后置增强)这两个接口。他们分别对应着方法实现前方法实现后的作用

MethodBeforeAdvice(前置增强)类

public class Before implements MethodBeforeAdvice {

  
   @Override
   public void before(Method method, Object[] objects, Object object) throws Throwable {
       //method : 要执行的目标对象的方法
       //objects : 被调用的方法的参数
       //Object : 目标对象
   	   System.out.println("包装")
       System.out.println( object.getClass().getName() );
       System.out.println( method.getName())
       //通过Spring的API接口和参数我们可以获取类的方法和类别,也能进行操作。
       
  }
}

AfterReturningAdvice(后置增强)类:

public class After implements AfterReturningAdvice {
  
   @Override
   public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
       //returnValue 返回值
       //method被调用的方法
       //args 被调用的方法的对象的参数
       //target 被调用的目标对象
       System.out.println("结账");
       System.out.println(target.getClass().getName());//被调用的类名
       System.out.println(method.getName());//被调用的方法名
       System.out.println(returnValue);//一般为null
  }
}

还要去xml中注册和连接这些类:

<?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">

   <!--注册-->
   <bean id="factory" class="com.nicht.service.Factory"/>
   <bean id="before" class="com.nicht.log.Before"/>
   <bean id="after" class="com.nicht.log.After"/>

   <!--aop的配置-->
   <aop:config>
       <!--切入点 expression:表达式匹配要执行的方法-->
       <aop:pointcut id="pointcut" expression="execution(* com.nicht.service.Factory.*(..))"/>
       <!--其中execution( *  *  *  *  *)分别代表 修饰词 返回值 类名 方法名  参数 其中*代表都行 -->
       <!--执行环绕; advice-ref执行方法 . pointcut-ref切入点-->
       <aop:advisor advice-ref="before" pointcut-ref="pointcut"/>
       <aop:advisor advice-ref="after" pointcut-ref="pointcut"/>
   </aop:config>
</beans>

客户(ProxyPatternDemo):

public class Test {
   @Test
   public void test(){
       ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
       Image image = (Image) context.getBean("factory");
       //因为AOP是使用了代理模式,所以代理的还是接口
       image.commodity1();
  }
}

2:通过自定义类来实现

通过自定义类我们就不需要去实现Spring的API接口了,但自定义类的话,也无法方便的去获取类的反射来对类的属性进行操作

创建自己的一个切入类:

public class test {

   public void before(){
       System.out.println("包装");
  }
   public void after(){
       System.out.println("结账");
  }
   
}

在bean.xml中配置:

<!--注册-->
<bean id="factory" class="com.nicht.service.Factory"/>
<bean id="test" class="com.nicht.service.test"/>

<!--aop的配置-->
<aop:config>
   <!--第二种方式:使用AOP的标签实现-->
   <aop:aspect ref="test">
       <aop:pointcut id="test1" expression="execution(* com.nicht.service.Factory.*(..))"/>
       <aop:before pointcut-ref="test1" method="before"/>
       <aop:after pointcut-ref="test1" method="after"/>
   </aop:aspect>
</aop:config>

3:通过注解来实现

通过注解的话,我们就无需在xml中进行注册和配置,但灵活性会降低,且之后的维护难度会加大

使用注解来实现的话,需要了解四个注解

  • @Aspect切面声明,标注在类、接口(包括注解类型)或枚举上。
  • @Before前置通知, 在目标方法(切入点)执行之前执行。
  • @After后置通知, 在目标方法(切入点)执行之后执行
  • @Around环绕通知:目标方法执行前后分别执行一些代码,发生异常的时候执行另外一些代码

创建一个自定义的切入类:

package com.nicht.service;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class AnnotationPointcut {
   // After , Before  和 Around中也要添加要进行增强的方法路径
   @Before("execution(* com.nicht.service.Factory.*(..))")
   public void before(){
       System.out.println("包装");
  }

   @After("execution(* com.nicht.service.Factory.*(..))")
   public void after(){
       System.out.println("结账");
  }

   @Around("execution(* com.nicht.service.Factory.*(..))")
   //在目标前后的前置和后置增强前在添加代码  
   public void around(ProceedingJoinPoint jp) throws Throwable {
       System.out.println("环绕前");
       //执行目标方法proceed,围绕于jp
       Object proceed = jp.proceed();
       System.out.println("环绕后");
       
  }
}

在xml中设置注解的使用

<bean id="annotationPointcut" class="com.nicht.config.AnnotationPointcut"/>
<aop:aspectj-autoproxy/>
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值