Spring中AOP动态代理

来源:互联网 发布:linux ftp 根目录 编辑:程序博客网 时间:2024/05/02 02:15

一、代理模式

  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 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 陌陌好友找不到了怎么办 换了手机号陌陌号找不到怎么办 微信解析包出现问题怎么办 老板找借口不发工资怎么办 苹果手机忘记id帐号密码怎么办 苹果6手机帐号密码忘记怎么办 狗狗在窝里撒尿怎么办 爸妈和媳妇不好怎么办 如果老婆不用老公的钱怎么办 老公不给老婆钱花怎么办 奶少孩子不愿意吸怎么办 孩子识字少不愿意读书怎么办 夏天孩子不愿意喝水尿量少黄怎么办 孩子不喜欢与别人沟通怎么办 异地恋他爸妈不同意怎么办 刚装修家里很臭怎么办 狗弄得家里很臭怎么办 抑郁症患者不愿意回家见家人怎么办 属猴的人养猫怎么办 对方电话拉黑我怎么办 手机号码换了淘宝账号怎么办 扣扣号有密码无法加好友怎么办 扣扣帐号忘记了怎么办 扣扣帐号被冻结怎么办 xp忘了开机密码怎么办 手机怎样知道好友qq密码怎么办 找回微信密码申诉失败怎么办 若微信号被盗钱怎么办 微信号被倒了怎么办 微信支付密码忘了怎么办 微信钱包密码忘了怎么办 忘记qq锁屏密码怎么办 微信里保密柜密码忘记了怎么办 qq漫游记录密码忘了怎么办 当你很烦的时候怎么办 当你烦的时候怎么办 面对刁蛮不讲理的老婆怎么办 老是想以前的事怎么办 被心机婊陷害了怎么办 分手了还被骚扰怎么办 苹果手机屏幕出现冷暖屏怎么办