首页 > 其他分享 >Aop原理

Aop原理

时间:2023-03-27 22:33:58浏览次数:37  
标签:do target Object 代理 method Aop 原理 public

AOP相关的概念

1)Aspect:切面,切入系统的一个切面。比如事务管理是一个切面,权限管理也是一个切面。

2)Join point:连接点,也就是可以进行横向切入的位置。

3)Advice:通知,切面在某个连接点执行的操作(分为:Before advice,After returning advice,After throwing advice,After (finally) advice,Around advice)。

4)Pointcut:切点,符合切点表达式的连接点,也就是真正被切入的地方。

1) JDK动态代理

主要使用到 InvocationHandler 接口和 Proxy.newProxyInstance() 方法。JDK动态代理要求被代理实现一个接口,只有接口中的方法才能够被代理。其方法是将被代理对象注入到一个中间对象,而中间对象实现InvocationHandler接口,在实现该接口时,可以在 被代理对象调用它的方法时,在调用的前后插入一些代码。而 Proxy.newProxyInstance() 能够利用中间对象来生产代理对象。插入的代码就是切面代码。所以使用JDK动态代理可以实现AOP。我们看个例子:

被代理对象实现的接口,只有接口中的方法才能够被代理:

public interface UserService {
    public void addUser(User user);
    public User getUser(int id);
}

被代理对象:

public class UserServiceImpl implements UserService {
    public void addUser(User user) {
        System.out.println("add user");
    }
    public User getUser(int id) {
        User user = new User();
        user.setId(id);
        System.out.println("getUser");
        return user;
    }
}

代理中间类:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class ProxyUtil implements InvocationHandler {
  //被代理的对象   private Object target;   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {    System.out.println("do method before");    Object result = method.invoke(target, args);    System.out.println("do method after");   return result;   }   ProxyUtil(Object target){    this.target = target;   }   public Object getTarget() {    return target;   }   public void setTarget(Object target) {    this.target = target;   } }

测试:

import java.lang.reflect.Proxy;
import User;
public class ProxyTest {
  public static void main(String[] args){
// 被代理的对象     Object proxyedObject = new UserServiceImpl();     ProxyUtil proxyUtils = new ProxyUtil(proxyedObject);    //生成代理对象,对被代理对象的这些接口进行代理:UserServiceImpl.class.getInterfaces()   UserService proxyObject = (UserService) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), UserServiceImpl.class.getInterfaces(), proxyUtils);   proxyObject.getUser(1);   proxyObject.addUser(new User());   } }

结果:

do method before

getUser

do method after

do method before

add user

do method after

该方式有一个要求,被代理的对象必须实现接口,而且只有接口中的方法才能被代理。

2)CGLIB(code generate libary)

字节码生成技术实现AOP,其实就是继承被代理对象,然后override需要被代理的方法,在覆盖该方法时,自然是可以插入我们自己的代码的。因为需要override被代理对象的方法,所以CGLIB技术实现AOP时,就必须要求需要被代理的方法不能是final方法,因为final方法不能被子类覆盖。我们使用CGLIB实现上面的例子:
package aop;

import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class CGProxy implements MethodInterceptor{
    private Object target;    // 被代理对象
    public CGProxy(Object target){
        this.target = target;
    }
    public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy proxy) throws Throwable {
        System.out.println("do method before");
        Object result = proxy.invokeSuper(arg0, arg2);
        System.out.println("do method after");
        return result;
    }
    public Object getProxyObject() {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.target.getClass());    // 设置父类
        // 设置回调
     // 在调用父类方法时,回调 this.intercept() enhancer.setCallback(this); // 创建代理对象 return enhancer.create(); } }

测试:

public class CGProxyTest {
    public static void main(String[] args){
        Object proxyedObject = new UserServiceImpl();    // 被代理的对象
        CGProxy cgProxy = new CGProxy(proxyedObject);
        UserService proxyObject = (UserService) cgProxy.getProxyObject();
        proxyObject.getUser(1);
        proxyObject.addUser(new User());
    }
}

结果:

do method before

getUser

do method after

do method before

add user

do method after

它的原理是生成一个父类enhancer.setSuperclass(this.target.getClass())的子类enhancer.create(),然后对父类的方法进行拦截enhancer.setCallback(this). 对父类的方法进行覆盖,所以父类方法不能是final的。

spring实现AOP的相关源码:

@SuppressWarnings("serial")
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
    @Override
    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
            Class<?> targetClass = config.getTargetClass();
            if (targetClass == null) {
                throw new AopConfigException("TargetSource cannot determine target class: " +
                        "Either an interface or a target is required for proxy creation.");
            }
            if (targetClass.isInterface()) {
                return new JdkDynamicAopProxy(config);
            }
            return new ObjenesisCglibAopProxy(config);
        }
        else {
            return new JdkDynamicAopProxy(config);
        }
    }
}

如果被代理对象实现了接口,那么就使用JDK的动态代理技术,反之则使用CGLIB来实现AOP,所以Spring默认是使用JDK的动态代理技术实现AOP的。

JdkDynamicAopProxy:
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {    
    @Override
    public Object getProxy(ClassLoader classLoader) {
        if (logger.isDebugEnabled()) {
            logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
        }
        Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
        findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
        return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
    }
}

Spring AOP的配置

Spring中AOP的配置一般有两种方法,一种是使用 aop:config 标签在xml中进行配置,一种是使用注解以及@Aspect风格的配置。

1)基于aop:config的AOP配置

下面是一个典型的事务AOP的配置:

<tx:advice id="transactionAdvice" transaction-manager="transactionManager"?>
        <tx:attributes >
            <tx:method name="add*" propagation="REQUIRED" />
            <tx:method name="*" propagation="SUPPORTS" />
        </tx:attributes>
</tx:advice>
<aop:config>
        <aop:pointcut id="transactionPointcut" expression="execution(* com.zby.service..*Impl.*(..))" />
        <aop:advisor pointcut-ref="transactionPointcut" advice-ref="transactionAdvice" />
</aop:config>

2) 基于注解和@Aspect风格的AOP配置 

<!-- 使用annotation定义事务 -->
<tx:annotation-driven transaction-manager="transactionManager" />

扫描切面:

<context:component-scan base-package="com.zby.aop" />

启用@AspectJ风格的注解:

<aop:aspectj-autoproxy proxy-target-class="true"/>

 proxy-target-class="true" 这个最好不要随便使用,它是指定只能使用CGLIB代理。

切面失效

因为Spring AOP是基于动态代理对象的,那么如果target中的方法不是被代理对象调用的,那么就不会织入切面代码。

失效示例:

切面:

@Aspect
@Order(100)
public class MethodMonitorUMPAspect {

Logger logger = LoggerFactory.getLogger(UMPAspect.class);

/**
* 环绕增强切面
*
* @param pjp
* @return
*/
@Around(value = "@annotation(methodMonitor)")
public Object intercept(ProceedingJoinPoint pjp, MethodMonitor methodMonitor) throws Throwable {
    logger.info("do method before");
    obj = pjp.proceed();
    logger.info("do method after");
    return obj;
  }
}
@Service("userService")
public class UserServiceImpl implements UserService{
   public void a() { b(); }
@methodMonitor public void b() {
    System.out.println("do b"); } }

虽然b()方法被调用了,但是因为不是代理对象调用的,所以切面并没有执行。这就是Spring aop的缺陷。解决方法如下:

首先:将 <aop:aspectj-autoproxy /> 改为:

<aop:aspectj-autoproxy expose-proxy="true"/>

修改失效示例:

@Service("userService")
public class UserServiceImpl implements UserService{
   public void a() {
         ((UserService)AopContext.currentProxy()).b();
    }

    @methodMonitor
    public void b() {   
    System.out.println("do b"); } }

结果:

do method before

do b

do method after

((UserService)AopContext.currentProxy())先获得当前的代理对象,然后在调用b() 方法就可以了。expose-proxy="true" 表示将当前代理对象暴露出去,不然 AopContext.currentProxy()返回的是null。


 

标签:do,target,Object,代理,method,Aop,原理,public
From: https://www.cnblogs.com/zhengbiyu/p/17263302.html

相关文章

  • 使用copilot生成vue响应式原理
    //生成vue的响应式原理functiondefineReactive(obj,key,val){//递归observe(val);//创建Dep实例constdep=newDep();Object.defineProperty(obj......
  • Exp3-免杀原理
    基础问题回答(1)杀软是如何检测出恶意代码的?根据特征码检测:就是恶意代码都有一些特征数据,也就是特征码,这些特征数据存储在AV厂商的特征库中。如果一个文件被检测出他的某......
  • TLS/SSL工作原理及握手过程
    一、TLS/SSL基础概念1、概念TLS传输层安全性协议(TransportLayerSecurity)SSL安全套接层(SecureSocketsLayer)目的:为互联网通信提供安全以及数据完整性保障。HTTPS=......
  • Spring AOP、AspectJ、CGLIB
     静态代理和动态代理AOP代理则可分为静态代理和动态代理两大类,其中静态代理是指使用AOP框架提供的命令进行编译,从而在编译阶段就可生成AOP代理类,因此也称为编译时......
  • 计算机组成原理
    计算机系统1.硬件2.软件系统软件:用来管理计算机系统应用软件:为执行特定任务而编写相关概念解释微处理器:CPU机器字长:32/64位处理器,计算机一次整数运算所能处理......
  • 【JavaScript快速排序算法】不同版本原理分析
    说明快速排序(QuickSort),又称分区交换排序(partition-exchangesort),简称快排。快排是一种通过基准划分区块,再不断交换左右项的排序方式,其采用了分治法,减少了交换的次数。它的......
  • 某大厂面试题:说一说Java、Spring、Dubbo三者SPI机制的原理和区别
    大家好,我是三友~~今天来跟大家聊一聊Java、Spring、Dubbo三者SPI机制的原理和区别。其实我之前写过一篇类似的文章,但是这篇文章主要是剖析dubbo的SPI机制的源码,中间只是......
  • 第二章 1.3节 目录结构与基本运行原理
    1.1Nginx目录结构说明[root@k8s-master01~]#tree/usr/local/nginx//usr/local/nginx/├──client_body_temp├──conf#存放一系列配置文件的目......
  • 波峰焊的工作原理和操作流程
    一、波峰焊是什么是一种将熔化的焊料喷流成特定形状的焊料波峰,将预先装有元器件的pcb印制板通过焊料波峰,使元器件焊端或引脚与印制板焊盘之间机械与电气连接的一种工艺流程......
  • 分布式技术原理与算法解析 02 - 分布式计算
    分布式计算模式之MRMapReduce就是将复杂的、难以直接解决的大问题,分割为规模较小的、可直接解决的小问题。这些子问题相互独立且和原问题形式相同,可递归地求解,然后将子问......