Bean加载到初始化一览图
Bean的加载
package com.zhao;
import com.zhao.Interface.PressService;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
public class SourceCodeLearning {
public static void main(String[] args) {
//从容器中获取名字为user的bean
BeanFactory bf = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
PressService pressService = (PressService) bf.getBean("pressService");
//调用bean的方法
String price = pressService.say();
System.out.println(price);
}
}
PressService pressService = (PressService) bf.getBean("pressService"); 这句代码做了bean的加载和初始化。跟进源码,得到
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
protected T doGetBean(final String name, @Nullable final Class requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
// 1 提取对应的beanName
final String beanName = transformedBeanName(name);
Object bean;
/**
* 检查缓存中或者实例工厂中是否有对应的实例
* 为什么首先会使用这段代码呢,
* 因为在创建单例bean的时候会存在依赖注入的情况,而在创建依赖注入的时候为了避免循环依赖,
* Spring创建bean的原则是不等bean创建完成就会将创建bean的ObjectFactory提早曝光
* 也就是将 ObjectFactory 加入到缓存中,一旦下一个bean创建时候需要依赖上个bean则直接使用ObjectFactory
*/
// 2 直接尝试从缓存或者 SingletonFactories 中的 ObjectFactory 中获取
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
if (logger.isDebugEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
}
}
// 3 返回对应的实例,有时候存在诸如BeanFactory 的情况并不是直接返回实例本身而是返回指定方法返回的实例
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
// 4 只有在单例情况下才会尝试解决循环依赖,原型模式情况下,如果存在
// 4 A中有B的属性,B中有A的属性,那么当依赖注入的时候,就会产生当A还未创建完的时候因为对于B的创建再次返回创建A
// 4 造成循环依赖,也就是下面的情况 isPrototypeCurrentlyInCreation(beanName) 为true
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
BeanFactory parentBeanFactory = getParentBeanFactory();
// 5 如果 beanDefinitionMap 中也就是在所有已经加载的类中不包括 beanName 则尝试从
// 5 parentBeanFactory 中检测
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
String nameToLookup = originalBeanName(name);
if (parentBeanFactory instanceof AbstractBeanFactory) {
return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
nameToLookup, requiredType, args, typeCheckOnly);
}
// 递归到 BeanFactory 中寻找
else if (args != null) {
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else {
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
}
// 如果不是仅仅做类型检查则是创建 bean,这里要进行记录
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}
try {
// 6 将存储Xml配置文件的 GernericBeanDefinition 转换为 RootBeanDefinition,
// 6 如果指定BeanName 是子 Bean 的话同时会合并父类的相关属性
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
String[] dependsOn = mbd.getDependsOn();
// 7 若存在依赖则需要递归实例化依赖的bean
if (dependsOn != null) {
for (String dep : dependsOn) {
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
// 缓存依赖调用
registerDependentBean(dep, beanName);
try {
getBean(dep);
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
}
}
}
// 8.1 实例化依赖的 bean 后便可以实例化 mbd 本身了
// 8.1 singleton 模式的创建
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
// 8.2 创建bean
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
else if (mbd.isPrototype()) {
// 8 Prototype 模型的创建(new)
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
else {
// 8 指定的 scope 上实例化 bean
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
}
try {
Object scopedInstance = scope.get(beanName, () -> {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
});
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
catch (IllegalStateException ex) {
throw new BeanCreationException(beanName,
"Scope '" + scopeName + "' is not active for the current thread; consider " +
"defining a scoped proxy for this bean if you intend to refer to it from a singleton",
ex);
}
}
}
catch (BeansException ex) {
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
}
// 9 检查需要的类型是否符合 bean 的实际类型
if (requiredType != null && !requiredType.isInstance(bean)) {
try {
T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
if (convertedBean == null) {
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
return convertedBean;
}
catch (TypeMismatchException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Failed to convert bean '" + name + "' to required type '" +
ClassUtils.getQualifiedName(requiredType) + "'", ex);
}
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
}
return (T) bean;
}
}
1 提取对应的beanName
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
private final Map singletonObjects = new ConcurrentHashMap<>(256);
/** Cache of singleton factories: bean name --> ObjectFactory */
private final Map> singletonFactories = new HashMap<>(16);
/** Cache of early singleton objects: bean name --> bean instance */
private final Map earlySingletonObjects = new HashMap<>(16);
/** Set of registered singletons, containing the bean names in registration order */
private final Set registeredSingletons = new LinkedHashSet<>(256);
/** Map between containing bean names: bean name --> Set of bean names that the bean contains */
private final Map> containedBeanMap = new ConcurrentHashMap<>(16);
/** Map between dependent bean names: bean name --> Set of dependent bean names */
private final Map> dependentBeanMap = new ConcurrentHashMap<>(64);
/** Map between depending bean names: bean name --> Set of bean names for the bean's dependencies */
private final Map> dependenciesForBeanMap = new ConcurrentHashMap<>(64);
@Override
@Nullable
public Object getSingleton(String beanName) {
// 参数 true 设置标识允许早期依赖
return getSingleton(beanName, true);
}
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 检查缓存中是否存在实例
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 如果为空,则锁定全局变量并进行处理
synchronized (this.singletonObjects) {
// 如果此 bean 正在加载则不处理
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
// 当某些方法需要提前初始化的时候则会调用 addSingletFactory 方法将对应的
// ObjectFactory 初始化策略存储在 singletonFactories
ObjectFactory singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// 调用预先设定的 getObject() 方法
singletonObject = singletonFactory.getObject();
// 记录在缓存中,earlySingletonObjects 和 singletonFactories 互斥
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
}
讲解:这个方法首先尝试从 singletonObjects 里面获取实例,如果获取不到再从 earlySingletonObjects 里面获取,如果还是获取不到,再尝试从 singletonFactories 里面获取 beanName 对应的 ObjectFactory ,然后调用这个 ObjectFactory 的 getObject 来创建bean,并放到 earlySingletonObjects 里面去,并且从 singletonObjects 里面 remove 掉这个 ObjectFactory ,而对于后续的所有内存操作都只为了循环依赖检测时候使用,也就是在 allowEarlyReference 为true的情况下才会使用。
简单解释两点:singletonFactories 集合中的值,类型是 ObjectFactory,这个类是用来创建bean的,可能有人不解,创建 bean 不是用反射去创建吗?为什么来了一个 ObjectFactory 创建,没错,无论什么bean最终都会通过反射来创建,但你需要在配置文件中配置你要创建的这个 bean 的各种信息。但 FactoryBean 就不同,你可以通过getObject()方法直接返回需要创建的bean。
第二点:singletonObjects 创建完 bean 后为什么要 remove 掉这个 ObjectFactory。因为这是创建单例bean,bean实例化放入 earlySingletonObjects 后,再次获取在缓存中就可以得到,就无需再保留这个 ObjectFactory 了。
这里设计用于存储 bean 的不同 map,简单解释一下:
singletonObjects :用于保存 BeanName 和创建 bean 实例之间的关系,bean name --> bean instance
singletonFactories : 用于保存 BeanName 和创建 bean 的工厂之间的关系,bean name --> ObjectFactory
earlySingletonObjects : 也是保存 BeanName 和创建 bean 实例之间的关系,与 singletonObjects 的不同之处在于,当一个单例 bean 被放到这里面后,那么当 bean 还在创建过程中,就可以通过 getBean 方法获取到了,其目的是用来检测循环引用。
registeredSingletons : 用来保存当前所有已注册的 bean
3 返回对应的实例,返回getObject()实例或者返回FactoryBean实例
protected Object getObjectForBeanInstance(
Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
// Don't let calling code try to dereference the factory if the bean isn't a factory.
if (BeanFactoryUtils.isFactoryDereference(name)) {
if (beanInstance instanceof NullBean) {
return beanInstance;
}
if (!(beanInstance instanceof FactoryBean)) {
throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
}
}
// Now we have the bean instance, which may be a normal bean or a FactoryBean.
// If it's a FactoryBean, we use it to create a bean instance, unless the
// caller actually wants a reference to the factory.
if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
return beanInstance;
}
Object object = null;
if (mbd == null) {
object = getCachedObjectForFactoryBean(beanName);
}
if (object == null) {
// Return bean instance from factory.
FactoryBean factory = (FactoryBean) beanInstance;
// Caches object obtained from FactoryBean if it is a singleton.
if (mbd == null && containsBeanDefinition(beanName)) {
mbd = getMergedLocalBeanDefinition(beanName);
}
boolean synthetic = (mbd != null && mbd.isSynthetic());
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}
return object;
}
Spring在获取bean的规则有这样一条:尽可能保证所有bean初始化后都会调用注册的BeanPostProcessor 的 postProcessAfterInitialization 方法进行处理。在实际开发过程中大可以针对此特性设计自己的业务逻辑。
8.1 singleton 模式的创建 (在第一次加载bean的时候,缓存中没有,所以要新创建)
public Object getSingleton(String beanName, ObjectFactory singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
// 全局变量需要同步
synchronized (this.singletonObjects) {
// 首先检查对应的bean是否已经加载过,
// 因为 singleton 模式其实就是复用已创建的bean,这一步是必须的
Object singletonObject = this.singletonObjects.get(beanName);
// 如果为空才可以进行 singleton 的 bean 的初始化
if (singletonObject == null) {
if (this.singletonsCurrentlyInDestruction) {
throw new BeanCreationNotAllowedException(beanName,
"Singleton bean creation not allowed while singletons of this factory are in destruction " +
"(Do not request a bean from a BeanFactory in a destroy method implementation!)");
}
if (logger.isDebugEnabled()) {
logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
}
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
// 初始化 bean
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
catch (IllegalStateException ex) {
// Has the singleton object implicitly appeared in the meantime ->
// if yes, proceed with it since the exception indicates that state.
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
throw ex;
}
}
catch (BeanCreationException ex) {
if (recordSuppressedExceptions) {
for (Exception suppressedException : this.suppressedExceptions) {
ex.addRelatedCause(suppressedException);
}
}
throw ex;
}
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
afterSingletonCreation(beanName);
}
if (newSingleton) {
// 加入缓存
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
以上代码做的事情大概是:
检查缓存中是否已经加载过
若没有加载,则记录beanName的正在加载状态,在 beforeSingletonCreation 方法中记录
加载单例前记录加载状态。(对循环依赖进行检测)
通过调用参数传入的 ObjectFactory 的个体 Object 方法实例化bean
加载单例后的处理方法调用(移除缓存该 bean 加载状态的记录)
将结果记录至缓存并删除加载bean过程中所记录的各种辅助状态
返回处理结果
8.2 创建bean return createBean(beanName, mbd, args);
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
if (logger.isDebugEnabled()) {
logger.debug("Creating instance of bean '" + beanName + "'");
}
RootBeanDefinition mbdToUse = mbd;
// 锁定 class,根据设置的 class 属性或者根据 className 来解析 Class
Class resolvedClass = resolveBeanClass(mbd, beanName);
if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
mbdToUse = new RootBeanDefinition(mbd);
mbdToUse.setBeanClass(resolvedClass);
}
try {
// 验证及准备覆盖的方法
mbdToUse.prepareMethodOverrides();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
beanName, "Validation of method overrides failed", ex);
}
try {
// 给 BeanPostProcessors 一个机会来返回代理来替代真正的实例
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
return bean;
}
}
catch (Throwable ex) {
throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
"BeanPostProcessor before instantiation of bean failed", ex);
}
try {
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isDebugEnabled()) {
logger.debug("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
// A previously detected exception with proper bean creation context already,
// or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.
throw ex;
}
catch (Throwable ex) {
throw new BeanCreationException(
mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
}
}
这个方法主要做了这样几件事
根据谁的 class 属性或者根据 className 来解析 Class
对 override 属性进行标记或验证
应用初始化前的后置处理器,解析指定的 bean 是否存在初始化前的短路操作
创建bean
对于第二点简单解释下,在bean实例化的时候如果检测到存在 methodOverrides 属性,会动态的为当前bean生成代理并使用对应的拦截器为 bean 做增强处理
创建 bean 前的短路,是这一步 if (bean != null) { return bean; },如果对bean增强后,bean已经创建好了,就直接返回,不会再去创建了。保证单例
第三点的代码,Bean实例化前的后置处理器,增强bean的功能
protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
Object bean = null;
if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
// Make sure bean class is actually resolved at this point.
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
Class targetType = determineTargetType(beanName, mbd);
if (targetType != null) {
// 在实例化之前应用 Bean 后处理器
bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
if (bean != null) {
// 实例化之后应用 Bean 后处理器
bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
}
}
}
mbd.beforeInstantiationResolved = (bean != null);
}
return bean;
}
applyBeanPostProcessorsBeforeInstantiation(targetType, beanName); 这段代码是给子类一个修改 beanDefinition 的机会,在bean实例化前会调用后处理方法进行处理。
@Nullable
protected Object applyBeanPostProcessorsBeforeInstantiation(Class beanClass, String beanName) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
if (result != null) {
return result;
}
}
}
return null;
}
实例化之后引用 bean 后处理器
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
Object current = beanProcessor.postProcessAfterInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}
循环依赖
对象A中有属性对象B,对象B中有属性对象A。当创建对象A时先创建A中的属性对象B,去创建对象B,又得创建对象A。
对象 A 中有属性 B
package com.zhao.service;
public class A {
private B b;
public A(B b) {
this.b = b;
}
public void sayHello() {
System.out.println("hello");
}
}
对象 B 中有属性 A
package com.zhao.service;
public class B {
private A a;
public B(A a) {
this.a = a;
}
public void sayHello() {
System.out.println("hello");
}
}
配置文件
启动类
package com.zhao;
import com.zhao.Interface.PressService;
import com.zhao.service.A;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
public class SourceCodeLearning {
public static void main(String[] args) {
//从容器中获取名字为user的bean
BeanFactory bf = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
A a = (A) bf.getBean("a");
}
}
控制台报错
这种构造方法造成的循环依赖是无法解决的。只能通过报错表示循环依赖
Spring容器将每一个正在创建的bean标识符放在一个 “当前创建bean池” 中,bean 标识符在创建过程中将一直保持在这个池中,因此如果在创建bean过程中发现自己已经在 “当前创建bean池” 里时,将抛出 BeanCurrentlyInCreationException 异常表示循环依赖;而对于创建完毕的 bean 将从 “当前创建bean池” 中清除掉。
以上是构造方法的循环依赖
Spring 对于 setter 注入方式构成的循环依赖是可以解决的,但是只能解决单例作用域的 bean 循环依赖。它是通过暴露一个单例工厂方法,从而使其它 bean 能引用到该bean。
通过
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
这段代码实现。
具体步骤如下:
Spring 容器创建单例 “A” bean,首先根据无参构造器常见 bean,并暴露一个 “ObjectFactory” 用于返回一个提前暴露一个创建中的 bean,并将 “A” 标识符放到 “当前创建 bean 池”,然后进行 setter 注入 B。
Spring 容器创建单例 “B” bean,首先根据无参构造器常见 bean,并暴露一个 “ObjectFactory” 用于返回一个提前暴露一个创建中的 bean,并将 “B” 标识符放到 “当前创建 bean 池”,然后进行 setter 注入 A。
至此完成 setter 依赖注入,其中最大的作用就是 缓存
但是对于多例对象,Spring是无法完成依赖注入的。因为 prototype 对象,Spring 是不进行缓存的。因此无法提前暴露一个创建中的 bean.
创建Bean
前面通过代理或者将bean增强后改变了 bean,那直接返回即可。就是那个短路操作,如果经Bean实例化前的后置处理器,已经创建出代理bean了,那么就返回。如若不然,就按常规的 bean创建,这是在。Object beanInstance = doCreateBean(beanName, mbdToUse, args); 方法中完成的
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
// 根据指定的 bean 使用对应的策略创建新的实例,如工厂方法,构造函数自动注入,简单初始化
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = instanceWrapper.getWrappedInstance();
Class beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
// Allow post-processors to modify the merged bean definition.
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
// 应用 MergedBeanDefinitionPostProcessors
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Post-processing of merged bean definition failed", ex);
}
mbd.postProcessed = true;
}
}
/**
* 是否需要提早曝光:单例 & 允许循环依赖 & 当前 bean 正在创建中,检测循环依赖
*/
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isDebugEnabled()) {
logger.debug("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
// 为避免后期循环依赖,可以在 bean 初始化完成前将创建实例的 ObjectFactory 加入工厂
addSingletonFactory(beanName,
// 对 bean 再一次依赖引用,主要应用 SmartInstantiationAware BeanPostProcessor
// 其中我们熟知的 AOP 就是在这里将 advice 动态织入 bean 中,若没有则直接返回 bean,不做任何处理
() -> getEarlyBeanReference(beanName, mbd, bean)
);
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
// 对 bean 进行填充,将各个属性值注入,其中,可能存在依赖于其他 bean 的属性,则会递归初始依赖 bean
populateBean(beanName, mbd, instanceWrapper);
// 调用除耍滑方法,比如 init method
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
throw (BeanCreationException) ex;
}
else {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
}
}
if (earlySingletonExposure) {
Object earlySingletonReference = getSingleton(beanName, false);
// earlySingletonReference 只有在检测到有循环依赖的情况下才会不为空
if (earlySingletonReference != null) {
// 如果 exposedObject 没有在初始化方法中被修改,也就是没有被增强
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
String[] dependentBeans = getDependentBeans(beanName);
Set actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
// 检测依赖
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
/**
* 因为 bean创建后其所依赖的 bean 一定是已经创建的
* actualDependentBeans 不为空则表示当前 bean 创建后其依赖的 bean 却没有
* 全部创建完,也就是说存在循环依赖
*/
if (!actualDependentBeans.isEmpty()) {
throw new BeanCurrentlyInCreationException(beanName,
"Bean with name '" + beanName + "' has been injected into other beans [" +
StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
"] in its raw version as part of a circular reference, but has eventually been " +
"wrapped. This means that said other beans do not use the final version of the " +
"bean. This is often the result of over-eager type matching - consider using " +
"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
}
// Register bean as disposable.
try {
// 根据 scope 注册 bean
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}
return exposedObject;
}
这个方法的大概思路是
如果是单例则需要首先清除缓存
实例化 bean,将BeanDefinition转换为BeanWrapper。
MergedBeanDefinitionPostProcessors 的应用:bean合并后的处理,Autowired 注解正式通过此方法实现诸如类型的预解析
依赖处理
属性填充
循环依赖检查:单例可以解决,但是对于构造和多例只能抛出异常
注册DisposableBean:如果配置了 destroyMethod,这里需要注册以便在销毁的时候调用
完成创建并返回
创建bean分为了很多步,我们先从 instanceWrapper = createBeanInstance(beanName, mbd, args); 方法分析
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
// 解析class
Class beanClass = resolveBeanClass(mbd, beanName);
if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
}
Supplier instanceSupplier = mbd.getInstanceSupplier();
if (instanceSupplier != null) {
return obtainFromSupplier(instanceSupplier, beanName);
}
// 如果工厂方法不为空则使用工厂方法初始化策略
if (mbd.getFactoryMethodName() != null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
boolean resolved = false;
boolean autowireNecessary = false;
if (args == null) {
synchronized (mbd.constructorArgumentLock) {
// 一个类有多个构造方法,每个构造方法都有不同的参数,所以调用前需要先根据参数锁定
// 构造函数或对应的工厂方法
if (mbd.resolvedConstructorOrFactoryMethod != null) {
resolved = true;
autowireNecessary = mbd.constructorArgumentsResolved;
}
}
}
// 如果已经解析过则使用解析好的构造函数方法不需要再次锁定
if (resolved) {
if (autowireNecessary) {
// 构造函数自动注入
return autowireConstructor(beanName, mbd, null, null);
}
else {
// 使用默认构造函数构造
return instantiateBean(beanName, mbd);
}
}
// 需要根据参数解析构造函数
Constructor[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
if (ctors != null ||
mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR ||
mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
// 构造函数自动注入
return autowireConstructor(beanName, mbd, ctors, args);
}
// 使用默认构造函数构造
return instantiateBean(beanName, mbd);
}
这个方法的大概意思是
如果在 RootBeanDefinition 中存在factoryMethodName属性,或者说在配置文件中配置了 factoryMethod 属性,那么Spring 会尝试使用 instantiateUsingFactoryMethod(beanName, mbd, args); 方法根据 RootBeanDefinition 中的配置生成 bean 的实例
解析构造函数并进行构造函数的实例化。因为一个bean对应的类中可能会有多个构造函数,而每个构造函数的参数不同,Spring在根据参数集类型去判断最终会使用哪个构造函数进行实例化。但是,判断的过程是个比较消耗性能的步骤,所以采用缓存机制。如果已经解析过则不需要重复解析而是直接从 RootBeanDefinition 中属性 resolvedConstructorOrFactoryMethod 缓存的值去取,否则需要再次解析,并将解析的结果添加至 RootBeanDefinition 中的属性 resolvedConstructorOrFactoryMethod 中去。
实例化 bean 无非是有参构造的实例 或 无参构造的实例。这里只针对无参构造讲解,也就是 instantiateBean(beanName, mbd); 这个方法
protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
try {
Object beanInstance;
final BeanFactory parent = this;
if (System.getSecurityManager() != null) {
beanInstance = AccessController.doPrivileged((PrivilegedAction) () ->
getInstantiationStrategy().instantiate(mbd, beanName, parent),
getAccessControlContext());
}
else {
beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
}
BeanWrapper bw = new BeanWrapperImpl(beanInstance);
initBeanWrapper(bw);
return bw;
}
catch (Throwable ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
}
}
如果没有参数,直接调用实例化策略进行实例化就行了,也就是 getInstantiationStrategy().instantiate(mbd, beanName, parent) 中的 instantiate 方法
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
// 如果有需要覆盖或者动态替换的方法则当然需要使用cglib进行动态代理,
// 因为可以在创建代理的同时将动态方法织入类中
// 但是如果没有需要动态改变的方法,为了方便直接反射就可以了
if (!bd.hasMethodOverrides()) {
Constructor constructorToUse;
synchronized (bd.constructorArgumentLock) {
constructorToUse = (Constructor) bd.resolvedConstructorOrFactoryMethod;
if (constructorToUse == null) {
final Class clazz = bd.getBeanClass();
if (clazz.isInterface()) {
throw new BeanInstantiationException(clazz, "Specified class is an interface");
}
try {
if (System.getSecurityManager() != null) {
constructorToUse = AccessController.doPrivileged(
(PrivilegedExceptionAction>) clazz::getDeclaredConstructor);
}
else {
constructorToUse = clazz.getDeclaredConstructor();
}
bd.resolvedConstructorOrFactoryMethod = constructorToUse;
}
catch (Throwable ex) {
throw new BeanInstantiationException(clazz, "No default constructor found", ex);
}
}
}
return BeanUtils.instantiateClass(constructorToUse);
}
else {
// Must generate CGLIB subclass.
return instantiateWithMethodInjection(bd, beanName, owner);
}
}
程序中如果用户没有使用 replace 或者 lookup 的配置方法,那么直接使用反射的方式创建对象,如果使用了这两个属性,需要将这两个属性提供的功能切入进去,所以就必须使用动态代理的方式将包含两个逻辑的拦截增强器设置进去,这样才可以保证在调用方法的时候会被相应的拦截器增强。
还记得我们是从哪个方法进来的吗?spring的源码就是这样,犹如洋葱,很容易忘记剥到第几层了。是 doCreateBean 这个方法,创建 bean 的,现在 bean 已经创建好了,由于咱们创建的是单例 bean,所以要在缓存中记录这个 bean 的 ObjectFactory。
是否需要提早曝光: 单例 & 允许循环依赖 & 当前 bean 正在创建中,检测循环依赖 boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isDebugEnabled()) {
logger.debug("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
// 为避免后期循环依赖,可以在 bean 初始化完成前将创建实例的 ObjectFactory 加入工厂
addSingletonFactory(beanName,
// 对 bean 再一次依赖引用,主要应用 SmartInstantiationAware BeanPostProcessor
// 其中我们熟知的 AOP 就是在这里将 advice 动态织入 bean 中,若没有则直接返回 bean,不做任何处理
() -> getEarlyBeanReference(beanName, mbd, bean)
);
}
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}
Spring处理循环依赖的解决方法,在 B 中有属性A,A 此时已创建,但是还没来得及填充属性,此时 B 就调用 ObjectFactory 创建A(其实返回的还是创建的 A,地址值一样),并将填充A到自己的属性,此时 B 已初始化,将 B 属性填充给 A,使 A 初始化。解决了循环依赖
属性填充 populateBean(beanName, mbd, instanceWrapper);
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
if (bw == null) {
if (mbd.hasPropertyValues()) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
}
else {
// 没有可填充的属性
return;
}
}
// 给 InstantiationAwareBeanPostProcessors 最后一次机会在属性设置前来改变 bean
// 如:可以用来支持属性注入的类型
boolean continueWithPropertyPopulation = true;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
// 返回值为是否继续填充 bean
if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
continueWithPropertyPopulation = false;
break;
}
}
}
}
// 如果后处理器发出停止填充命令则终止后续的执行
if (!continueWithPropertyPopulation) {
return;
}
PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||
mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
// Add property values based on autowire by name if applicable.
// 根据名称自动注入
if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {
autowireByName(beanName, mbd, bw, newPvs);
}
// Add property values based on autowire by type if applicable.
// 根据类型自动注入
if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
autowireByType(beanName, mbd, bw, newPvs);
}
pvs = newPvs;
}
// 后处理器已经初始化
boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
// 需要依赖检查
boolean needsDepCheck = (mbd.getDependencyCheck() != RootBeanDefinition.DEPENDENCY_CHECK_NONE);
if (hasInstAwareBpps || needsDepCheck) {
if (pvs == null) {
pvs = mbd.getPropertyValues();
}
PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
if (hasInstAwareBpps) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
// 对所有需要依赖检查的属性进行后处理
pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
if (pvs == null) {
return;
}
}
}
}
if (needsDepCheck) {
// 依赖检查,对应 depends On 属性,3.0 已弃用此属性
checkDependencies(beanName, mbd, filteredPds, pvs);
}
}
if (pvs != null) {
// 将属性应用到 bean 中
applyPropertyValues(beanName, mbd, bw, pvs);
}
}
这大概流程是
InstantiationAwareBeanPostProcessor 处理器的 postProcessAfterInstantiation 函数的应用,此函数可以控制程序是否继续进行属性的填充
根据注入类型(byName / byType),提取依赖的bean,并统一存入 PropertyValues 中
应用 InstantiationAwareBeanPostProcessor 处理器中的 postProcessPropertyValues 方法,对属性获取完毕填充前对属性再次处理,典型应用是 RequiredAnnotationBeanPostProcessor 类中对属性的验证。
将所有 PropertyValues 中的属性填充至 BeanWrapper 中
其中有根据类型注入和根据Name注入
名称注入代码如下
protected void autowireByName(
String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
// 寻找 bw 中需要依赖注入的属性
String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
for (String propertyName : propertyNames) {
if (containsBean(propertyName)) {
// 递归初始化相关的 bean
Object bean = getBean(propertyName);
pvs.add(propertyName, bean);
// 注册依赖
registerDependentBean(propertyName, beanName);
if (logger.isDebugEnabled()) {
logger.debug("Added autowiring by name from bean name '" + beanName +
"' via property '" + propertyName + "' to bean named '" + propertyName + "'");
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("Not autowiring property '" + propertyName + "' of bean '" + beanName +
"' by name: no matching bean found");
}
}
}
}
这段代码大概意思是
在传入的参数 pvs 中找出已经加载的 bean,并递归实例化,进而加入到 pvs 中
类型注入代码如下
protected void autowireByType(
String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
TypeConverter converter = getCustomTypeConverter();
if (converter == null) {
converter = bw;
}
Set autowiredBeanNames = new LinkedHashSet<>(4);
// 寻找 bw 中寻找依赖注入的属性
String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
for (String propertyName : propertyNames) {
try {
PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
// Don't try autowiring by type for type Object: never makes sense,
// even if it technically is a unsatisfied, non-simple property.
if (Object.class != pd.getPropertyType()) {
// 探测指定属性的 set 方法
MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
// Do not allow eager init for type matching in case of a prioritized post-processor.
boolean eager = !PriorityOrdered.class.isInstance(bw.getWrappedInstance());
DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager);
// 解析指定 beanName 的属性所匹配的值,并把解析的属性名称存储在
// autowiredBeanNames 中,当属性存在多个封装 bean 时如:
// @Autowired private List aList;将会找到所有匹配 A 类型的
// bean 并将其注入
Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter);
if (autowiredArgument != null) {
pvs.add(propertyName, autowiredArgument);
}
for (String autowiredBeanName : autowiredBeanNames) {
// 注册依赖
registerDependentBean(autowiredBeanName, beanName);
if (logger.isDebugEnabled()) {
logger.debug("Autowiring by type from bean name '" + beanName + "' via property '" +
propertyName + "' to bean named '" + autowiredBeanName + "'");
}
}
autowiredBeanNames.clear();
}
}
catch (BeansException ex) {
throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, propertyName, ex);
}
}
}
这段代码大概的意思是
寻找类型匹配的 bean,然后遍历这些属性并寻找类型匹配的 bean。 在 resolveDependency 方法中实现。
至此已经完成了所有属性的获取,但是获取的属性是以 PropertyValues 形式存在的,还没有应用到已经实例化的 bean 中,这一工作是在 applyPropertyValues 实现的
将 applyPropertyValues 中的属性注入至 bean 中
protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
if (pvs.isEmpty()) {
return;
}
if (System.getSecurityManager() != null && bw instanceof BeanWrapperImpl) {
((BeanWrapperImpl) bw).setSecurityContext(getAccessControlContext());
}
MutablePropertyValues mpvs = null;
List original;
if (pvs instanceof MutablePropertyValues) {
mpvs = (MutablePropertyValues) pvs;
// 如果 mpvs 中的值已经被转换为对应的类型那么可以直接设置到 beanwapper 中
if (mpvs.isConverted()) {
// Shortcut: use the pre-converted values as-is.
try {
bw.setPropertyValues(mpvs);
return;
}
catch (BeansException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Error setting property values", ex);
}
}
original = mpvs.getPropertyValueList();
}
else {
// 如果 pvs 并不是使用 MutablePropertyValues 封装的类型,那么直接使用原始的属性获取方法
original = Arrays.asList(pvs.getPropertyValues());
}
TypeConverter converter = getCustomTypeConverter();
if (converter == null) {
converter = bw;
}
// 获取对应的解析器
BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);
// Create a deep copy, resolving any references for values.
List deepCopy = new ArrayList<>(original.size());
boolean resolveNecessary = false;
// 遍历属性,将属性转换为对应类的对应属性的类型
for (PropertyValue pv : original) {
if (pv.isConverted()) {
deepCopy.add(pv);
}
else {
String propertyName = pv.getName();
Object originalValue = pv.getValue();
Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
Object convertedValue = resolvedValue;
boolean convertible = bw.isWritableProperty(propertyName) &&
!PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);
if (convertible) {
convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);
}
// Possibly store converted value in merged bean definition,
// in order to avoid re-conversion for every created bean instance.
if (resolvedValue == originalValue) {
if (convertible) {
pv.setConvertedValue(convertedValue);
}
deepCopy.add(pv);
}
else if (convertible && originalValue instanceof TypedStringValue &&
!((TypedStringValue) originalValue).isDynamic() &&
!(convertedValue instanceof Collection || ObjectUtils.isArray(convertedValue))) {
pv.setConvertedValue(convertedValue);
deepCopy.add(pv);
}
else {
resolveNecessary = true;
deepCopy.add(new PropertyValue(pv, convertedValue));
}
}
}
if (mpvs != null && !resolveNecessary) {
mpvs.setConverted();
}
// Set our (possibly massaged) deep copy.
try {
bw.setPropertyValues(new MutablePropertyValues(deepCopy));
}
catch (BeansException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Error setting property values", ex);
}
}
初始化 Bean
Bean初始化,无非是调用无参构造创建 bean,创建bean前后实现一些接口就可以对 bean 进行增强,做一些自己想做的处理。博主印象最深刻的场景就是做缓存预热处理,一些热点数据频繁从数据库读取,造成数据库压力过大,于是在每次启动项目前,就将那些热点数据加载至 redis 缓存中,加载热点数据的时机就是创建 Spring 容器的时候,Spring容器对 bean 的增强是可以做到这一点
Eg:
package com.zhao.BeanLifeCycle;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class BeanLifecycle implements BeanNameAware, ApplicationContextAware, InitializingBean, DisposableBean {
/**
* 1。构造器
*/
public BeanLifecycle() {
System.out.println("1.【Bean级别】构造器执行了");
}
/**
* 2。BeanNameAware 接口方法实现
* @param name
*/
@Override
public void setBeanName(String name) {
System.out.println("2.【Bean级别】setBeanName 方法执行了");
}
/**
* 3。ApplicationContextAware 接口方法实现
* @param applicationContext
* @throws BeansException
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("3.【Bean级别】setApplicationContext 方法执行了");
}
/**
* 4。InitializingBean 接口方法实现
* @throws Exception
*/
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("4.【Bean级别】afterPropertiesSet 方法执行了");
}
/**
* 5。init-method 指定的方法执行了
*/
public void lifecycleInit(){
System.out.println("5.【Bean级别】init-method 指定的方法执行了");
}
/**
* 6。sayHello 方法执行了
*/
public void sayHello(){
System.out.println("6.【Bean级别】sayHello 方法执行了");
}
/**
* 7。DisposableBean 接口方法实现
* @throws Exception
*/
@Override
public void destroy() throws Exception {
System.out.println("7.【Bean级别】destroy 方法执行了");
}
/**
* 8。destroy-method 指定的方法执行了
*/
public void lifecycleDestroy(){
System.out.println("8.【Bean级别】destroy-method 指定的方法执行了");
}
}
启动类
package com.zhao;
import com.zhao.BeanLifeCycle.BeanLifecycle;
import com.zhao.Interface.PressService;
import com.zhao.service.A;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;
public class SourceCodeLearning {
public static void main(String[] args) {
ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
BeanLifecycle bean = (BeanLifecycle) app.getBean("beanLifecycle", BeanLifecycle.class);
bean.sayHello();
app.close();
}
}
打印结果
后处理器,容器级接口方法
package com.zhao.BeanLifeCycle;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
import java.beans.PropertyDescriptor;
/**
*
*/
public class ContainerLifecycle extends InstantiationAwareBeanPostProcessorAdapter {
/**
* 构造器
*/
public ContainerLifecycle() {
System.out.println("⓵.【容器级别】ContainerLifecycle 构造器执行了");
}
/**
* 接口方法和实例化 Bean 之前调用
* @param beanClass
* @param beanName
* @return
* @throws BeansException
*/
@Override
public Object postProcessBeforeInstantiation(Class beanClass, String beanName) throws BeansException {
System.out.println("⓶.【容器级别】postProcessBeforeInstantiation 方法执行了,class= "+beanClass);
return null;
}
/**
* 设置某个属性时调用
* @param pvs
* @param pds
* @param bean
* @param beanName
* @return
* @throws BeansException
*/
@Override
public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
System.out.println("⓷.【容器级别】postProcessPropertyValues 方法执行了,beanName= "+bean.getClass());
return pvs;
}
/**
* 接口方法和实例化 Bean 之后调用
* @param bean
* @param beanName
* @return
* @throws BeansException
*/
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("⓸.【容器级别】postProcessAfterInitialization 方法执行了,beanName= "+bean.getClass());
return null;
}
}
打印结果
可以发现,每创建一个 bean,就会调用一次容器接口方法
而且容器级别的销毁方法要先于 bean级别的销毁方法
工厂级别的接口方法
package com.zhao.BeanLifeCycle;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
public class FactoryLifecycle implements BeanFactoryPostProcessor {
/**
* 构造器
*/
public FactoryLifecycle() {
System.out.println("一 【工厂级别】FactoryLifecycle 构造器执行了");
}
/**
* Bean 实例化之前
* @param beanFactory
* @throws BeansException
*/
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("二 【工厂级别】postProcessBeanFactory 方法执行了");
}
}
启动引导类
package com.zhao;
import com.zhao.BeanLifeCycle.BeanLifecycle;
import com.zhao.Interface.PressService;
import com.zhao.service.A;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;
/**
* Bean 级别 + 容器级 + 工厂级
*/
public class SourceCodeLearning {
public static void main(String[] args) {
ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
BeanLifecycle bean = (BeanLifecycle) app.getBean("beanLifecycle", BeanLifecycle.class);
bean.sayHello();
app.close();
}
}
打印结果
实现了 BeanFactoryPostProcessor,工厂级别的 postProcessBeanFactory,最先执行,而且只会执行一次
当我们执行完实例化 bean 后,还记得是从哪个方法进来的吗?是在doCreateBean 方法中属性填充完成后,调用 exposedObject = initializeBean(beanName, exposedObject, mbd); 进行初始化的,下面的方法
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction) () -> {
invokeAwareMethods(beanName, bean);
return null;
}, getAccessControlContext());
}
else {
// 对特殊的 bean 处理:Aware,BeanClassLoaderAware,BeanFactorAware
invokeAwareMethods(beanName, bean);
}
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
// 应用后处理器
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
// 激活用户自定义的 init 方法
invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
}
if (mbd == null || !mbd.isSynthetic()) {
// 后处理器应用
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
Bean级别的方法:
实现各种 Aware 接口,如:BeanNameAware,BeanFactoryAware,ResourceLoaderAware,ApplicationContextAware,InitializingBean 等
// Bean 级别的方法
// 实现了 aware 接口的方法
private void invokeAwareMethods(final String beanName, final Object bean) {
if (bean instanceof Aware) {
if (bean instanceof BeanNameAware) {
((BeanNameAware) bean).setBeanName(beanName);
}
if (bean instanceof BeanClassLoaderAware) {
ClassLoader bcl = getBeanClassLoader();
if (bcl != null) {
((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
}
}
if (bean instanceof BeanFactoryAware) {
((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
}
}
}
// 配置了 Init-Method 属性或者实现了 InitlizingBean 接口
protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
throws Throwable {
// 首先会检查是否是 InitlizingBean,如果是的话需要调用 afterPropertiesSet 方法
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
if (logger.isDebugEnabled()) {
logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
}
if (System.getSecurityManager() != null) {
try {
AccessController.doPrivileged((PrivilegedExceptionAction) () -> {
((InitializingBean) bean).afterPropertiesSet();
return null;
}, getAccessControlContext());
}
catch (PrivilegedActionException pae) {
throw pae.getException();
}
}
else {
// 属性初始化后的处理
((InitializingBean) bean).afterPropertiesSet();
}
}
if (mbd != null && bean.getClass() != NullBean.class) {
String initMethodName = mbd.getInitMethodName();
if (StringUtils.hasLength(initMethodName) &&
!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
!mbd.isExternallyManagedInitMethod(initMethodName)) {
// 调用自定义的初始化方法
invokeCustomInitMethod(beanName, bean, mbd);
}
}
}
容器级别的方法: BeanPostProcessor
在调用客户自定义初始化方法前以及调用自定义的初始化方法后分别会调用 postProcessBeforeInstantiation 和 postProcessAfterInitialization 方法,使用户可以根据自己的业务需求进行相应的处理
@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
Object current = beanProcessor.postProcessAfterInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}
销毁的方法
在 AbstractBeanFactory 类中 registerDisposableBeanIfNecessary 方法
protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) {
AccessControlContext acc = (System.getSecurityManager() != null ? getAccessControlContext() : null);
if (!mbd.isPrototype() && requiresDestruction(bean, mbd)) {
if (mbd.isSingleton()) {
// 单例模式下注册需要销毁的 bean,此方法中会处理实现 DisposableBean 的 Bean
// 并且对所有的 bean 使用 DestructionAwareBeanPostProcessor 处理,
// DisposableBean DestructionAwareBeanPostProcessor
registerDisposableBean(beanName,
new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc));
}
else {
// 自定义 scope 的处理
Scope scope = this.scopes.get(mbd.getScope());
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + mbd.getScope() + "'");
}
scope.registerDestructionCallback(beanName,
new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc));
}
}
}
你可能感兴趣的:(spring,java)
JavaScript Fetch
noPermission
JavaScript javascript 前端 开发语言
一、什么是fetch?在前端的发展历程中用于请求网络资源的方式主要有三种:从原生的XMLHttpRequest到jQuery的Ajax再到现在主流的axios,其中Ajax和axios都是对XMLHttpRequest的封装(本质上都是使用XMLHttpRequest方法获取网络资源),这些封装库让开发者对XMLHttpRequest的使用变得更简单高效。然而fetch的推出使开发者对网络资源的异
JAVA架构师需要掌握的常用架构模式有哪些?
猿享天开
Java开发从入门到精通 java 架构 开发语言
引言Java架构师必须掌握常用技术组合及其选型逻辑。技术组合的选择直接影响系统的可扩展性、性能和维护成本。以下是当前主流技术组合、选型原则及常用架构应用的详细说明:一、当前主流技术组合及其应用场景1.基础开发框架技术组合应用场景核心优势SpringBoot+MyBatis+MySQL中小型单体应用、快速迭代业务开发效率高、ORM轻量、数据库兼容性强SpringBoot+JPA+PostgreSQL
ServletRequest 和 HttpServletRequest 的关系
牛马白菜价
tomcat servlet
1.ServletRequest和HttpServletRequest的关系ServletRequest接口:它是JavaServletAPI中的一个核心接口,定义了客户端请求的基本信息和操作方法。该接口提供了通用的请求处理功能,适用于各种协议的请求。例如,它可以获取请求的参数、属性等信息。HttpServletRequest接口:HttpServletRequest是ServletRequest
【JavaScript】异常处理详解
Peter-Lu
# JavaScript javascript 前端 ecmascript
文章目录一、JavaScript异常处理概述1.什么是异常?2.为什么需要异常处理?二、JavaScript中的异常类型1.系统异常示例2.自定义异常示例三、异常处理的基本语法1.try...catch详解2.finally详解四、throw关键字1.手动抛出异常2.自定义异常类型五、异常处理的最佳实践1.避免过度捕获异常2.提供有意义的错误信息3.使用finally释放资源4.避免在finall
Java高级特性(基础知识点总结)
杰—
java
文章目录第三章:java高级API1️⃣什么是集合面试题:集合分为2个顶级接口:分别为Collection和Map面试题面试题2:面试题3Map接口:HashMap的数据结构面试题:面试题面试题包装类:JavaApi输入流和输出流会使用File类操作文件或目录File类的构造方法IO流的分类4大顶级抽象父类字符集基础知识:字节输出流写数据的步骤流的关闭与刷新第三章:java高级API1️⃣什么是集
使用Spring Boot开发后端应用:在IntelliJ IDEA中的实践指南
风亦辰739
后端 spring boot java intellij-idea
一、什么是SpringBoot?SpringBoot是一个开源框架,用于简化Java应用的构建过程,尤其是Web应用。它是基于Spring框架的,提供了许多开箱即用的功能,极大地简化了Spring应用的配置和开发过程。SpringBoot让开发者无需关注繁琐的配置,可以专注于业务逻辑的实现。SpringBoot的优势:自动配置:SpringBoot根据项目的依赖自动配置相关功能,避免手动配置繁琐的
Django学习笔记(第一天:Django基本知识简介与启动)
S1901
python Django django 学习 笔记
博主毕业已经工作一年多了,最基本的测试工作已经完全掌握。一方面为了解决当前公司没有自动化测试平台的痛点,另一方面为了向更高级的测试架构师转型,于是重温Django的知识,用于后期搭建测试自动化平台。为什么不选择Java:Python语法简洁易读,适合快速开发。Selenium、Appium、pytest、Requests等工具对Python支持极好,且有丰富的文档和社区资源。Django简介Dja
详解javascript的bind方法
北京王老师
js vue java javascript python
JS的bind()方法创建一个新的函数,在bind()被调用时,这个新函数的this被指定为bind()的第一个参数,而其余参数将作为新函数的参数,供调用时使用。通过bind可解决两个问题:1)以隐蔽和优雅的方式解决依赖注入的问题,可以将函数执行需要的上下文环境对象通过bind方法传递给函数,在函数内部直接使用this来引用。2)解决多层调用时this对象无法传递给内部函数的问题,实际是第一个问题
详解 JavaScript 中 fetch 方法
ttod_qzstudio
JavaScript JavaScript
在现代的Web开发中,与服务器进行数据交互是一项常见且重要的任务。JavaScript提供了多种方式来实现这一功能,其中fetch方法是一个强大且灵活的工具。本文将详细介绍fetch方法的各个方面,帮助你更好地理解和使用它。什么是fetch方法fetch是JavaScript中用于发起网络请求的现代API,它提供了一种更简洁、更强大的方式来处理网络通信。fetch方法返回一个Promise对象,该
spring cloud gateway + nacos
没事偷着乐琅
微服务 微服务
pom.xml父类:org.springframework.bootspring-boot-starter-parent2.2.5.RELEASE2.2.5.RELEASE1.82.2.1.RELEASEorg.springframework.cloudspring-cloud-dependencies${spring-cloud.version}pomimportcom.alibaba.clou
Struts2 命令执行漏洞 S2-045 复现:深入剖析与实战演练
垚垚 Securify 前沿站
十大漏洞 网络 系统安全 web安全 struts 安全 计算机网络 后端
目录前言一、漏洞原理:框架解析缺陷引发的安全危机二、复现环境搭建:搭建模拟战场,重现漏洞场景三、复现步骤:步步为营,揭开漏洞利用的面纱四、漏洞危害与修复建议:正视漏洞危害,筑牢安全防线前言在当今网络安全形势日益严峻的大环境下,Web应用框架的安全问题始终是信息安全领域关注的焦点。Struts2作为一款广泛应用于JavaWeb开发的开源框架,其安全性直接关系到众多Web应用的稳定运行。今天,我们将深
Nacos集成spring cloud gateway
舞娘展颜
gateway spring cloud alibaba
今天使用Nacos集成springcloudgateway,记录一下最开始我创建了一个module:cloud-gateway,并且在pom文件里配置这样(部分代码截取):com.exampledemo1.0.0然后启动的时候,就说已经使用了gateway,让去掉spring-boot-starter-web的依赖,因为我的父级pom里引入了spring-boot-starter-web。Spri
eclipse2017支持的Servlet的版本_jsp进阶(servlet篇)
weixin_39814369
Servlet篇(jsp重点)@author:杜勇帅@email:820482874@qq.com-一.Servlet基础一.Servlet概述Servlet是运行在服务器端的java程序(类),作用是接收并处理客户端的请求,生成响应给客户端。学习了Servlet后,jsp页面只负责展示数据,Servlet负责处理请求(调用Dao实现具体功能),并将数据转发到jsp页面上去展示(生成响应给客户端)
java lambda表达式编译_什么是Java 8 Lambda表达式编译?
我是一只萤火虫呀
java lambda表达式编译
参见英文答案>HowwillJavalambdafunctionsbecompiled?考虑下面的Java8代码段。publicstaticvoidmain(String[]args){Listintegers=Arrays.asList(1,2,3,4,5);Consumerconsumer=x->System.out.print(x);integers.forEach(consumer);}什
线程同步CountDownLatch的使用
初夏の猫
java 开发语言
CountDownLatch是Java中一个常用的同步工具类,主要用于协调多个线程的执行。它可以让一个或多个线程等待,直到其他线程执行完毕后才能继续。常见的使用场景是多个线程执行一些并行任务,主线程需要等待所有线程完成任务才能继续执行。基本概念:CountDownLatch类接受一个初始计数值,这个计数值通常是线程的数量。每个线程在完成其任务时调用countDown()方法将计数值减1,直到计数值
spring项目中读取类路径下文件的方式
初夏の猫
spring java sql
//1.Class.getResourceAsStream()InputStreaminputStream=LoadConfigUtil.class.getResourceAsStream("/mybatis-config.xml");//2.ClassPathResourcefontResource=newClassPathResource("fonts/msyh.ttf");InputStre
2021-03-01-深入浅出解析jsp
热爱Java的程序猿
后端 jsp servlet java
来历servlet的缺点导致jsp的产生JSP全称是JavaServerPagesjsp=html+js+Java+Tag用法servlet只负责响应请求产生数据,并把数据通过转发技术带给jsp,数据的显示交给jsp来做在长期的软件实践中,人们逐渐把servlet作为web应用中的控制器组件来使用,而把JSP技术作为数据显示模板来使用原理所有的jsp最后都编译成servlet,编译后的路径一般在t
Springboot(五十)SpringBoot3集成sentinel并连接sentinel-dashboard
camellias_
spring boot sentinel 后端
对,你没看错,又是sentinel。我真是够了,而且,我觉得这应该不是最后一次,以后应该还会写到关于sentinel的学习记录。前边我们了解了sentinel如何使用。相对来讲还是比较简单的。之后学到自定义注解的时候,还自定义了一个sentinel注解来实现限流。用着相对来讲还是很方便的。但是呢,有一个小小的问题。官方推荐使用sentinel-dashboard,这玩意我一直没用明白。我得项目一直
Springboot(五十一)SpringBoot3整合Sentinel-nacos持久化策略
camellias_
spring boot sentinel java
上文中我记录了在Springboot项目中链接sentinel-dashboard使用限流规则的全过程。但是呢,有一个小小的问题,我重启了一下我本地的sentinel-dashboard服务,然后,我之前创建的所有的流控规则都没了……这……好像有点不合理啊,咱就不能找地儿存储一下?你这一重启就没了,我这可咋整。Sentinel的流控配置是可以存储在nacos配置中心中的。但是这个我好想没有在官方文
小记 Java stream 中 peek()
神奏
盛开 java lambda
peek函数:接受一个函数作为参数。这个函数会被应用到每个元素上,并将结果元素映射成一个新的元素。相比于类似foreach,更类似于lambda中的map函数。map函数:接受一个函数作为参数。这个函数会被应用到每个元素上,并将返回值映射成一个新的元素。简单来说,map函数就是对流对象(集合中的所有对象)进行操作并返回一个Stream对象,这个Object对象可以是源对象的类型,也可以是其他类型。
HttpServletRequest 作用
冰糖心158
2025 Java面试系列 Java 开发 java
HttpServletRequest接口在JavaServletAPI中扮演着至关重要的角色,它是Servlet处理客户端HTTP请求的核心对象。每次客户端(例如浏览器)向服务器发送一个HTTP请求时,Servlet容器(例如Tomcat)都会创建一个HttpServletRequest对象,并将客户端的请求数据封装在这个对象中,然后将这个对象传递给Servlet的service()方法(或者do
Java进阶之泛型
m0_74824483
面试 学习路线 阿里巴巴 java 开发语言
泛型(Generics)定义泛型:允许在定义类、接口和方法时使用类型参数,从而在编译时捕获类型错误,提高代码的类型安全性和复用性。主要用途类型安全:避免类型转换错误,编译时检查类型安全。代码复用:通过泛型可以编写通用的类和方法,适用于多种数据类型。消除强制类型转换:在使用泛型时,编译器会自动进行类型转换,减少代码中的强制类型转换。泛型的基本语法标记符T是类型参数,通常使用T、E、K、V等字母来表示
ES java客户端中关于BucketSortPipelineAggregationBuilder的问题
Sunager
ElasticSearch elasticsearch java
es聚合后进行分页(非标准分页,涉及到es底层的检索方式)es桶聚合后排序介绍见ES指南介绍//kibana对聚合后的数据GET/_search{"query":{"bool":{"filter":[{"match":{"question":"好的"}},{"range":{
SpringBoot Jwt令牌的使用(黑马javaweb)
liuaiguo75
SpringBoot JAVA Idea spring boot 后端 java spring intellij-idea log4j mybatis
JWT概念JSONWebToken(JWT)是一种开放标准(RFC7519),它定义了一种紧凑和自包含的方式,用于作为JSON对象在各方之间安全地传输信息。这个信息可以被验证和信任,因为它是数字签名的。JWTs可以使用秘密(使用HMAC算法)或使用RSA或ECDSA的公钥/私钥对进行签名。JWT作用1、授权2、信息交换JWT示例代码1、SpringBoot中引入JWTio.jsonwebtoken
4、《Spring Boot 中的依赖管理》
wolf犭良
SpringBoot spring boot 后端 java
SpringBoot中的依赖管理摘要在SpringBoot项目开发的过程中,依赖管理是一项极为关键的工作,它直接关系到项目的稳定性、性能以及开发效率。本文将深入剖析Maven和Gradle这两种主流构建工具在SpringBoot项目中的依赖管理方式,详细介绍如何精准地引入依赖、巧妙地排除不必要的依赖,以及高效地处理版本冲突,帮助开发者牢牢掌控项目依赖,为项目的顺利推进筑牢根基。一、Maven在Sp
SpringBoot适配Sentinel
清扬叶
sentinel 后端
Sentinel介绍随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。Sentinel具有以下特征:丰富的应用场景:Sentinel承接了阿里巴巴近10年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、实时熔断下游不可用应用等。完备的实时监控:Senti
java.lang.IllegalStateException: No thread-bound request found 解决方法
摸鱼码长
子线程共享 异步Feign调用 java 后端 经验分享 spring boot
1、让我们看看这熟悉的报错java.lang.IllegalStateException:Nothread-boundrequestfound:Areyoureferringtorequestattributesoutsideofanactualwebrequest,orprocessingarequestoutsideoftheoriginallyreceivingthread?Ifyouare
面试官:工作中,你有遇到过Spring事务失效的时候吗?
孙悟饭Z
spring java 数据库
在日常开发中,Spring事务管理是保证数据一致性的重要手段,但你是否遇到过明明加了@Transactional注解,事务却“神秘失效”的情况?这种问题不仅让新手抓狂,连老司机也偶尔翻车。本文总结了12种常见的Spring事务失效场景,结合代码示例和解决方案,帮你彻底避开这些坑!目录一、访问权限问题二、final或static方法三、方法内部调用1.使用AOP的AopContext2.注入自身四、
浅谈Java Spring Boot 框架分析和理解
微笑的曙光(StevenLi)
JAVA java spring boot 开发语言
SpringBoot是一个简化Spring开发的框架,它遵循“约定优于配置”的原则,通过内嵌的Tomcat、Jetty或Undertow等容器,使得开发者能够快速构建独立运行的、生产级别的基于Spring框架的应用程序。SpringBoot包含了大量的自动配置功能,可智能识别已存在的库并配置相应组件,从而减少手动配置的工作量。JavaSpringBoot是一个基于Spring框架的开源Java框架
springboot接入sentinel案例
ITzhongzi
spring boot sentinel 后端
版本控制springboot:3.3.2spring-cloud-starter-alibaba-sentinel:2023.0.3.2sentinel-dashboard:1.8.8项目源代码pom4.0.0org.springframework.bootspring-boot-starter-parent3.3.2com.dubbosentinel_demo20.0.1-SNAPSHOTsen
web前段跨域nginx代理配置
刘正强
nginx cms Web
nginx代理配置可参考server部分
server {
listen 80;
server_name localhost;
spring学习笔记
caoyong
spring
一、概述
a>、核心技术 : IOC与AOP
b>、开发为什么需要面向接口而不是实现
接口降低一个组件与整个系统的藕合程度,当该组件不满足系统需求时,可以很容易的将该组件从系统中替换掉,而不会对整个系统产生大的影响
c>、面向接口编口编程的难点在于如何对接口进行初始化,(使用工厂设计模式)
Eclipse打开workspace提示工作空间不可用
0624chenhong
eclipse
做项目的时候,难免会用到整个团队的代码,或者上一任同事创建的workspace,
1.电脑切换账号后,Eclipse打开时,会提示Eclipse对应的目录锁定,无法访问,根据提示,找到对应目录,G:\eclipse\configuration\org.eclipse.osgi\.manager,其中文件.fileTableLock提示被锁定。
解决办法,删掉.fileTableLock文件,重
Javascript 面向对面写法的必要性?
一炮送你回车库
JavaScript
现在Javascript面向对象的方式来写页面很流行,什么纯javascript的mvc框架都出来了:ember
这是javascript层的mvc框架哦,不是j2ee的mvc框架
我想说的是,javascript本来就不是一门面向对象的语言,用它写出来的面向对象的程序,本身就有些别扭,很多人提到js的面向对象首先提的是:复用性。那么我请问你写的js里有多少是可以复用的,用fu
js array对象的迭代方法
换个号韩国红果果
array
1.forEach 该方法接受一个函数作为参数, 对数组中的每个元素
使用该函数 return 语句失效
function square(num) {
print(num, num * num);
}
var nums = [1,2,3,4,5,6,7,8,9,10];
nums.forEach(square);
2.every 该方法接受一个返回值为布尔类型
对Hibernate缓存机制的理解
归来朝歌
session 一级缓存 对象持久化
在hibernate中session一级缓存机制中,有这么一种情况:
问题描述:我需要new一个对象,对它的几个字段赋值,但是有一些属性并没有进行赋值,然后调用
session.save()方法,在提交事务后,会出现这样的情况:
1:在数据库中有默认属性的字段的值为空
2:既然是持久化对象,为什么在最后对象拿不到默认属性的值?
通过调试后解决方案如下:
对于问题一,如你在数据库里设置了
WebService调用错误合集
darkranger
webservice
Java.Lang.NoClassDefFoundError: Org/Apache/Commons/Discovery/Tools/DiscoverSingleton
调用接口出错,
一个简单的WebService
import org.apache.axis.client.Call;import org.apache.axis.client.Service;
首先必不可
JSP和Servlet的中文乱码处理
aijuans
Java Web
JSP和Servlet的中文乱码处理
前几天学习了JSP和Servlet中有关中文乱码的一些问题,写成了博客,今天进行更新一下。应该是可以解决日常的乱码问题了。现在作以下总结希望对需要的人有所帮助。我也是刚学,所以有不足之处希望谅解。
一、表单提交时出现乱码:
在进行表单提交的时候,经常提交一些中文,自然就避免不了出现中文乱码的情况,对于表单来说有两种提交方式:get和post提交方式。所以
面试经典六问
atongyeye
工作 面试
题记:因为我不善沟通,所以在面试中经常碰壁,看了网上太多面试宝典,基本上不太靠谱。只好自己总结,并试着根据最近工作情况完成个人答案。以备不时之需。
以下是人事了解应聘者情况的最典型的六个问题:
1 简单自我介绍
关于这个问题,主要为了弄清两件事,一是了解应聘者的背景,二是应聘者将这些背景信息组织成合适语言的能力。
我的回答:(针对技术面试回答,如果是人事面试,可以就掌
contentResolver.query()参数详解
百合不是茶
android query()详解
收藏csdn的博客,介绍的比较详细,新手值得一看 1.获取联系人姓名
一个简单的例子,这个函数获取设备上所有的联系人ID和联系人NAME。
[java]
view plain
copy
public void fetchAllContacts() {
ora-00054:resource busy and acquire with nowait specified解决方法
bijian1013
oracle 数据库 kill nowait
当某个数据库用户在数据库中插入、更新、删除一个表的数据,或者增加一个表的主键时或者表的索引时,常常会出现ora-00054:resource busy and acquire with nowait specified这样的错误。主要是因为有事务正在执行(或者事务已经被锁),所有导致执行不成功。
1.下面的语句
web 开发乱码
征客丶
spring Web
以下前端都是 utf-8 字符集编码
一、后台接收
1.1、 get 请求乱码
get 请求中,请求参数在请求头中;
乱码解决方法:
a、通过在web 服务器中配置编码格式:tomcat 中,在 Connector 中添加URIEncoding="UTF-8";
1.2、post 请求乱码
post 请求中,请求参数分两部份,
1.2.1、url?参数,
【Spark十六】: Spark SQL第二部分数据源和注册表的几种方式
bit1129
spark
Spark SQL数据源和表的Schema
case class
apply schema
parquet
json
JSON数据源 准备源数据
{"name":"Jack", "age": 12, "addr":{"city":"beijing&
JVM学习之:调优总结 -Xms -Xmx -Xmn -Xss
BlueSkator
-Xss -Xmn -Xms -Xmx
堆大小设置JVM 中最大堆大小有三方面限制:相关操作系统的数据模型(32-bt还是64-bit)限制;系统的可用虚拟内存限制;系统的可用物理内存限制。32位系统下,一般限制在1.5G~2G;64为操作系统对内存无限制。我在Windows Server 2003 系统,3.5G物理内存,JDK5.0下测试,最大可设置为1478m。典型设置:
java -Xmx355
jqGrid 各种参数 详解(转帖)
BreakingBad
jqGrid
jqGrid 各种参数 详解 分类:
源代码分享
个人随笔请勿参考
解决开发问题 2012-05-09 20:29 84282人阅读
评论(22)
收藏
举报
jquery
服务器
parameters
function
ajax
string
读《研磨设计模式》-代码笔记-代理模式-Proxy
bylijinnan
java 设计模式
声明: 本文只为方便我个人查阅和理解,详细的分析以及源代码请移步 原作者的博客http://chjavach.iteye.com/
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/*
* 下面
应用升级iOS8中遇到的一些问题
chenhbc
ios8 升级iOS8
1、很奇怪的问题,登录界面,有一个判断,如果不存在某个值,则跳转到设置界面,ios8之前的系统都可以正常跳转,iOS8中代码已经执行到下一个界面了,但界面并没有跳转过去,而且这个值如果设置过的话,也是可以正常跳转过去的,这个问题纠结了两天多,之前的判断我是在
-(void)viewWillAppear:(BOOL)animated
中写的,最终的解决办法是把判断写在
-(void
工作流与自组织的关系?
comsci
设计模式 工作
目前的工作流系统中的节点及其相互之间的连接是事先根据管理的实际需要而绘制好的,这种固定的模式在实际的运用中会受到很多限制,特别是节点之间的依存关系是固定的,节点的处理不考虑到流程整体的运行情况,细节和整体间的关系是脱节的,那么我们提出一个新的观点,一个流程是否可以通过节点的自组织运动来自动生成呢?这种流程有什么实际意义呢?
这里有篇论文,摘要是:“针对网格中的服务
Oracle11.2新特性之INSERT提示IGNORE_ROW_ON_DUPKEY_INDEX
daizj
oracle
insert提示IGNORE_ROW_ON_DUPKEY_INDEX
转自:http://space.itpub.net/18922393/viewspace-752123
在 insert into tablea ...select * from tableb中,如果存在唯一约束,会导致整个insert操作失败。使用IGNORE_ROW_ON_DUPKEY_INDEX提示,会忽略唯一
二叉树:堆
dieslrae
二叉树
这里说的堆其实是一个完全二叉树,每个节点都不小于自己的子节点,不要跟jvm的堆搞混了.由于是完全二叉树,可以用数组来构建.用数组构建树的规则很简单:
一个节点的父节点下标为: (当前下标 - 1)/2
一个节点的左节点下标为: 当前下标 * 2 + 1
&
C语言学习八结构体
dcj3sjt126com
c
为什么需要结构体,看代码
# include <stdio.h>
struct Student //定义一个学生类型,里面有age, score, sex, 然后可以定义这个类型的变量
{
int age;
float score;
char sex;
}
int main(void)
{
struct Student st = {80, 66.6,
centos安装golang
dcj3sjt126com
centos
#在国内镜像下载二进制包
wget -c http://www.golangtc.com/static/go/go1.4.1.linux-amd64.tar.gz
tar -C /usr/local -xzf go1.4.1.linux-amd64.tar.gz
#把golang的bin目录加入全局环境变量
cat >>/etc/profile<
10.性能优化-监控-MySQL慢查询
frank1234
性能优化 MySQL慢查询
1.记录慢查询配置
show variables where variable_name like 'slow%' ; --查看默认日志路径
查询结果:--不用的机器可能不同
slow_query_log_file=/var/lib/mysql/centos-slow.log
修改mysqld配置文件:/usr /my.cnf[一般在/etc/my.cnf,本机在/user/my.cn
Java父类取得子类类名
happyqing
java this 父类 子类 类名
在继承关系中,不管父类还是子类,这些类里面的this都代表了最终new出来的那个类的实例对象,所以在父类中你可以用this获取到子类的信息!
package com.urthinker.module.test;
import org.junit.Test;
abstract class BaseDao<T> {
public void
Spring3.2新注解@ControllerAdvice
jinnianshilongnian
@Controller
@ControllerAdvice,是spring3.2提供的新注解,从名字上可以看出大体意思是控制器增强。让我们先看看@ControllerAdvice的实现:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Co
Java spring mvc多数据源配置
liuxihope
spring
转自:http://www.itpub.net/thread-1906608-1-1.html
1、首先配置两个数据库
<bean id="dataSourceA" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close&quo
第12章 Ajax(下)
onestopweb
Ajax
index.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/
BW / Universe Mappings
blueoxygen
BO
BW Element
OLAP Universe Element
Cube Dimension
Class
Charateristic
A class with dimension and detail objects (Detail objects for key and desription)
Hi
Java开发熟手该当心的11个错误
tomcat_oracle
java 多线程 工作 单元测试
#1、不在属性文件或XML文件中外化配置属性。比如,没有把批处理使用的线程数设置成可在属性文件中配置。你的批处理程序无论在DEV环境中,还是UAT(用户验收
测试)环境中,都可以顺畅无阻地运行,但是一旦部署在PROD 上,把它作为多线程程序处理更大的数据集时,就会抛出IOException,原因可能是JDBC驱动版本不同,也可能是#2中讨论的问题。如果线程数目 可以在属性文件中配置,那么使它成为
推行国产操作系统的优劣
yananay
windows linux 国产操作系统
最近刮起了一股风,就是去“国外货”。从应用程序开始,到基础的系统,数据库,现在已经刮到操作系统了。原因就是“棱镜计划”,使我们终于认识到了国外货的危害,开始重视起了信息安全。操作系统是计算机的灵魂。既然是灵魂,为了信息安全,那我们就自然要使用和推行国货。可是,一味地推行,是否就一定正确呢?
先说说信息安全。其实从很早以来大家就在讨论信息安全。很多年以前,就据传某世界级的网络设备制造商生产的交