菜鸟的成长之路——AOP的两种代理方式

  spring中使用了两种动态代理方式JDK动态代理和CGLib动态代理。
  代理的英文称呼是Proxy,顾名思义就是一个人代表另个人,或者一个机构代表另个机构去做某个事情。动态代理的动态意思就是,在程序运行时利用反射机制动态创建而成。

1、JDK动态代理代码实现是什么样的?

  我们对于动态代理的研究是基于IOC的,我们需要这四个类:目标对象接口、目标对象类、代理类、测试类,直接上代码:
Lecturer.java(目标类接口)

package com.spring.test;

public interface Lecturer {
    public void ClassBegin();
}

LecturerImpl.java(目标类)

package com.spring.test;

public class LecturerImpl implements Lecturer{
    public void ClassBegin() {
        // TODO Auto-generated method stub
        System.out.println("讲师开始上课,同学激烈的讨论!");
    }
}

  我们这里写了一个讲师的接口,原因是因为JDK动态代理只能代理接口,这也是JDK动态代理的一个局限,我们继续。
LecturerProxy.java(代理类)

package com.spring.test;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class LecturerProxy implements InvocationHandler {

    private LecturerImpl target;
    public void setTarget(LecturerImpl target) {
        this.target = target;
    }
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // TODO Auto-generated method stub
        System.out.println("上课前学校要求点名!");
//      用反射执行target的method方法
        method.invoke(target, args);
        System.out.println("下课的时候学校要求点名!");
        return null;
    }

}

ApplicationContext.java

<?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:p="http://www.springframework.org/schema/p"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context-4.3.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">

    <bean id="lecturer" class="com.spring.test.LecturerImpl"></bean>
    <bean id="proxy" class="com.spring.test.LecturerProxy">
        <property name="target" ref="lecturer"></property>
    </bean>
</beans>

  在配置文件里,我们将目标对象的bean注入到代理类的bean中,这样代理类就成了目标对象的代理类,代码继续。
SpringTest.java

import java.lang.reflect.Proxy;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.spring.test.Lecturer;
import com.spring.test.LecturerImpl;
import com.spring.test.LecturerProxy;

public class SpringTest{

    public static void main(String[] args) {
        ApplicationContext content = new ClassPathXmlApplicationContext("applicationcontext.xml");
        Lecturer lecturer = (LecturerImpl) content.getBean("lecturer");
        LecturerProxy proxy = (LecturerProxy) content.getBean("proxy");
        Lecturer lecturerProxy = (Lecturer) Proxy.newProxyInstance(lecturer.getClass().getClassLoader(), lecturer.getClass().getInterfaces(), proxy);
        lecturerProxy.ClassBegin();

    }
}

  在测试类中,我们分别获取了目标对象的bean和代理类的bean,然后使用Proxy类的newProxyInstance方法,将目标对象的接口给到了代理类,让代理类可以调用相应方法。
  此时,代理类将增强织入到目标对象的每个方法上,调用任何方法都会进行点名。我们如果只想将增强织入都某个切点,在代理类中判断一下就可以了,代码如下:
LecturerProxy.java

package com.spring.test;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class LecturerProxy implements InvocationHandler {

    private LecturerImpl target;
    public void setTarget(LecturerImpl target) {
        this.target = target;
    }
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // TODO Auto-generated method stub
        if(method.getName().equals("ClassBegin")) {
            System.out.println("上课前学校要求点名!");
//          用反射执行target的method方法
            method.invoke(target, args);
            System.out.println("下课的时候学校要求点名!");
        }
        return null;
    }

}

  提醒一下,在所有导包过程中,主要不要导错成CGLib的包。

2、CGLib动态代理代码实现是什么样的?

  CGLib动态代理,不要求目标对象一定是接口,需要三个类:目标对象类、代理类、测试类。代码如下:

Lecturer.java(目标对象类)

package com.spring.test;

public class Lecturer{
    public void ClassBegin() {
        // TODO Auto-generated method stub
        System.out.println("讲师开始上课,同学激烈的讨论!");
    }
}

LecturerProxy.java

package com.spring.test;

import java.lang.reflect.Method;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

public class LecturerProxy implements MethodInterceptor{

    private Enhancer enhancer = new Enhancer();

    public Object getProxy(Class clazz) {
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        return enhancer.create();
    }

    public Object intercept(Object obj, Method method, Object[] arg2, MethodProxy methodProxy) throws Throwable {
        // TODO Auto-generated method stub
        if(method.getName().equals("ClassBegin")) {
            System.out.println("上课前点名!");
            methodProxy.invokeSuper(obj, arg2);
            System.out.println("下课时候点名!");
        }
        return null;
    }

}

  我们来分析一下这个代理类。他实现了方法拦截器的接口MethodInterceptor。在这个代理类中,我们声明了一个增强类Enhancer对象,并封装方法getProxy给增强类设置父类为目标对象的class,设置子类为当前代理类对象,并返回一个实例。

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:p="http://www.springframework.org/schema/p"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context-4.3.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">

    <bean id="lecturer" class="com.spring.test.Lecturer"></bean>
    <bean id="proxy" class="com.spring.test.LecturerProxy"></bean>
</beans>

SpringTest.java

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.spring.test.Lecturer;
import com.spring.test.LecturerProxy;

public class SpringTest{

    public static void main(String[] args) {
        ApplicationContext content = new ClassPathXmlApplicationContext("applicationcontext.xml");
        Lecturer lecturer = (Lecturer) content.getBean("lecturer");
        LecturerProxy proxy = (LecturerProxy) content.getBean("proxy");
        Lecturer lecturerProxy = (Lecturer) proxy.getProxy(lecturer.getClass());
        lecturerProxy.ClassBegin();

    }
}

  在测试类中,我们获取到目标对象和代理类对象,调用代理对象的getProxy方法,将目标对象的class传参并返回一个目标对象的增强,然后增强充当了目标对象的代理职责。

3、两种代理是否就是spring中的AOP了呢?

  这两种代理方式是spring AOP的实现原理,但不是springAOP,这两种代理方式有以下缺陷:
  目标类的所有方法都被拦截,而有时候我们只希望对目标类的某些特定方法进行横切逻辑。
  使用硬编码的形式进行横切,也就是在运行过程中无法修改。
  需要我们编码代理类,并使用编码为其指定目标对象,目标对象单一,无法通用。
  这三个问题,在springAOP中占有重要地位,springAOP也很好的解决了这些问题,我们下面几节课会聊到。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值