SpringAOP代理模式详解

SpringAOP

1. 主要内容


在这里插入图片描述

2. 代理模式


  • 代理模式在Java开发中是一种比较常见的设计模式,设计目的旨在为服务类与客户类之间插入其他功能,插入的功能对于调用者是透明的,起到伪装控制的作用,如租房的例子:房客、中介、房东,对应于代理模式中即:客户类、代理类、委托类(被代理类)

  • 为某一个对象(委托类)提供一个代理(代理类),用来控制对这个对象的访问。委托类和代理类有一个共同的父类或父接口,代理类会对请求做预处理,过滤,将请求分配给指定对象。

  • 生活中常见的代理情况:租房中介、婚庆公司等代理模式的两个设计原则:

    • 1.代理类与委托类具有相似的行为(共同)
    • 2.代理类增强委托类的行为

在这里插入图片描述

  • 常用的代理模式
    • 静态代理
    • 动态代理

3. 静态代理


  • 某个对象提供一个代理,代理角色固定,以控制对这个对象的访问,代理类和委托类有共同的父类或父接口这样在任何使用委托类对象的地方都可以用代理对象替代。代理类负责请求的预处理,过滤、将请求分派给委托类处理、以及委托类执行完请求后的后续处理。
3.1 代理的三要素

  • a、有共同的行为(结婚) ,接口

  • b. 目标角色(新人) 实现行为

  • c、代理角色(婚庆公司) —实现行为增强目标对象行为

3.2 静态代理的特点

  • 1、目标角色陶定

  • 2、在应用程序执行前就得到目标角色

  • 3、代理对象会增强目标对象的行为

  • 4,有可能存在多个代理引起类爆炸" (缺点)

3.3 实现
3.3.1 例1
/**
 * 接口
 * @author lms
 * @date 2021-10-03 - 21:11
 * 实现静态代理的方式:代理人和代理必须有相同的行为(方法),
 * 即代理帮代理人去执行代理人的行为,并可以额外的做一些事情
 */
public interface RentHouse {
    public void rent();
}
// 目标对象
public class Person implements RentHouse {
    @Override
    public void rent() {
        System.out.println("张三想要租房子,预算1000..........");
    }
}
// 代理类
public class Proxy implements RentHouse {
    // 目标对象(不推荐使用构造器的方式进行目标对象的注入)
    private RentHouse target;

    // 不推荐使用构造器注入的方式
    //    public Proxy(RentHouse target) {
    //        this.target = target;
    //    }

    public void setTarget(RentHouse target) {
        this.target = target;
    }

    @Override
    public void rent() {
        System.out.println("正在紧锣密鼓的找房子............");
        target.rent();
        System.out.println("签合同...............");
    }
}
// 主启动类
public class Starter {
    public static void main(String[] args) {
        Person person = new Person();
        // 代理帮person这个人进行租房子
        Proxy proxy = new Proxy();
        proxy.setTarget(person);
        proxy.rent();
    }
}
3.3.2 例2
public interface UserService {
    public void add();
    public void delete();
    public void update();
    public void query();
}
public class UserserviceImpl implements UserService{
    @Override
    public void add() {
        System.out.println("=========add=========");
    }
    @Override
    public void delete() {
        System.out.println("=========delete=========");
    }
    @Override
    public void update() {
        System.out.println("=========update=========");
    }

    @Override
    public void query() {
        System.out.println("=========query=========");
    }
}
// 给目标方法进行打印日志,使用代理类的方式
public class UserServiceProxy implements UserService {

    private UserService userService;

    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    @Override
    public void add() {
        log();
        userService.add();
    }

    @Override
    public void delete() {
        log();
        userService.delete();
    }

    @Override
    public void update() {
        log();
        userService.update();
    }

    @Override
    public void query() {
        log();
        userService.query();
    }

    // 给每个方法添加一个日志功能
    public void log() {
        System.out.println("======log=======");
    }
}

4. 动态代理


  • 相比于静态代理,动态代理在创建代理对象上更加的灵活,动态代理类的字节码在程序运行时,由Java反射机制动态产生,它会根据需要,通过反射机制在程序运行期,动态的为目标对象创建代理对象,无需程序员手动编写它的源代码。动态代理不仅简化了编程工作,而且提高了软件系统的可扩展性,因为反射机制可以生成任意类型的动态代理类,代理的行为可以代理多个方法,即满足生产需要的同时又达到代码通用的目的。

  • 动态代理的两种实现方式:

    • 1.JDK动态代理
    • 2.CGLB动态代理
4.1 动态代理的特点

  • 目标对象不固定

  • 在应用程序执行时动态创建目标对系

  • 代理对象会增强目标对象的行为

4.2. JDK动态代理

  • 注: JDK动态代理的目标对象必须有接口实现

4.2.1. newProxylnstance
  • Proxy

    • **Proxy**类是专门完成代理的操作类,可以通过此类为一个或多个接口动态的生成实现类。
4.2.2 实现 InvocationHandler 接口
  • 重写invoke方法
4.2.3 实现
package com.liu.aop.demo3;

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

/**
 * @author lms
 * @date 2021-10-03 - 22:22
 * 代理类,不像静态代理中的代理类是一个固定的只能代理一个需要的代理类
 */
//调用该方法会自动生成代理类
public class ProxyInvocationHandlerPlus implements InvocationHandler {

    // 被代理的类
    private Object target;

    public void setTarget(Object target) {
        this.target = target;
    }

    // 得到代理类
    public Object getProxy() {
        /**
         * 参数1:类加载器,来定义需要被代理的类
         * 参数2:代理类需要实现被代理类的接口列表
         * 参数3:调度方法调用的调用处理函数(也就是InvocationHandler接口的实现类)
         */
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                this);
    }

    // 执行被代理类中的方法,并返回执行的结果
    // 该方法作用: 只要使用代理对象调用方法,该invoke方法就会被执行
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        log();

        // 通过method可以获取到当前执行的方法名
        // String name = method.getName();

        // 下面语句个人理解:invoke(执行)target对象中的method(是一个具体的接口方法)方法
        // 动态代理的本质:就是使用反射的机制实现
        Object result = method.invoke(target, args);

        log();
        return result;
    }

    // 打印日志()
    public void log() {
        System.out.println("====[debug]====");
    }
}
package com.liu.aop.demo3;

import com.liu.aop.demo2.UserService;
import com.liu.aop.demo2.UserserviceImpl;

/**
 * @author lms
 * @date 2021-10-03 - 22:16
 */
public class Starter {
    public static void main(String[] args) {
//        // 被代理类
//        RentHouse person = new Person();
//
//        ProxyInvocationHandler handler = new ProxyInvocationHandler();
//        // 设置handler中的被代理类的属性值
//        handler.setRentHouse(person);
//        // 通过反射的方式获取代理类
//        RentHouse proxy = (RentHouse) handler.getProxy();
//        // 通过代理类执行被代理的方法
//        proxy.rent();

        // 针对userService
        UserService userservice = new UserserviceImpl();
        // 获取代理类
        ProxyInvocationHandlerPlus handlerPlus = new ProxyInvocationHandlerPlus();
        handlerPlus.setTarget(userservice);
        // 获取代理对象
        UserService proxy = (UserService) handlerPlus.getProxy();
        proxy.query();
    }
}
4.3 CGLIB 动态代理

  • 使用继承的思想:创建一个代理类,代理类是目标类的子类,代理类对目标类中的方法进行重写。

  • JDK的动态代理机制只能代理实现了接口的类,因而没有实现接口的类就不能使用JDK的动态代理CGLIB是针对类来实现的动态代理,它的原理:是对指定的目标类生成一个子类,并覆盖父类(目标类)中的方法实现增强的行为,但是因为采用的是继承,所以目标类不能使用 final 进行修饰
4.3.1 cglib 依赖
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>
4.3.2 定义类
  • 实现 MethodInterceptor
package com.liu.aop.cglib;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * @author lms
 * @date 2021-10-04 - 9:20
 */
public class CglibInterceptor implements MethodInterceptor {

    // 目标类
    private Object target;

    // set注入目标类(需要被代理的类)
    public void setTarget(Object target) {
        this.target = target;
    }

    // 获取代理对象
    public Object getProxy() {
        // 通过Enhance对象的create()方法可以生成一个类,用于生成代理对象
        Enhancer enhancer = new Enhancer();
        // 设置父类(将目标类作为代理类的父类)
        enhancer.setSuperclass(target.getClass());
        // 设置拦截器,回调对象为本身对象(因为该对象已经实现了MethodInterceptor接口)
        enhancer.setCallback(this);
        // 生成一个代理类对象并返回
        return enhancer.create();
    }

    /**
     * 拦截器:
     *     1. 目标对象的方法调用
     *     2. 增强行为
     * @param o           由CGLIB动态生成的代理类实例
     * @param method      实体类所调用的被代理的方法引用(也就是student中的study()方法,
     *                    即调用哪个方法就是哪个)
     * @param objects     调用方法的参数值
     * @param methodProxy 生成的代理类对方法的代理引用
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        // 返回代理对象调用的方法
        // String result = method.getName();

        // 额外功能的增强
        System.out.println("先做到每天早起..........");

        // 执行目标对象中的方法,并传递目标方法的形参
        Object result = method.invoke(target, objects);

        // 额外的功能增强
        System.out.println("记得每天按时吃饭,锻炼,复习.........");

        return result;
    }
}
  • 目标类
public class Student {

    public void study(){
        System.out.println("每天都要努力学习Java............");
    }
}
  • 实现
public class Starter {
    public static void main(String[] args) {
        Student student = new Student();
        // 创建代理类
        CglibInterceptor cglibInterceptor = new CglibInterceptor();
        cglibInterceptor.setTarget(student);
        // 获取代理类对象
        Student proxy = (Student) cglibInterceptor.getProxy();
        // 调用目标类方法
        proxy.study();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值