Spring 知识点 应用 记录

导读:本篇文章讲解 Spring 知识点 应用 记录,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

BeanFactory

创建bean的时候创建 FactoryBean 也是一样的。 如下 代码,参考自 spring jpa
bean Name 是 entityManagerFactoryXXX 类型为 EntityManagerFactory 类型。不是方法返回类型LocalContainerEntityManagerFactoryBean 。

@Bean(name = "entityManagerFactoryXXX")
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() {

        HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        vendorAdapter.setGenerateDdl(true);

        LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
        factory.setJpaVendorAdapter(vendorAdapter);
//        LocalContainerEntityManagerFactoryBean  类 配置的就是 entity 的路径,扫描该包下的@entity注解,对应 @EnableJpaRepositories 配置的就是 Repositorie的路径
        factory.setPackagesToScan("com.xxx.xxx");
        factory.setDataSource(dataSource());
//        EntityManagerFactory entityManagerFactory = factory.getObject();
        return factory;

BeanFactory 原理(spring怎么利用的) 主要代码 boolean isFactoryBean = isFactoryBean(beanName, mbd);
-》根据beanType 获取bean时 : spring 查找自己的管理的所有bean , 发现 entityManagerFactoryXXX 的类型为BeanFactory (视为非实际类型《自己想的名词》)特殊处理 调用 getObjectType 来确定实际的类型。实际类型与 目标类型匹配了,就匹配了。
-》根据beanName 获取bean时:直接索引到了entityManagerFactoryXXX 发现是 BeanFactory 直接调用 getObject 就拿到了具体的实体。
参考自 csdn
关联原理 : spring 根据type 获取 springBean 流程: 1、获取 beanNames (所有的beanName) 循环 beanName 来获取 bean。2、再判断bean的type 是否匹配(包括BeanFactory的特殊处理)。

spring创建代理的代码处

  1. DefaultAopProxyFactory
    在这里插入图片描述

cglib 创建类的代码处

  1. AbstractClassGenerator在这里插入图片描述
  2. Enhancer
    在这里插入图片描述

Application Properties


JPA

再做JPA 逆向生成表时遇到 问题 No identifier specified for entity: XXX.XXX.XXX

没有找到id 在 entity中?有啊。 仔细看了代码 发现 @id 引得包是 import org.springframework.data.annotation.Id 而正确的@id 需引入 javax.persistence.Id

我靠今天发现 javax.persistence.Id 是在javax.persistence 包下。这个包是由 spring-boot-start-data-jpa引入不是java原生的

配置Hibernate 的 映射方式

#决定 sql 的列名 映射为 bean中的属性 名
spring.jpa.hibernate.implicit_naming_strategy=org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy
#决定 bean中的属性 名 映射为 sql 的列名
spring.jpa.hibernate.physical_naming_strategy=org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
参考自 : hibernate 官网 用户指南

表关联

各种关联关系 参考 SpringBoot重点详解–@JoinColumn注解
不生成外键 JPA不生成外键

JPA 缓存问题

同一事务下同条件俩次查询返回的是一个对象,没走数据库。这会导致 第二次查询感知不到任何中间的update操作。
场景/现象:伪代码1、findOne 。2、自己手写的sql来update3、findOne 。现象 1和3操作返回的对象都是同一个,直接缓存取得没走数据库。
在这里插入图片描述
相关场景验证:

  1. 不在事务中 俩条同条件查询 走了俩次数据库。俩次返回不是同一对象。
  2. 在MySQL命令行中开启事务 第二次查询可以查询到 中间所做修改。
    用到命令 set session transaction isolation level repeatable read ;设置MySQL 事务隔离级别为可重复读。
    解决:添加 JPA注解让自己的sql刷新缓存,@Modifying(clearAutomatically=true,flushAutomatically = true)
    参考:https://blog.csdn.net/weixin_43770545/article/details/103732082

Spring 的BeanUtils.copyProperties在拷贝属性时忽略空值

先将原始值 赋值到 updateObj
BeanUtils.copyProperties(oldObj,updateObj);
再将预期值 赋值到 updateObj
BeanUtils.copyProperties(newObj,updateObj,getNullPropertyNames(newValue));

 public static String[] getNullPropertyNames (Object source) {
        final BeanWrapper src = new BeanWrapperImpl(source);
        java.beans.PropertyDescriptor[] pds = src.getPropertyDescriptors();

        Set<String> emptyNames = new HashSet<String>();
        for(java.beans.PropertyDescriptor pd : pds) {
            Object srcValue = src.getPropertyValue(pd.getName());
            if (srcValue == null) emptyNames.add(pd.getName());
        }
        String[] result = new String[emptyNames.size()];
        return emptyNames.toArray(result);
    }

BeanDefinitionRegistryPostProcessor接口 将类注入到spring

参考 : https://blog.csdn.net/lichuangcsdn/article/details/89694363

ApplicationListener 容器初始化后调用 (可实现获取spring bean包含某个注解)

参考 : https://www.iteye.com/blog/fanyc-2224809


Spring Data Rest 使用记录

  1. get 的sort curl -v “http://localhost:8080/people/search/nameStartsWith?name=K&sort=name,desc”

  2. update的url: MockMvcRequestBuilders.patch(“/user/1”)
    这里的user/1来源 是get中 _embedded.users[0]._links.self.href 里的值 。Restful 风格妙啊。


Spring Bean 继承间的关系

接口A继承接口B 。接口A继承接口C。
Spring 代理生成的 A@xxxx会包含B@xxxx的方法以及C@xxxx的方法。
灵感来自 :XxxRepository 实现自多个接口,JpaRepository,JpaSpecificationExecutor


Spring Boot Test debug不进入Controller中的断点

单元测试的类中对Controller加了@MockBean注解

//    @MockBean 这就是罪魁祸首
    @Autowired
    private XxxController xxxController;

自定义注解的类注入Spring,类似于mybits的类注入

参考自:auto-generate-enum-api-parent

  1. 先获取所有的类的Set集合 (Set<Class<?>>),一般用包扫描
  2. 根据每个Class创建一个class文件)用for循环
// classPool 是static的 获取classPoll
            ClassPool classPool = ClassPool.getDefault();
            classPool.importPackage("java.util");
            classPool.importPackage("java.lang");
            // 创建class 根据classPoll
            CtClass ctClass = classPool.makeClass(prefix + clazz.getSimpleName());
            // 创建class文件
            ClassFile classFile = ctClass.getClassFile();
            // 获取class文件的常量池,后续代码使用
            ConstPool constPool = classFile.getConstPool();
            // class文件加注解 
            AnnotationsAttribute annotationsAttribute = generateAnnotationsAttribute(classPool, constPool, config.getBasePath());
            classFile.addAttribute(annotationsAttribute);

            // 为class增加方法  ,用ctMethod
            CtMethod ctMethod = generateMethod(args);
            generateController.addMethod(ctMethod);
  1. 将自己新创建的Class 注入到spring。使用BeanDefinition。
for (Class clazz : enumControllerGenerator.generateClazz()) {
            BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
            GenericBeanDefinition definition = (GenericBeanDefinition) builder.getRawBeanDefinition();
            definition.setBeanClass(clazz);
            definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
            registry.registerBeanDefinition(clazz.getSimpleName(), definition);
        }

Spring注入bean的几种约定方式 / 或者叫自己编辑配置类

  1. 直接注入bean
  2. 使用beanFactory
    自己理解就是给Spring传 一个创建者模式就可以创建bean并注入到Spring

Spring注入bean的几种约定方式

自己记录 :
思路:@Import 的注入bean到spring容器。spring注解之@Import注解的三种使用方式

  1. 包扫描 直接注入类
  2. 配置类的全路径
  3. 调用Registrar
  4. 调用BeanDefinitionRegistrar
  5. 用spring提供的@import 这个是导入第三方包常用。怕你没法给jar里加@bean不会引入包。

Spring 不跨bean调用注解(@Transactional)不生效解决方案

SpringUtils.getBean(this).function()

@Component
public class SpringUtils implements ApplicationContextAware {
    private static ApplicationContext context;

    public SpringUtils() {
    }

    public void setApplicationContext(ApplicationContext applicationContext) {
        context = applicationContext;
    }

    public static ApplicationContext getApplicationContext() {
        return context;
    }

    public static <T> T getBean(String name) {
        return (T) context.getBean(name);
    }

    public static <T> T getBean(T obj) {
        return (T) context.getBean(obj.getClass());
    }

    public static <T> T getBean(Class<T> clazz) {
        return context.getBean(clazz);
    }

    public static <T> T getBean(String name, Class<T> clazz) {
        return context.getBean(name, clazz);
    }
}

Spring Bean Factory 获取bean流程

AbstractApplicationContext 流程
@Override
	public <T> T getBean(Class<T> requiredType) throws BeansException {
		assertBeanFactoryActive();
		return getBeanFactory().getBean(requiredType);
	}

Bean Factory的具体实现类基本都是调用的 AbstractApplicationContext中的方法尤其是get类型的方法。所以直接看这里。

自己多态了下参数,
@Override
	public <T> T getBean(Class<T> requiredType, @Nullable Object... args) throws BeansException {
		Assert.notNull(requiredType, "Required type must not be null");
		//ResolvableType 就是把  java 的Type 封装了一下
		Object resolved = resolveBean(ResolvableType.forRawClass(requiredType), args, false);
		if (resolved == null) {
			throw new NoSuchBeanDefinitionException(requiredType);
		}
		return (T) resolved;
	}
  1. resolveNamedBean通过beanname来取bean
  2. 取不到就重父bean factory 取。
@Nullable
	private <T> T resolveBean(ResolvableType requiredType, @Nullable Object[] args, boolean nonUniqueAsNull) {
	// 这里先去根据bean name  来取bean  resolveNamedBean方法中有个 String[] candidateNames = getBeanNamesForType(requiredType);的操作
		NamedBeanHolder<T> namedBean = resolveNamedBean(requiredType, args, nonUniqueAsNull);
		if (namedBean != null) {
			return namedBean.getBeanInstance();
		}
		BeanFactory parent = getParentBeanFactory();
		if (parent instanceof DefaultListableBeanFactory) {
			return ((DefaultListableBeanFactory) parent).resolveBean(requiredType, args, nonUniqueAsNull);
		}
		else if (parent != null) {
			ObjectProvider<T> parentProvider = parent.getBeanProvider(requiredType);
			if (args != null) {
				return parentProvider.getObject(args);
			}
			else {
				return (nonUniqueAsNull ? parentProvider.getIfUnique() : parentProvider.getIfAvailable());
			}
		}
		return null;
	}

bean name 来自this.beanDefinitionNames 。this 是一个bean factory
bean name 还来之 this.manualSingletonNames

DefaultListableBeanFactory
private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) {
		List<String> result = new ArrayList<>();

		// Check all bean definitions.
		for (String beanName : this.beanDefinitionNames) {
		//n行代码
		// 这里来匹配name  和 type
matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit);
}
for (String beanName : this.manualSingletonNames) {// 一堆判断}

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/76477.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!