代理模式的定义: 为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
以上为百度百科对代理模式的定义;通俗一点讲,java中的代理模式就是你想要访问一个目标对象,但是不直接访问它,而是通过它的代理对象访问(因为代理对象可以对目标对象做增强操作),这就是java的代理模式。示意图如下:
在java中,有三种代理模式,分别为静态代理,动态代理,cglib代理。
先创建一个父类或者接口,被代理对象和代理对象都应该继承父类或者实现接口。
创建接口Movie.java
public interface Movie {
void play();
}
创建实现类RealMovie.java
public class RealMovie implements Movie {
@Override
public void play() {
System.out.println("播放金刚大战哥斯拉");
}
}
创建代理类ProxyMovie.java
public class ProxyMovie implements Movie {
Movie movie;
public ProxyMovie(Movie movie) {
this.movie = movie;
}
@Override
public void play() {
System.out.println("电影还没开始,买点爆米花");
movie.play();
System.out.println("电影结束了,买个哥斯拉模型");
}
}
创建测试类Test.java
public class Test {
public static void main(String[] args) {
RealMovie movie = new RealMovie();
ProxyMovie proxyMovie = new ProxyMovie(movie);
proxyMovie.play();
}
}
运行结果
总结:在不改变RealMovie的前提下,使用ProxyMovie对其进行了增强。但是由于目标对象和代理对象都实现了同一个接口,一旦这个接口添加或者删除方法,那么代理对象和目标对象都要进行相应的操作,耦合度太高。
静态代理的应用:在使用实现Runnable接口实现多线程的时候,将Runnable接口的实现类的对象作为Thread的构造函数的参数;
为了解决静态代理的代理类必需实现接口的所有方法的问题,动态代理诞生了,java的动态代理有如下的特点:
1、代理对象不需要跟目标对象实现同一接口或继承同一个父类(解决了静态代理的痛点);
2、代理对象的生成利用jdk提供的api,在JVM内存中动态的构建代理对象。
创建接口Movie.java
public interface Movie {
void play();
}
创建实现类RealMovie.java
public class RealMovie implements Movie{
@Override
public void play() {
System.out.println("播放侏罗纪世界");
}
}
创建代理类ProxyMovie.java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class ProxyMovie implements InvocationHandler {
private Object movie;
public ProxyMovie(Object movie) {
this.movie = movie;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("电影还没开始,买瓶可乐");
method.invoke(movie, args);
System.out.println("电影还结束,买一只恐龙模型");
return null;
}
}
创建测试类Test.java
public class Test {
public static void main(String[] args) {
RealMovie realMovie = new RealMovie();
InvocationHandler proxyMovie = new ProxyMovie(realMovie);
// 创建代理对象
Movie movie = (Movie)Proxy.newProxyInstance(realMovie.getClass().getClassLoader(), realMovie.getClass().getInterfaces(), proxyMovie);
System.out.println(movie.getClass().getSimpleName());
movie.play();
}
}
运行结果
总结:在这里,代理类没有实现Movie接口,而是实现了InvocationHandler接口,然后重写了invoke方法,与Movie接口的耦合度降低。
在使用jdk动态代理时,被代理对象是实现了一个接口的,假如实际中被代理对象没有实现接口该怎么办呢,cglib动态代理便应运而生,原理是创建一个目标对象的子类对象,然后进行增强操作,所以被代理对象的类不能是final类型的(无法被继承,也就没有子类),被调用的方法也不能是final和static类型的(不会执行代理功能)。
在pom.xml文件中添加cglib依赖
<dependency>
<groupId>cglibgroupId>
<artifactId>cglibartifactId>
<version>3.3.0version>
dependency>
创建CglibService.java
public class CglibService {
public void movie() {
System.out.println("播放侏罗纪公园");
}
}
创建CglibServiceProxy.java类
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibServiceProxy implements MethodInterceptor {
private Object object;
public CglibServiceProxy(Object object) {
this.object = object;
}
public Object getProxyInstance() {
//创建代理类
Enhancer enhancer = new Enhancer();
//继承要被代理的类
enhancer.setSuperclass(object.getClass());
//设置回调函数
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("电影即将开始,大家有序进入影厅");
Object obj = method.invoke(object);
System.out.println("电影结束,大家离开电影院");
return obj;
}
}
创建CglibProxyTest.java
public class CglibProxyTest {
public static void main(String[] args) {
CglibService service = new CglibService();
CglibServiceProxy proxy = new CglibServiceProxy(service);
CglibService proxyInstance = (CglibService)proxy.getProxyInstance();
proxyInstance.movie();
}
}
运行结果
总结:由以上可以看出,使用cglib代理可以代理一些没有父类的目标对象,所以以后如果要代理没有实现接口的类,可以选择使用cglib代理。