在面试时,设计模式总是一个经典的问题。面试官希望你不仅能够背出模式的名字,还能结合实际代码进行深入分析,尤其是在Spring框架中的实现。Spring框架背后巧妙地使用了许多经典的设计模式,让我们来看看这些模式如何在Spring中发挥作用,并且教你如何通过实际代码展示这些设计模式在面试中的应用。
1. 单例模式:Spring容器中的VIP客户
单例模式确保一个类只有一个实例,并且该实例在整个应用中是共享的。Spring中,所有默认的Bean都是单例的。容器初始化时会创建一个实例,之后再也不会创建新的对象。
代码示例:
@Configuration
public class AppConfig {
@Bean
public UserService userService() {
return new UserServiceImpl();
}
}
分析:
@Bean注解的userService()方法返回一个UserServiceImpl实例。- 因为Spring默认使用单例模式,所以
UserService的实例会在容器启动时创建一次,之后每次请求都会返回同一个实例。 - 这样可以避免频繁创建对象,节省内存,提高性能。
2. 工厂模式:Spring的“定制工厂”
工厂模式通过工厂类来创建对象。在Spring中,BeanFactory和ApplicationContext就充当了“工厂”的角色,它们负责管理Bean的创建和生命周期。
代码示例:
public interface BeanFactory {
Object getBean(String beanName) throws BeansException;
boolean containsBean(String name);
boolean isSingleton(String name);
boolean isPrototype(String name);
Class<?> getType(String name);
}
分析:
BeanFactory是一个接口,定义了Spring容器如何获取和管理Bean的基本操作。getBean()方法用于返回指定名称的Bean实例,isSingleton()判断该Bean是否为单例。- 这种设计将Bean的创建和管理解耦,你不需要关心对象的创建过程,Spring容器会根据配置动态创建和管理这些对象。
实现示例:
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
implements ConfigurableListableBeanFactory {
protected Map<String, BeanDefinition> beanDefinitionMap =
new ConcurrentHashMap<>(256);
@Override
public Object getBean(String beanName) throws BeansException {
Object singleton = getSingleton(beanName);
if (singleton == null) {
singleton = createBean(beanName);
}
return singleton;
}
}
分析:
DefaultListableBeanFactory是BeanFactory接口的一个实现。- 通过
getBean()方法,它首先会尝试从缓存中获取已经存在的单例Bean(通过getSingleton())。如果找不到,它会创建一个新的Bean(通过createBean())。 DefaultListableBeanFactory会通过管理一个beanDefinitionMap来存储所有的Bean定义,这个Map包含了所有Bean的配置信息。
3. 代理模式:AOP中的“幕后英雄”
Spring的AOP(面向切面编程)是通过代理模式来增强目标对象的功能,比如事务管理、日志记录等。Spring可以通过JdkDynamicAopProxy和Cglib动态代理来创建代理对象。
代码示例:
@Aspect
@Component
public class LoggingAspect {
@Before("execution(* com.example.service.*.*(..))")
public void logBefore(JoinPoint joinPoint) {
System.out.println("Method " + joinPoint.getSignature().getName() + " is about to be called");
}
}
分析:
@Aspect注解标记了这个类为一个切面,它会对com.example.service包下的所有方法进行增强。@Before注解表示方法会在目标方法执行前执行。这里是日志记录增强,可以在方法执行前输出方法名称。- Spring通过代理模式,在目标方法执行前后插入增强逻辑(如日志记录、事务处理等),从而不改变原有的业务逻辑代码。
4. JdkDynamicAopProxy:Spring的代理超级英雄
Spring使用动态代理(基于JdkDynamicAopProxy)来在运行时生成代理类,这样我们就不需要手动为每个目标类创建代理对象了。代理对象负责调用目标方法,并且可以在调用时增加其他逻辑。
代码示例:
public class JdkDynamicAopProxy implements AopProxy {
private final AdvisedSupport advised;
public JdkDynamicAopProxy(AdvisedSupport advised) {
this.advised = advised;
}
@Override
public Object getProxy(ClassLoader classLoader) {
return Proxy.newProxyInstance(classLoader, advised.getTargetClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, advised.getTarget(), method, args);
return advised.getMethodInterceptor().invoke(invocation);
}
});
}
}
分析:
JdkDynamicAopProxy利用Java的反射机制,创建了一个代理对象。这个对象实现了目标对象的接口,并通过InvocationHandler来处理方法调用。Proxy.newProxyInstance()方法动态生成代理类,代理对象会将方法调用转发给目标对象,同时可以通过MethodInterceptor增强目标方法的行为(如事务、日志等)。
5. ApplicationListener:Spring的“耳朵”
ApplicationListener 是Spring事件机制中的一部分,类似于观察者模式。通过它,我们可以监听Spring容器中的各种事件,做出相应的处理。
代码示例:
@Component
public class EventListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
System.out.println("ApplicationContext is refreshed!");
}
}
分析:
ApplicationListener接口允许我们定义事件处理逻辑,在事件发生时执行特定的操作。- 这个例子中,
EventListener监听了ContextRefreshedEvent事件,每次 Spring 容器刷新时,onApplicationEvent()方法就会被调用。你可以在这个方法里编写自定义的处理逻辑。
面试官会怎么反应?
如果你能在面试中通过实际的代码示例来展示Spring如何实现设计模式,面试官肯定会对你产生浓厚的兴趣。你不仅能背出设计模式的定义,还能结合实际项目中Spring的实现,展示你对技术的深刻理解。
小结:设计模式,不仅要记住,还要灵活运用
通过这篇文章,我们展示了Spring框架如何使用设计模式来提高开发效率。Spring不仅仅是用这些模式来“装饰”代码,更通过它们让开发过程更加灵活和高效。
下次面试时,准备好这些设计模式的实际应用代码,让面试官看到你不仅掌握了理论,更能把它们灵活地运用到实际开发中!