Java核心篇,二十三种设计模式(一),结构型——代理模式

一、代理模式

1.何为代理模式

一种java设计模式,它的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等

代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务

简单的说就是,我们在访问实际对象时,是通过代理对象来访问的,代理模式就是在访问实际对象时引入一定程度的间接性,因为这种间接性,可以附加多种用途

2.代理模式的优点

  • 优点一:可以隐藏真实目标类的实现;
  • 优点二:可以实现客户与真实目标类间的解耦,在不修改真实目标类代码的情况下能够做一些额外的处理

二、静态代理

1.定义

由程序员创建或特定工具自动生成源代码,也就是在编译时就已经将接口,被代理类,代理类等确定下来。在程序运行之前,代理类的.class文件就已经生成

2.简单实现

①学生接口

public interface IStudent {
    public abstract void giveMoney();
}

②学生实现类

public class StudentImpl implements IStudent{

    private String name;

    public StudentImpl(String name){
        this.name = name;
    }


    @Override
    public void giveMoney() {
        System.out.println(name + "同学上交了学费");
    }
}

③学生代理类

public class StudentProxy implements IStudent {

    private IStudent student;

    public StudentProxy(IStudent student){
        this.student = student;
    }

    @Override
    public void giveMoney() {
        //交学费以前可以做的事
        before();
        student.giveMoney();
        //交学费以后可以做的其他事
        after();
    }

    public void before(){
        System.out.println("班长替班主任向全班同学收取学费");
    }

    public void after(){
        System.out.println("班长将全班同学的学费交给了班主任");
    }
}

④使用静态代理

public class Realize {

    private static IStudent student;
    private static IStudent studentProxy;

    public static void main(String[] args) {
        student = new StudentImpl("小明");
        studentProxy = new StudentProxy(student);
        studentProxy.giveMoney();
    }
}

三、动态代理

1.定义

在程序运行时,运用反射机制动态创建而成,动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码

2.基于接口的动态代理

(1)如何创建代理对象

使用Proxy类中的newProxyInstance方法

(2)创建代理对象的要求:

被代理类最少实现一个接口,如果没有则不能使用

(3)newProxyInstance方法的参数:

ClassLoader: 类加载器

它是用于加载代理对象字节码的。和被代理对象使用相同的类加载器

固定写法:

[对象].getClass().getClassLoader()
[类].class.getClassLoader()

Class[]: 字节码数组

它是用于让代理对象和被代理对象有相同方法

固定写法:

[对象].getClass().getInterfaces();
[类].class.getInterfaces();

InvocationHandler: 用于提供增强的代码

它是让我们写如何代理。我们一般都是些一个该接口的实现类,通常情况下都是匿名内部类(不是必须的),此接口的实现类都是谁用谁写

new InvocationHandler(){
            /**
             * 执行被代理对象的任何方法都会经过invoke方法
             * 方法参数的含义:
             * proxy ===> 代理对象的引用
             * method ===> 当前执行的方法
             * args ===> 当前执行方法所需的参数
             * return ===> 和被代理对象方法有相同的返回值
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                ......
            }
};

(4)代码实现(方法一:匿名内部类)

①学生接口

public interface IStudent {
    public abstract void giveMoney();
}

②接口实现类

public class StudentImpl implements IStudent{

    private String name;

    public StudentImpl(String name){
        this.name = name;
    }

    @Override
    public void giveMoney() {
        System.out.println(name + "同学上交了学费");
    }
}

③实现动态代理并执行

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

public class Realize {

    private static IStudent student;

    public static void before(){
        System.out.println("班长替班主任向全班同学收取学费");
    }

    public static void after(){
        System.out.println("班长将全班同学的学费交给了班主任");
    }

    public static void main(String[] args) {
        student = new StudentImpl("小明");
        IStudent iStudent = (IStudent) Proxy.newProxyInstance(student.getClass().getClassLoader(), student.getClass().getInterfaces(), new InvocationHandler() {
            /**
             * 执行被代理对象的任何方法都会经过invoke方法
             * 方法参数的含义:
             * proxy ===> 代理对象的引用
             * method ===> 当前执行的方法
             * args ===> 当前执行方法所需的参数
             * return ===> 和被代理对象方法有相同的返回值
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                before();
                /**
                 * invoke方法的参数
                 * 第一个为被代理对象
                 * 第二个为方法参数-args
                 */
                Object obj = method.invoke(student, args);
                after();
                return obj;
            }
        });

        iStudent.giveMoney();
    }
}

(5)代码实现(方法二:自定义类,实现动态代理接口)

①学生接口

public interface IStudent {
    public abstract void giveMoney();
}

②接口实现类

public class StudentImpl implements IStudent{

    private String name;

    public StudentImpl(String name){
        this.name = name;
    }

    @Override
    public void giveMoney() {
        System.out.println(name + "同学上交了学费");
    }
}

③动态代理接口的实现类

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

public class StudentProxy<T> implements InvocationHandler {

    private T target;

    public StudentProxy(T target){
        this.target = target;
    }

    public void before(){
        System.out.println("班长替班主任向全班同学收取学费");
    }

    public void after(){
        System.out.println("班长将全班同学的学费交给了班主任");
    }

    /**
     * 执行被代理对象的任何方法都会经过invoke方法
     * 方法参数的含义:
     * proxy ===> 代理对象的引用
     * method ===> 当前执行的方法
     * args ===> 当前执行方法所需的参数
     * return ===> 和被代理对象方法有相同的返回值
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        /**
         * invoke方法的参数
         * 第一个为被代理对象
         * 第二个为方法参数-args
         */
        Object obj = method.invoke(target, args);
        after();
        return obj;
    }
}

④使用动态代理

import java.lang.reflect.Proxy;

public class Realize {

    private static IStudent student;
    private static StudentProxy studentProxy;

    public static void main(String[] args) {
        student = new StudentImpl("小明");
        studentProxy = new StudentProxy(student);

        /**
         * newProxyInstance方法的参数:
         * ClassLoader: 类加载器
         * 它是用于加载代理对象字节码的。和被代理对象使用相同的类加载器
         * Class[]: 字节码数组
         * 它是用于让代理对象和被代理对象有相同方法
         * InvocationHandler: 用于提供增强的代码
         * 它是让我们写如何代理。我们一般都是些一个该接口的实现类,通常情况下都是匿名内部类(不是必须的),此接口的实现类都是谁用谁写
         */
        IStudent iStudent = (IStudent) Proxy.newProxyInstance(student.getClass().getClassLoader(), student.getClass().getInterfaces(), studentProxy);
        iStudent.giveMoney();
    }
}

3.基于子类的动态代理

1.确保项目是Maven工程

2.在pom.xml中设置打包方式

<packaging>jar</packaging>

3.在pom.xml中导入依赖坐标

<dependencies>
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>2.1_3</version>
        </dependency>
</dependencies>

4.最终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>org.example</groupId>
    <artifactId>demo19</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <dependencies>
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>2.1_3</version>
        </dependency>
    </dependencies>


</project>

5.依赖与使用

依赖类 ===> Enhancer

使用方法 ===> Enhancer.create()

6.要求

被代理类不能是最终类

7.参数

Class ===> 用于指定被代理对象的字节码

Callback ===> 用于提供增强的代码;我们一般写该接口的子接口实现类——>MethodInterceptor

8.代码实现(方法一:匿名内部类)

(1)学生接口

public interface IStudent {
    public abstract void giveMoney();
}

(2)学生接口实现类

public class StudentImpl implements IStudent{

    private String name;

    /**
     * 当实现CGLIB代理的时候,如果目标对象没有定义无参构造函数,当Enhancer对象create代理对象的时候,就会报错
     * 因为Spring通过CGLIB生成代理类对象时,并没有将目标对象的构造函数的参数及其类型进行设定,导致了CGLIB在生成代理类对象时,会使用默认的构造函数生成,结果目标对象类没有默认构造函数,CGLIB生成子类时,也没有加入默认构造函数,所以,异常的发生成为必然
     * 所以这里需要加入默认构造函数
     */
    public StudentImpl(){
        this.name = null;
    }

    public StudentImpl(String name){
        this.name = name;
    }

    @Override
    public void giveMoney() {
        System.out.println(name + "同学上交了学费");
    }
}

(3)动态代理的实现

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

import java.lang.reflect.Method;

public class Realize {

    private static IStudent student;

    public static void before(){
        System.out.println("班长替班主任向全班同学收取学费");
    }

    public static void after(){
        System.out.println("班长将全班同学的学费交给了班主任");
    }

    public static void main(String[] args) {

        student = new StudentImpl("小明");

        /**
         * Class ===> 用于指定被代理对象的字节码
         * Callback ===> 用于提供增强的代码;我们一般写该接口的子接口实现类——>MethodInterceptor
         */
        IStudent iStudent = (IStudent) Enhancer.create(student.getClass(), new MethodInterceptor() {
             /**
              * 执行被代理对象的任何方法都会经过该方法
              * o ===> 代理对象的引用
              * method ===> 当前执行的方法
              * objects ===> 当前执行方法所需的参数
              * methodProxy ===> 当前执行方法的代理对象
              * return ===> 和被代理对象方法有相同的返回值
              */
             @Override
             public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                 before();
                 Object obj = method.invoke(student);
                 after();
                 return obj;
             }
        });

         iStudent.giveMoney();
    }
}

9.代码实现(方法二:自定义接口实现类)

(1).学生接口

public interface IStudent {
    public abstract void giveMoney();
}

(2)学生接口实现类

public class StudentImpl implements IStudent{

    private String name;

    /**
     * 当实现CGLIB代理的时候,如果目标对象没有定义无参构造函数,当Enhancer对象create代理对象的时候,就会报错
     * 因为Spring通过CGLIB生成代理类对象时,并没有将目标对象的构造函数的参数及其类型进行设定,导致了CGLIB在生成代理类对象时,会使用默认的构造函数生成,结果目标对象类没有默认构造函数,CGLIB生成子类时,也没有加入默认构造函数,所以,异常的发生成为必然
     * 所以这里需要加入默认构造函数
     */
    public StudentImpl(){
        this.name = null;
    }

    public StudentImpl(String name){
        this.name = name;
    }

    @Override
    public void giveMoney() {
        System.out.println(name + "同学上交了学费");
    }
}

(3)学生代理类

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

public class StudentProxy<T> implements MethodInterceptor {

    private T target;

    public StudentProxy(T target){
        this.target = target;
    }

    public void before(){
        System.out.println("班长替班主任向全班同学收取学费");
    }

    public void after(){
        System.out.println("班长将全班同学的学费交给了班主任");
    }

    /**
     * 执行被代理对象的任何方法都会经过 intercept方法
     * 方法参数的含义:
     * o ===> 代理对象的引用
     * method ===> 当前执行的方法
     * objects ===> 当前执行方法所需的参数
     * return ===> 和被代理对象方法有相同的返回值
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        before();
        Object obj = method.invoke(target);
        after();
        return obj;
    }
}

(4)使用动态代理

import net.sf.cglib.proxy.Enhancer;

public class Realize {

    private static IStudent student;
    private static StudentProxy<IStudent> studentProxy;

    public static void main(String[] args) {

        student = new StudentImpl("小明");
        studentProxy = new StudentProxy<IStudent>(student);

        /**
         * Class ===> 用于指定被代理对象的字节码
         * Callback ===> 用于提供增强的代码;我们一般写该接口的子接口实现类——>MethodInterceptor
         */
        IStudent iStudent = (IStudent) Enhancer.create(student.getClass(), studentProxy);
        iStudent.giveMoney();
    }
}

 

posted @ 2020-06-26 19:34  IT蓝月  阅读(144)  评论(0编辑  收藏  举报
Live2D