使用代理模式进行开发的Spring Boot应用

前言

代理模式是一种结构型设计模式,它允许通过代理对象来控制对真实对象的访问。代理模式可以提供额外的功能,如延迟加载、访问控制、日志记录等。

在Spring Boot开发中,代理模式可以用于控制对对象的访问,并在访问前后添加额外的逻辑。

实现

开发例子

代理模式在权限管理中的应用

假设我们正在开发一个博客系统,系统中有两种用户角色:普通用户和管理员。为了保护敏感数据和功能,我们可以使用代理模式来实现权限管理。

首先,我们创建一个名为BlogService的接口,该接口定义了博客相关的操作方法。然后,我们创建一个名为BlogServiceImpl的真实对象,该对象实现了BlogService接口。最后,我们创建一个名为BlogServiceProxy的代理对象,该对象实现了BlogService接口,并在操作方法中添加权限验证的逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public interface BlogService {
void createBlog(Blog blog);
void deleteBlog(long id);
}

public class BlogServiceImpl implements BlogService {
@Override
public void createBlog(Blog blog) {
// 实现创建博客的逻辑
}

@Override
public void deleteBlog(long id) {
// 实现删除博客的逻辑
}
}

public class BlogServiceProxy implements BlogService {
private BlogService blogService;
private User currentUser;

public BlogServiceProxy(BlogService blogService, User currentUser) {
this.blogService = blogService;
this.currentUser = currentUser;
}

@Override
public void createBlog(Blog blog) {
if (currentUser.isAdmin()) {
blogService.createBlog(blog);
} else {
throw new UnauthorizedAccessException("Only admins can create blogs");
}
}

@Override
public void deleteBlog(long id) {
if (currentUser.isAdmin()) {
blogService.deleteBlog(id);
} else {
throw new UnauthorizedAccessException("Only admins can delete blogs");
}
}
}

在使用代理模式的时候,我们可以通过BlogServiceProxy对象来调用博客相关的操作方法,代理对象会在执行前进行权限验证,保护敏感数据和功能。

代理模式在缓存管理中的应用

假设我们正在开发一个电商平台的商品管理模块。在该模块中,我们需要频繁地查询和展示商品信息,为了提高系统性能,我们可以使用代理模式来实现缓存管理。

首先,我们创建一个名为ProductService的接口,该接口定义了商品相关的操作方法。然后,我们创建一个名为ProductServiceImpl的真实对象,该对象实现了ProductService接口。最后,我们创建一个名为ProductServiceProxy的代理对象,该对象实现了ProductService接口,并在操作方法中添加缓存管理的逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public interface ProductService {
Product getProduct(long id);
}

public class ProductServiceImpl implements ProductService {
@Override
public Product getProduct(long id) {
// 实现根据ID查询商品的逻辑
}
}

public class ProductServiceProxy implements ProductService {
private ProductService productService;
private Map<Long, Product> productCache = new HashMap<>();

public ProductServiceProxy(ProductService productService) {
this.productService = productService;
}

@Override
public Product getProduct(long id) {
if (productCache.containsKey(id)) {
return productCache.get(id);
} else {
Product product = productService.getProduct(id);
productCache.put(id, product);
return product;
}
}
}


通过使用代理模式,我们可以在调用商品查询方法时,先从缓存中查找,如果缓存中不存在则调用真实对象的方法并将结果缓存起来,提高系统性能。

使用代理模式控制对敏感资源的访问

在Spring Boot应用中,我们可能需要对某些敏感资源的访问进行控制,例如需要进行身份验证、权限验证等。我们可以使用代理模式来实现这些访问控制。
使用代理模式进行身份验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public interface Resource {
void access();
}

public class ResourceImpl implements Resource {
private String name;

public ResourceImpl(String name) {
this.name = name;
}

@Override
public void access() {
System.out.println("Accessing resource: " + name);
}
}

public class ProxyResource implements Resource {
private Resource resource;
private String username;
private String password;

public ProxyResource(String name, String username, String password) {
this.resource = new ResourceImpl(name);
this.username = username;
this.password = password;
}

@Override
public void access() {
if (authenticate(username, password)) {
resource.access();
} else {
System.out.println("Access denied");
}
}

private boolean authenticate(String username, String password) {
// 身份验证的操作
return username.equals("admin") && password.equals("password");
}
}

在使用时,我们可以创建代理对象,并进行敏感资源的访问。

1
2
Resource resource = new ProxyResource("Sensitive Resource", "admin", "password");
resource.access();

通过以上示例,我们可以看到使用代理模式可以控制对敏感资源的访问,通过代理对象进行身份验证,从而实现访问控制。

使用代理模式进行延迟加载

在某些场景下,我们可能需要延迟加载某些资源,以提高应用的性能和效率。我们可以使用代理模式进行延迟加载。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public interface Image {
void display();
}

public class RealImage implements Image {
private String filename;

public RealImage(String filename) {
this.filename = filename;
loadFromDisk();
}

private void loadFromDisk() {
// 加载图片的操作
System.out.println("Loading image from disk: " + filename);
}

@Override
public void display() {
System.out.println("Displaying image: " + filename);
}
}

public class ProxyImage implements Image {
private RealImage realImage;
private String filename;

public ProxyImage(String filename) {
this.filename = filename;
}

@Override
public void display() {
if (realImage == null) {
realImage = new RealImage(filename);
}
realImage.display();
}
}


在使用时,我们可以创建代理对象,并进行延迟加载图片。

1
2
3
Image image = new ProxyImage("image.jpg");
image.display();

通过以上示例,我们可以看到使用代理模式可以实现延迟加载,只有在需要显示图片时才进行实际的加载操作,从而提高了应用的性能和效率。

访问控制

在Spring Boot开发中,我们可能需要对某些对象进行访问控制,以实现权限控制、安全验证等功能。使用代理模式,我们可以在访问对象时进行控制。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public interface AccessibleObject {
void access();
}

public class AccessibleObjectImpl implements AccessibleObject {
@Override
public void access() {
// 实现访问逻辑
}
}

public class AccessibleObjectProxy implements AccessibleObject {
private AccessibleObject object;
private String username;

public AccessibleObjectProxy(String username) {
this.username = username;
}

@Override
public void access() {
if (isAdminUser(username)) {
if (object == null) {
object = new AccessibleObjectImpl();
}

object.access();
} else {
throw new UnauthorizedAccessException();
}
}

private boolean isAdminUser(String username) {
// 实现判断用户是否为管理员的逻辑
}
}

通过代理模式,我们可以使用AccessibleObjectProxy类作为代理对象,并在调用访问方法前进行权限验证和安全控制。

1
2
3
AccessibleObject object = new AccessibleObjectProxy("admin");
object.access();

使用JDK动态代理

JDK动态代理允许我们在运行时为一个接口生成实现类,这对Spring Boot来说非常方便。

比如我们有一个UserService接口:

1
2
3
4
5
6
7
public interface UserService {

void addUser(User user);

void updateUser(User user);

}

然后我们提供一个实现类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Service
public class UserServiceImpl implements UserService {

@Override
public void addUser(User user) {
// add user logic
}

@Override
public void updateUser(User user) {
// update user logic
}

}

现在我们想为这个接口添加一个日志功能,可以使用代理来实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Component
public class UserServiceProxy implements InvocationHandler {

private UserService target;

public UserServiceProxy(UserService target) {
this.target = target;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

// log method invocation
log.info("Calling method: " + method.getName());

// invoke method
Object result = method.invoke(target, args);

// log result
log.info("Method result: " + result);

return result;
}

}

之后我们可以通过代理将日志功能加到服务中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@Service
public class UserServiceImpl implements UserService {

@Override
public void addUser(User user) {
// implementation
}

// other methods

}

@Service
public class ProxyFactory {

@Autowired
private UserService target;

@Bean
public UserService proxy() {
return (UserService) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new UserServiceProxy(target));
}

}

这样每次调用UserService接口方法时,都会附加日志功能。

使用CGLIB动态代理

对于没有实现接口的类,我们可以使用CGLIB动态代理。例如:

1
2
3
4
5
6
7
8
@Component
public class UserDao {

public void save(User user) {
// dao logic
}

}

同样我们可以为这个类添加通知功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Component
public class UserDaoProxy implements MethodInterceptor {

@Autowired
private UserDao target;

@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {

// log method invocation
log.info("Calling method: " + method.getName());

// invoke method
Object result = proxy.invoke(target, args);

// log result
log.info("Method result: " + result);

return result;
}

}

创建代理类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Component
public class ProxyFactory {

@Autowired
private UserDao target;

@Bean
public UserDao proxyDao() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(new UserDaoProxy());
return (UserDao) enhancer.create();
}

}

总结

享元模式是一种在Spring Boot开发中非常有用的设计模式,它可以帮助我们减少对象的创建,提高系统的性能和资源利用率。

本篇文章由AI生成