最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring事件監(jiān)聽(tīng)器之@EventListener原理分析

 更新時(shí)間:2021年12月31日 09:05:10   作者:這是一條海魚  
這篇文章主要介紹了Spring事件監(jiān)聽(tīng)器之@EventListener原理分析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

Spring事件監(jiān)聽(tīng)器之@EventListener原理

Spring為我們提供的一個(gè)事件監(jiān)聽(tīng)、訂閱的實(shí)現(xiàn),內(nèi)部實(shí)現(xiàn)原理是觀察者設(shè)計(jì)模式;為的就是業(yè)務(wù)系統(tǒng)邏輯的解耦,提高可擴(kuò)展性以及可維護(hù)性。事件發(fā)布者并不需要考慮誰(shuí)去監(jiān)聽(tīng),監(jiān)聽(tīng)具體的實(shí)現(xiàn)內(nèi)容是什么,發(fā)布者的工作只是為了發(fā)布事件而已。

在spring中我們可以通過(guò)實(shí)現(xiàn)ApplicationListener接口或者@EventListener接口來(lái)實(shí)現(xiàn)事件驅(qū)動(dòng)編程

比如我們做一個(gè)電商系統(tǒng),用戶下單支付成功后,我們一般要發(fā)短信或者郵箱給用戶提示什么的,這時(shí)候就可以把這個(gè)通知業(yè)務(wù)做成一個(gè)單獨(dú)事件監(jiān)聽(tīng),等待通知就可以了;把它解耦處理。

public class OrderEvent extends ApplicationEvent {
    public OrderEvent(Object source) {
        super(source);
    }
}
@Component
public class OrderEventListener  {
    @EventListener
    public void listener(OrderEvent event) {
        System.out.println("i do OrderEventListener" );
    }
}
@Controller
@RequestMapping("person")
public class PersonController implements ApplicationContextAware {
    private ApplicationContext applicationContext;
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
    @ResponseBody
    @GetMapping("publishOrderEvent")
    public String publishOrderEvent() {
        applicationContext.publishEvent(new OrderEvent("我發(fā)布了事件?。。?));
        System.out.println(" publishOrderEvent ");
        return "發(fā)送事件了!";
    }
}

EventListenerMethodProcessor是@EventListener的解析類,他是一個(gè)SmartInitializingSingleton和BeanFactoryPostProcessor

一、解析@EventListener前的準(zhǔn)備工作

1.1 EventListenerFactory和EventListenerMethodProcessor的注入

EventListenerFactory是把@EventListener標(biāo)注的方法變成ApplicationListener的關(guān)鍵,其是在容器最初期(refresh方法發(fā)生前)就放到容器中去

public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors( BeanDefinitionRegistry registry, Object source) {
        //獲取對(duì)象
		DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
		
        //org.springframework.context.event.internalEventListenerProcessor
        //@EventListener注解處理器
		if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
			RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
		}
        //org.springframework.context.event.internalEventListenerProcessor
        //內(nèi)部管理的EventListenerFactory的bean名稱
		if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
			RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
		}
		return beanDefs;
	}
  • 如果容器中沒(méi)有名字是org.springframework.context.event.internalEventListenerProcessor的bean,那么就注入一個(gè)EventListenerMethodProcessor到容器中
  • 如果容器中沒(méi)有名字是org.springframework.context.event.internalEventListenerProcessor的bean,那么就注入一個(gè)DefaultEventListenerFactory到容器中

1.2 EventListenerMethodProcessor和EventListenerFactory關(guān)系的建立

EventListenerMethodProcessor會(huì)在容器啟動(dòng)時(shí)被注入到容器中,他是一個(gè)BeanFactoryPostProcessor,EventListenerMethodProcessor和EventListenerFactory關(guān)系的建立就發(fā)生在其方法postProcessBeanFactory中

public class EventListenerMethodProcessor implements SmartInitializingSingleton, ApplicationContextAware, BeanFactoryPostProcessor {
	@Nullable
	private List<EventListenerFactory> eventListenerFactories;
    //初始化eventListenerFactories
	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
		this.beanFactory = beanFactory;
		//獲取容器中所有的EventListenerFactory,并把他們實(shí)例化
		Map<String, EventListenerFactory> beans = beanFactory.getBeansOfType(EventListenerFactory.class, false, false);
		
		List<EventListenerFactory> factories = new ArrayList<>(beans.values());
		AnnotationAwareOrderComparator.sort(factories);
		//將EventListenerFactory儲(chǔ)存到緩存eventListenerFactories中,便于后來(lái)使用
		this.eventListenerFactories = factories;
	}
}

EventListenerFactory的實(shí)例化時(shí)機(jī)只比BeanFactoryPostProcessor完點(diǎn),他比BeanPostProcessor實(shí)例化時(shí)機(jī)早

二、開(kāi)始解析@EventListener

EventListenerMethodProcessor是一個(gè)SmartInitializingSingleton,所以他會(huì)在所以bean實(shí)例化后,執(zhí)行其afterSingletonsInstantiated方法

注意:只有單例的SmartInitializingSingleton,才會(huì)執(zhí)行其afterSingletonsInstantiated方法

2.1 基本流程

public class EventListenerMethodProcessor implements SmartInitializingSingleton, ApplicationContextAware, BeanFactoryPostProcessor {
    @Override
	public void afterSingletonsInstantiated() {
		ConfigurableListableBeanFactory beanFactory = this.beanFactory;
		Assert.state(this.beanFactory != null, "No ConfigurableListableBeanFactory set");
		// 這里厲害了,用Object.class 是拿出容器里面所有的Bean定義~~~  一個(gè)一個(gè)的檢查
		String[] beanNames = beanFactory.getBeanNamesForType(Object.class);
		for (String beanName : beanNames) {
		     // 
			if (!ScopedProxyUtils.isScopedTarget(beanName)) {
				Class<?> type = null;
				try {
				     // 防止是代理,吧真實(shí)的類型拿出來(lái)
					type = AutoProxyUtils.determineTargetClass(beanFactory, beanName);
				}
				catch (Throwable ex) {
					if (logger.isDebugEnabled()) {
						logger.debug("", ex);
					}
				}
				if (type != null) {
				    // 對(duì)專門的作用域?qū)ο筮M(jìn)行兼容~~~~(絕大部分都用不著)
					if (ScopedObject.class.isAssignableFrom(type)) {
						try {
							Class<?> targetClass = AutoProxyUtils.determineTargetClass(
									beanFactory, ScopedProxyUtils.getTargetBeanName(beanName));
							if (targetClass != null) {
								type = targetClass;
							}
						}
						catch (Throwable ex) {
							// An invalid scoped proxy arrangement - let's ignore it.
							if (logger.isDebugEnabled()) {
								logger.debug("Could not resolve target bean for scoped proxy '" + beanName + "'", ex);
							}
						}
					}
					try {
					    // 真正處理這個(gè)Bean里面的方法們。。。
						processBean(beanName, type);
					}
					catch (Throwable ex) {
						throw new BeanInitializationException("", ex);
					}
				}
			}
		}
	}
	private void processBean(final String beanName, final Class<?> targetType) {
	         //類上有@Component注解
			if (!this.nonAnnotatedClasses.contains(targetType) &&!targetType.getName().startsWith("java") &&!isSpringContainerClass(targetType)) {
	 			Map<Method, EventListener> annotatedMethods = null;
				try {
				    //獲取類中用@EventListener標(biāo)注方法的信息
					annotatedMethods = MethodIntrospector.selectMethods(targetType,
							(MethodIntrospector.MetadataLookup<EventListener>) method ->
									AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class));
				}
				catch (Throwable ex) {
					// An unresolvable type in a method signature, probably from a lazy bean - let's ignore it.
					if (logger.isDebugEnabled()) {
						logger.debug("Could not resolve methods for bean with name '" + beanName + "'", ex);
					}
				}
	            //如果annotatedMethods為空,那代表類中沒(méi)有用@EventListener標(biāo)注的方法
				if (CollectionUtils.isEmpty(annotatedMethods)) {
					this.nonAnnotatedClasses.add(targetType);
					if (logger.isTraceEnabled()) {
						logger.trace("" + targetType.getName());
					}
				}
				else {
					// 類中存在用@EventListener標(biāo)注的方法
					ConfigurableApplicationContext context = this.applicationContext;
					Assert.state(context != null, "No ApplicationContext set");
					//獲取容器中所有EventListenerFactory
					List<EventListenerFactory> factories = this.eventListenerFactories;
					Assert.state(factories != null, "EventListenerFactory List not initialized");
					for (Method method : annotatedMethods.keySet()) {
						for (EventListenerFactory factory : factories) {
							if (factory.supportsMethod(method)) {
								// 簡(jiǎn)單的說(shuō),就是把這個(gè)方法弄成一個(gè)可以執(zhí)行的方法(主要和訪問(wèn)權(quán)限有關(guān))
							  // 這里注意:若你是JDK的代理類,請(qǐng)不要在實(shí)現(xiàn)類里書寫@EventListener注解的監(jiān)聽(tīng)器,否則會(huì)報(bào)錯(cuò)的。(CGLIB代理的木關(guān)系) 
								Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName)); 
                                //利用EventListenerFactory創(chuàng)建ApplicationListener,詳情后面說(shuō)
								ApplicationListener<?> applicationListener =
										factory.createApplicationListener(beanName, targetType, methodToUse);
								//如果ApplicationListener是ApplicationListenerMethodAdapter類,那么執(zhí)行其init方法
								if (applicationListener instanceof ApplicationListenerMethodAdapter) {
									((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);
								}
								//放到容器中
								context.addApplicationListener(applicationListener);
								//@EventListener方法只能解析一次
								break;
							}
						}
					}
					if (logger.isDebugEnabled()) {
						logger.debug();
					}
				}
			}
		}
}	

獲取容器中所有的類,把用@Component標(biāo)注的類上所有的@EventListener方法用EventListenerFactory解析成一個(gè)ApplicationListener

@EventListener方法只要有到一個(gè)可以解析他的EventListenerFactory,就不會(huì)讓其他EventListenerFactory解析他了 所以如果容器中存在多個(gè)EventListenerFactory,我要注意他的順序

2.2 EventListenerFactory解析@EventListener

在這里插入圖片描述

public interface EventListenerFactory {
    //是否支持當(dāng)前方法
	boolean supportsMethod(Method method);
	
	//生成一個(gè)ApplicationListener
	ApplicationListener<?> createApplicationListener(String beanName, Class<?> type, Method method);
}

EventListenerFactory有2個(gè)字類DefaultEventListenerFactory和TransactionalEventListenerFactory,DefaultEventListenerFactory是處理@EventListener,而TransactionalEventListenerFactory是處理@TransactionalEventListener的,Spring默認(rèn)就有DefaultEventListenerFactory,而TransactionalEventListenerFactory是沒(méi)有的,所以我們想要支持@TransactionalEventListener,就要注冊(cè)一個(gè)TransactionalEventListenerFactory,也就是要說(shuō)要使用@EnableTransactionManagement注解

public class DefaultEventListenerFactory implements EventListenerFactory, Ordered {
	private int order = LOWEST_PRECEDENCE;
	@Override
	public boolean supportsMethod(Method method) {
		return true;
	}
	@Override
	public ApplicationListener<?> createApplicationListener(String beanName, Class<?> type, Method method) {
		return new ApplicationListenerMethodAdapter(beanName, type, method);
	}
}

ApplicationListenerMethodAdapter一個(gè)ApplicationListener,他是用來(lái)包裝@EventListener標(biāo)注的方法

public class ApplicationListenerMethodAdapter implements GenericApplicationListener {
	private final String beanName; //@EventListener方法所屬bean的名字
	private final Method method;//@EventListener標(biāo)注的方法
	private final Method targetMethod;//@EventListener標(biāo)注的真實(shí)方法對(duì)象,防止其是代理方法
    //方法申明,如public void demo.Ball.applicationContextEvent(demo.OrderEvent)
	private final AnnotatedElementKey methodKey;
	private final List<ResolvableType> declaredEventTypes;//存儲(chǔ)方法的參數(shù)
	private final String condition;//@EventListener的condition
	private final int order;
	private ApplicationContext applicationContext;
	private EventExpressionEvaluator evaluator;//@EventListener的EventExpressionEvaluator
	public ApplicationListenerMethodAdapter(String beanName, Class<?> targetClass, Method method) {
		this.beanName = beanName;
		this.method = BridgeMethodResolver.findBridgedMethod(method);
		this.targetMethod = (!Proxy.isProxyClass(targetClass) ?AopUtils.getMostSpecificMethod(method, targetClass) : this.method);
		this.methodKey = new AnnotatedElementKey(this.targetMethod, targetClass);
		//獲取方法上的@EventListener注解對(duì)象
		EventListener ann = AnnotatedElementUtils.findMergedAnnotation(this.targetMethod, EventListener.class);
		this.declaredEventTypes = resolveDeclaredEventTypes(method, ann);
		this.condition = (ann != null ? ann.condition() : null);
		this.order = resolveOrder(this.targetMethod);
	}
    public void onApplicationEvent(ApplicationEvent event) {
		processEvent(event);
	}
	
    public void processEvent(ApplicationEvent event) {
		Object[] args = resolveArguments(event);
		//根據(jù)@EventListener的condition,判斷是否要處理
		if (shouldHandle(event, args)) {
		    //調(diào)用方法
			Object result = doInvoke(args);
			if (result != null) {
			    //如果有監(jiān)聽(tīng)器可以監(jiān)聽(tīng)這個(gè)結(jié)果,那么可以觸發(fā)那個(gè)監(jiān)聽(tīng)器
				handleResult(result);
			}
			else {
				logger.trace("No result object given - no result to handle");
			}
		}
	}
}

EventListener.Factory

EventListener.Factory監(jiān)聽(tīng)網(wǎng)絡(luò)請(qǐng)求全過(guò)程

網(wǎng)上介紹的并不多,關(guān)于它的使用方式,可能會(huì)存在很多坑。

主要是為了監(jiān)聽(tīng)網(wǎng)絡(luò)請(qǐng)求過(guò)程。

首先OkHttpClient.Builder.eventListenerFactory需要的是一個(gè)實(shí)現(xiàn)了EventListener接口的工廠類。

簡(jiǎn)單的實(shí)現(xiàn)方式。

public class HttpEventListener extends EventListener {
      private final long callId;
  	  final AtomicLong nextCallId = new AtomicLong(1L);
        @Override
        public EventListener create(Call call) {
            long callId = nextCallId.getAndIncrement();
            return new HttpEventListener(callId, System.nanoTime());
        }
    public HttpEventListener(long callId, long callStartNanos) {
        this.callId = callId;
        this.callStartNanos = callStartNanos;
    }
    private long dnsStartTime;
    private long dnsParseTime;
    @Override
    public void dnsStart(Call call, String domainName) {
        super.dnsStart(call, domainName);
        dnsStartTime = System.nanoTime();
    }
    @Override
    public void dnsEnd(Call call, String domainName, List<InetAddress> inetAddressList) {
        super.dnsEnd(call, domainName, inetAddressList);
        dnsParseTime = System.nanoTime() - dnsStartTime;//dns解析耗時(shí)
    }
//自動(dòng)補(bǔ)全剩余實(shí)現(xiàn)方法
}

EventListener.create方法在okHttpClient.newCall后執(zhí)行

dnsParseTime可以算出dns解析耗時(shí),還可以監(jiān)聽(tīng)每次dns解析的domain,解析的結(jié)果inetAddressList。

這個(gè)是比較好用的。

問(wèn)題是如何將這些數(shù)據(jù)回傳回來(lái)呢

在OkHttpClient構(gòu)造時(shí)傳入自定義參數(shù)

OkHttpClient.Builder builder = new OkHttpClient.Builder();
final ResponseTag tag = new ResponseTag();
        tag.logHandler = logHandler;
        httpClient.newCall(requestBuilder.tag(tag).build()).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
              
            }
            @Override
            public void onResponse(Call call, Response response) throws IOException {
                
            }
        });
    //自動(dòng)補(bǔ)全剩余實(shí)現(xiàn)方法
public class HttpEventListener extends EventListener {
    /**
     * 每次請(qǐng)求的標(biāo)識(shí)
     */
    private long callId = 1L;
    /**
     * 每次請(qǐng)求的開(kāi)始時(shí)間,單位納秒
     */
    private final long callStartNanos;
    private long total_elapsed_time;
    private long dns_elapsed_time;
    private long connect_elapsed_time;
    private long tls_connect_elapsed_time;
    private long request_elapsed_time;
    private long wait_elapsed_time;
    private long response_elapsed_time;
    private Client.ResponseTag responseTag;
    private LogHandler logHandler;
    private long start_dns_elapsed_time;
    private long start_total_elapsed_time;
    private long start_connect_elapsed_time;
    private long start_tls_connect_elapsed_time;
    private long start_request_elapsed_time;
    private long start_response_elapsed_time;
    public HttpEventListener(long callId, Client.ResponseTag responseTag, long callStartNanos) {
        this.callId = callId;
        this.callStartNanos = callStartNanos;
        this.responseTag = responseTag;
        this.logHandler = responseTag.logHandler;
    }
    public static final Factory FACTORY = new Factory() {
        final AtomicLong nextCallId = new AtomicLong(1L);
        @Override
        public EventListener create(@NotNull Call call) {
            long callId = nextCallId.getAndIncrement();
            return new HttpEventListener(callId, (Client.ResponseTag) call.request().tag(), System.nanoTime());
        }
    };
    @Override
    public void callStart(Call call) {
        super.callStart(call);
        start_total_elapsed_time = System.currentTimeMillis();
    }
    @Override
    public void dnsStart(Call call, String domainName) {
        super.dnsStart(call, domainName);
        start_dns_elapsed_time = System.currentTimeMillis();
    }
    @Override
    public void dnsEnd(Call call, String domainName, List<InetAddress> inetAddressList) {
        super.dnsEnd(call, domainName, inetAddressList);
        dns_elapsed_time = System.currentTimeMillis() - start_dns_elapsed_time;//dns解析耗時(shí)
        logHandler.send("dns_elapsed_time", dns_elapsed_time);
    }
    @Override
    public void connectStart(Call call, InetSocketAddress inetSocketAddress, Proxy proxy) {
        super.connectStart(call, inetSocketAddress, proxy);
        start_connect_elapsed_time = System.currentTimeMillis();
    }
    @Override
    public void secureConnectStart(Call call) {
        super.secureConnectStart(call);
        start_tls_connect_elapsed_time = System.currentTimeMillis();
    }
    @Override
    public void secureConnectEnd(Call call, Handshake handshake) {
        super.secureConnectEnd(call, handshake);
        tls_connect_elapsed_time = System.currentTimeMillis() - start_tls_connect_elapsed_time;
        logHandler.send("tls_connect_elapsed_time", tls_connect_elapsed_time);
    }
    @Override
    public void connectEnd(Call call, InetSocketAddress inetSocketAddress, Proxy proxy, Protocol protocol) {
        super.connectEnd(call, inetSocketAddress, proxy, protocol);
        connect_elapsed_time = System.currentTimeMillis() - start_connect_elapsed_time;
        logHandler.send("connect_elapsed_time", connect_elapsed_time);
    }
    @Override
    public void connectFailed(Call call, InetSocketAddress inetSocketAddress, Proxy proxy, Protocol protocol, IOException ioe) {
        super.connectFailed(call, inetSocketAddress, proxy, protocol, ioe);
    }
    @Override
    public void connectionAcquired(Call call, Connection connection) {
        super.connectionAcquired(call, connection);
    }
    @Override
    public void connectionReleased(Call call, Connection connection) {
        super.connectionReleased(call, connection);
    }
    @Override
    public void requestHeadersStart(Call call) {
        super.requestHeadersStart(call);
        start_request_elapsed_time = System.currentTimeMillis();
    }
    @Override
    public void requestHeadersEnd(Call call, Request request) {
        super.requestHeadersEnd(call, request);
    }
    @Override
    public void requestBodyStart(Call call) {
        super.requestBodyStart(call);
    }
    @Override
    public void requestBodyEnd(Call call, long byteCount) {
        super.requestBodyEnd(call, byteCount);
        request_elapsed_time = System.currentTimeMillis() - start_request_elapsed_time;
        logHandler.send("request_elapsed_time", request_elapsed_time);
    }
    @Override
    public void responseHeadersStart(Call call) {
        super.responseHeadersStart(call);
        start_response_elapsed_time = System.currentTimeMillis();
    }
    @Override
    public void responseHeadersEnd(Call call, Response response) {
        super.responseHeadersEnd(call, response);
    }
    @Override
    public void responseBodyStart(Call call) {
        super.responseBodyStart(call);
    }
    @Override
    public void responseBodyEnd(Call call, long byteCount) {
        super.responseBodyEnd(call, byteCount);
        response_elapsed_time = System.currentTimeMillis() - start_response_elapsed_time;
        wait_elapsed_time = System.currentTimeMillis() - start_request_elapsed_time;
        logHandler.send("response_elapsed_time", response_elapsed_time);
        logHandler.send("wait_elapsed_time", wait_elapsed_time);
    }
    @Override
    public void callEnd(Call call) {
        super.callEnd(call);
        total_elapsed_time = System.currentTimeMillis() - start_total_elapsed_time;
        logHandler.send("total_elapsed_time", total_elapsed_time);
    }
    @Override
    public void callFailed(Call call, IOException ioe) {
        super.callFailed(call, ioe);
    }
}
//利用反射將logHandler打回來(lái)的數(shù)據(jù)存到對(duì)象
public static LogHandler getUplogHandler(final Object obj) {
        final String setMethod = "set";
        LogHandler logHandler = new LogHandler() {
            @Override
            public void send(String key, Object value) {
                try {
                    if (value instanceof String) {
                        Method setByKey = obj.getClass().getMethod(setMethod + StringUtils.upperCase(key), Class.forName("java.lang.String"));
                        setByKey.invoke(obj, value);
                    } else if (value instanceof Integer) {
                        Method setByKey = obj.getClass().getMethod(setMethod + StringUtils.upperCase(key), int.class);
                        setByKey.invoke(obj, value);
                    } else if (value instanceof Long) {
                        Method setByKey = obj.getClass().getMethod(setMethod + StringUtils.upperCase(key), long.class);
                        setByKey.invoke(obj, value);
                    }
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
            @Override
            public Object getUploadInfo() {
                return obj;
            }
        };
        return logHandler;
    }

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Mybatis中的常用OGNL表達(dá)式

    Mybatis中的常用OGNL表達(dá)式

    這篇文章主要介紹了Mybatis中的常用OGNL表達(dá)式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • MybatisPlus使用代碼生成器遇到的小問(wèn)題(推薦)

    MybatisPlus使用代碼生成器遇到的小問(wèn)題(推薦)

    這篇文章主要介紹了MybatisPlus使用代碼生成器遇到的小問(wèn)題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-08-08
  • SpringBoot內(nèi)嵌tomcat處理有特殊字符轉(zhuǎn)義的問(wèn)題

    SpringBoot內(nèi)嵌tomcat處理有特殊字符轉(zhuǎn)義的問(wèn)題

    這篇文章主要介紹了SpringBoot內(nèi)嵌tomcat處理有特殊字符轉(zhuǎn)義的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-06-06
  • dom4j創(chuàng)建和解析xml文檔的實(shí)現(xiàn)方法

    dom4j創(chuàng)建和解析xml文檔的實(shí)現(xiàn)方法

    下面小編就為大家?guī)?lái)一篇dom4j創(chuàng)建和解析xml文檔的實(shí)現(xiàn)方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-06-06
  • Java找不到或無(wú)法加載主類及編碼錯(cuò)誤問(wèn)題的解決方案

    Java找不到或無(wú)法加載主類及編碼錯(cuò)誤問(wèn)題的解決方案

    今天小編就為大家分享一篇關(guān)于Java找不到或無(wú)法加載主類及編碼錯(cuò)誤問(wèn)題的解決方案,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2019-02-02
  • SpringBoot內(nèi)置tomcat參數(shù)調(diào)優(yōu)的實(shí)現(xiàn)

    SpringBoot內(nèi)置tomcat參數(shù)調(diào)優(yōu)的實(shí)現(xiàn)

    springboot內(nèi)置了tomcat, 并給我們?cè)O(shè)置了默認(rèn)參數(shù), 我們?cè)趺礃有薷膕pringboot內(nèi)置的tomcat參數(shù),本文就詳細(xì)的來(lái)介紹一下,感興趣的可以了解一下
    2023-09-09
  • Springboot實(shí)現(xiàn)動(dòng)態(tài)定時(shí)任務(wù)管理的示例代碼

    Springboot實(shí)現(xiàn)動(dòng)態(tài)定時(shí)任務(wù)管理的示例代碼

    最近在做spring boot項(xiàng)目開(kāi)發(fā)中,由于使用@EnableScheduling注解和@Scheduled注解來(lái)實(shí)現(xiàn)的定時(shí)任務(wù),只能靜態(tài)的創(chuàng)建定時(shí)任務(wù),不能動(dòng)態(tài)修改、添加、刪除、啟/停任務(wù),下面通過(guò)本文給大家介紹Springboot實(shí)現(xiàn)動(dòng)態(tài)定時(shí)任務(wù)管理的方法,感興趣的朋友跟隨小編一起看看吧
    2023-07-07
  • SpringBoot項(xiàng)目將mybatis升級(jí)為mybatis-plus的方法

    SpringBoot項(xiàng)目將mybatis升級(jí)為mybatis-plus的方法

    本文主要介紹了SpringBoot項(xiàng)目將mybatis升級(jí)為mybatis-plus的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-01-01
  • 關(guān)于.java編譯成.class?與?.class反編譯成.java問(wèn)題

    關(guān)于.java編譯成.class?與?.class反編譯成.java問(wèn)題

    這篇文章主要介紹了關(guān)于.java編譯成.class?與?.class反編譯成.java問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-09-09
  • Java常用數(shù)據(jù)流全面大梳理

    Java常用數(shù)據(jù)流全面大梳理

    計(jì)算機(jī)程序中,獲取數(shù)據(jù)的方式有多種,比如:程序中直接給出、鍵盤輸入、從數(shù)據(jù)文件中讀取、從數(shù)據(jù)庫(kù)中讀取、通過(guò)網(wǎng)絡(luò)讀取等。為了更有效地進(jìn)行數(shù)據(jù)的輸入/輸出操作,Java將各種數(shù)據(jù)源的數(shù)據(jù),抽象為“數(shù)據(jù)流”,及stream
    2021-10-10

最新評(píng)論

三穗县| 新乡县| 中西区| 马尔康县| 邓州市| 锡林浩特市| 苏尼特左旗| 苍溪县| 施甸县| 金湖县| 玉山县| 嘉祥县| 凤城市| 车险| 资阳市| 平陆县| 固安县| 积石山| 福泉市| 米脂县| 上杭县| 太和县| 墨竹工卡县| 登封市| 高雄县| 石台县| 西充县| 象州县| 皮山县| 施秉县| 祁东县| 成都市| 甘洛县| 大冶市| 武隆县| 南宁市| 平塘县| 广德县| 青龙| 阿勒泰市| 江源县|