代理模式
代理模式(Proxy Pattern)是一种结构性模式。代理模式为一个对象提供了一个替身,以控制对这个对象的访问。即通过代理对象访问目标目标对象,可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。
分类
主要分为静态代理、动态代理(JDK动态代理、CGLIB动态代理)。
博文推荐
案例描述
本例以教师教学为例。教师教学或发起随堂考试。
一、静态代理
静态代理是定义父类或者接口,然后被代理对象(即目标对象)与代理对象一起实现相同的接口或者是继承相同父类。代理对象与目标对象实现相同的接口,然后通过调用相同的方法来调用目标对象的方法。
- 特点:代理对象与目标对象(被代理对象)都需要实现接口。
- 优点:可不修改目标对象的功能,通过代理对象对目标功能扩展。
- 缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,一旦接口增加方法,目标对象与代理对象都要维护。
本例UML类图关系
具体代码实现
TeacherService(教师服务)
package com.lcq.proxypattern;
/**
* @Author: coder
* @CreateTime: 2023-03-11
* @Description: 教师服务
*/
public interface TeacherService {
void teach();
}
TeacherServiceImpl(教师服务实现类)
package com.lcq.proxypattern;
/**
* @Author: coder
* @CreateTime: 2023-03-11
* @Description: 教师服务实现类
*/
public class TeacherServiceImpl implements TeacherService{
@Override
public void teach() {
System.out.println("教书育人");
}
}
TeacherServiceProxy(教师服务代理)
package com.lcq.proxypattern;
/**
* @Author: coder
* @CreateTime: 2023-03-11
* @Description: 教师服务代理类
*/
public class TeacherServiceProxy implements TeacherService{
//目标对象(被代理对象)
private TeacherService target = null;
public TeacherServiceProxy(TeacherService target){
this.target = target;
}
@Override
public void teach() {
System.out.println("静态代理开始");
target.teach();//执行目标对象的方法
System.out.println("静态代理结束");
}
}
Client
package com.lcq.proxypattern;
import com.sun.javaws.Main;
/**
* @Author: coder
* @CreateTime: 2023-03-11
* @Description: 静态代理
*/
public class Client {
public static void main(String[] args) {
//目标对象(被代理对象)
TeacherService target = new TeacherServiceImpl();
//代理对象
TeacherService proxy = new TeacherServiceProxy(target);
System.out.println(proxy);
System.out.println(proxy.getClass());
proxy.teach();
}
}
运行结果
com.lcq.proxypattern.TeacherServiceProxy@2503dbd3 class
com.lcq.proxypattern.TeacherServiceProxy
静态代理开始
教书育人
静态代理结束
由测试可以看出通过代理类可以扩展目标对象方法,实现目标方法功能增强。
二、JDK动态代理
利用JDK的API动态的在内存中创建代理对象,主要利用反射中的import java.lang.reflect.Proxy
。
通过Proxy.newProxyInstance()
方法
Proxy.newProxyInstance(
ClassLoader loader,//目标对象类加载器
Class<?>[] interfaces,//目标对象的接口类型
InvocationHandler h)//执行目标对象的方法回调
- 特点:目标对象需要实现接口,而代理对象无需实现接口。当目标对象增加方法时,目标对象只需覆盖相应的方法,而动态代理无需覆盖。相比于静态代理而言,JDK动态代理更加灵活,但依旧违反了开闭原则。
本例UML类图关系
具体代码实现
TeacherService
package com.lcq.proxypattern.dynamic;
/**
* @Author: coder
* @CreateTime: 2023-03-11
* @Description: TODO
*/
public interface TeacherService {
/**
*
* @author coder
* @date 2023/3/11 15:57
* @description 教学
* @param
* @return
*/
void teach();
/**
*
* @author coder
* @date 2023/3/11 14:34
* @description 随堂考试
* @param subject 考试科目
* @return 返回考试科目相关信息
*/
String test(String subject);
}
TeacherServiceImpl
package com.lcq.proxypattern.dynamic;
/**
* @Author: coder
* @CreateTime: 2023-03-11
* @Description: 教师服务实现类
*/
public class TeacherServiceImpl implements TeacherService {
@Override
public void teach() {
System.out.println("教书育人");
}
@Override
public String test(String subject) {
System.out.println(subject+"随堂考试开始");
return "今日考试科目-"+subject;
}
}
ProxyFactory(代理工厂)
package com.lcq.proxypattern.dynamic;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @Author: coder
* @CreateTime: 2023-03-11
* @Description: JDK代理工厂
*/
public class ProxyFactory {
//目标对象(被代理对象)
private Object target;
public ProxyFactory(){
}
public ProxyFactory(Object target){
this.target = target;
}
public void setTarget(Object target){
this.target = target;
}
public Object getProxyInstance(){
return Proxy.newProxyInstance(
target.getClass().getClassLoader(), //目标对象类加载器
target.getClass().getInterfaces(), //目标对象的接口类型
new InvocationHandler() {//执行目标对象的方法回调
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("jdk动态代理开始");
Object returnVal = method.invoke(target, args);
System.out.println("jdk动态代理结束");
return returnVal;//目标对象方法返回值
}
});
}
}
Client
package com.lcq.proxypattern.dynamic;
/**
* @Author: coder
* @CreateTime: 2023-03-11
* @Description: JDK动态代理
*/
public class Client {
public static void main(String[] args) {
//目标对象(被代理对象)
TeacherService target = new TeacherServiceImpl();
//代理工厂
ProxyFactory proxyFactory = new ProxyFactory();
//设置代理对象
proxyFactory.setTarget(target);
//代理对象
TeacherService proxy = (TeacherService) proxyFactory.getProxyInstance();
System.out.println(proxy.getClass());
System.out.println("----------------");
proxy.teach();
System.out.println("----------------");
String math = proxy.test("数学");
System.out.println("----------------");
System.out.println("test方法返回值:"+math);
}
}
运行结果
class com.sun.proxy.$Proxy0
----------------
jdk动态代理开始
教书育人
jdk动态代理结束
----------------
jdk动态代理开始
数学随堂考试开始
jdk动态代理结束
----------------
test方法返回值:今日考试科目-数学
三、CGLIB动态代理
Cglib代理也叫作子类代理,它使目标对象不需要实现接口,是在内存中构建一个子类对象从而实现对目标对象功能扩展,有的也将Cglib代理归属到动态代理。
- 特点:目标对象与代理对象都无需实现接口。
- 相关jar包
特别注意:代理的类不能为final,否则报错java.lang.IllegalArgumentException ,如果目标对象的方法如果为final或static,那么就不会被拦截(即不会执行目标对象额外的业务方法)。
本例UML类图关系
具体代码实现如下
TeacherServiceImpl
package com.lcq.proxypattern.cglib;
/**
* @Author: coder
* @CreateTime: 2023-03-11
* @Description: 教师服务
*/
public class TeacherServiceImpl {
public void teach() {
System.out.println("教书育人");
}
public String test(String subject){
System.out.println(subject+"随堂考试开始");
return "今日考试科目-"+subject;
}
}
ProxyFactory
package com.lcq.proxypattern.cglib;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* @Author: coder
* @CreateTime: 2023-03-11
* @Description: cglib的动态代理
*/
public class ProxyFactory implements MethodInterceptor {
//目标对象
private Object target;
public ProxyFactory(Object target){
this.target = target;
}
//获取代理对象实例
public Object getProxyInstance(){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());//设置目标对象
enhancer.setCallback(this);//当前对象回调
return enhancer.create();//创建代理对象
}
@Override
public Object intercept(
Object o,//代理对象
Method method,//执行的方法
Object[] args,//方法参数
MethodProxy methodProxy//代理方法
) throws Throwable {
System.out.println("cglib动态代理开始");
Object returnVal = method.invoke(target, args);
System.out.println("cglib动态代理结束");
return returnVal;
}
}
Client
package com.lcq.proxypattern.cglib;
/**
* @Author: coder
* @CreateTime: 2023-03-11
* @Description: cglib动态代理
*/
public class Client {
public static void main(String[] args) {
//目标对象
TeacherServiceImpl target = new TeacherServiceImpl();
//代理对象
TeacherServiceImpl proxy = (TeacherServiceImpl) new ProxyFactory(target).getProxyInstance();
//执行目标对象方法
proxy.teach();
System.out.println("---------------------");
String subject = proxy.test("数学");
System.out.println("test方法返回值:"+subject);
}
}
运行结果
cglib动态代理开始
教书育人
cglib动态代理结束
---------------------
cglib动态代理开始
数学随堂考试开始
cglib动态代理结束
test方法返回值:今日考试科目-数学