首页 > 编程语言 > 详细

记一下Spring

时间:2021-07-22 23:47:33      阅读:22      评论:0      收藏:0      [点我收藏+]

IOC

Spring包扫描注解

 @SpringBootApplication
 @ComponentScan(basePackages = {"com.shemy.spring"},
         // 排除
         excludeFilters = {
         @ComponentScan.Filter(type = FilterType.ANNOTATION, value = {Controller.class}),
         @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = {ServiceA.class})},
         // 包含
         includeFilters = {
         @ComponentScan.Filter(type = FilterType.CUSTOM, value = CustomTypeFilter.class)},
         // FilterType.CUSTOM要生效需要将useDefaultFilters设置成false
         useDefaultFilters = false)
 public class Application {
 ?
     public static void main(String[] args) {
         ConfigurableApplicationContext run = SpringApplication.run(Application.class, args);
         String[] beanDefinitionNames = run.getBeanDefinitionNames();
         for (String beanDefinitionName : beanDefinitionNames) {
             System.out.println("bean定义信息:" + beanDefinitionName);
         }
     }
 ?
 }

 

FilterType类型:

  • ANNOTATION

    • 需要排除/包含的注解

  • ASSIGNABLE_TYPE

    • 需要排除/包含的类型

  • ASPECTJ

    • 匹配给定 AspectJ 类型模式表达式

  • REGEX

    • 匹配正则表达式

  • CUSTOM

    • 自定义的,创建一个类实现TypeFilter接口方法

    • 可以根据class信息匹配规则

    •  public class CustomTypeFilter implements TypeFilter {
           @Override
           public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
               // 获取当前类的注解源信息
               AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
       ?
               // 获取当前类的class的源信息
               ClassMetadata classMetadata = metadataReader.getClassMetadata();
       ?
               // 获取当前类的资源信息
               Resource resource = metadataReader.getResource();
       ?
               if (classMetadata.getClassName().contains("service")) {
                   return true;
               }
               return false;
           }
       }

       

bean的作用域 @scope

  • @Scope作用域的取值

    1. singleton 单例模式(默认)

    2. prototype 多例模式

    3. request 同一个请求

    4. session 同一个会话

  • 在不指定@scope情况下,所有的bean默认都是单例的,是饿汉式加载,IOC容器启动的时候就好创建实例对象

  • 将@scope指定为prototype时,表示为多实例的,是懒汉式加载。IOC容器启动的时候不会创建实例对象,只有第一次使用的时候才会去创建

 

懒加载@Lazy

加@Lazy的Bean在第一次使用的时候才会触发创建实例对象

 @Bean
     @Lazy
     public Person person() {
         return new Person();
     }

条件判断@Conditional

首先实现Condition,重写matches方法的判断逻辑

 public class CustomConditional implements Condition {
     @Override
     public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
         // 判断是否有serviceA实例对象,如果有返回true
         if (Objects.requireNonNull(context.getBeanFactory()).containsBean("serviceA")) {
             return true;
         }
         return false;
     }
 }
 @Component
 public class ConditionTest {
 ?
     @Bean
     public ServiceA serviceA() {
         return new ServiceA();
     }
 ?
     @Bean
     // 如果有ServiceA实例对象,则创建serviceB对象
     @Conditional(value = CustomConditional.class)
     public ServiceB serviceB() {
         return new ServiceB();
     }
 }

 

IOC容器实现的两种方式

BeanFactory接口

IOC容器基本实现是Spring内部接口的使用接口,不提供给开发人员进行使用(加载配置文件时候不会创建对象,在获取对象时才会创建对象。)

ApplicationContext接口

BeanFactory接口的子接口,提供更多更强大的功能,提供给开发人员使用(加载配置文件时候就会把在配置文件对象进行创建)

IOC容器添加组件的方式

@Bean

@CompenScan --> @Controller @Service @Compent @Repository

@Import(自动装配原理)

@SpringBootApplication
 // 可以导入组件、实现ImportSelector(重写selectImports,通过全类名路径导入)、
 // 实现ImportBeanDefinitionRegistrar(重写registerBeanDefinitions, 构造一个RootBeanDefinitions,通过注册器注册到容器中)
 // 应用场景:导入第三方组件
 @Import({ServiceA.class, ServiceB.class})
 public class Application {
 ?
     public static void main(String[] args) {
         ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
         String[] beanDefinitionNames = context.getBeanDefinitionNames();
         for (String beanDefinitionName : beanDefinitionNames) {
             System.out.println("bean定义信息:" + beanDefinitionName);
         }
     }
 }

 

FactoryBean

实现FactoryBean

 public class CustomFactoryBean implements FactoryBean<ServiceA> {
     @Override
     public ServiceA getObject() throws Exception {
         return new ServiceA();
     }
 ?
     @Override
     public Class<?> getObjectType() {
         return ServiceA.class;
     }
 ?
     @Override
     public boolean isSingleton() {
         return true;
     }
 }
 @Configuration
 public class MyConfig {
 ?
     @Bean
     public CustomFactoryBean customFactoryBean() {
         return new CustomFactoryBean();
     }
 }
 @SpringBootApplication
 public class Application {
 ?
     public static void main(String[] args) {
         ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
 ?
         Object serviceA = context.getBean("customFactoryBean");
         System.out.println(serviceA);
 ?
         // 加个&可以获取FactoryBean实例
         Object serviceA1 = context.getBean("&customFactoryBean");
         System.out.println(serviceA1);
     }
 }

输出

 com.shemy.spring.transactional.ServiceA@d0ec63
 com.shemy.spring.transactional.CustomFactoryBean@5a5c128

Bean的生命周期

从bean的创建—>初始化—>销毁方法,由Spring容器管理Bean的生命周期,可以指定Bean的初始化方法和销毁方法

针对单实例bean的话,容器启动的时候,bean的对象就创建了,而且容器销毁的时候,也会调用Bean的销毁方法。 针对多实例bean的话,容器启动的时候,bean是不会被创建的而是在获取bean的时候被创建,而且bean的销毁不受IOC容器的管理。

具体步骤:

  1. 通过构造器创建Bean实例(无参构造方法)

  2. 为Bean设置属性值和对其他Bean的引用(调用set方法)

  3. 把Bean实例传递给Bean的后置处理器方法

  4. 调用Bean的初始化方法

  5. 把Bean实例传递给Bean的后置处理器方法

  6. 使用Bean

  7. 当容器关闭时,调用Bean的销毁方法

通过自己指定的初始化方法和销毁方法

@Configuration
 public class MyConfig {
 ?
     // 指定init方法和destroy方法
     @Bean(initMethod = "init", destroyMethod = "destroy")
     public Person person() {
         return new Person();
     }
 }
 ?
 public class Person {
 ?
     public Person() {
         System.out.println("构造方法");
     }
 ?
     public void init() {
         System.out.println("初始化方法");
     }
 ?
     public void destroy() {
         System.out.println("销毁方法");
     }
 }
 ?
 @SpringBootApplication
 public class Application {
 ?
     public static void main(String[] args) {
         ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
 ?
         Object person = context.getBean("person");
         System.out.println(person);
     }
 }
输出结果
 构造方法
 初始化方法
 com.shemy.spring.transactional.Person@37d00a23
 销毁方法

通过实现InitializingBean和DisposableBean的接口,实现Bean的初始化和销毁方法

 public class Person2 implements InitializingBean, DisposableBean {
 ?
     @Override
     public void destroy() throws Exception {
         System.out.println("DisposableBean的destroy方法");
     }
 ?
 ?
     @Override
     public void afterPropertiesSet() throws Exception {
         System.out.println("InitializingBean 的afterPropertiesSet方法");
     }
 }
 ?
 @Configuration
 public class MyConfig2 {
 ?
     @Bean
     public Person2 person2() {
         return new Person2();
     }
 }

输出结果

 InitializingBean 的afterPropertiesSet方法
 com.shemy.spring.init.two.Person2@3c2772d1
 DisposableBean的destroy方法

通过JSR250规范提供的注解@PostConstruct 和@PreDestroy注解实现Bean的初始化和销毁方法

 public class Person3 {
     
     public Person3() {
         System.out.println("构造方法");
     }
 ?
     @PostConstruct
     public void init() {
         System.out.println("初始化方法");
     }
 ?
     @PreDestroy
     public void destroy() {
         System.out.println("销毁方法");
     }
 }
 ?
 @Configuration
 public class MyConfig3 {
 ?
     @Bean
     public Person3 person3() {
         return new Person3();
     }
 }

输出结果

 构造方法
 初始化方法
 com.shemy.spring.init.sr250.Person3@7ff2b8d2
 销毁方法

通过Spring的BeanPostProcessor 的bean的后置处理器,会拦截所有bean的创建过程

  • postProcessBeforeInitialization在init之前调用

  • postProcessAfterInitialization在init之后调用

@Component
 public class PersonBeanPostProcessor implements BeanPostProcessor {
 ?
     @Override
     public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
         System.out.println("初始化之前:" + beanName);
         return bean;
     }
 ?
     @Override
     public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
         System.out.println("初始化之后" + beanName);
         return bean;
     }
 }

 

自动装配

@Autowired

只按照byType 注入,想使用按名称装配,可以结合@Qualifier注解一起使用

@Resource

默认按byName自动注入,也提供按照byType 注入;

AOP

什么是AOP

面向切面编程,利用AOP可以对业务逻辑的各个部分进行隔离,降低各个模块的耦合度,提高程序的可重用性。

通俗的讲:不改变原来代码的情况下,增加新的功能。

实现原理—动态代理

  1. 有接口--JDk动态代理

    创建接口实现类的代理对象,增强类的方法

  2. 没有接口--CGLIB动态代理

    创建当前类子类的代理对象,增强类的方法

AOP术语

  1. 连接点

    类里面的哪些方法可以被增强,这些方法叫做连接点

  2. 切入点

    实际真正被增强的方法叫做切入点

  3. 通知(增强)

    实际增强的逻辑部分叫做通知,通知分为5种类型

    • 前置通知 @Before

    • 后置通知 @AfterReturning

    • 环绕通知 @Around

    • 异常通知 @AfterThrowing

    • 最终通知 @After

  4. 切面

    把通知应用到切入点的过程叫做切面

AspectJ

Spring AOP支持的AspectJ切入点指示符

  • execution:用于匹配方法执行的连接点;

  • within:用于匹配指定类型内的方法执行;

  • this:用于匹配当前AOP代理对象类型的执行方法;注意是AOP代理对象的类型匹配,这样就可能包括引入接口也类型匹配;

  • target:用于匹配当前目标对象类型的执行方法;注意是目标对象的类型匹配,这样就不包括引入接口也类型匹配;

  • args:用于匹配当前执行的方法传入的参数为指定类型的执行方法;

  • @within:用于匹配所以持有指定注解类型内的方法;

  • @target:用于匹配当前目标对象类型的执行方法,其中目标对象持有指定的注解;

  • @args:用于匹配当前执行的方法传入的参数持有指定注解的执行;

  • @annotation:用于匹配当前执行方法持有指定注解的方法;

  • bean:Spring AOP扩展的,AspectJ没有对于指示符,用于匹配特定名称的Bean对象的执行方法;

  • reference pointcut:表示引用其他命名切入点,只有@ApectJ风格支持,Schema风格不支持。

 
@Aspect
 @Component
 public class CustomAdvice {
 ?
     @Pointcut("@annotation(com.shemy.spring.annotation.Log)")
     private void myPoint() {
     }
 ?
     @Before("myPoint()")
     public void before(JoinPoint joinPoint) {
         String name = joinPoint.getSignature().getName();
         Object[] args = joinPoint.getArgs();
         System.out.println("前置通知: 方法:" + name + ",参数:" + Arrays.toString(args));
     }
 ?
     @AfterReturning(pointcut = "myPoint()", returning = "result")
     public void afterReturning(JoinPoint joinPoint, Object result) {
         System.out.println("后置通知:" + result);
     }
 ?
     @AfterThrowing(pointcut = "myPoint()", throwing = "e")
     public void afterThrowing(JoinPoint joinPoint, Exception e) {
         System.out.println("异常通知:" + e);
     }
 ?
     @After("myPoint()")
     public void after(JoinPoint joinPoint) {
         System.out.println("最终通知");
     }
 ?
     @Around("myPoint()")
     public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
         System.out.println("环绕通知--前");
         Object proceed = joinPoint.proceed();
         System.out.println("环绕通知--后");
         return proceed;
     }
 }

 

执行顺序:环绕通知前 -->前置通知-->目标方法-->环绕通知后-->最终通知-->后置通知

技术分享图片

异常执行顺序:环绕通知前 -->前置通知-->目标方法-->最终通知-->异常通知

技术分享图片

事务

Spring事务的传播机制

  1. @Transactional(propagation=Propagation.REQUIRED) 如果有事务, 那么加入事务, 没有的话新建一个(默认情况下)

  2. @Transactional(propagation=Propagation.NOT_SUPPORTED) 容器不为这个方法开启事务

  3. @Transactional(propagation=Propagation.REQUIRES_NEW) 不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务

  4. @Transactional(propagation=Propagation.MANDATORY) 必须在一个已有的事务中执行,否则抛出异常

  5. @Transactional(propagation=Propagation.NEVER) 以非事务方式执行,如果存在事务则抛出异常(与Propagation.MANDATORY相反)

  6. @Transactional(propagation=Propagation.SUPPORTS) 如果其他bean调用这个方法,在其他bean中声明事务,那就用事务.如果其他bean没有声明事务,那就不用事务.

  7. @Transactional(propagation=Propagation.NESTED)

    如果当前事务存在,则在嵌套事务中执行,否则行为类似于REQUIRED ,

    NESTED是 如果当前事务出现异常,本身进行回滚操作,但是父任务不会进行回滚操作 ,不过如果父任务发生异常,子任务会进行回滚操作 。

传播属性描述
REQUIRED 如果有事务在运行,当前的方法就在这个事务内运行,否则就开启一个新的事务,并在自己的事务内运行
REQUIRED_NEW 当前方法必须启动新的事务,并在它自己的事务中运行,如果有事务正在运行,则将它挂起
SUPPORTS 如果有事务在运行,当前的方法就在该事务内运行,否则他可以不运行在事务中
NOT_SUPPORTED 当前的方法不应该运行在事务中,如果有运行的事务,将它挂起
MANDATORY 当前方法必须运行在事务中,如果没有正在运行的事务,则抛出异常
NEVER 当前的方法不应该运行在事务中,如果有正在运行的事务,则抛出异常
NESTED 如果有事务在运行,当前方法就应该在这个事务的嵌套事务内运行,否则就启动一个新的事务,并在事务中运行

事务的隔离级别

  1. READ UNCOMMITTED(未提交读):事务中的修改,即使没有提交,对其他事务也都是可见的。事务可以读取未提交的数据,这也被称为脏读。

  2. READ COMMITTED(提交读):一个事务从开始直到提交之前,所做的任何修改对其他事务都是不可见的。这个级别有时候也叫做不可重复读,因为两次执行同样的查询,可能会得到不一样的结果。

  3. REPEATABLE READ(可重复读):解决了脏读的问题,MySQL的默认事务隔离级别。该级别保证了在同一个事务中多次读取同样记录的结果是一致的。可重复读隔离级别还是无法解决另外一个幻读的问题。幻读指的是当某个事务在读取某个范围内的记录时,另外一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围的记录时,会产生幻行。

  4. SERIALIZABLE(可串行化)。最高的隔离级别。它通过强制事务串行执行,避免了幻读的问题。会在读取的每一行数据上都加锁,可能导致大量的超时和锁争用的问题。

隔离级别脏读不可重读幻读加锁读
未提交读 Yes Yes Yes No
提交读 No Yes Yes No
可重复读 No No Yes No
可串行化 No No No Yes

设置方法:@Transactional(isolation = Isolation.REPEATABLE_READ)

不设置,默认和数据库隔离级别一致

timeout

超时时间, 默认为-1,不超时。单位秒

readOnly

是否只读,默认false

rollbackFor

回滚,设置哪些异常进行回滚

norollbackFor

不回滚,设置哪些异常不进行回滚

 

Spring5的新特性

  1. 框架代码基于JDK1.8,运行时兼容JDK1.9

  2. 自带了通用的日志封装

  3. 支持@Nullable注解,表示方法、属性、参数可以为空

  4. 支持函数式注册对象

  5. 整合Junit5单元测试

  6. WebFlux,与Spring MVC不同,它不需要Servlet API,是完全异步且非阻塞的,并且通过Reactor项目实现了Reactive Streams规范。

    【spring-webflux + Reactor + Netty】响应式的、异步非阻塞的

记一下Spring

原文:https://www.cnblogs.com/Dzsom/p/15046391.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!