Spring框架中用到的设计模式
Spring 框架作为 Java 领域广泛应用的企业级开发框架,巧妙运用了多种设计模式来提升代码的可维护性、可扩展性与灵活性。下面将为大家详细介绍 Spring 中常用的几种设计模式,并结合具体案例进行说明。
工厂模式
- 原理:工厂模式是一种创建型设计模式,它将对象的创建和使用分离。通过一个工厂类来负责创建对象,这样可以根据不同的条件创建不同类型的对象,提高代码的可维护性和可扩展性。
- 在 Spring 中的应用:Spring 通过
BeanFactory
或ApplicationContext
来创建对象。BeanFactory
是 Spring IoC 容器的最基本接口,它负责管理和创建 Bean。ApplicationContext
是BeanFactory
的子接口,提供了更多企业级功能,比如国际化、事件发布等 。在 Spring 配置文件(XML 或注解配置)中定义 Bean 的信息,Spring 容器在启动时会根据这些配置信息,使用工厂模式创建并管理 Bean。例如,在 XML 配置中定义<bean id="userService" class="com.example.service.UserService"/>
,Spring 容器会根据这个配置创建UserService
的实例。
案例:
XML 配置方式:在 applicationContext.xml
中定义一个 UserService 的 Bean。
<bean id="userService" class="com.example.service.UserService"/>
然后在代码中获取该 Bean:
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class Main {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");com.example.service.UserService userService = context.getBean("userService", com.example.service.UserService.class);userService.doSomething();}
}
注解配置方式:在 UserService
类上添加 @Service
注解标识它是一个服务 Bean,Spring 会自动扫描并创建实例。
import org.springframework.stereotype.Service;@Service
public class UserService {public void doSomething() {System.out.println("UserService is doing something.");}
}
接着在配置类上添加 @ComponentScan
注解来开启扫描,再通过依赖注入使用该 Bean。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;@SpringBootApplication
@ComponentScan(basePackages = "com.example")
public class Application implements CommandLineRunner {private final UserService userService;@Autowiredpublic Application(UserService userService) {this.userService = userService;}public static void main(String[] args) {SpringApplication.run(Application.class, args);}@Overridepublic void run(String... args) throws Exception {userService.doSomething();}
}
单例模式
- 原理:单例模式是一种创建型设计模式,确保一个类只有一个实例,并提供一个全局访问点来访问这个实例。这样可以节省系统资源,并且在需要全局统一状态或共享资源时非常有用。
- 在 Spring 中的应用:在 Spring 中,Bean 的默认作用域是单例(
singleton
)。这意味着在整个 Spring 容器的生命周期内,对于同一个 Bean 定义,只会创建一个实例。例如,配置一个UserService
Bean,无论在多少个地方注入UserService
,获取到的都是同一个实例。可以通过@Scope("singleton")
注解(或者在 XML 配置中指定scope="singleton"
)显式指定 Bean 的作用域为单例,不过默认就是单例,所以通常可以省略。
案例:
import org.springframework.stereotype.Service;@Service
public class CounterService {private int count = 0;public int increment() {return ++count;}
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class Application implements CommandLineRunner {private final CounterService counterService1;private final CounterService counterService2;@Autowiredpublic Application(CounterService counterService1, CounterService counterService2) {this.counterService1 = counterService1;this.counterService2 = counterService2;}public static void main(String[] args) {SpringApplication.run(Application.class, args);}@Overridepublic void run(String... args) throws Exception {System.out.println("Counter value from service1: " + counterService1.increment());System.out.println("Counter value from service2: " + counterService2.increment());// 输出结果中两个counterService的count值是连续递增的,说明它们是同一个实例}
}
策略模式
- 原理:策略模式是一种行为型设计模式,它定义了一系列算法,将每个算法都封装成一个独立的类,使得它们可以相互替换。这样可以让算法的变化独立于使用算法的客户端,提高代码的灵活性和可维护性。
- 在 Spring 中的应用:Spring 中的
Resource
接口及其实现类体现了策略模式。Resource
接口用于访问各种资源,比如文件系统资源、类路径资源、网络资源等。不同的实现类(如ClassPathResource
、FileSystemResource
、UrlResource
等)针对不同类型的资源,实现了不同的资源获取策略。例如,当需要读取类路径下的一个配置文件时,可以使用ClassPathResource
,而如果要读取文件系统中的文件,则可以使用FileSystemResource
。
案例:
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.util.FileCopyUtils;import java.io.IOException;
import java.io.InputStreamReader;public class ResourceExample {public static void main(String[] args) throws IOException {// 获取类路径下的资源Resource classPathResource = new ClassPathResource("config.properties");EncodedResource encodedClassPathResource = new EncodedResource(classPathResource, "UTF-8");String contentClassPath = FileCopyUtils.copyToString(new InputStreamReader(encodedClassPathResource.getResource().getInputStream()));System.out.println("ClassPath Resource Content: " + contentClassPath);// 获取文件系统中的资源Resource fileSystemResource = new FileSystemResource("C:/test.txt");EncodedResource encodedFileSystemResource = new EncodedResource(fileSystemResource, "UTF-8");String contentFileSystem = FileCopyUtils.copyToString(new InputStreamReader(encodedFileSystemResource.getResource().getInputStream()));System.out.println("FileSystem Resource Content: " + contentFileSystem);}
}
代理模式
- 原理:代理模式是一种结构型设计模式,为其他对象提供一种代理以控制对这个对象的访问。代理对象可以在客户端和目标对象之间起到中介作用,在不改变目标对象的情况下,增加一些额外的功能,比如日志记录、权限控制、事务管理等。
- 在 Spring 中的应用:Spring 的 AOP(面向切面编程)实现依赖于动态代理。Spring 提供了两种动态代理方式:JDK 的反射机制和 CGLIB 库。当目标对象实现了接口时,Spring 默认使用 JDK 动态代理来创建代理对象;当目标对象没有实现接口时,Spring 会使用 CGLIB 库来创建代理对象。通过 AOP,我们可以将一些通用的横切逻辑(如日志记录、事务管理)从业务逻辑中分离出来,以提高代码的可维护性和可复用性。例如,通过定义一个切面类,使用
@Before
、@After
等注解来指定在目标方法执行前后要执行的逻辑。
案例:
定义一个业务接口和实现类
public interface UserService {void addUser();
}
public class UserServiceImpl implements UserService {@Overridepublic void addUser() {System.out.println("Adding user...");}
}
定义切面类
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;@Aspect
@Component
public class UserServiceAspect {@Around("execution(* com.example.service.UserService.addUser(..))")public Object aroundAddUser(ProceedingJoinPoint joinPoint) throws Throwable {System.out.println("Before adding user");Object result = joinPoint.proceed();System.out.println("After adding user");return result;}
}
配置类开启 AOP 扫描
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;@Configuration
@ComponentScan(basePackages = "com.example")
@EnableAspectJAutoProxy
public class ApplicationConfig {
}
测试类
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class Main {public static void main(String[] args) {ApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class);UserService userService = context.getBean(UserService.class);userService.addUser();}
}
模板方法模式
- 原理:模板方法模式是一种行为型设计模式,它定义了一个算法的骨架,将一些步骤延迟到子类中实现。这样可以使得子类在不改变算法结构的情况下,重新定义算法中的某些步骤,提高代码的复用性。
- 在 Spring 中的应用:Spring 提供了
JdbcTemplate
、RedisTemplate
等模板对象。以JdbcTemplate
为例,它封装了 JDBC 操作的通用步骤,如获取数据库连接、创建 SQL 语句、执行 SQL、处理结果集等。开发人员只需要关注具体的 SQL 语句和业务逻辑,而不需要重复编写繁琐的 JDBC 操作代码。JdbcTemplate
中定义了一些抽象方法和钩子方法,子类可以根据需要重写这些方法来定制特定的操作。
案例:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Service;import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;@Service
public class UserService {private final JdbcTemplate jdbcTemplate;@Autowiredpublic UserService(JdbcTemplate jdbcTemplate) {this.jdbcTemplate = jdbcTemplate;}public List<User> getUsers() {String sql = "SELECT id, name, age FROM users";return jdbcTemplate.query(sql, new RowMapper<User>() {@Overridepublic User mapRow(ResultSet rs, int rowNum) throws SQLException {User user = new User();user.setId(rs.getInt("id"));user.setName(rs.getString("name"));user.setAge(rs.getInt("age"));return user;}});}
}class User {private int id;private String name;private int age;// getters and setterspublic int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}
}
适配器模式
- 原理:适配器模式是一种结构型设计模式,它将一个类的接口转换成客户希望的另一个接口。使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
- 在 Spring 中的应用:
- Spring AOP:在 Spring AOP 中,增强或通知(
Advice
)使用到了适配器模式。不同类型的通知(如前置通知BeforeAdvice
、后置通知AfterAdvice
等)需要适配到Advisor
接口,以便在目标方法执行的不同阶段执行相应的通知逻辑。 - Spring MVC:Spring MVC 中,使用适配器模式来适配
Controller
。DispatcherServlet
作为前端控制器,需要调用不同类型的Controller
(如基于注解的@Controller
、实现Controller
接口的类等)。通过适配器,DispatcherServlet
可以统一调用不同类型的Controller
,而不需要为每种类型的Controller
编写不同的调用逻辑。
- Spring AOP:在 Spring AOP 中,增强或通知(
案例(Spring MVC 中适配器模式示例):
定义一个简单的 Controller 接口和实现类
public interface Controller {String handleRequest();
}
public class HelloController implements Controller {@Overridepublic String handleRequest() {return "Hello, Spring MVC!";}
}
定义适配器类
import org.springframework.web.servlet.HandlerAdapter;
import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;public class CustomHandlerAdapter implements HandlerAdapter {@Overridepublic boolean supports(Object handler) {return handler instanceof Controller;}@Overridepublic ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {Controller controller = (Controller) handler;String result = controller.handleRequest();return new ModelAndView().addObject("message", result);}@Overridepublic long getLastModified(HttpServletRequest request) {return 0;}
}
配置 Spring MVC
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
import org.springframework.web.servlet.view.InternalResourceViewResolver;import java.util.HashMap;
import java.util.Map;@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.example")
public class WebConfig {@Beanpublic SimpleUrlHandlerMapping simpleUrlHandlerMapping() {Map<String, Object> urlMap = new HashMap<>();urlMap.put("/hello", new HelloController());SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();mapping.setUrlMap(urlMap);return mapping;}@Beanpublic HandlerAdapter customHandlerAdapter() {return new CustomHandlerAdapter();}@Beanpublic ViewResolver internalResourceViewResolver() {InternalResourceViewResolver resolver = new InternalResourceViewResolver();resolver.setPrefix("/WEB-INF/views/");resolver.setSuffix(".jsp");return resolver;}
}
这样,DispatcherServlet
就能通过CustomHandlerAdapter
适配并调用HelloController
的方法。
综上所述,Spring 框架通过合理运用工厂模式、单例模式、策略模式、代理模式、模板方法模式和适配器模式等,让代码结构更加清晰,也更便于开发者进行扩展和维护,为企业级应用开发提供了坚实的基础和高效的开发方式。