spring beanfactory --实现自己简单的 bean工厂类

本文深入探讨Spring框架中BeanFactory的作用与工作流程,包括Bean的实例化、属性注入及初始化过程。并通过示例代码展示了如何使用BeanFactory创建Bean实例,涉及Spring配置文件解析与反射技术的应用。
当从BeanFactory中通过getBean()方法获取一个bean的时候,BeanFactory会经过如下的步骤来构建Bean的实例,这正是实例化Bean的过程:
 从名字上看就是 Spring 的一个 Bean 工厂。如果单从工厂模式的角度思考,它就是用来“生产 Bean ”,然后提供给客户端。
1、调用Bean的默认构造方法,或者在指定的构造方法,生成bean实例(暂称为instance1);
3、如果Bean的配置文件中注入了Bean属性值,则在instance1基础上进行属性注入形成instance2,这种注入是覆盖性的。
2、如果Bean实现了InitializingBean接口,则调用afterPropertiesSet()方法,来改变或操作instance2,得到instance3;
4、如果Bean的配置文件中指定了init-method="init"属性,则会调用指定的初始化方法,则在instance3的基础上调用初始化方法init(),将对象最终初始化为instance4;当然,这个初始化的名字是任意的。
 
instance1、2、3、4是一个实例在不同阶段的状态。
 
上述是spring中的beanfactory功能 下面我们利用dom4j来解析applicationContext.xml,借助于java的反射技术,来创建bean实例
我们依然是针对接口编程 我们的演示接口为移动 采用两个类来实现这个接口 一个命名为汽车 另外一个命名为飞机
public interface Moveable {

    void run();

}

 

汽车实现
public class Car implements Moveable{

    public void run(){

        System.out.println("拖着四个轮子满街跑car·····");

    }

}

 

飞机实现
public class Plane implements Moveable{

 

    public void run() {

        System.out.println("拖着翅膀天空飞plane......");

    }

}

 

按照以前的方式 我们要测试上述 建立一个main函数
public class Test {

 

    public static void main(String[] args) throws Exception {

        Car car = new Car();

        car.run();

    }

}

 

对的,这样简简单单的就能够测试了  现在我们吃饱了没事干了 我们引入第三方创建bean实例,在在主函数中通过getBean方法获得这个bean,并且
强转为我们需要的类型 如下
 Object o = factory.getBean("c");

       Moveable m = (Moveable)o;

        m.run();


如何实现上面的呢 首先我们需要一个类工厂能够根据我们实现的类例如Car,Plane来动态的创建Bean 这就用到了

Object o = Class.forName(className).newInstance(); 也就是之前的第三种反射方法
上面的className怎么来的呢 恩 通过dom4j 来解析配置文件 
首先我们需要一个工厂类的接口
public interface BeanFactory {

    Object getBean(String id);

}

 

提供getBean方法
接着我们去实现这个工厂类
public ClassPathXmlApplicationContext(String fileName) throws Exception{

        SAXReader reader = new SAXReader();

        Document document = reader.read(this.getClass().getClassLoader().getResourceAsStream(fileName));

        Element root = document.getRootElement();

        List list = root.elements();

        for(int index= 0 ; index < list.size();index++){

        Element bean = (Element) list.get(index);

        String id = bean.attributeValue("id");

        String className = bean.attributeValue("class");

        Object o = Class.forName(className).newInstance();

        beans.put(id,o);

        }

    }

 

实现getBean方法方法
    public Object getBean(String id) {

        return beans.get(id);

    }

上述的dom4j读取的配置文件为

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"

    xmlns:tx="http://www.springframework.org/schema/tx" xmlns:p="http://www.springframework.org/schema/p" xmlns:util="http://www.springframework.org/schema/util" xmlns:jdbc="http://www.springframework.org/schema/jdbc"

    xmlns:cache="http://www.springframework.org/schema/cache"

    xsi:schemaLocation="

    http://www.springframework.org/schema/context

    http://www.springframework.org/schema/context/spring-context.xsd

    http://www.springframework.org/schema/beans

    http://www.springframework.org/schema/beans/spring-beans.xsd

    http://www.springframework.org/schema/tx

    http://www.springframework.org/schema/tx/spring-tx.xsd

    http://www.springframework.org/schema/jdbc

    http://www.springframework.org/schema/jdbc/spring-jdbc-3.1.xsd

    http://www.springframework.org/schema/cache

    http://www.springframework.org/schema/cache/spring-cache-3.1.xsd

    http://www.springframework.org/schema/aop

    http://www.springframework.org/schema/aop/spring-aop.xsd

    http://www.springframework.org/schema/util

    http://www.springframework.org/schema/util/spring-util.xsd">

    <bean id="c" class="com.spring.Car"></bean>

    <bean id="p" class="com.spring.Plane"></bean>

</beans>

 

好了 大功告成了 “我们吃饱撑了” 通过第三方bean工厂类 来实现了上述功能
public class Test {

 

    /**

     * @param args

     * @throws DocumentException 

     */

    public static void main(String[] args) throws Exception {

        BeanFactory factory = new ClassPathXmlApplicationContext("applicationContext.xml");

        Object o = factory.getBean("c");

        Moveable m = (Moveable)o;

        m.run();

    }

 

}

 

下面是运行结果
拖着四个轮子满街跑car·····

转载于:https://www.cnblogs.com/winAlaugh/p/5355792.html

<think>我们使用Spring Boot整合Quartz时,任务实现通常是一个标准的Spring Bean,但需要遵循Quartz的Job接口规范。 在Spring Boot 2.7.15中,我们可以采用两种方式实现任务: 1. 实现org.quartz.Job接口,并在execute方法中编写业务逻辑。 2. 使用MethodInvokingJobDetailFactoryBean来调用Spring Bean中的方法(这种方式在较新版本中已不推荐,推荐使用第一种方式结合Spring注入)。 但是,直接实现Job接口的中无法使用Spring的依赖注入,因为Quartz每次执行作业时都会创建一个新的Job实例。为了解决这个问题,Spring提供了AdaptableJobFactory,以及它的子SpringBeanJobFactory,但更常用的是Spring Boot Quartz Starter中已经配置好的AutowiringSpringBeanJobFactory,它可以在Job实例中注入Spring管理的Bean。 下面我们将详细讲解如何创建一个支持依赖注入的任务实现。 步骤: 1. 创建一个实现org.quartz.Job接口的。 2. 使用Spring的@Component注解标记该,使其成为Spring容器管理的Bean。 3. 在中需要注入的Bean上使用@Autowired注解。 4. 配置Quartz使用AutowiringSpringBeanJobFactory,这样在创建Job实例时会从Spring容器中获取Bean并进行依赖注入。 注意:在Spring Boot 2.7.15中,默认已经配置了AutowiringSpringBeanJobFactory,所以我们只需要正常定义Job并加上@Component即可。 示例代码: 1. 任务实现: ```java import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component // 关键:让Spring管理该Bean public class SampleJob implements Job { // 注入Spring Bean @Autowired private SomeService someService; @Override public void execute(JobExecutionContext context) throws JobExecutionException { // 在此处调用业务逻辑 someService.executeBusinessLogic(); } } ``` 2. 服务SomeService: ```java import org.springframework.stereotype.Service; @Service public class SomeService { public void executeBusinessLogic() { System.out.println("Executing business logic at: " + new java.util.Date()); } } ``` 3. 配置触发器与任务详情(配置): 在Spring Boot中,我们可以使用`SchedulerFactoryBean`来配置Quartz Scheduler,但Spring Boot已经自动配置了Scheduler,我们只需要通过配置来定义JobDetail和Trigger。 注意:为了避免同一个JobDetail被多次注册,我们通常使用`@ConditionalOnMissingBean`注解或者在配置时检查是否已存在。 配置示例: ```java import org.quartz.*; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class QuartzConfig { @Bean public JobDetail sampleJobDetail() { // 绑定任务,注意这里使用withIdentity指定唯一标识 return JobBuilder.newJob(SampleJob.class) .withIdentity("sampleJob") .storeDurably() // 即使没有Trigger关联时也不删除 .build(); } @Bean public Trigger sampleJobTrigger() { // 定义触发器,每隔5秒执行一次 SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule() .withIntervalInSeconds(5) .repeatForever(); return TriggerBuilder.newTrigger() .forJob(sampleJobDetail()) .withIdentity("sampleTrigger") .withSchedule(scheduleBuilder) .build(); } } ``` 4. 如果使用Cron表达式,则触发器可以这样配置: ```java @Bean public Trigger cronJobTrigger() { return TriggerBuilder.newTrigger() .forJob(sampleJobDetail()) .withIdentity("sampleCronTrigger") .withSchedule(CronScheduleBuilder.cronSchedule("0/10 * * * * ?")) // 每10秒 .build(); } ``` 5. 如果希望从属性文件中读取cron表达式,可以使用${}占位符: ```java @Bean public Trigger cronJobTrigger(@Value("${cron.expression}") String cron) { return TriggerBuilder.newTrigger() .forJob(sampleJobDetail()) .withIdentity("sampleCronTrigger") .withSchedule(CronScheduleBuilder.cronSchedule(cron)) .build(); } ``` 6. 在application.properties中配置: ```properties cron.expression=0/10 * * * * ? ``` 注意事项: - 在Job中,我们通过@Component注解将其声明为Spring Bean,这样AutowiringSpringBeanJobFactory就可以在创建Job实例时从Spring容器中获取该Bean,并注入依赖。 - Job必须有无参构造函数,因为Quartz在实例化Job时会调用无参构造。 - 默认情况下,Spring Boot Quartz会创建一个内存中的JobStore(RAMJobStore),如果需要持久化到数据库,需要配置spring.quartz.job-store-type=jdbc,并配置数据源。 如果遇到注入问题,可以检查: - 是否使用了正确的Job(在JobDetail中指定)。 - 是否在Spring Boot的扫描包路径下。 如果使用的是持久化存储(JDBC JobStore),那么Job必须可序列化(实现Serializable接口),但是请注意,JobDetail中的JobDataMap在序列化时可能会遇到版本问题,建议不要在JobDataMap中存储复杂的不可序列化对象。 另一种方式:不使用AutowiringSpringBeanJobFactory,而通过JobDataMap注入? 但是,通过JobDataMap注入的方式需要手动管理,且只能传递基本型或可序列化的对象,对于Spring Bean则无法注入。因此,推荐使用上述方式。 如何自定义JobFactory? 如果默认的AutowiringSpringBeanJobFactory不满足需求,我们可以自定义JobFactory,但通常不需要。 示例:自定义JobFactory(继承AdaptableJobFactory,并重写createJobInstance方法) 但Spring Boot已经提供了AutowiringSpringBeanJobFactory,我们通常直接使用即可。 如果必须自定义,可以这样: ```java public class CustomSpringBeanJobFactory extends AutowiringSpringBeanJobFactory { @Override protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception { // 在创建Job实例前后可以做一些自定义操作 return super.createJobInstance(bundle); } } ``` 然后在配置中设置自定义的JobFactory: ```java @Autowired private AutowireCapableBeanFactory beanFactory; @Bean public SchedulerFactoryBean schedulerFactoryBean() { SchedulerFactoryBean factory = new SchedulerFactoryBean(); AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory(); jobFactory.setBeanFactory(beanFactory); factory.setJobFactory(jobFactory); // 其他配置... return factory; } ``` 但在Spring Boot自动配置中,已经配置了AutowiringSpringBeanJobFactory,所以如果没有特殊需求,我们无需重新配置。 总结:Spring Boot Quartz任务实现就是一个实现了Job接口的Spring Bean,通过AutowiringSpringBeanJobFactory实现依赖注入。 相关问题: 1. 如何解决Quartz Job中无法注入Spring Bean的问题? 2. 在Quartz持久化存储中,任务需要满足什么条件? 3. 如何动态创建和注册Quartz任务? 4. 在集群环境下,Quartz任务执行有哪些注意事项? 按照要求,相关问题格式如下: </think>### Spring Boot Quartz 任务实现详解 在 Spring Boot 2.7.15 中实现 Quartz 任务,需要创建符合 Quartz 规范的作业,并通过 Spring 进行管理。以下是完整的实现方案: #### 1. 基础任务实现实现 `Job` 接口) ```java import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.springframework.stereotype.Component; @Component public class EmailNotificationJob implements Job { @Override public void execute(JobExecutionContext context) throws JobExecutionException { // 从JobDataMap获取参数 String recipient = context.getJobDetail().getJobDataMap().getString("recipient"); String subject = context.getJobDetail().getJobDataMap().getString("subject"); // 实际业务逻辑 System.out.println("Sending email to: " + recipient); System.out.println("Subject: " + subject); System.out.println("Time: " + new java.util.Date()); // 可添加邮件发送实际代码 } } ``` #### 2. 支持依赖注入的任务实现(推荐方式) ```java import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class ReportGenerationJob implements Job { // 通过JobFactory注入Spring Bean private final ReportService reportService; @Autowired public ReportGenerationJob(ReportService reportService) { this.reportService = reportService; } @Override public void execute(JobExecutionContext context) throws JobExecutionException { // 从触发器获取参数 String reportType = context.getTrigger().getJobDataMap().getString("reportType"); // 调用注入的服务 reportService.generateReport(reportType); } } // 服务 @Service public class ReportService { public void generateReport(String type) { System.out.println("Generating " + type + " report at: " + new java.util.Date()); // 实际报表生成逻辑 } } ``` #### 3. 完整的任务配置 ```java import org.quartz.*; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class QuartzJobConfig { @Bean public JobDetail emailJobDetail() { // 构建JobDetail并设置参数 return JobBuilder.newJob(EmailNotificationJob.class) .withIdentity("emailNotificationJob", "notificationGroup") .usingJobData("recipient", "admin@example.com") .usingJobData("subject", "Daily Report") .storeDurably() // 持久化存储 .build(); } @Bean public Trigger emailJobTrigger() { // 每天上午9点触发 CronScheduleBuilder schedule = CronScheduleBuilder.dailyAtHourAndMinute(9, 0); return TriggerBuilder.newTrigger() .forJob(emailJobDetail()) .withIdentity("emailTrigger", "notificationGroup") .withSchedule(schedule) .build(); } @Bean public JobDetail reportJobDetail() { return JobBuilder.newJob(ReportGenerationJob.class) .withIdentity("reportGenerationJob", "reportsGroup") .storeDurably() .build(); } @Bean public Trigger reportJobTrigger() { // 每30分钟执行一次 SimpleScheduleBuilder schedule = SimpleScheduleBuilder.simpleSchedule() .withIntervalInMinutes(30) .repeatForever(); return TriggerBuilder.newTrigger() .forJob(reportJobDetail()) .withIdentity("reportTrigger", "reportsGroup") .withSchedule(schedule) .startNow() .build(); } } ``` ### 关键实现细节 #### 1. 依赖注入的处理 在 Quartz 作业中实现依赖注入需要特殊处理: ```java @Configuration public class QuartzConfig { @Autowired private ApplicationContext applicationContext; @Bean public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource) { SchedulerFactoryBean factory = new SchedulerFactoryBean(); factory.setJobFactory(springBeanJobFactory()); factory.setDataSource(dataSource); return factory; } @Bean public SpringBeanJobFactory springBeanJobFactory() { AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory(); jobFactory.setApplicationContext(applicationContext); return jobFactory; } } // 自定义JobFactory支持依赖注入 public class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory { private AutowireCapableBeanFactory beanFactory; @Override public void setApplicationContext(ApplicationContext context) { beanFactory = context.getAutowireCapableBeanFactory(); } @Override protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception { Object job = super.createJobInstance(bundle); beanFactory.autowireBean(job); // 关键注入点 return job; } } ``` #### 2. 任务并发控制 ```java @DisallowConcurrentExecution // 禁止并发执行 @PersistJobDataAfterExecution // 执行后持久化数据 @Component public class DataProcessingJob implements Job { @Override public void execute(JobExecutionContext context) { // 长时间处理任务 processLargeData(); } private void processLargeData() { // 数据处理逻辑 } } ``` #### 3. 动态参数传递 ```java public void scheduleReportJob(String reportType) { JobDataMap jobDataMap = new JobDataMap(); jobDataMap.put("reportType", reportType); JobDetail job = JobBuilder.newJob(ReportGenerationJob.class) .withIdentity("dynamicReportJob", "reportsGroup") .usingJobData(jobDataMap) .build(); Trigger trigger = TriggerBuilder.newTrigger() .startAt(DateBuilder.futureDate(10, DateBuilder.IntervalUnit.MINUTE)) .build(); scheduler.scheduleJob(job, trigger); } ``` ### 最佳实践 #### 1. 任务异常处理 ```java @Component public class SafeJob implements Job { @Override public void execute(JobExecutionContext context) throws JobExecutionException { try { riskyOperation(); } catch (Exception e) { // 记录详细错误 log.error("Job execution failed", e); // 重试逻辑(最多3次) JobExecutionException ex = new JobExecutionException(e); if (context.getRefireCount() < 3) { ex.setRefireImmediately(true); } throw ex; } } } ``` #### 2. 任务幂等性设计 ```java @Component public class IdempotentJob implements Job { @Autowired private JobExecutionLogRepository logRepository; @Override public void execute(JobExecutionContext context) { String jobId = context.getJobDetail().getKey().toString(); // 检查是否已执行 if (logRepository.existsByJobId(jobId)) { return; // 已执行过,跳过 } // 执行业务逻辑 processBusiness(); // 记录执行日志 logRepository.save(new JobExecutionLog(jobId, new Date())); } } ``` #### 3. 任务生命周期管理 ```java @Component public class StatefulJob implements Job, InterruptableJob { private volatile boolean interrupted = false; @Override public void execute(JobExecutionContext context) { while (!interrupted) { // 执行部分任务 partialTask(); } } @Override public void interrupt() throws UnableToInterruptJobException { interrupted = true; // 设置中断标志 } } ``` ### 常见问题解决方案 #### 问题:作业无法注入依赖 **原因**:Quartz 原生 JobFactory 不会处理 Spring 依赖 **解决方案**: 1. 使用自定义的 `AutowiringSpringBeanJobFactory` 2. 在配置中注册自定义 JobFactory ```java @Bean public SchedulerFactoryBean schedulerFactory(SpringBeanJobFactory jobFactory) { SchedulerFactoryBean factory = new SchedulerFactoryBean(); factory.setJobFactory(jobFactory); factory.setOverwriteExistingJobs(true); return factory; } ``` #### 问题:任务执行后数据未持久化 **解决方案**:添加注解 `@PersistJobDataAfterExecution` ```java @PersistJobDataAfterExecution @Component public class CounterJob implements Job { @Override public void execute(JobExecutionContext context) { JobDataMap dataMap = context.getJobDetail().getJobDataMap(); int count = dataMap.getInt("count", 0); dataMap.put("count", count + 1); } } ``` ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值