@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; } }
@Scope作用域的取值
singleton 单例模式(默认)
prototype 多例模式
request 同一个请求
session 同一个会话
在不指定@scope情况下,所有的bean默认都是单例的,是饿汉式加载,IOC容器启动的时候就好创建实例对象
将@scope指定为prototype时,表示为多实例的,是懒汉式加载。IOC容器启动的时候不会创建实例对象,只有第一次使用的时候才会去创建
加@Lazy的Bean在第一次使用的时候才会触发创建实例对象
@Bean @Lazy public Person person() { return new Person(); }
首先实现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容器基本实现是Spring内部接口的使用接口,不提供给开发人员进行使用(加载配置文件时候不会创建对象,在获取对象时才会创建对象。)
BeanFactory接口的子接口,提供更多更强大的功能,提供给开发人员使用(加载配置文件时候就会把在配置文件对象进行创建)
@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
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的创建—>初始化—>销毁方法,由Spring容器管理Bean的生命周期,可以指定Bean的初始化方法和销毁方法
针对单实例bean的话,容器启动的时候,bean的对象就创建了,而且容器销毁的时候,也会调用Bean的销毁方法。 针对多实例bean的话,容器启动的时候,bean是不会被创建的而是在获取bean的时候被创建,而且bean的销毁不受IOC容器的管理。
具体步骤:
通过构造器创建Bean实例(无参构造方法)
为Bean设置属性值和对其他Bean的引用(调用set方法)
把Bean实例传递给Bean的后置处理器方法
调用Bean的初始化方法
把Bean实例传递给Bean的后置处理器方法
使用Bean
当容器关闭时,调用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
销毁方法
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方法
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
销毁方法
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可以对业务逻辑的各个部分进行隔离,降低各个模块的耦合度,提高程序的可重用性。
通俗的讲:不改变原来代码的情况下,增加新的功能。
有接口--JDk动态代理
创建接口实现类的代理对象,增强类的方法
没有接口--CGLIB动态代理
创建当前类子类的代理对象,增强类的方法
连接点
类里面的哪些方法可以被增强,这些方法叫做连接点
切入点
实际真正被增强的方法叫做切入点
通知(增强)
实际增强的逻辑部分叫做通知,通知分为5种类型
前置通知 @Before
后置通知 @AfterReturning
环绕通知 @Around
异常通知 @AfterThrowing
最终通知 @After
切面
把通知应用到切入点的过程叫做切面
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; } }
执行顺序:环绕通知前 -->前置通知-->目标方法-->环绕通知后-->最终通知-->后置通知
异常执行顺序:环绕通知前 -->前置通知-->目标方法-->最终通知-->异常通知
@Transactional(propagation=Propagation.NOT_SUPPORTED) 容器不为这个方法开启事务
@Transactional(propagation=Propagation.REQUIRES_NEW) 不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务
@Transactional(propagation=Propagation.MANDATORY) 必须在一个已有的事务中执行,否则抛出异常
@Transactional(propagation=Propagation.NEVER) 以非事务方式执行,如果存在事务则抛出异常(与Propagation.MANDATORY相反)
@Transactional(propagation=Propagation.SUPPORTS) 如果其他bean调用这个方法,在其他bean中声明事务,那就用事务.如果其他bean没有声明事务,那就不用事务.
@Transactional(propagation=Propagation.NESTED)
如果当前事务存在,则在嵌套事务中执行,否则行为类似于REQUIRED ,
传播属性 | 描述 |
---|---|
REQUIRED | 如果有事务在运行,当前的方法就在这个事务内运行,否则就开启一个新的事务,并在自己的事务内运行 |
REQUIRED_NEW | 当前方法必须启动新的事务,并在它自己的事务中运行,如果有事务正在运行,则将它挂起 |
SUPPORTS | 如果有事务在运行,当前的方法就在该事务内运行,否则他可以不运行在事务中 |
NOT_SUPPORTED | 当前的方法不应该运行在事务中,如果有运行的事务,将它挂起 |
MANDATORY | 当前方法必须运行在事务中,如果没有正在运行的事务,则抛出异常 |
NEVER | 当前的方法不应该运行在事务中,如果有正在运行的事务,则抛出异常 |
NESTED | 如果有事务在运行,当前方法就应该在这个事务的嵌套事务内运行,否则就启动一个新的事务,并在事务中运行 |
READ UNCOMMITTED(未提交读):事务中的修改,即使没有提交,对其他事务也都是可见的。事务可以读取未提交的数据,这也被称为脏读。
READ COMMITTED(提交读):一个事务从开始直到提交之前,所做的任何修改对其他事务都是不可见的。这个级别有时候也叫做不可重复读,因为两次执行同样的查询,可能会得到不一样的结果。
REPEATABLE READ(可重复读):解决了脏读的问题,MySQL的默认事务隔离级别。该级别保证了在同一个事务中多次读取同样记录的结果是一致的。可重复读隔离级别还是无法解决另外一个幻读的问题。幻读指的是当某个事务在读取某个范围内的记录时,另外一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围的记录时,会产生幻行。
SERIALIZABLE(可串行化)。最高的隔离级别。它通过强制事务串行执行,避免了幻读的问题。会在读取的每一行数据上都加锁,可能导致大量的超时和锁争用的问题。
隔离级别 | 脏读 | 不可重读 | 幻读 | 加锁读 |
---|---|---|---|---|
未提交读 | Yes | Yes | Yes | No |
提交读 | No | Yes | Yes | No |
可重复读 | No | No | Yes | No |
可串行化 | No | No | No | Yes |
设置方法:@Transactional(isolation = Isolation.REPEATABLE_READ)
不设置,默认和数据库隔离级别一致
超时时间, 默认为-1,不超时。单位秒
是否只读,默认false
回滚,设置哪些异常进行回滚
不回滚,设置哪些异常不进行回滚
框架代码基于JDK1.8,运行时兼容JDK1.9
自带了通用的日志封装
支持@Nullable注解,表示方法、属性、参数可以为空
支持函数式注册对象
整合Junit5单元测试
WebFlux,与Spring MVC不同,它不需要Servlet API,是完全异步且非阻塞的,并且通过Reactor项目实现了Reactive Streams规范。
原文:https://www.cnblogs.com/Dzsom/p/15046391.html