【Spring】008-代理模式

一、概述

1、重要性

为什么要学习代理模式?因为这就是SpringAOP的底层原理!

面试必问:Spring AOP 与 Spring MVC;

 

2、代理模式的分类

静态代理;

动态代理;

 

3、什么是代理模式

参考:【Java设计模式】001-代理模式

 

二、静态代理

1、角色分析

抽象角色:

一般会使用接口或抽象类来解决(比如:租房);

 

真实角色:

被代理的角色(比如:房东);

 

代理角色:

代理角色,代理真是角色后,一般会做一些附属操作(比如:租房中介);

 

客户:

访问代理对象的人(比如:租客);

 

2、代码实现

第一步:新建一个Moudle,创建房东与中介的公共租房接口

package com.zibo.demo01;

//代理角色和被代理角色的公共接口,因为代理角色要帮被代理角色完成其需要做的事情
//出租接口,房东要出租房子,中介就要帮房东出租房子,所以出租房子这个事情是二者都要做的
//写一个公共接口使其二者都实现改接口,即可达到约束二者都做同一件事情的目的
public interface Rent {
    void rent();
}

第二步:创建房东类,实现租房接口

package com.zibo.demo01;

//被代理对象——房东,房东只做一件事情,就是出租房子,其他啥都不想干
//房东实现出租接口
public class Landlord implements Rent {
    @Override
    public void rent() {
        System.out.println("房东将房子出租!");
    }
}

第三步:创建中介代理类,实现租房接口

package com.zibo.demo01;

//代理角色,需要帮助被代理角色完成出租房子的事情
//中介肯定是要为房东出租房子的,所以必须和房东一起实现出租接口
//但是代理对象往往还有做一些其他附属操作,中介要做一些其他工作来完成房子出租的整个流程
public class Agent implements Rent {

    private Landlord landlord;

    public Agent() {
    }

    public Agent(Landlord landlord) {
        this.landlord = landlord;
    }

    @Override
    public void rent() {
        seeHouse();
        landlord.rent();
        signAContract();
        charge();
    }

    private void seeHouse(){
        System.out.println("中介带租客看房子!");
    }

    private void signAContract(){
        System.out.println("中介与租户签合同!");
    }

    private void charge(){
        System.out.println("中介向租户收取中介费!");
    }

    public Landlord getLandlord() {
        return landlord;
    }

    public void setLandlord(Landlord landlord) {
        this.landlord = landlord;
    }
}

第四步:创建客户租房测试类

package com.zibo.demo01;

//客户类,租户租房子去找中介租
public class User {
    public static void main(String[] args) {
        //实例化房东
        Landlord landlord = new Landlord();
        //实例化中介
        Agent agent = new Agent(landlord);
        //通过中介租房子
        agent.rent();
    }
}

测试结果:

中介带租客看房子!
房东将房子出租!
中介与租户签合同!
中介向租户收取中介费!

 

3、代理模式的好处

可以使真实角色(被代理角色)的业务更加纯粹;

公共业务交给代理代理角色,实现了业务分工;

公共业务发生扩展的时候,方便集中管理;

 

4、代理模式的缺点

一个真实角色(被代理角色)就会产生一个代理角色,代码量会翻倍!

 

三、深入静态代理

1、搭建环境

第一步:写一个UserService接口

package com.zibo.demo02;

public interface UserService {
    void save();
    void delete();
    void update();
    void query();
}

第二步:写一个UserService接口的实现类

package com.zibo.demo02;

public class UserServiceImpl implements UserService {
    @Override
    public void save() {
        System.out.println("保存了一个用户!");
    }

    @Override
    public void delete() {
        System.out.println("删除了一个用户!");
    }

    @Override
    public void update() {
        System.out.println("更新了一个用户!");
    }

    @Override
    public void query() {
        System.out.println("查询了一个用户!");
    }
}

第三步:写客户端类进行测试

package com.zibo.demo02;

public class Client {
    public static void main(String[] args) {
        UserService userService = new UserServiceImpl();
        userService.save();
    }
}

测试结果:

保存了一个用户!

 

2、问个问题

假如我们要在各个方法执行的前后做一些事情,我们该怎么办?难道要在所有方法前后修改代码?如此:

package com.zibo.demo02;

public class UserServiceImpl implements UserService {
    @Override
    public void save() {
        System.out.println("保存用户前要做的事情");
        System.out.println("保存了一个用户!");
        System.out.println("保存用户后要做的事情");
    }

    @Override
    public void delete() {
        System.out.println("删除用户前要做的事情");
        System.out.println("删除了一个用户!");
        System.out.println("删除用户后要做的事情");
    }

    @Override
    public void update() {
        System.out.println("更新用户前要做的事情");
        System.out.println("更新了一个用户!");
        System.out.println("更新用户后要做的事情");
    }

    @Override
    public void query() {
        System.out.println("查询用户前要做的事情");
        System.out.println("查询了一个用户!");
        System.out.println("查询用户后要做的事情");
    }
}

这岂不是很反人性!!!

忽然想起刚才演示的房东、中介和租客的代理模式,不由得我们心生一计!

 

3、正所谓:自己动手,丰衣足食

第一步:我们新建一个代理类UserAgent

package com.zibo.demo02;

public class UserAgent implements UserService {

    private UserService userService;

    public UserAgent() {
    }

    public UserAgent(UserService userService) {
        this.userService = userService;
    }

    private void doSthBefore(){
        System.out.println("在此之前做一些事情");
    }

    private void doSthAfter(){
        System.out.println("在此之后做一些事情");
    }

    private void doSthAfter2(){
        System.out.println("在此之后再做一些事情");
    }

    @Override
    public void save() {
        doSthBefore();
        userService.save();
        doSthAfter();
        doSthAfter2();
    }

    @Override
    public void delete() {
        doSthBefore();
        userService.delete();
        doSthAfter();
        doSthAfter2();
    }

    @Override
    public void update() {
        doSthBefore();
        userService.update();
        doSthAfter();
        doSthAfter2();
    }

    @Override
    public void query() {
        doSthBefore();
        userService.query();
        doSthAfter();
        doSthAfter2();
    }
}

第二步:将UserServiceImpl改回去

package com.zibo.demo02;

public class UserServiceImpl implements UserService {
    @Override
    public void save() {
        System.out.println("保存了一个用户!");
    }

    @Override
    public void delete() {
        System.out.println("删除了一个用户!");
    }

    @Override
    public void update() {
        System.out.println("更新了一个用户!");
    }

    @Override
    public void query() {
        System.out.println("查询了一个用户!");
    }
}

第三步:修改测试类

package com.zibo.demo02;

public class Client {
    public static void main(String[] args) {
        UserService service = new UserServiceImpl();
        UserAgent agent = new UserAgent(service);
        agent.save();
    }
}

测试结果:

在此之前做一些事情
保存了一个用户!
在此之后做一些事情
在此之后再做一些事情

发生了什么:

我们在没有修改UserServiceImpl类的情况下,对UserServiceImpl类的功能进行了增强!

这就是AOP的底层原理!

 

四、动态代理

1、静态代理有一个很严重的缺点

一个真实角色(被代理角色)就会产生一个代理角色,代码量会翻倍!

 

怎么办:

既然静态代理一个真实角色都会产生一个代理角色,导致代码量翻倍,那么我们让每一个代理角色自动生成不就不会代码量翻倍了吗!

 

2、动态代理概述

①动态代理和静态代理角色一样;

②动态代理的代理类是动态生成的,不是直接写好的;

③动态代理分为两大类:基于接口的动态代理、基于类的动态代理;

基于接口:JDK动态代理(我们本次使用的);

基于类:cglib;

java字节码:javasist;

 

3、需要了解两个类

Proxy类:

代理;

 

InvocationHandler类:

调用处理程序;

 

4、让我们来尝试一下实现动态代理

(我们在静态代理的代码上进行修改:租房案例)

第一步:写一个自动生成代理角色的类

package com.zibo.demo03;

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

public class ProxyInvocationHandler implements InvocationHandler {
    //第一步:指定代理谁
    private Object target;
    //无参构造
    public ProxyInvocationHandler() {
    }
    //带参构造
    public ProxyInvocationHandler(Object target) {
        this.target = target;
    }

    //第二步:生成代理对象
    public Object getProxyObject(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
    }
    /**
     * 第三步:执行代理对象的方法
     * @param proxy 代理对象(不可直接用没需用target)
     * @param method 所执行的方法
     * @param args 给所执行的方法传的参数
     * @return 返回执行结果
     * @throws Throwable 异常
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("方法执行之前可执行其他内容");
        Object invoke = method.invoke(target, args);
        System.out.println("方法执行之后可执行其他内容");
        return invoke;
    }
}

第二步:修改User测试类

package com.zibo.demo03;

//客户类,租户租房子去找中介租
public class User {
    public static void main(String[] args) {
        //实例化房东
        Landlord landlord = new Landlord();
        //生成代理对象:中介
        ProxyInvocationHandler handler = new ProxyInvocationHandler(landlord);
        Rent rent = (Rent) handler.getProxyObject();
        //使用代理对象
        rent.rent();
    }
}

测试结果:

方法执行之前可执行其他内容
房东将房子出租!
方法执行之后可执行其他内容

备注:

自动生成代理角色的类不太好理解,可根据其中注释理解,另一方面,这个类的写法基本是固定的;

这就是AOP的底层原理!

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值