设计模式---代理模式
1. 简介
代理模式(Proxy Pattern)是一种结构型设计模式,它能够为其他对象提供一个代理以控制对这个对象的访问。代理模式在不直接访问实际对象的情况下,提供了对目标对象的间接访问。通过引入一个代理对象来间接操作实际对象,可以在不改变实际对象代码的前提下,增加额外的功能操作,如访问控制、延迟初始化、日志记录、事务管理等。
代理模式通常分为几种类型:
-
静态代理:在代码编译时就已经确定代理类和目标类的关系。
-
动态代理:在代码运行时动态创建代理类。
-
JDK动态代理:利用
java.lang.reflect.Proxy
类和java.lang.reflect.InvocationHandler
接口在运行时创建代理对象。 -
CGLIB动态代理:使用CGLIB库在运行时生成目标类的子类作为代理对象(实现MethodInterceptor接口)。
-
2. 静态代理
静态代理是一种编译时就确定代理关系的方式。在静态代理中,代理类和目标类的关系是固定的,需要事先定义好。
实现步骤:
-
定义一个接口,目标对象和代理对象都实现这个接口。
-
创建目标对象的实现类。
-
创建代理类,实现相同的接口,并在内部持有目标对象的引用。
-
在代理类的方法中调用目标对象的方法,并在调用前后进行额外的操作。
示例代码:
package xiaokai.proxy.staticproxy;
/**
* Author:yang
* Date:2024-09-30 13:58
* Description:定义接口
*/
public interface IStaticProxyService {
String sayHello();
}
package xiaokai.proxy.staticproxy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* Author:yang
* Date:2024-09-29 10:47
* Description:基础方法实现类
*/
@Service
@Slf4j
public class DefaultService implements IStaticProxyService{
@Override
public String sayHello() {
log.info("DefaultService sayHello");
return "Hello ! ";
}
}
/**
* Author:yang
* Date:2024-09-29 11:00
* Description:代理服务--->对实现功能进行增强
*/
@Service
@Slf4j
public class ProxyService implements IStaticProxyService {
@Autowired
private DefaultService defaultService;
@Override
public String sayHello() {
log.info("ProxyService sayHello");
return defaultService.sayHello();
}
}
测试:
@SpringBootTest(classes = ProxyApplication.class)
@RunWith(SpringRunner.class)
public class ProxyTest {
@Autowired
private ProxyService proxyService;
@Test
public void testProxy() {
proxyService.sayHello();
}
}
结果:对基础方法进行了增强
ProxyService sayHello
DefaultService sayHello
优点:
-
代理类和目标类的关系在编译时就已经确定,结构清晰。
缺点:
-
每一个目标类都需要一个代理类,如果有很多目标类,会导致代理类的数量大量增加,增加系统的复杂性。
3. 动态代理
动态代理是在运行时创建代理对象的方式,不需要事先定义代理类。在Java中,动态代理通常是通过反射API实现的。
3.1 JDK
-
目标对象必须实现一个或多个接口。
-
创建一个实现了
InvocationHandler
接口的类。 -
使用
Proxy.newProxyInstance
方法在运行时动态创建代理对象。
示例代码:
package xiaokai.proxy.dynamicproxy.jdkservice;
/**
* Author:yang
* Date:2024-09-29 11:16]
* Description:问候服务接口
*/
public interface IGreetingService {
public String sayHello();
}
package xiaokai.proxy.dynamicproxy.jdkservice;
import lombok.extern.slf4j.Slf4j;
/**
* Author:yang
* Date:2024-09-29 11:17
* Description:
*/
@Slf4j
public class GreetingService implements IGreetingService {
@Override
public String sayHello() {
log.info("sayHello");
return "1";
}
}
package xiaokai.proxy.dynamicproxy.jdkservice;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* Author:yang
* Date:2024-09-29 11:23
*/
@Slf4j
public class JDKProxyService implements InvocationHandler {
private Object target;
public JDKProxyService(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log.info("before invoke method: " + method.getName());
Object result = method.invoke(target, args);
log.info("after invoke method: " + method.getName());
return result;
}
}
测试:
package xiaokai.proxy.dynamicproxy;
import xiaokai.proxy.dynamicproxy.jdkservice.GreetingService;
import xiaokai.proxy.dynamicproxy.jdkservice.IGreetingService;
import xiaokai.proxy.dynamicproxy.jdkservice.JDKProxyService;
import java.lang.reflect.Proxy;
/**
* Author:yang
* Date:2024-09-29 14:59
*/
public class Main {
public static void main(String[] args) {
// 原始对象
GreetingService original = new GreetingService();
JDKProxyService proxyService = new JDKProxyService(original);
// 创建代理对象
IGreetingService instance = (IGreetingService) Proxy.newProxyInstance(original.getClass().getClassLoader(), original.getClass().getInterfaces(), proxyService);
instance.sayHello();
}
}
结果:
JDKProxyService - before invoke method: sayHello
GreetingService - sayHello
JDKProxyService - after invoke method: sayHello
优点:
-
可以在运行时动态创建代理对象,不需要为每个目标类编写代理类。
-
目标类只需实现接口即可,代理类由JDK在运行时自动生成。
缺点:
-
只能代理实现了接口的类。
3.2 CGLIB
-
目标对象不需要实现接口,CGLIB通过生成目标对象的子类来实现代理。
-
创建一个实现了
MethodInterceptor
接口的类。 -
使用CGLIB的
Enhancer
类在运行时动态创建代理对象。
添加依赖:
<dependencies>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.1</version>
</dependency>
</dependencies>
示例代码:
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* Author:yang
* Date:2024-09-29 16:27
*/
public class CglibHandler implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("before invoke");
// 使用invokeSuper来调用父类方法
Object result = methodProxy.invokeSuper(o, objects);
System.out.println("after invoke");
return result;
}
}
public class CglibService{
public void sayHello() {
System.out.println("hello, I am CglibService");
}
}
测试:
public class Main {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(CglibService.class);
enhancer.setCallback(new CglibHandler());
CglibService instance = (CglibService) enhancer.create();
instance.sayHello();
}
}
结果:
before invoke
hello, I am CglibService
after invoke
优点:
-
可以代理没有实现接口的类。
-
通过继承目标类实现代理,不需要目标类实现接口。
缺点:
-
代理对象的创建比JDK动态代理稍微复杂一些。
-
需要引入第三方库CGLIB。
4. 应用场景
-
访问控制:在访问实际对象之前进行权限检查。
-
延迟初始化:在实际需要时才创建对象,提高系统性能。
-
日志记录:在调用目标对象方法前后记录日志信息。
-
事务管理:在方法调用前后添加事务处理逻辑。