相信这个点也是面试常考的,面试无非两种,
一种是手撕( 一次面试设计模式手撕经历 )
一种就是结合框架问
前置知识
设计模式:
菜鸟教程教的还是非常详细的 墙裂推荐!!
www.runoob.com/design-patt…
整理的代码:
github.com/scwlkq/java…
Spring中的设计模式
代理模式
比如 鸡哥是个明星,他有个经纪人,合作的事情都是由经纪人代理执行
静态代理
静态代理中,我们对目标对象的每个方法的增强都是手动完成的(后面会具体演示代码),非常不灵活(比如接口一旦新增加方法,目标对象和代理对象都要进行修改)且麻烦(需要对每个目标类都单独写一个代理类)。 实际应用场景非常非常少,日常开发几乎看不到使用静态代理的场景。
代码:
我们定义一个用户服务类
public interface UserService {
String login(String username, String password);
}
给出它的实现:
public class UserServiceImpl implements UserService {
@Override
public String login(String username, String password) {
if("admin".equals(username) && "123456".equals(password)){
return "ok";
}
return "no";
}
}
这时候的代理类:
public class UserProxy implements UserService {
private final UserService userService;
public UserProxy(UserService userService) {
this.userService = userService;
}
@Override
public String login(String username, String password) {
//调用方法之前,我们可以添加自己的操作
System.out.println("before method login()");
String result = userService.login(username, password);
//调用方法之后,我们同样可以添加自己的操作
System.out.println("after method login()");
return result;
}
}
使用:
public class Main {
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
UserProxy userProxy = new UserProxy(userService);
System.out.println(userProxy.login("admin", "123456"));
}
}
针对每个服务 我们需要实现对应的方法的代理,很不方便
动态代理
相比于静态代理来说,动态代理更加灵活。我们不需要针对每个目标类都单独创建一个代理类,并且也不需要我们必须实现接口,我们可以直接代理实现类( CGLIB 动态代理机制)。
jdk代理
也就是说:你通过Proxy
类的 newProxyInstance()
创建的代理对象在调用方法的时候,实际会调用到实现InvocationHandler
接口的类的 invoke()
方法。 你可以在 invoke()
方法中自定义处理逻辑,比如在方法执行前后做什么事情。
JDK 动态代理有一个最致命的问题是其只能代理实现了接口的类。
代码示例:
首先定义一个接口和实现类:
public interface UserService {
String login(String username, String password);
}
public class UserServiceImpl implements UserService {
@Override
public String login(String username, String password) {
if("admin".equals(username) && "123456".equals(password)){
return "ok";
}
return "no";
}
}
然后 我们实现InvocationHandler这个处理接口:
这个可以理解为 你要代理类在这个方法前后干些啥
public class DebugInvocationHandler implements InvocationHandler {
private final Object target;
public DebugInvocationHandler(Object object){
this.target = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before:"+method.getName());
Object result = method.invoke(target, args);
System.out.println("after:"+method.getName());
return result;
}
}
再定义一个Jdk代理类的工厂:
import java.lang.reflect.Proxy;
public class JdkProxyFactory {
public static Object getProxy(Object target){
System.out.println(target.getClass().getClassLoader());
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new DebugInvocationHandler(target)
);
}
}
使用:
public class Main {
public static void main(String[] args) {
UserService userService = (UserService) JdkProxyFactory.getProxy(new UserServiceImpl());
System.out.println(userService.login("admin", "123456"));
}
}
CGLib代理
CGLIBopen in new window(Code Generation Library)是一个基于ASMopen in new window的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成。CGLIB 通过继承方式实现代理。(你可以看到下面的Enhancer类就是继承的被代理对象)
简而言之,你需要引入CGLIB的maven依赖
cglib
cglib
3.3.0
定义一个方法:
public class UserService {
public String login(String username, String password) {
if("admin".equals(username) && "123456".equals(password)){
return "ok";
}
return "no";
}
}
关键是要实现这个MethodInterceptor:
public class DebugMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//调用方法之前,我们可以添加自己的操作
System.out.println("before method " + method.getName());
Object object = methodProxy.invokeSuper(o, objects);
//调用方法之后,我们同样可以添加自己的操作
System.out.println("after method " + method.getName());
return object;
}
}
然后同样构建factory:
public class CglibProxyFactory {
public static Object getProxy(Class clazz){
// 创建动态代理增强类
Enhancer enhancer = new Enhancer();
// 设置类加载器
enhancer.setClassLoader(clazz.getClassLoader());
// 设置被代理类
enhancer.setSuperclass(clazz);
// 设置方法拦截器
enhancer.setCallback(new DebugMethodInterceptor());
// 创建代理类
return enhancer.create();
}
}
使用:
public class Main {
public static void main(String[] args) {
UserService userService = (UserService) CglibProxyFactory.getProxy(UserService.class);
System.out.println(userService.login("admin", "123456"));
}
}
JDK 动态代理和 CGLIB 动态代理对比
静态代理和动态代理的对比
单例模式
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
MySingleton mySingleton = (MySingleton) context.getBean("mySingleton");
mySingleton.doSomething();
在上面的代码中,我们在Spring的配置文件中定义了一个名为mySingleton的Bean,它的类是com.example.MySingleton,作用域为singleton(即单例)。然后在Java代码中,我们通过ApplicationContext获取该Bean,并调用它的doSomething()方法。
使用Spring框架可以方便地管理单例对象,同时也可以很容易地实现依赖注入和控制反转等功能。
AbstractBeanFactory的getBean里。getBean的doGetBean方法调用getSingleton进行bean的创建。
是一个双检锁的单例
@Override
@Nullable
public Object getSingleton(String beanName) {
return getSingleton(beanName, true);
}
/**
* Return the (raw) singleton object registered under the given name.
* Checks already instantiated singletons and also allows for an early
* reference to a currently created singleton (resolving a circular reference).
* @param beanName the name of the bean to look for
* @param allowEarlyReference whether early references should be created or not
* @return the registered singleton object, or {@code null} if none found
*/
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
工厂模式
在Spring框架中,AbstractBeanFactory是BeanFactory的一个抽象实现,它提供了一些通用的功能和实现细节,以便其他具体的BeanFactory实现可以继承或扩展它。AbstractBeanFactory实现了BeanFactory接口中的大部分方法,并提供了一些额外的方法和功能,例如BeanDefinition的注册和解析、BeanPostProcessor的处理、Bean生命周期管理等。
BeanFactory是Spring框架中的一个核心接口,它定义了一些方法,用于获取和管理Bean对象。BeanFactory接口的具体实现包括:XmlBeanFactory、DefaultListableBeanFactory、ApplicationContext等。这些实现类都继承了AbstractBeanFactory,并在此基础上进行了扩展和优化,以满足不同的使用场景和需求。因此,AbstractBeanFactory和BeanFactory之间是一种继承关系。
在Spring框架中,ApplicationContext和BeanFactory是一种继承关系。ApplicationContext接口扩展了BeanFactory接口,并提供了更多的功能和特性,例如:
ApplicationContext是Spring框架中最常用的接口之一,它是一个集成了多种功能的容器,可以管理Bean对象的生命周期,并提供了丰富的功能和服务,例如自动装配、依赖注入、Bean后置处理器等。与此相比,BeanFactory接口只提供了最基本的Bean管理功能,没有提供其他高级功能。
因此,ApplicationContext可以看作是BeanFactory的一个超集,它在BeanFactory的基础上进行了扩展和优化,提供了更多的功能和服务,使得开发者可以更加方便地使用Spring框架。
适配器模式
实现方式:
SpringMVC中的适配器HandlerAdatper。
实现原理:
HandlerAdatper根据Handler规则执行不同的Handler。
实现过程:
DispatcherServlet根据HandlerMapping返回的handler,向HandlerAdatper发起请求,处理Handler。
HandlerAdapter根据规则找到对应的Handler并让其执行,执行完毕后Handler会向HandlerAdapter返回一个ModelAndView,最后由HandlerAdapter向DispatchServelet返回一个ModelAndView。
实现意义:
HandlerAdatper使得Handler的扩展变得容易,只需要增加一个新的Handler和一个对应的HandlerAdapter即可。
因此Spring定义了一个适配接口,使得每一种Controller有一种对应的适配器实现类,让适配器代替controller执行相应的方法。这样在扩展Controller时,只需要增加一个适配器类就完成了SpringMVC的扩展了。
策略模式
非常优雅~
ResourceLoader接口定义了获取资源的方法,而Resource接口则表示一个资源对象。在Spring中,ResourceLoader和Resource接口使用策略模式来实现不同类型的资源的加载和处理。
ResourceLoader接口定义了getResource方法,该方法返回一个Resource对象,表示一个资源。Spring中提供了多种Resource实现类,例如ClassPathResource、FileSystemResource、UrlResource等,它们分别用于加载类路径下的资源、文件系统中的资源和URL中的资源。
ResourceLoader和Resource接口之间的关系可以看作是一种策略模式。ResourceLoader接口相当于Context类,而不同类型的Resource实现类相当于具体的策略类。使用ResourceLoader接口可以方便地加载和处理不同类型的资源,而无需关心具体的实现细节。
HandlerInterceptor是Spring MVC框架中的一个拦截器接口,用于在请求处理过程中进行拦截和处理。在Spring MVC中,HandlerInterceptor使用策略模式来实现不同类型的拦截器的注册和处理。
HandlerInterceptor接口定义了三个方法:preHandle、postHandle和afterCompletion,分别对应请求处理前、请求处理后和请求处理完成后的处理逻辑。在Spring MVC中,可以通过实现HandlerInterceptor接口来编写自定义的拦截器,并通过配置文件将其注册到Spring MVC框架中。
HandlerInterceptor和拦截器实现之间的关系可以看作是一种策略模式。HandlerInterceptor接口相当于Context类,而不同类型的拦截器实现类相当于具体的策略类。使用HandlerInterceptor接口可以方便地注册和处理不同类型的拦截器,而无需关心具体的实现细节。
观察者模式
Spring的事件驱动模型用了「观察者模式」,具体实现就是ApplicationContextEvent、ApplicationListener
具体的可拓展机制可以查看另一篇文章:juejin.cn/post/722260…