目录
[7、 Spring框架中的Bean生命周期](#7、 Spring框架中的Bean生命周期)
1、SpringBoot的事务管理
在mybatis中,我们还要创建一个动态代理 来处理业务中的事务,但是SpringBoot提供了注解的方式快速的实现了事务的相关逻辑。
如果我们现在有一个方法是批量创建多个Consume对象 ,创建了一个bathAdd()方法 ,方法中如果出现了一个异常代码片段,没有事务的情况下前面的对象会被新增在数据库中,后面的则不会执行新增。这样的逻辑是不对的,所以我们在这个方法前面加上@Transaction的注解。
Service层代码:
java
@Service
public class CustomerServiceImp extends ServiceImpl<ConsumeMapper, Consumer> implements ICustomerService{
@Resource
private ConsumeMapper dao;
@Transactional
@Override
public void batchAdd() {
dao.insert(new Consumer(7,"牛",new Date(),"男"));
int a = 10/0;
dao.insert(new Consumer(8,"牛1",new Date(),"男"));
dao.insert(new Consumer(9,"牛2",new Date(),"男"));
}
}
@Transactional:表示这个方法中的所有数据库操作将在一个事务中执行。如果在方法执行过程中抛出异常,事务将会回滚。
@Resource
private ConsumeMapper dao;
表示注入了一个名为dao的ConsumeMapper对象,用于执行数据库操作。
controller层代码:
java
@RestController
public class ConsumerControllerImp {
@Autowired
private ICustomerService service;
@RequestMapping("/add")
public String batchAdd() {
service.batchAdd();
return "成功";
}
}
@RestController:标记这个类是一个Spring MVC控制器,并且返回的内容会直接作为HTTP响应的Body(通常是JSON格式)。相比于@Controller,@RestController省去了额外的@ResponseBody注解。
通过controller层调用方法batchAdd()是调用service层的batchAdd()。
测试类主入口:
java
@SpringBootApplication
@EnableTransactionManagement
public class Springboot01CenterTxApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot01CenterTxApplication.class, args);
}
}
@EnableTransactionManagement:启用Spring的注解驱动的事务管理功能。它使得Spring能够识别和处理类或方法上的@Transactional注解,从而管理事务边界,对有@Transactional的方法或类进行事务管理。
2、SpringBoot的异步任务
在项目开发中,绝大多数情况下都是通过同步方式处理业务逻辑 的,但是比如批量处理数据,批量发送邮件,批量发送短信等操作容易造成阻塞的情况,之前大部分都是使用多线程来完成此类任务,而在 Spring 3+之后,就已经内置了@Async注解来完美解决这个问题,从而提高效率。
异步执行 的核心概念 是任务在不同线程 中执行,而不是在同一个线程中按照顺序执行。具体来说,异步执行允许任务几乎同时 进行,但它们是在不同线程 中并行处理的,,而不是像同步执行那样在同一个线程中按顺序完成。
从controller层调用方法batchAdd()到service层:
service层代码:
java
@Service
public class CustomerServiceImp implements ICustomerService{
@Override
@Async
public void batchAdd() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("======>批量新增");
}
}
@Async:这是Spring提供的异步处理注解 。标记在方法上时,该方法将在单独的线程中异步执行,不会阻塞调用者线程。使用这个注解的前提是需要在配置类或启动类中启用异步功能,例如通过@EnableAsync来实现。
Thread.sleep(3000);: 这行代码让当前线程休眠3秒钟,模拟批量添加操作的延迟。
测试类主入口:
java
@SpringBootApplication
@EnableAsync // 开启异步处理
public class Springboot04CenterAysncApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot04CenterAysncApplication.class, args);
}
}
@EnableAsync:当你在启动类中使用了@EnableAsync时,Spring会扫描整个应用程序,识别并处理所有标记了@Async注解的方法 。标记了@Async的方法会在一个单独的线程池 中异步执行 ,而不是在主线程中执行。这使得主线程不会被这些方法阻塞,从而可以继续处理其他任务。
3、SpringBoot定时任务调度
SpringTask:在项目开发中,经常需要执行一些定时任务,比如每月1号凌晨需要汇总上个月的数据分析报表;每天凌晨分析前一天的日志信息等定时操作。Spring 为我们提供了异步执行定时任务调度的方式。
就比如我想要每隔4秒给我发送一个提醒:
之间创建一个MyJob类:
@Component
public class MyJob {
//秒 分 时 日 月 星期 年
// 从3秒开始每隔4秒执行一次
@Scheduled(cron = "3/4 00 22 15 8 ?")
public void show(){
System.out.println("陕西发布暴雪蓝色预警");
}
}
@Scheduled标识的方法会进行定时处理,需要通过 cron 属性来指定 cron 表达式:秒 分 时 日 月 星期 年。
在上面的代码中,'3/4'代表从第三秒开始,每个寺庙执行一次。'00'代表在 0 分钟时执行。其他的就是对应的时、日、月星期执行,一般不加年,加年会报错。
总体可理解为:在8月15号的22点00分第三秒的时候开始执行,每隔4秒执行一次。
测试类主入口:
@SpringBootApplication
@EnableScheduling // 开启任务调度
public class Springboot03CenterJobApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot03CenterJobApplication.class, args);
}
}
其中@EnableScheduling用于启用 Spring 的定时任务执行功能。当你在配置类上使用 @EnableScheduling
注解时,Spring 会扫描并识别使用了 @Scheduled
注解的方法,并按照指定的时间间隔或时间点执行这些方法。
4、SpringBoot整合Mail发送邮件
首先添加坐标:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
其次是配置yml信息,用来配置你的发送邮箱。
spring:
mail:
password: 邮箱授权码
username: 邮箱账号,不用加后缀
host: smtp.qq.com (邮箱类型)
properties:
smtp:
ssl:
enable: true
最后是在测试类中去完成方法并测试:
首先是简单的发送邮件,只有文字发送:
@Autowired(required = false)
private JavaMailSenderImpl javaMailSender;
//发送简单邮件
@Test
void show1() {
//1.创建邮件对象
SimpleMailMessage simpleMailMessage = new SimpleMailMessage();
//2.设置信息
simpleMailMessage.setSubject("疫情全面放开");
simpleMailMessage.setText("2022年好难,做了11个月的核酸,结果在第12个月阳了~~~");
simpleMailMessage.setFrom("?@qq.com");
simpleMailMessage.setTo("?@qq.com");
//3.发送邮件
javaMailSender.send(simpleMailMessage);
System.out.println("发送成功~~~");
}
这里面首先用使用 SimpleMailMessage类创建一个简单的邮件对象。
- setSubject("疫情全面放开"):设置邮件的主题为 "疫情全面放开"。
- setText("2022年好难,做了11个月的核酸,结果在第12个月阳了~~~"):设置邮件的正文内容。
- setFrom("?@qq.com"):设置发件人地址。
- setTo("?@qq.com"):设置收件人地址。
其次是发送复杂邮件,就比如带张照片等等操作均是复杂发送:
@Test
void show2()throws Exception {
//1.创建邮件对象
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
//2.创建MimeMessageHelper
MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage,true);
//3.设置信息
mimeMessageHelper.setSubject("程序员的误解");
mimeMessageHelper.setText("程序员是个<span style='color:red'>高薪,高危</span>的职业",true);
mimeMessageHelper.addAttachment("1.jpg",new File("?.jpg"));
mimeMessageHelper.setFrom("?4@qq.com");
mimeMessageHelper.setTo("?@qq.com");
//4.发送邮件
javaMailSender.send(mimeMessage);
}
其他的跟上面的代码一致,新增了一个addAttachment()方法,第一个参数为附件在邮件中显示的文件名,收件人下载附件时,看到的文件名将是这个参数指定的名称。第二个参数为这是一个 File 对象,表示需要附加到邮件中的实际文件。它可以是本地存储在文件系统中的任何文件。
5、Spring框架中的Bean的作用域
singleton:Spring只会为该bean对象只会创建唯一实例Spring中的bean默认都是单例:
prototype:每次获取bean,Spring会创建一个新的bean实例;
request:每一次HTTP请求,Spring会创建一个新的bean实例;
session:不同的HTTP会话,Spring会创建不同的bean实例;
通过XML方式设置bean的作用域:
XML
<bean id="demoDaoBean" class="com.apesource.dao.DemoDAOImpl" scope="singleton"/>
通过注解方式设置bean的作用域:
java
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class DemoDAOImpl implements IDemoDAO{ }
6、Spring框架中的Bean的线程安全
对于prototype 作用域的Bean,每次都创建一个新对象,也就是线程之间不存在Bean共享,因此不会有线程安全问题。
对于singleton 作用域的Bean,所有的线程都共享一个单例状态的Bean,存在资源竞争,因此是存在线程安全问题的。
解决办法:对于singleton作用域的单例bean ,它的线程安全问题,常见有两种解决办法:
- 在bean中尽量避免定义可变的成员变量(用于保存数据的成员变量);
- 在类中定义一个ThreadLocal 成员变量,将需要可变的成员变量保存 ThreadLocal 中;
7、 Spring框架中的Bean生命周期
Spring Bean的生命周期总体分四个阶段 :实例化=>属性注入=>初始化=>销毁。
Step1 实例化Bean:根据配置文件中Bean 的定义,利用| Java Reflection 反射技术创建Bean的实例。
Step2 注入对象依赖的属性值(或对象)。
Step3 处理各种Aware接口 :Spring会检测该Bean 是否实现了xxxAware 接口,通过Aware类型的接口,可以让Spring框架为当前 Bean 注入相应的内容。
- 如果Bean实现BeanNameAware接口,会调用它实现的setBeanName(String beanId)方法,注入Bean的名字;
- 如果Bean实现BeanClassLoaderAware 接口,调用setBeanClassLoader()方法,注入ClassLoader对象的实例;
- 如果Bean实现BeanFactoryAware接口,会调用它实现的setBeanFactory()方法,注入的是Spring工厂;
- 如果Bean实现ApplicationContextAware接口,会调用setApplicationContext()方法,注入Spring上下文;
Step4 执行BeanPostProcessor前置处理:如果想对Bean进行一-些自定义的前置处理, 那么可以让Bean实现了BeanPostProcessor 接口,将会在该阶段调用postProcessBeforeInitialization(0bject obj,String s)方法。
**Step5 执行InitializingBean初始化方法:**如果 Bean 实现了InitializingBean接口,执行afeterPropertiesSet()方法。
Step6 执行init-method自定义初始化方法:如果 Bean在Spring配置文件中配置了init-method属性,则会自动调用其配置的初始化方法。
Step7 执行BeanPostProcessor后置处理:如果这个Bean 实现了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法,由于这个方法是在Bean初始化结束后调用;
以上几个步骤完成后, Bean已经被正确创建,可以正常使用这个Bean了。
Step8 执行DisposableBean销毁Bean:当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean 这个接口,会调用其实现的destroy() 方法执行销毁;
Step9 执行destroy-method自定义销毁方法 :如果这个Bean的Spring配置中配置了destroy
-method属性,会自动调用其配置的自定义销毁方法。
8、Spring框架如何解决循环依赖?
循环依赖问题是指:类与类之间的依赖关系形成了闭环,就会导致循环依赖问题的产生。例如A类依赖了B类,B类依赖了C类,而最后C类又依赖了A类,这样就形成了循环依赖问题;
9、Spring框架中有哪些注解?
用于声明Bean的注解:
- @Component:定义通用Bean 的注解,可标注任意类为Bean。如果一个Bean不知道属于哪个层,可以使用@Component 注解标注。
- @Repository:定义数据访问层Bean的注解。
- @Service:定义业务层Bean的注解。
- @Controller:定义控制器Bean的注解。
用于注入的注解:
- @Autowired:按类型自动注入
- @Qualifier:按名称自动注入
声明配置、扫描、启用特性的注解:
- @Configuration:声明配置类
- @ComponentScan:组件扫描
- @EnableScheduling:启用任务调度
- @EnableAspectJAutoProxy:启用自动代理工厂
10、Spring框架中用到的设计模式
- 工厂模式:Spring 使用工厂模式,通过BeanFactory 或Applicat ionContext来创建对象;
- 单例模式:Bean 默认作用域为单例,按照单例设计模式进行设计实现;
- 策略模式:Resource 的实现类,针对不同的资源文件,实现了不同方式的资源获取策略;
- 代理模式:Spring的AOP的实现依靠动态代理(JDK的反射和CGLIB);
- 模板方法:Spring提供了JdbcTemplate,RedisTemplate 等模板对象,将相同的操作步骤进行了封装;
- 适配器模式:Spring AOP的增强或通知( Advice )使用到了适配器模式, Spring MVC中也用到了适配器模式适配Controller;