Spring中AOP动态代理

一、代理模式

  Spring的AOP面向切面的思想采用了代理模式。代理模式是对象的结构模式。代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。代理模式的UML图如下:
这里写图片描述
在代理模式中的角色:

  • 抽象对象角色(AbstractObject):声明了目标对象和代理对象的共同接口,这样一来在任何可以使用目标对象的地方都可以使用代理对象。
  • 目标对象角色(RealObject):定义了代理对象所代表的目标对象。
  • 代理对象角色(ProxyObject):代理对象内部含有目标对象的引用,从而可以在任何时候操作目标对象;代理对象提供一个与目标对象相同的接口,以便可以在任何时候替代目标对象。代理对象通常在客户端调用传递给目标对象之前或之后,执行某个操作,而不是单纯地将调用传递给目标对象。

示例代码:

public abstract class AbstractObject {
    //操作
    public abstract void operation();
}

public class RealObject extends AbstractObject {
    @Override
    public void operation() {
        //一些操作
        System.out.println("一些操作");
    }
}

public class ProxyObject extends AbstractObject{
    RealObject realObject = new RealObject();
    @Override
    public void operation() {
        //调用目标对象之前可以做相关操作
        System.out.println("before");        
        realObject.operation();        
        //调用目标对象之后可以做相关操作
        System.out.println("after");
    }
}

public class Client {
    public static void main(String[] args) {
        AbstractObject obj = new ProxyObject();
        obj.operation();
    }
}

二、AOP中的两种动态代理实现原理

  AOP:面向切面、面向方面、面向接口是一种横切技术,主要用于以下场景:事务管理: (1)数据库事务:(2)编程事务(3)声明事物;日志处理;安全验证。
  AOP中采用了两种动态代理:采用JDK反射机制动态代理和用CGLIB动态代理。java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
(1)如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP。
(2)如果目标对象实现了接口,可以强制使用CGLIB实现AOP。
(3)如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换。
1、JDK反射机制动态代理
  实现java.lang.reflect.InvocationHandler接口,复写其中的invoke方法。
  生成代理核心方法是:

newProxyInstance (obj.getClass().getClassLoader(),obj.getClass().getInterfaces(), InvocationHandler invocationHandler);  

  从 obj.getClass().getInterfaces() 可以看出,如果直接用 JDK 的反射必须创建接口。动态代理过程主要包括以下几步:
(1)实现InvocationHandler接口
(2)创建代理类(通过java API)
Proxy.newProxyInstance(动态加载代理类,代理类实现接口,使用handler);
(3)调用invoke方法(虚拟机自动调用方法)
下面代码演示其过程:
声明接口

public interface IHello {
  public void sayHello(String name);

  public void sayWorld(String name);
}

接口实现类

public class HelloImpl implements IHello {
  public void sayHello(String name) {
    System.out.println(name + " say Hello!");
  }
  public void sayWorld(String name) {
    System.out.println(name + " say World!");
  }
}

代理类

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class HelloHandler implements InvocationHandler {
  private Object targetObject;

  public Object createProxy(Object targetObject){
    this.targetObject = targetObject;
    return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), this);
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    Object obj = null;
    try {
      obj = method.invoke(this.targetObject, args);
    } catch (Exception e) {
      e.printStackTrace();
    }
    return obj;
  }
}

测试

HelloProxy proxy = new HelloProxy();
    proxy.bind(new HelloImpl());
    proxy.sayHello("Haiwi ");

执行结果
这里写图片描述
2、CGLIB动态代理
  CGLib(Code Generation Library)是一个强大的,高性能,高质量的Code生成类库。它可以在运行期扩展Java类与实现Java接口。cglib封装了asm,可以在运行期动态生成新的class。asm 是一个强大的 Java 字节码生成框架,和 BCEL 或者 SERP 很类似,但是小很多,可以动态修改 java 字节码。
  CGLib 可以不用接口,它底层调用asm 动态生成一个代理类去覆盖父类中非 final 的方法,然后实现 MethodInterceptor 接口的 intercept 方法,这样以后直接调用重写的方法,比 JDK 要快。但是加载CGLib消耗时间比直接 JDK 反射时间长,开发的过程中,如果是反复动态生成新的代理类推荐用 JDK 自身的反射,反之用 CGLib。
  CGLib实现代理的过程是:CGLib采用非常底层的字节码技术,可以为一个类创建子类,并在子类中采用方法拦截的结束拦截所有父类方法的调用,并顺势织入横切逻辑。我们采用CGLib技术可以编写一个可以为任何类创建织入横切逻辑代理对象的代理创建器。
如果要强制Spring使用代理,需在spring配置文件中添加:

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

三、Spring中AOP动态代理举例

这里写图片描述
applicationContext.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"
    xmlns:tx="http://www.springframework.org/schema/tx"
    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="dataService" class="com.haiwi.service.impl.DataServiceImpl" />
    <bean id="userLoginService" class="com.haiwi.service.impl.UserLoginServiceImpl" />
    <bean id="logAdvice" class="com.haiwi.advice.impl.LogAdviceImpl" />
    <aop:config>
        <!-- 方面 -->
        <aop:aspect id="logAspect" ref="logAdvice">
            <!-- 切入面 对 【com.haiwi.service.impl】下的所有类的所有方法做切面处理 -->
            <aop:pointcut id="logPointcut" expression="execution(* com.haiwi.service.impl.*.*(..))" />

            <!-- 织入(通知作用于切入点) -->
            <aop:before pointcut-ref="logPointcut" method="before" />
            <aop:after pointcut-ref="logPointcut" method="after" />
            <aop:after-returning pointcut-ref="logPointcut" method="afterReturn"/>
        </aop:aspect>
    </aop:config> 
</beans>

  该配置主要创建一个日志记录的切面,对com.haiwi.service.impl包下的类调用做日志记录。
通知类如下

package com.haiwi.advice;
import org.aspectj.lang.JoinPoint;
public interface LogAdvice {

  public void before(JoinPoint jp);
  public void after(JoinPoint jp);

  public void around(JoinPoint jp);

  public void afterReturn(JoinPoint jp);

}
//==============================================
package com.haiwi.advice;
import org.aspectj.lang.JoinPoint;
public interface LogAdvice {

  public void before(JoinPoint jp);
  public void after(JoinPoint jp);

  public void around(JoinPoint jp);

  public void afterReturn(JoinPoint jp);

}

业务逻辑类

package com.haiwi.service;
public interface UserLoginService {
  public boolean loginIn();

  public boolean loginOut();

}
//==============================================
package com.haiwi.service.impl;
import com.haiwi.service.UserLoginService;
public class UserLoginServiceImpl implements UserLoginService {
  @Override
  public boolean loginIn() {
    System.out.println("登录成功!");
    return true;
  }
  @Override
  public boolean loginOut() {
    System.out.println("退出成功!");
    return true;
  }
}

测试类

public static void main(String[] args) {
    ApplicationContext ac = new ClassPathXmlApplicationContext("resource/spring/applicationContext.xml");
    UserLoginService uls = (UserLoginService)ac.getBean("userLoginService");
    uls.loginIn();
    System.exit(0);
  }

执行结果
这里写图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值