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创建代理的代码处
- DefaultAopProxyFactory
cglib 创建类的代码处
- AbstractClassGenerator
- Enhancer
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操作返回的对象都是同一个,直接缓存取得没走数据库。
相关场景验证:
- 不在事务中 俩条同条件查询 走了俩次数据库。俩次返回不是同一对象。
- 在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 使用记录
-
get 的sort curl -v “http://localhost:8080/people/search/nameStartsWith?name=K&sort=name,desc”
-
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
- 先获取所有的类的Set集合 (Set<Class<?>>),一般用包扫描
- 根据每个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);
- 将自己新创建的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的几种约定方式 / 或者叫自己编辑配置类
- 直接注入bean
- 使用beanFactory
自己理解就是给Spring传 一个创建者模式就可以创建bean并注入到Spring
Spring注入bean的几种约定方式
自己记录 :
思路:@Import 的注入bean到spring容器。spring注解之@Import注解的三种使用方式
- 包扫描 直接注入类
- 配置类的全路径
- 调用Registrar
- 调用BeanDefinitionRegistrar
- 用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;
}
- resolveNamedBean通过beanname来取bean
- 取不到就重父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