基于zookeeper和quartz实现分布式定时调度
yuyutoo 2024-10-16 15:46 5 浏览 0 评论
利用zookeeper的特性,来控制quartz实现分布式调度,保证quartz的单点运行,同时解除quartz自身分布式部署对数据库的依赖,保证同一时刻只有一个quartz应用在执行任务。
实现方式
利用zk的分布式独占锁,控制quartz应用执行节点,让拿到独占锁的quartz应用执行调度,没有拿到独占锁的quartz处理等待状态。
类图
核心代码
public class TriggerBean { /** * 标识 */ private String key; /** * 所属组 */ private String group; /** * 描述 */ private String description; /** * 启动时间 */ private String startTime; /** * 结束时间 */ private String endTime; /** * 优先级 */ private Integer priority; /** * 日历名称 */ private String calendarName; /** * 失火指令(参数0,1,2) * MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY = -1 * MISFIRE_INSTRUCTION_SMART_POLICY = 0 (默认) * MISFIRE_INSTRUCTION_FIRE_ONCE_NOW = 1 * MISFIRE_INSTRUCTION_DO_NOTHING = 2 */ private Integer misfireInstruction; /** * 任务代理类 */ private JobDetailProxyBean jobDetail; public String getKey() { return key; } public void setKey(String key) { this.key = key; } public String getGroup() { return group; } public void setGroup(String group) { this.group = group; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getStartTime() { return startTime; } public void setStartTime(String startTime) { this.startTime = startTime; } public String getEndTime() { return endTime; } public void setEndTime(String endTime) { this.endTime = endTime; } public Integer getPriority() { return priority; } public void setPriority(Integer priority) { this.priority = priority; } public String getCalendarName() { return calendarName; } public void setCalendarName(String calendarName) { this.calendarName = calendarName; } public Integer getMisfireInstruction() { return misfireInstruction; } public void setMisfireInstruction(Integer misfireInstruction) { this.misfireInstruction = misfireInstruction; } public JobDetailProxyBean getJobDetail() { return jobDetail; } public void setJobDetail(JobDetailProxyBean jobDetail) { this.jobDetail = jobDetail; } } public class CronTriggerBean extends TriggerBean { /** * CRON表达式 */ private String cronExpression; public String getCronExpression() { return cronExpression; } public void setCronExpression(String cronExpression) { this.cronExpression = cronExpression; } } public class SimpleTriggerBean extends TriggerBean { /** * 时间间隔(秒) */ private Integer interval; /** * 重复次数(默认:-1为无限循环) */ private Integer repeatCount; public Integer getInterval() { return interval; } public void setInterval(Integer interval) { this.interval = interval; } public Integer getRepeatCount() { return repeatCount; } public void setRepeatCount(Integer repeatCount) { this.repeatCount = repeatCount; } } public class SchedulerFactoryBean implements InitializingBean { protected static Logger logger = Logger.getLogger(SchedulerFactoryBean.class); /** * 触发器列表 */ private List<Object> triggers; /** * zooKeeper工厂 */ private ZookeeperFactory zooKeeperFactory; /** * Spring初始化方法 * @throws SchedulerException */ public void afterPropertiesSet() throws SchedulerException { this.initSchedulerFactory(); } /** * 初始化调度器工厂 * @throws SchedulerException */ public void initSchedulerFactory() throws SchedulerException { //初始化StdSchedulerFactory StdSchedulerFactory schedulerFactory = SchedulerUtils.initStdSchedulerFactory(); //获取调度器 Scheduler scheduler = schedulerFactory.getScheduler(); //装载调度器 for(Object triggerObject : this.getTriggers()){ if(triggerObject instanceof CronTriggerBean){ CronTriggerBean cronTriggerBean = (CronTriggerBean)triggerObject; //获取任务代理类对象 JobDetailProxyBean jobDetailProxyBean = cronTriggerBean.getJobDetail(); //装配任务 JobDetail jobDetail = SchedulerUtils.assemblyJobDetail(jobDetailProxyBean); //设置zooKeeper连接工厂 jobDetail.getJobDataMap().put("zooKeeperFactory",this.getZooKeeperFactory()); //装配触发器 CronTrigger cronTrigger = SchedulerUtils.assemblyCronTrigger(cronTriggerBean); scheduler.scheduleJob(jobDetail, cronTrigger); // System.out.println("CronTriggerBean"); }else{ SimpleTriggerBean simpleTriggerBean = (SimpleTriggerBean)triggerObject; //获取任务代理类对象 JobDetailProxyBean jobDetailProxyBean = simpleTriggerBean.getJobDetail(); //装配任务 JobDetail jobDetail = SchedulerUtils.assemblyJobDetail(jobDetailProxyBean); //设置zooKeeper连接工厂 jobDetail.getJobDataMap().put("zooKeeperFactory",this.getZooKeeperFactory()); //装配触发器 SimpleTrigger simpleTrigger = SchedulerUtils.assemblySimpleTrigger(simpleTriggerBean); scheduler.scheduleJob(jobDetail, simpleTrigger); // System.out.println("SimpleTriggerBean"); } } scheduler.start(); logger.info("调度器已启动"); } public List<Object> getTriggers() { return triggers; } public void setTriggers(List<Object> triggers) { this.triggers = triggers; } public ZookeeperFactory getZooKeeperFactory() { return zooKeeperFactory; } public void setZooKeeperFactory(ZookeeperFactory zooKeeperFactory) { this.zooKeeperFactory = zooKeeperFactory; } }
package com.ab.scheduling.quartz; import com.ab.scheduling.quartz.constant.Constant; import org.apache.curator.RetryPolicy; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.framework.state.ConnectionStateListener; import org.apache.curator.retry.ExponentialBackoffRetry; import org.apache.log4j.Logger; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooDefs; import org.springframework.beans.factory.InitializingBean; import java.util.Collections; import java.util.List; /** * Zookeeper 工厂类 * Date: 14-4-2 * Time: 下午4:03 */ public class ZookeeperFactory implements InitializingBean{ public static Logger logger = Logger.getLogger(ZookeeperFactory.class); /** * zookeeper服务地址 */ private String hosts; /** * 回话的超时时间(毫秒) */ private Integer sessionTimeOut; /** * 连接的超时时间(毫秒) */ private Integer connectionTimeOut; /** * 命名空间 **/ private String nameSpace; /** * zookeeper管理对象 */ private CuratorFramework zkTools; /** * 独享队列节点 */ private String monopolyQueueNode; /** * 连接状态 */ private String connectionState; /** * 会话ID */ private long sessionId; /** * Spring初始化方法 */ public void afterPropertiesSet(){ this.connection(); this.addListener(); } /** * 连接 */ public void connection(){ RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, Integer.MAX_VALUE); zkTools = CuratorFrameworkFactory .builder() .connectString(hosts) .namespace(nameSpace) .retryPolicy(retryPolicy) .connectionTimeoutMs(connectionTimeOut == null ? 30000 : connectionTimeOut) .sessionTimeoutMs(sessionTimeOut == null ? 30000 : sessionTimeOut) .build(); zkTools.start(); } /** * 连接状态监听 */ public void addListener(){ zkTools.getConnectionStateListenable().addListener(new ConnectionStateListener() { public void stateChanged(CuratorFramework client, ConnectionState newState) { if (newState.equals(ConnectionState.CONNECTED)) { logger.info("连接"); connectionState = "CONNECTED"; try { sessionId = zkTools.getZookeeperClient().getZooKeeper().getSessionId(); registerMonopolyQueue(); } catch (Exception e) { logger.error("注册独占队列失败"); } } if (newState.equals(ConnectionState.RECONNECTED)) { logger.info("重新连接"); connectionState = "CONNECTED"; try { if(sessionId != zkTools.getZookeeperClient().getZooKeeper().getSessionId()) { registerMonopolyQueue(); } } catch (Exception e) { logger.error("注册独占队列失败"); } } if (newState.equals(ConnectionState.LOST)) { logger.info("丢失"); connectionState = "LOST"; } if (newState.equals(ConnectionState.SUSPENDED)) { logger.info("暂停"); connectionState = "SUSPENDED"; } if (newState.equals(ConnectionState.READ_ONLY)) { logger.info("只读"); connectionState = "READ_ONLY"; } } }); } /** * 注册独占队列 */ private void registerMonopolyQueue() throws Exception { if(zkTools.checkExists().watched().forPath(Constant.MONOPOLY) == null){ zkTools.create() .withMode(CreateMode.PERSISTENT) .withACL(ZooDefs.Ids.OPEN_ACL_UNSAFE) .forPath(Constant.MONOPOLY); logger.info("创建独享锁队列节点成功!"); } if(monopolyQueueNode == null || (monopolyQueueNode != null && zkTools.checkExists().forPath(monopolyQueueNode)==null)) { monopolyQueueNode = zkTools.create() .withMode(CreateMode.EPHEMERAL_SEQUENTIAL) .withACL(ZooDefs.Ids.OPEN_ACL_UNSAFE) .forPath(Constant.MONOPOLY + Constant.SEPARATOR + Constant.QUEUE_NODE); logger.info("成功加入独享锁队列"); } } /** * 获得独占锁的执行权限 * @return 执行权限标识 * @throws KeeperException * @throws InterruptedException */ public boolean getMonopolyLock() throws Exception { boolean flag = false; if(connectionState != null && (connectionState.equals("CONNECTED") || connectionState.equals("RECONNECTED"))){ List<String> nodes = zkTools.getChildren().watched().forPath(Constant.MONOPOLY); if(nodes.size() > 0){ Collections.sort(nodes); //判断当前应用是否在队列的第一位 if((Constant.SEPARATOR + Constant.MONOPOLY + Constant.SEPARATOR + nodes.get(0)).equals(monopolyQueueNode)){ flag = true; } } } return flag; } /** * 关闭连接 */ public void close(){ if(zkTools != null){ zkTools.close(); zkTools = null; } } public String getHosts() { return hosts; } public void setHosts(String hosts) { this.hosts = hosts; } public Integer getSessionTimeOut() { return sessionTimeOut; } public void setSessionTimeOut(Integer sessionTimeOut) { this.sessionTimeOut = sessionTimeOut; } public Integer getConnectionTimeOut() { return connectionTimeOut; } public void setConnectionTimeOut(Integer connectionTimeOut) { this.connectionTimeOut = connectionTimeOut; } public String getNameSpace() { return nameSpace; } public void setNameSpace(String nameSpace) { this.nameSpace = nameSpace; } }
package com.ab.scheduling.quartz.common; import com.ab.scheduling.quartz.JobDetailProxyBean; import com.ab.scheduling.quartz.CronTriggerBean; import com.ab.scheduling.quartz.SimpleTriggerBean; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.quartz.*; import org.quartz.impl.StdSchedulerFactory; import org.quartz.simpl.SimpleThreadPool; import java.util.Properties; /** * Quartz调度工具类 * Date: 14-5-15 * Time: 下午6:10 */ public class SchedulerUtils { protected static Logger logger = Logger.getLogger(SchedulerUtils.class); /** * 初始化StdSchedulerFactory * @return StdSchedulerFactory */ public static StdSchedulerFactory initStdSchedulerFactory() { StdSchedulerFactory schedulerFactory = null; try{ schedulerFactory = (StdSchedulerFactory) Class.forName(StdSchedulerFactory.class.getName()).newInstance(); Properties mergedProps = new Properties(); // 设置Quartz线程池设置 mergedProps.setProperty(StdSchedulerFactory.PROP_THREAD_POOL_CLASS, SimpleThreadPool.class.getName()); mergedProps.setProperty("org.quartz.threadPool.threadCount", Integer.toString(10)); schedulerFactory.initialize(mergedProps); } catch (Exception e){ logger.error("初始化StdSchedulerFactory失败"); logger.error(e); } return schedulerFactory; } /** * 装配任务 * @param jobDetail 任务代理类 * @return JobDetail */ public static JobDetail assemblyJobDetail(JobDetailProxyBean jobDetail){ JobBuilder jobBuilder = JobBuilder.newJob(jobDetail.getClass()); //设置JobDetail身份标识与所属组 String key = jobDetail.getKey(); if(StringUtils.isNotBlank(key)){ jobBuilder = jobBuilder.withIdentity(key, jobDetail.getGroup()); }else{ jobBuilder = jobBuilder.withIdentity(IdentityUtils.generatorUUID("JOB"), jobDetail.getGroup()); } //设置任务描述 if(StringUtils.isNotBlank(jobDetail.getDescription())){ jobBuilder = jobBuilder.withDescription(jobDetail.getDescription()); } //设置JobDetail数据参数 JobDataMap jobDataMap = new JobDataMap(); jobDataMap.put("targetObject",jobDetail.getTargetObject()); //目标对象 jobDataMap.put("targetMethod",jobDetail.getTargetMethod()); //目标方法 jobDataMap.put("mode", jobDetail.getMode()); //运行模式 jobBuilder = jobBuilder.usingJobData(jobDataMap); return jobBuilder.build(); } /** * 装配表达式触发器 * @param cronTriggerBean 表达式触发器 * @return 表达式触发器 */ public static CronTrigger assemblyCronTrigger(CronTriggerBean cronTriggerBean){ TriggerBuilder triggerBuilder = TriggerBuilder.newTrigger(); //设置触发器身份标识与所属组 String key = cronTriggerBean.getKey(); if(StringUtils.isNotBlank(key)){ triggerBuilder = triggerBuilder.withIdentity(key, cronTriggerBean.getGroup()); }else{ triggerBuilder = triggerBuilder.withIdentity(IdentityUtils.generatorUUID("CronTrigger"), cronTriggerBean.getGroup()); } //设置描述 if(StringUtils.isNotBlank(cronTriggerBean.getDescription())){ triggerBuilder = triggerBuilder.withDescription(cronTriggerBean.getDescription()); } //设置启动时间 if(StringUtils.isNotBlank(cronTriggerBean.getStartTime())){ triggerBuilder = triggerBuilder.startAt(DateUtils.StringToDate(cronTriggerBean.getStartTime(), "yyyy-MM-dd HH:mm:ss")); }else{ triggerBuilder = triggerBuilder.startNow(); //当启动时间为空默认立即启动调度器 } //设置结束时间 if(StringUtils.isNotBlank(cronTriggerBean.getEndTime())){ triggerBuilder = triggerBuilder.endAt(DateUtils.StringToDate(cronTriggerBean.getEndTime(), "yyyy-MM-dd HH:mm:ss")); } //设置优先级 if(cronTriggerBean.getPriority() != null){ triggerBuilder = triggerBuilder.withPriority(cronTriggerBean.getPriority()); } //设置Cron表达式(不允许为空)与集火指令 CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cronTriggerBean.getCronExpression()); if(cronTriggerBean.getMisfireInstruction() != null){ if(cronTriggerBean.getMisfireInstruction() == Trigger.MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY) { cronScheduleBuilder = cronScheduleBuilder.withMisfireHandlingInstructionIgnoreMisfires(); } if(cronTriggerBean.getMisfireInstruction() == CronTrigger.MISFIRE_INSTRUCTION_FIRE_ONCE_NOW) { cronScheduleBuilder = cronScheduleBuilder.withMisfireHandlingInstructionFireAndProceed(); } if(cronTriggerBean.getMisfireInstruction() == CronTrigger.MISFIRE_INSTRUCTION_DO_NOTHING) { cronScheduleBuilder = cronScheduleBuilder.withMisfireHandlingInstructionDoNothing(); } } triggerBuilder = triggerBuilder.withSchedule(cronScheduleBuilder); return (CronTrigger)triggerBuilder.build(); } /** * 装配简单触发器 * @param simpleTriggerBean 简单触发器 * @return 简单触发器 */ public static SimpleTrigger assemblySimpleTrigger(SimpleTriggerBean simpleTriggerBean){ TriggerBuilder triggerBuilder = TriggerBuilder.newTrigger(); //设置触发器身份标识与所属组 String key = simpleTriggerBean.getKey(); if(StringUtils.isNotBlank(key)){ triggerBuilder = triggerBuilder.withIdentity(key, simpleTriggerBean.getGroup()); }else{ triggerBuilder = triggerBuilder.withIdentity(IdentityUtils.generatorUUID("SimpleTrigger"), simpleTriggerBean.getGroup()); } //设置描述 if(StringUtils.isNotBlank(simpleTriggerBean.getDescription())){ triggerBuilder = triggerBuilder.withDescription(simpleTriggerBean.getDescription()); } //设置启动时间 if(StringUtils.isNotBlank(simpleTriggerBean.getStartTime())){ triggerBuilder = triggerBuilder.startAt(DateUtils.StringToDate(simpleTriggerBean.getStartTime(), "yyyy-MM-dd HH:mm:ss")); }else{ triggerBuilder = triggerBuilder.startNow(); //当启动时间为空默认立即启动调度器 } //设置结束时间 if(StringUtils.isNotBlank(simpleTriggerBean.getEndTime())){ triggerBuilder = triggerBuilder.endAt(DateUtils.StringToDate(simpleTriggerBean.getEndTime(), "yyyy-MM-dd HH:mm:ss")); } //设置优先级 if(simpleTriggerBean.getPriority() != null){ triggerBuilder = triggerBuilder.withPriority(simpleTriggerBean.getPriority()); } //设置简单触发器 时间间隔(不允许为空)、执行次数(默认为-1)与集火指令 SimpleScheduleBuilder simpleScheduleBuilder = SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(20).withRepeatCount(-1); simpleScheduleBuilder = simpleScheduleBuilder.withIntervalInSeconds(simpleTriggerBean.getInterval()); if(simpleTriggerBean.getRepeatCount() != null){ simpleScheduleBuilder = simpleScheduleBuilder.withRepeatCount(simpleTriggerBean.getRepeatCount()); }else{ simpleScheduleBuilder = simpleScheduleBuilder.withRepeatCount(-1); } if(simpleTriggerBean.getMisfireInstruction() != null){ if(simpleTriggerBean.getMisfireInstruction() == Trigger.MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY) { simpleScheduleBuilder = simpleScheduleBuilder.withMisfireHandlingInstructionIgnoreMisfires(); } if(simpleTriggerBean.getMisfireInstruction() == SimpleTrigger.MISFIRE_INSTRUCTION_FIRE_NOW) { simpleScheduleBuilder = simpleScheduleBuilder.withMisfireHandlingInstructionFireNow(); } if(simpleTriggerBean.getMisfireInstruction() == SimpleTrigger.MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT) { simpleScheduleBuilder = simpleScheduleBuilder.withMisfireHandlingInstructionNextWithExistingCount(); } if(simpleTriggerBean.getMisfireInstruction() == SimpleTrigger.MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT) { simpleScheduleBuilder = simpleScheduleBuilder.withMisfireHandlingInstructionNextWithRemainingCount(); } if(simpleTriggerBean.getMisfireInstruction() == SimpleTrigger.MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT) { simpleScheduleBuilder = simpleScheduleBuilder.withMisfireHandlingInstructionNowWithExistingCount(); } if(simpleTriggerBean.getMisfireInstruction() == SimpleTrigger.MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT) { simpleScheduleBuilder = simpleScheduleBuilder.withMisfireHandlingInstructionNowWithRemainingCount(); } } triggerBuilder = triggerBuilder.withSchedule(simpleScheduleBuilder); return (SimpleTrigger)triggerBuilder.build(); } }
spring配置
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" default-autowire="byName"> <!--定时任务实现类--> <bean id="test1" class="com.jd.scheduling.quartz.test.Test1"/> <!--任务代理--> <bean id="jobDetail1" class="com.ab.scheduling.quartz.JobDetailProxyBean"> <property name="targetObject" ref="test1"/> <property name="targetMethod" value="test"/> </bean> <!--触发器--> <bean id="cronTrigger" class="com.ab.scheduling.quartz.CronTriggerBean"> <property name="jobDetail" ref="jobDetail1"/> <property name="cronExpression" value="0/10 * * * * ?"/> </bean> <!--zk配置--> <bean id="zooKeeperFactory" class="com.ab.scheduling.quartz.ZookeeperFactory"> <property name="hosts" value="127.0.0.1:2181"/> <property name="sessionTimeOut" value="15000"/> <property name="nameSpace" value="zk-scheduling"/> </bean> <!--调度工厂--> <bean id="schdulerFactory" autowire="no" class="com.ab.scheduling.quartz.SchedulerFactoryBean"> <property name="triggers"> <list> <ref bean="cronTrigger" /> </list> </property> <property name="zooKeeperFactory" ref="zooKeeperFactory"/> </bean> </beans>
相关推荐
- 深度解读Spring框架的核心原理
-
深度解读Spring框架的核心原理在Java开发的世界里,提到Spring框架,就像提起一位久经沙场的老将,它几乎成了企业级应用开发的代名词。那么,这个被无数开发者膜拜的框架究竟有何独特之处?今天,我...
- 「Spring认证」Spring 框架概述
-
Spring是最流行的企业Java应用程序开发框架。全球数以百万计的开发人员使用SpringFramework来创建高性能、易于测试和可重用的代码。Spring框架是一个开源的Java...
- 学习Spring框架 这一篇就够了
-
1.spring概述1.1Spring是什么(理解)...
- Spring框架双核解析:IOC与AOP的本质与实战
-
#Spring核心#IOC容器#AOP编程#Java框架设计...
- Spring Boot与传统Spring框架的对比:探索Java开发的新境界
-
SpringBoot与传统Spring框架的对比:探索Java开发的新境界在Java生态系统中,Spring框架无疑是一个里程碑式的存在。从最初的简单依赖注入容器,到如今覆盖企业级开发方方面面的庞大...
- Spring MVC框架源码深度剖析:从入门到精通
-
SpringMVC框架源码深度剖析:从入门到精通SpringMVC框架简介SpringMVC作为Spring框架的一部分,为构建Web应用程序提供了强大且灵活的支持。它遵循MVC(Model-V...
- Spring框架入门
-
一.spring是什么?Spring是分层...
- 程序员必知必会技能之Spring框架基础——面向切面编程!
-
面向切面编程AOP(AspectOrientedProgramming)与OOP(ObjectOrientedProgramming,面向对象编程)相辅相成。AOP提供了与OOP不同的抽象软件结...
- Spring Security安全框架深度解读:为你的应用穿上“钢铁铠甲”
-
SpringSecurity安全框架深度解读:为你的应用穿上“钢铁铠甲”在现代网络世界里,保护我们的应用程序免受各种威胁攻击至关重要。而在这个过程中,SpringSecurity框架无疑是我们最可...
- Spring框架的设计哲学与实现:打造轻量级的企业级Java应用
-
Spring框架的设计哲学与实现:打造轻量级的企业级Java应用Spring框架自2003年诞生以来,已成为企业级Java应用开发的代名词。它不仅仅是一个框架,更是一种设计理念和哲学的体现。本文将带你...
- Spring框架深度解析:从核心原理到底层实现的全方位避坑指南
-
一、Spring框架核心概念解析1.控制反转(IoC)与依赖注入(DI)Spring的核心思想是通过IoC容器管理对象的生命周期和依赖关系。传统开发中,对象通过new主动创建依赖对象,导致高耦合;而S...
- Java框架 —— Spring简介
-
简介一般来说,Spring指的是SpringFramework,它提供了很多功能,例如:控制反转(IOC)、依赖注入...
- Spring 框架概述,模块划分
-
Spring框架以控制反转(InversionofControl,IoC)和面向切面编程(Aspect-OrientedProgramming,AOP)为核心,旨在简化企业级应用开发,使开发者...
- spring框架怎么实现依赖注入?
-
依赖注入的作用就是在使用Spring框架创建对象时,动态的将其所依赖的对象注入到Bean组件中,其实现方式通常有两种,一种是属性setter方法注入,另一种是构造方法注入。具体介绍如下:●属性set...
- Spring框架详解
-
Spring是一种开放源码框架,旨在解决企业应用程序开发的复杂性。一个主要优点就是它的分层体系结构,层次结构让你可以选择要用的组件,同时也为J2EE应用程序开发提供了集成框架。 Spring特征...
你 发表评论:
欢迎- 一周热门
-
-
前端面试:iframe 的优缺点? iframe有那些缺点
-
带斜线的表头制作好了,如何填充内容?这几种方法你更喜欢哪个?
-
漫学笔记之PHP.ini常用的配置信息
-
推荐7个模板代码和其他游戏源码下载的网址
-
其实模版网站在开发工作中很重要,推荐几个参考站给大家
-
[干货] JAVA - JVM - 2 内存两分 [干货]+java+-+jvm+-+2+内存两分吗
-
正在学习使用python搭建自动化测试框架?这个系统包你可能会用到
-
织梦(Dedecms)建站教程 织梦建站详细步骤
-
【开源分享】2024PHP在线客服系统源码(搭建教程+终身使用)
-
2024PHP在线客服系统源码+完全开源 带详细搭建教程
-
- 最近发表
- 标签列表
-
- mybatis plus (70)
- scheduledtask (71)
- css滚动条 (60)
- java学生成绩管理系统 (59)
- 结构体数组 (69)
- databasemetadata (64)
- javastatic (68)
- jsp实用教程 (53)
- fontawesome (57)
- widget开发 (57)
- vb net教程 (62)
- hibernate 教程 (63)
- case语句 (57)
- svn连接 (74)
- directoryindex (69)
- session timeout (58)
- textbox换行 (67)
- extension_dir (64)
- linearlayout (58)
- vba高级教程 (75)
- iframe用法 (58)
- sqlparameter (59)
- trim函数 (59)
- flex布局 (63)
- contextloaderlistener (56)