● 为对象提供一个代理类,增强该对象的方法,控制对这个对象的访问
● 静态代理和动态代理:静态代理就是编译的时候就已经确定,而动态代理就是运行时才会生成
● 提供数据的缓存功能,避免数据库重复查询
public interface DataQuery {
String query(String key);
}
package com.hillky.desgin_learn.Proxy;
public class DataBaseQuery implements DataQuery{
@Override
public String query(String key) {
return "DataBaseQuery"+key;
}
}
package com.hillky.desgin_learn.Proxy.cacheProxy;
import lombok.NoArgsConstructor;
import java.util.HashMap;
public class CachingDataQueryProxy implements DataQuery{
private DataQuery dataQuery;
private static HashMap<String,String> cache=new HashMap<>();
public CachingDataQueryProxy(DataBaseQuery dataBaseQuery) {
this.dataQuery = dataBaseQuery;
}
public CachingDataQueryProxy() {
this.dataQuery=new DataBaseQuery();
}
@Override
public String query(String key) {
String result = cache.get(key);
if(result != null){
System.out.println("从缓存中取数据");
return result;
}
result = dataQuery.query(key);
System.out.println("从数据库取数据");
cache.put(key,result);
return result;
}
}
● 安全代理,可以实现访问控制、权限验证等安全相关功能。
package com.hillky.desgin_learn.Proxy.safeProxy;
public interface SensitiveDataQuery {
String queryData(String userId);
}
package com.hillky.desgin_learn.Proxy.safeProxy;
public class SensitiveDataQueryImpl implements SensitiveDataQuery{
@Override
public String queryData(String userId) {
return "SensitiveDataQuery form user :"+userId;
}
}
package com.hillky.desgin_learn.Proxy.safeProxy;
import lombok.Data;
@Data
public class SecurityProxy implements SensitiveDataQuery{
private SensitiveDataQuery sensitiveDataQuery;
private UserAuthenticator userAuthenticator;
public SecurityProxy() {
this.sensitiveDataQuery=new SensitiveDataQueryImpl();
}
@Override
public String queryData(String userId) {
if (userAuthenticator.hasPermission(userId)) {
return sensitiveDataQuery.queryData(userId);
} else {
return "Access Denied: Insufficient permission for user" + userId;
}
}
}
package com.hillky.desgin_learn.Proxy.safeProxy;
import java.util.Arrays;
import java.util.List;
public class UserAuthenticator {
private final List<String> authorizedUserIds;
public UserAuthenticator() {
// 模拟从数据库或配置文件中获取已授权的用户列表
authorizedUserIds = Arrays.asList("user1", "user2", "user3");
}
public boolean hasPermission(String userId) {
return authorizedUserIds.contains(userId);
}
}
● 用于需要延迟创建耗时或资源密集型对象
● 虚拟代理在初始时访问才创建实际对象,之后将直接使用该对象
package com.hillky.desgin_learn.Proxy.vitruralProxy;
public interface Image {
void display();
}
package com.hillky.desgin_learn.Proxy.vitruralProxy;
public class LargeImage implements Image{
private String imageUrl;
public LargeImage(String imageUrl) {
this.imageUrl = imageUrl;
}
@Override
public void display() {
System.out.println("Displaying image: " + imageUrl);
}
}
package com.hillky.desgin_learn.Proxy.vitruralProxy;
public class VirtualImageProxy implements Image{
private String imageUrl;
private LargeImage largeImage;
public VirtualImageProxy(String imageUrl) {
this.imageUrl = imageUrl;
}
@Override
public void display() {
if(largeImage==null){
largeImage=new LargeImage(imageUrl);
}
largeImage.display();
}
}
可以看到虚拟代理如何实现懒加载,以减少资源消耗和提高程序性能。
● 为本地对象提供与远程对象相同的接口,使得客户端可以透明地访问远程对象。需要做序列化反序列化,网络通信(不同语言的调用)
package com.hillky.desgin_learn.Proxy.remoteProxy;
public interface RemoteService {
String fetchData(String dataId);
}
package com.hillky.desgin_learn.Proxy.remoteProxy;
public class RemoteServiceImpl implements RemoteService{
@Override
public String fetchData(String dataId) {
return "Data from remote service: " + dataId;
}
}
package com.hillky.desgin_learn.Proxy.remoteProxy;
public class RemoteServiceProxy implements RemoteService{
private String remoteServiceUrl;
private RemoteService remoteService;
public RemoteServiceProxy(String remoteServiceUrl) {
this.remoteServiceUrl = remoteServiceUrl;
this.remoteService=new RemoteServiceImpl();
}
@Override
public String fetchData(String dataId) {
// 网络通信、序列化和反序列化等逻辑
System.out.println("Connecting to remote service at: " + remoteServiceUrl);
// 假设我们已经获取到远程服务的数据
String result = remoteService.fetchData(dataId);
System.out.println("Received data from remote service.");
return result;
}
}
请注意,为了简化代码,这里省略了实际的网 络通信、序列化和反序列化逻辑。
● 静态代理需要手动编写代理类,代理类与被代理类实现相同的接口,对被代理对象进行包装。在程序运行前,代理类的代码就已经生成,并在程序运 行时调用。静态代理的优点是简单易懂,缺点是需要手动编写代理类,代码复杂度较 高,且不易扩展。
● 动态代理是在程序运行时动态生成代理类,无需手动编写代理类,大大降低了代码的 复杂度。通过反射实现
package com.hillky.desgin_learn.Proxy.remoteProxy;
public interface RemoteService {
String fetchData(String dataId);
}
package com.hillky.desgin_learn.Proxy.remoteProxy;
public class RemoteServiceImpl implements RemoteService{
@Override
public String fetchData(String dataId) {
return "Data from remote service: " + dataId;
}
}
package com.hillky.desgin_learn.Proxy.jdkProxy;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.LinkedHashMap;
public class CacheInvocationHandler implements InvocationHandler {
private DataQuery dataQuery;
private HashMap<String,String> cache = new LinkedHashMap<>(256);
public CacheInvocationHandler(DataQuery databaseDataQuery) {
this.dataQuery = databaseDataQuery;
}
public CacheInvocationHandler() {
this.dataQuery=new DataBaseQuery();
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String result=null;
if(method.getName().equals("query")){
result = cache.get(args[0].toString());
if(result!=null){
System.out.println("数据从缓存重获取。");
return result;
}
result = (String) method.invoke(dataQuery, args);
cache.put(args[0].toString(),result);
return result;
}
// 当其他的方法被调用,不希望被干预,直接调用原生的方法
return method.invoke(dataQuery,args);
}
}
package com.hillky.desgin_learn.Proxy.jdkProxy;
import org.junit.jupiter.api.Test;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import static org.junit.jupiter.api.Assertions.*;
class CacheInvocationHandlerTest {
@Test
void test() {
// jdk提供的代理实现,主要是使用Proxy类来完成
// 1、classLoader:被代理类的类加载器
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// 2、代理类需要实现的接口数组
Class[] interfaces = new Class[]{DataQuery.class};
// 3、InvocationHandler
InvocationHandler invocationHandler = new CacheInvocationHandler();
DataQuery dataQuery = (DataQuery) Proxy.newProxyInstance(
classLoader, interfaces, invocationHandler
);
// 事实上调用query方法的使用,他是调用了invoke
String result = dataQuery.query("key1");
System.out.println(result);
System.out.println("--------------------");
result = dataQuery.query("key1");
System.out.println(result);
System.out.println("--------------------");
result = dataQuery.query("key2");
System.out.println(result);
System.out.println("++++++++++++++++++++++++++++++++++++");
// 事实上调用queryAll方法的使用,他是调用了invoke
result = dataQuery.queryAll("key1");
System.out.println(result);
System.out.println("--------------------");
result = dataQuery.queryAll("key1");
System.out.println(result);
System.out.println("--------------------");
result = dataQuery.queryAll("key2");
System.out.println(result);
System.out.println("--------------------");
}
}
● 在 Spring 中,AOP(面向切面编程)提供了一种有效的方式来对程序中的多个模块进行横切关注点的处理,例如日志、事务、缓存、安全等。从而实现对目标对象的增强
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-aopartifactId>
dependency>
@EnableAspectJAutoProxy
@SpringBootApplication
@EnableAspectJAutoProxy
public class DesginLearnApplication {
public static void main(String[] args) {
SpringApplication.run(DesginLearnApplication.class, args);
}
}
package com.hillky.desgin_learn.Proxy.aop;
public interface DataQuery {
String query(String queryKey);
}
package com.hillky.desgin_learn.Proxy.aop;
import org.springframework.stereotype.Component;
@Component
public class DataBaseQuery implements DataQuery{
@Override
public String query(String queryKey) {
System.out.println("正在从数据库查询数据");
return "result";
}
}
package com.hillky.desgin_learn.Proxy.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class CacheAspectj {
//定义切面
@Pointcut("execution(* com.hillky.desgin_learn.Proxy.aop.DataQuery.query(..))")
public void pointcut(){}
@Around("pointcut()")
public String around(ProceedingJoinPoint joinPoint){
Object[] args = joinPoint.getArgs();
String key = args[0].toString();
// 1、查询缓存,命中则返回
String result = Cache.get(key);
if(result != null){
System.out.println("数据从缓存中获取");
return result;
}
// 未命中则去数据库查询,实际上是调用被代理bean的方法
try {
result = joinPoint.proceed().toString();
// 如果查询有结果,进行缓存
Cache.put(key,result);
} catch (Throwable e) {
throw new RuntimeException(e);
}
return result;
}
}
package com.hillky.desgin_learn.Proxy.aop;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class Cache {
private static Map<String,String> map = new ConcurrentHashMap<>(256);
public static String get(String key){
return map.get(key);
}
public static void put(String key,String value){
map.put(key, value);
}
}
package com.hillky.desgin_learn.Proxy.aop;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
class CacheAspectjTest {
@Resource
private DataQuery dataQuery;
@Test
void test(){
dataQuery.query("key1");
dataQuery.query("key1");
dataQuery.query("key2");
}
}
代理和 AOP 是两个相关的概念,代理是 AOP 的一种实现方式,它们都可以 用于在程序中实现对目标对象的增强。区别在于,代理主要是针对单个对象的方法调 用进行增强,而 AOP 则是针对程序中多个模块的横切关注点进行增强。
动态代理是一种代理模式,它在运行时动态生成代理对象,而无需提前创建具体的代理类。
使用场景:
● 日志记录
● 性能监控
● 事务管理
● 权限验证
● 缓存
● aop
● 速率限制
一般都可以用aop方便实现
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-aopartifactId>
dependency>
@EnableAspectJAutoProxy
@SpringBootApplication
@EnableAspectJAutoProxy
public class DesginLearnApplication {
public static void main(String[] args) {
SpringApplication.run(DesginLearnApplication.class, args);
}
}
package com.hillky.desgin_learn.Proxy.aop;
public interface DataQuery {
String query(String queryKey);
}
package com.hillky.desgin_learn.Proxy.log;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LoggingAspect {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
//定义切面
@Pointcut("within(@org.springframework.web.bind.annotation.RestController *)")
public void restControllerMethods() {
}
@Before("restControllerMethods()")
public void logMethodCall(JoinPoint joinPoint){
String className = joinPoint.getSignature().getDeclaringTypeName();
String methodName = joinPoint.getSignature().getName();
logger.info("Entering method [{}.{}]", className, methodName);
}
@AfterReturning(pointcut = "restControllerMethods()", returning = "result")
public void logMethodReturn(JoinPoint joinPoint, Object result) {
String className = joinPoint.getSignature().getDeclaringTypeName();
String methodName = joinPoint.getSignature().getName();
logger.info("Exiting method [{}.{}], return value: {}", className,
methodName, result);
}
}
package com.hillky.desgin_learn.Proxy.log;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class SampleController {
@GetMapping("/hello")
public String hello() {
return "Hello, World!";
}
}
package com.hillky.desgin_learn.Proxy.aop;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
class CacheAspectjTest {
@Resource
private DataQuery dataQuery;
@Test
void test(){
dataQuery.query("key1");
dataQuery.query("key1");
dataQuery.query("key2");
}
}