Spring中AOP技术基础——静态代理、动态代理(JDK动态代理)、CGLIB代理

11 篇文章 1 订阅

代理模式

我们首先来了解一下什么是代理模式:

  • 为其他对象提供一个代理以控制对某个对象的访问。代理类主要负责为委托了(真实对象)预处理消息、过滤消息、传递消息给委托类,代理类不现实具体服务,而是利用委托类来完成服务,并将执行结果封装处理。
  • 其实就是代理类为被代理类预处理消息、过滤消息并在此之后将消息转发给被代理类,之后还能进行消息的后置处理。代理类和被代理类通常会存在关联关系(即上面提到的持有的被带离对象的引用),代理类本身不实现服务,而是通过调用被代理类中的方法来提供服务。

简单说就是用多个接口多个类来代替一个代码冗杂的类实现一个功能。

这其实对应着面向对象的设计原则,如果熟悉设计模式的同学会很容易理解,所以博主建议在学习代理模式之前去了解一下设计模式,尤其是面向对象七大原则

静态代理

  • 创建一个接口,然后创建被代理的类实现该接口并且实现该接口中的抽象方法。之后再创建一个代理类,同时使其也实现这个接口。在代理类中持有一个被代理对象的引用,而后在代理类方法中调用该对象的方法。

我们举个栗子,假设我要去西安旅游,要做的事情是,起床、吃饭、玩耍、睡觉这四件事

beanFactory.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: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
           https://www.springframework.org/schema/context/spring-context.xsd">
    <context:component-scan base-package="edu.xalead"/>    
    </bean>
</beans>

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>edu.xalead</groupId>
    <artifactId>Sping_Dome</artifactId>
    <version>1.0-SNAPSHOT</version>

   <properties>
       <spring-version>5.2.5.RELEASE</spring-version>
   </properties>
    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring-version}</version>
        </dependency>


        <!-- https://mvnrepository.com/artifact/org.springframework/spring-beans -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>${spring-version}</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring-version}</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring-version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
        </dependency>
    </dependencies>

</project>

Eat实体类

@Component
public class Eat {
    public void eating(){
        System.out.println("吃饭");
    }
}

Sleep实体类

@Component
public class Sleep {
    public void sleep(){
        System.out.println("睡觉");
    }
}

getUp实体类

@Component
public class getUp {
    public void getUp(){
        System.out.println("起床");
    }
}

MeInterface接口

@Component
public interface MeInterface {
    public void playInXian();
}

Me实体类

@Component("real_me")
public class Me implements MeInterface{
    public void playInXian() {
        System.out.println("在西安玩耍!!!");
    }
}

ProxyMe实体类

@Component("me")
public class ProxyMe implements MeInterface {

    @Resource
    private getUp getUp;
    @Resource
    private Eat eating;
    @Resource
    private Sleep sleep;
    @Resource(name = "real_me")
    private MeInterface me;
    public void playInXian() {
        getUp.getUp();
        eating.eating();
        me.playInXian();
        sleep.sleep();
    }
}

Spring测试类

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = MyConfig.class)
public class TestMe {
    @Resource(name = "me")
    private MeInterface me;

    @Test
    public void test1(){
        me.playInXian();
    }
}

在这里插入图片描述

  • 静态代理很容易完成对一个类的代理操作,但是它只能代理一个,如果代理的类有很多,就又要写很多重复的代码,非常的麻烦,所以有了动态代理,静态代理只是为了让我们明白什么是代理模式

动态代理(JDK动态代理)

  • 利用反射机制在运行时创建代理类
  • 接口被代理类不变,我们构建一个ProxyHandler类来实现invocationHandler接口

ProxyHandler类

我们需要注意,那三个类都可以注入,Object类我们没有办法注入,因为我们是动态代理,不只是代理一个类

@Data
@Component("handler")
public class ProxyHandler implements InvocationHandler {

    @Resource
    private Sleep sleep;
    @Resource
    private Eat eat;
    @Resource
    private getUp getUp;
    //不能注入,我们无法知道它是什么
    private Object object;

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object re = null;
        getUp.getUp();
        eat.eating();
        re = method.invoke(object,args);
        sleep.sleep();
        return re;
    }
}

测试:

newProxyInstance(类加载器,返回代理的接口集合,规范实现类)

RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = MyConfig.class)
public class TestMe {
    @Resource(name = "me")
    private MeInterface me;

    @Resource(name = "handler")
    private ProxyHandler proxyHandler;

    @Test
    public void test2(){
        proxyHandler.setObject(me);
        MeInterface meInterface = (MeInterface)Proxy.newProxyInstance(this.getClass().getClassLoader(), me.getClass().getInterfaces(), proxyHandler);
        meInterface.playInXian();
    }

    
}

在进行多个类的代理的时候我们可以发现我们经常会用到测试中的代码,所以我们将其封装起来

@Data
@Component
public class ProxyGenerator {
    private InvocationHandler invocationHandler;
    public Object createProxy(Object o){
        return Proxy.newProxyInstance(
                this.getClass().getClassLoader(),o.getClass().getInterfaces(),invocationHandler);
    }
}

我们在使用的时候,将ProxyGenerator类注入

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = MyConfig.class)
public class TestMe {
    @Resource(name = "me")
    private MeInterface me;

    @Resource(name = "handler")
    private ProxyHandler proxyHandler;

    @Resource
    private ProxyGenerator generator;
    
    public void test3(){
        proxyHandler.setObject(me);
        generator.setInvocationHandler(proxyHandler);
        MeInterface meInterface = (MeInterface)generator.createProxy(me);
        meInterface.playInXian();
    }
}

CGLIB代理

CGLIB是Spring自带的代理方法,它和JDK动态代理的区别是没有接口了,不需要返回代理的接口,对于我们初学者来说区别不大,但在开发的时候就不清楚了

ProxyMethod类

@Data
@Component("method")
public class ProxyMethod implements MethodInterceptor {
    @Resource
    private Sleep sleep;
    @Resource
    private Eat eat;
    @Resource
    private getUp getUp;
    private Object object;
    
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        Object re = null;
		getUp.getUp();
        eat.eating();
        re = method.invoke(object,args);
        sleep.sleep();
        return re;
    }
}

测试

//CGLIB代理
    @Resource
    private ProxyMethod proxyMethod;
    
 @Test
    public void test4(){
        proxyMethod.setObject(me);
        MeInterface meInterface = (MeInterface) Enhancer.create(me.getClass(), proxyMethod);
        meInterface.playInXian();
    }

若有误,请指教!

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值