信任这个点也是面试常考的,面试无非两种,
一种是手撕( 一次面试设计形式手撕阅历 )
一种便是结合结构问
前置常识
设计形式:
菜鸟教程教的仍是十分详细的 墙裂推荐!!
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依靠
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
界说一个办法:
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 动态署理比照
- JDK 动态署理只能署理完成了接口的类或许直接署理接口,而 CGLIB 能够署理未完成任何接口的类。
- JDK 动态署理功率更优异。
静态署理和动态署理的比照
- 灵敏性:动态署理愈加灵敏,不需求有必要完成接口,能够直接署理完成类。别的,静态署理中,接口一旦新增加办法,方针方针和署理方针都要进行修正,这是十分费事的!
- JVM 层面:静态署理在编译时就将接口、完成类、署理类这些都变成了一个个实践的 class 文件。而动态署理是在运行时动态生成类字节码,并加载到 JVM 中的。留意 动态署理都是在运行时动态生成字节码文件
单例形式
- 在Spring的配置文件中界说该类的Bean:
<bean id="mySingleton" class="com.example.MySingleton" scope="singleton"/>
- 在Java代码中获取该Bean:
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.
* <p>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接口,并供给了更多的功用和特性,例如:
- 支撑消息国际化和资源访问
- 支撑事情发布和监听
- 支撑AOP(面向切面编程)和事务办理
- 支撑Web运用程序开发
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接口
ResourceLoader接口界说了获取资源的办法,而Resource接口则表示一个资源方针。在Spring中,ResourceLoader和Resource接口运用战略形式来完成不同类型的资源的加载和处理。
ResourceLoader接口界说了getResource办法,该办法回来一个Resource方针,表示一个资源。Spring中供给了多种Resource完成类,例如ClassPathResource、FileSystemResource、UrlResource等,它们别离用于加载类途径下的资源、文件系统中的资源和URL中的资源。
ResourceLoader和Resource接口之间的联系能够看作是一种战略形式。ResourceLoader接口相当于Context类,而不同类型的Resource完成类相当于详细的战略类。运用ResourceLoader接口能够便利地加载和处理不同类型的资源,而无需关怀详细的完成细节。
- HandlerInterceptor接口
HandlerInterceptor是Spring MVC结构中的一个拦截器接口,用于在恳求处理进程中进行拦截和处理。在Spring MVC中,HandlerInterceptor运用战略形式来完成不同类型的拦截器的注册和处理。
HandlerInterceptor接口界说了三个办法:preHandle、postHandle和afterCompletion,别离对应恳求处理前、恳求处理后和恳求处理完成后的处理逻辑。在Spring MVC中,能够通过完成HandlerInterceptor接口来编写自界说的拦截器,并通过配置文件将其注册到Spring MVC结构中。
HandlerInterceptor和拦截器完成之间的联系能够看作是一种战略形式。HandlerInterceptor接口相当于Context类,而不同类型的拦截器完成类相当于详细的战略类。运用HandlerInterceptor接口能够便利地注册和处理不同类型的拦截器,而无需关怀详细的完成细节。
观察者形式
Spring的事情驱动模型用了「观察者形式」,详细完成便是ApplicationContextEvent、ApplicationListener
详细的可拓展机制能够检查另一篇文章:/post/722260…