1、代理模式的相关概念
代理模式:
有时候,用于控制对实际对象的访问,通常把这层访问控制封装程一个新的代理对象来替代实际对象,交由客户对象直接返回,因此引入代理模式
优点:可以在目标对象实现的基础上,增强额外的功能操作,即拓展目标对象的功能
被代理的对象可以是远程对象、创建开销大的对象或者需要安全控制的对象
代理模式主要有三种:静态代理、动态代理(JDK代理)、Cglib代理
2、静态代理
使用静态代理需要定义接口或者父类,即被代理对象(目标对象)与代理对象一起实现相同的接口或者是继承相同的父类。
缺点:代理对象需要和目标对象实现一样的接口,所以会有很多代理类出现,同时一旦接口增加方法,目标对象和代理对象都需要维护。
步骤:
- 定义一个接口
- 目标对象
- 使用静态代理方式,需要在代理对象中也实现相同的接口
- 调用的时候调用代理对象的方法来调用目标对象
注意:代理对象与目标对象要实现相同的接口,然后通过调用相同的方法来调用目标对象的方法
定义接口:
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)
步骤:
- 需要创建一个接口
- 创建一个类继承上面的接口
- 实现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();
}
}
代理模式在安全性控制、线程同步控制、缓存执行结果、代理创建一些开销很大的对象等方法应用广泛。