Java设计模式之代理模式(静态代理、JDK代理和Cglib代理)

1、代理模式的相关概念

代理模式:
有时候,用于控制对实际对象的访问,通常把这层访问控制封装程一个新的代理对象来替代实际对象,交由客户对象直接返回,因此引入代理模式

优点:可以在目标对象实现的基础上,增强额外的功能操作,即拓展目标对象的功能
被代理的对象可以是远程对象、创建开销大的对象或者需要安全控制的对象

代理模式主要有三种:静态代理、动态代理(JDK代理)、Cglib代理

2、静态代理

使用静态代理需要定义接口或者父类,即被代理对象(目标对象)与代理对象一起实现相同的接口或者是继承相同的父类。

缺点:代理对象需要和目标对象实现一样的接口,所以会有很多代理类出现,同时一旦接口增加方法,目标对象和代理对象都需要维护。

步骤

  1. 定义一个接口
  2. 目标对象
  3. 使用静态代理方式,需要在代理对象中也实现相同的接口
  4. 调用的时候调用代理对象的方法来调用目标对象

注意:代理对象与目标对象要实现相同的接口,然后通过调用相同的方法来调用目标对象的方法

定义接口

public inteface Work {
    
    
	void work();
}

目标对象

public Worker implements Work{
    
    
	@Override
	public void work(){
    
    
		System.out.println("正在工作中……");
	}
}

代理类(静态代理)

public class WorkerProxy implements Work{
    
    
	private Work target; // 目标对象,通过接口来聚合
	public void setTarget(Work target){
    
    
		this.target= target;
	}
	@Override
	public void work(){
    
    
		System.out.println("开始代理前");
		target.work();
		System.out.println("代理结束")}
}

测试

public class TestStaticProxy{
    
    
	
	public static  void main(String args){
    
    
		// 1.创建目标对象
		Work worker = new Worker();
		
		// 2.创建代理对象,同时将目标对象传给代理对象
		WorkerProxy proxy = new WorkerProxy();
		proxy.setTarget(worker);

		// 3.通过代理对象调用目标对象方法
		proxy.work();
	}
}

3、JDK动态代理

原理
代理对象不需要实现接口,但是目标对象需要实现接口,否则不能用JDK动态代理

使用到的API:

  • java.lang.reflect.Proxy
  • java.lang.reflect.InvocationHandler接口

public static Class<?> getProxyClass(ClassLoader loader, Class<?> [] interfaces, InvocationHandlder handler)

步骤

  1. 需要创建一个接口
  2. 创建一个类继承上面的接口
  3. 实现InvocationHandlder接口,进行动态代理

接口
public abstract

public interface Work{
    
    
	 void work();
	 boolean isComplete(String name);
}

实现类

public class GeneralWorker implements Work{
    
    
	@Override
	public void work(){
    
    
		System.out.println("我正在做一般的工作");
	}

	@Override
	public boolean isComplete(String name){
    
    
		boolean complete = false;
		if(name.indexOf("搬运") > 0){
    
    
			complete = true;
		}else{
    
    
			complete = false;
		}
		return complete;
	}
}

JDK动态代理的生成代理对象的类

public class MyInvocationHandler implements InvocationHandler {
    
    
    private Object target; // 目标对象

    public MyInvocationHandler() {
    
    
    }

    public MyInvocationHandler(Object target) {
    
    
        this.target = target;
    }

    public Object getTarget() {
    
    
        return target;
    }

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
        System.out.println("起的比鸡都早");
        Object result = method.invoke(target, args);
        System.out.println("睡的比狗的晚");
        return result;
    }
}

代码测试

public class TestJDKProxy{
    
    
	
	@Test
	public void  testJDKProxy(){
    
    
		GeneralWorker target = new GeneralWorker();
        MyInvocationHandler handler = new MyInvocationHandler();
        handler.setTarget(target);
        // 这里强制转化的类必须是接口类型的,如果是GeneralWorker类型的话就会出现类型转换错误
        Work instance = (Work)Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), handler);
        instance.work(); 
	}
}

4、CGLIB动态代理

原理
静态代理JDK代理模式都需要目标对象实现一个接口,但有时目标对象只是一个普通的Java类,并没有实现任何接口,在这种情况下,可以使用目标对象的子类来实现代理这就是Cglib代理

Cglib代理也称为子类代理,他是在内存中构建一个子类对象从而实现目标对象功能创建。

Cglib包的底层是通过失宠字节码处理框架ASM来转换字节码生成新的子类

在进行Cglib 的测试前:
一般的Java项目需要导入的jar:

  • asm.jar
  • asm-commons.jar
  • asm-tree.jar
  • cglib-3.2.12.jar

Maven项目就容易一些,直接在.pom文件中配置:

<dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>compile</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.ow2.asm/asm -->
        <dependency>
            <groupId>org.ow2.asm</groupId>
            <artifactId>asm</artifactId>
            <version>8.0.1</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.ow2.asm/asm-commons -->
        <dependency>
            <groupId>org.ow2.asm</groupId>
            <artifactId>asm-commons</artifactId>
            <version>8.0.1</version>
        </dependency>
        <dependency>
            <groupId>org.ow2.asm</groupId>
            <artifactId>asm-tree</artifactId>
            <version>8.0.1</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/cglib/cglib -->
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.2.12</version>
        </dependency>

普通的类(可以不实现接口):
这个普通类不能是final类,因为final类无法继承,所以会java.lang.IllalArgumentException

package design.model.proxy;

public class GeneralBean {
    
    
    private String attribute;

    public void setAttribute(String attribute){
    
    
        this.attribute = attribute;

    }    public String getAttribute() {
    
    
        System.out.println("正在获取属性");
        return attribute;
    }
}

生成代理对象的工厂类

package design.model.proxy;

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

import java.lang.reflect.Method;

/**
 * 静态代理和JDK代理模式都需要
 * */
public class CglibProxyFactory implements MethodInterceptor {
    
    
    private Object target; // 维护一个目标对象

    public void setTarget(Object target) {
    
    
        this.target = target;
    }
    public  Object createProxy(){
    
    
    	// 创建一个工具类
        Enhancer enhancer = new Enhancer();
        // 设置服务
        enhancer.setSuperclass(target.getClass());
        设置回调函数
        enhancer.setCallback(this);
        // 创建子类并返回
        return enhancer.create();
    }
	
	// 重写MethodInterceptor接口的intercept()方法
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    
    
        System.out.println("Cglib代理模式:前置");
        Object invokeResult = method.invoke(target, objects);
        System.out.println("Cglib代理模式:后置");

        return invokeResult;
    }
}

代码测试

package design.model.proxy;

import org.junit.Test;

public class TestCglibProxy {
    
    
    @Test
    public void testCglib(){
    
    
        GeneralBean bean = new GeneralBean();
        bean.setAttribute("获取属性");
        CglibProxyFactory factory = new CglibProxyFactory();
        factory.setTarget(bean);
        GeneralBean proxy = (GeneralBean)factory.createProxy();
        String attribute = proxy.getAttribute();
    }
}


代理模式在安全性控制、线程同步控制、缓存执行结果、代理创建一些开销很大的对象等方法应用广泛。

猜你喜欢

转载自blog.csdn.net/Hicodden/article/details/110915699
今日推荐