99-源码案例

1. FactoryBean

  • 一般情况下,Spring通过反射机制利用bean的class属性指定实现类,来进行实例化bean
  • 某些情况下,实例化bean过程比较复杂,如果按照传统的方式,则需要在<bean>标签中提供大量的配置信息,配置方式的灵活性是受限的。为此,Spring可以通过实现FactoryBean的接口来定制实例化bean的逻辑
public class Car {

    private String name;
    private String brand;
    private Integer speed;
}
public class CarFactoryBean implements FactoryBean<Car> {

    private String carInfo;

    @Override
    public Class<?> getObjectType() {
        return Car.class;
    }

    @Override
    public Car getObject() throws Exception {
        Car car = new Car();
        String[] split = carInfo.split(",");
        car.setName(split[0]);
        car.setBrand(split[1]);
        car.setSpeed(Integer.valueOf(split[2]));
        return  car;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }
}










 













<bean id="car" class="com.listao.test.CarFactoryBean" >
    <property name="carInfo" value="大黄蜂,玛莎拉蒂,250"></property>
</bean>
public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("test.xml");
        Car car = (Car) context.getBean("car");
        System.out.println(car);
    }
}


 




2. initPropertySources()

public class MyClassPathXmlApplicationContext extends ClassPathXmlApplicationContext {

    public MyClassPathXmlApplicationContext(String... configLocations){
        super(configLocations);
    }

    @Override
    protected void initPropertySources() {
        getEnvironment().setRequiredProperties("OS");
    }
}







 



3. customizeBeanFactory()

  • BeanFactory属性设置
    • allowBeanDefinitionOverriding:是否允许覆盖同名称的不同定义的对象
    • allowCircularReferences:是否允许bean之间的循环依赖
public class MyClassPathXmlApplicationContext extends ClassPathXmlApplicationContext {

    MyClassPathXmlApplicationContext(String... locations){
        super(locations);
    }

    @Override
    protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
        super.setAllowBeanDefinitionOverriding(true);
        super.setAllowCircularReferences(true);
        super.customizeBeanFactory(beanFactory);
    }

}







 






4. MyXmlTag

public class User {
    private String userName;
    private String email;
}
public class UserBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {

    @SuppressWarnings("rawtypes")
    protected Class getBeanClass(Element element) {
        return User.class;
    }

    protected void doParse(Element element, BeanDefinitionBuilder bean) {
        String userName = element.getAttribute("userName");
        String email = element.getAttribute("email");
        if (StringUtils.hasText(userName)) {
            bean.addPropertyValue("userName", userName);
        }
        if (StringUtils.hasText(email)){
            bean.addPropertyValue("email", email);
        }
    }

}
public class MyNamespaceHandler extends NamespaceHandlerSupport {

    public void init() {
        registerBeanDefinitionParser("listao", new UserBeanDefinitionParser());
    }

}
  • resource/META-INF目录下创建三个文件
http\://www.listao.com/schema/user=com.listao.selftag.MyNamespaceHandler
http\://www.listao.com/schema/user.xsd=META-INF/user.xsd
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema"
        targetNamespace="http://www.listao.com/schema/user"
        xmlns:tns="http://www.listao.com/schema/user"
        elementFormDefault="qualified">
    <element name="listao">
        <complexType>
            <attribute name ="id" type = "string"/>
            <attribute name ="userName" type = "string"/>
            <attribute name ="email" type = "string"/>
        </complexType>
    </element>
</schema>
public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new MyClassPathXmlApplicationContext("test2.xml");
        User user = (User) context.getBean("testbean");
        System.out.println("username:" + user.getUserName() + "  " + "email:" + user.getEmail());
    }
}


 




<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aaa="http://www.listao.com/schema/user"

       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.listao.com/schema/user http://www.listao.com/schema/user.xsd">

    <aaa:listao id="testbean" userName="lee" email="bbb"/>
</beans>



 



 

 

5. ignoreDependency*

	/**
	 * 自动装配时忽略的类
	 *
	 * Ignore the given dependency type for autowiring:
	 * for example, String. Default is none.
	 * @param type the dependency type to ignore
	 */
	void ignoreDependencyType(Class<?> type);

	/**
	 * 自动装配时忽略的接口
	 *
	 * Ignore the given dependency interface for autowiring.
	 * <p>This will typically be used by application contexts to register
	 * dependencies that are resolved in other ways, like BeanFactory through
	 * BeanFactoryAware or ApplicationContext through ApplicationContextAware.
	 * <p>By default, only the BeanFactoryAware interface is ignored.
	 * For further types to ignore, invoke this method for each type.
	 * @param ifc the dependency interface to ignore
	 * @see org.springframework.beans.factory.BeanFactoryAware
	 * @see org.springframework.context.ApplicationContextAware
	 */
	void ignoreDependencyInterface(Class<?> ifc);

6. MyPropertyEditor

  • 在日常的工作中,一些特殊案例需要自定义属性解析器来完成对应的属性解析工作
class Address {
    private String district;
    private String city;
    private String province;
}
public class Customer {
    private String name;
    private Address address;
}
public class AddressPropertyEditor extends PropertyEditorSupport {

    @Override
    public void setAsText(String text) {
        try {
            String[] adds = text.split("-");
            Address address = new Address();
            address.setProvince(adds[0]);
            address.setCity(adds[1]);
            address.setDistrict(adds[2]);
            this.setValue(address);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}



 













public class MyPropertyEditorRegistrar implements PropertyEditorRegistrar {

    @Override
    public void registerCustomEditors(PropertyEditorRegistry registry) {
        registry.registerCustomEditor(Address.class,new AddressPropertyEditor());
    }

}

public class Test3 {

    public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("propertyEditor.xml");
        Customer c = ac.getBean("customer", Customer.class);

        System.out.println(c.getAddress());
    }

}



 






<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="customer" class="com.listao.propertyEditor.Customer">
        <property name="name" value="Jack" />
        <property name="address" value="浙江-杭州-西湖" />
    </bean>
    <!-- 第一种方式 -->
    <bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
        <property name="propertyEditorRegistrars">
            <list>
                <bean class="com.listao.propertyEditor.MyPropertyEditorRegistrar"/>
            </list>
        </property>
    </bean>
    <!-- 第二种方式 -->
    <bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
        <property name="customEditors">
            <map>
                <entry key="com.listao.propertyEditor.Address" value="com.listao.propertyEditor.AddressPropertyEditor"/>
            </map>
        </property>
    </bean>
</beans>










 
 
 
 
 



 
 
 
 
 


7. MyBFPP

  • 只需要在xml文件中声明成为一个bean即可
public class MyClassPathXmlApplicationContext extends ClassPathXmlApplicationContext {

    public MyClassPathXmlApplicationContext(String... configLocations){
        super(configLocations);
    }

    @Override
    protected void initPropertySources() {
        System.out.println("扩展initPropertySource");
        getEnvironment().setRequiredProperties("username");
    }

    @Override
    protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
        super.setAllowBeanDefinitionOverriding(false);
        super.setAllowCircularReferences(false);
        // 1.
        super.addBeanFactoryPostProcessor(new MyBeanFactoryPostProcessor());
        super.customizeBeanFactory(beanFactory);
    }

}

















 




8. MyBDRPP

public class MyBeanDefinition implements BeanDefinitionRegistryPostProcessor, Ordered {

    private String name;

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        System.out.println("postProcessBeanDefinitionRegistry----------------------");
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        System.out.println("postProcessBeanFactory========================");
    }

    @Override
    public int getOrder() {
        return 0;
    }

}
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor, Ordered {

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        // 1.
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(MyBeanDefinition.class);
        builder.addPropertyValue("name", "zhangsan");
        registry.registerBeanDefinition("aa", builder.getBeanDefinition());
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        System.out.println("MyBeanDefinitionRegistryPostProcessor-------");
    }

    @Override
    public int getOrder() {
        return 0;
    }

}





 
 
 













<bean class="com.listao.MyBeanDefinitionRegistryPostProcessor"></bean>

9. MyConverter

public class Target {
    private String id;
    private Ooxx ooxx;
}
public class MyConverter implements Converter<String, Ooxx> {

    @Override
    public Ooxx convert(String str) {
        System.out.println("MyConverter.convert()");

        Ooxx ooxx = new Ooxx();

        String[] splits = str.split("_");
        ooxx.setId(Integer.parseInt(splits[0]));
        ooxx.setName(splits[1]);
        return ooxx;
    }
}



 










public static void main(String[] args) {
    ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("x7_myConverter.xml");
    System.out.println("------------------- ac.over -------------------");

    Target target = ac.getBean(Target.class);
    System.out.println("target = " + target);

    ac.close();
}

 







<bean id="target" class="com.listao.myConverter.Target">
    <property name="id" value="listao"/>
    <!-- private Ooxx ooxx; -->
    <property name="ooxx" value="131_邯郸市"/>
</bean>

<bean id="myConverter" class="com.listao.myConverter.MyConverter"/>

<!-- @Bean进行注解实现 -->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters">
        <set>
            <ref bean="myConverter"/>
        </set>
    </property>
</bean>










 

 



10. <lookup-method>

  • 单例模式的bean只会被创建一次,IoC容器会缓存该bean实例以供下次使用
  • 原型模式的bean每次都会创建一个全新的bean,IOC容器不会缓存该bean的实例
  • 单例模式bean引用了原型模式的bean呢?如果无特殊处理,则被引用的原型模式的bean也会被缓存,<lookup-method>解决该问题
public class Fruit {
    public Fruit() {
        System.out.println("I got Fruit");
    }
}
public class Apple extends Fruit {
    public Apple() {
        System.out.println("I got a fresh apple");
    }
}
public class Banana extends Fruit {
    public Banana() {
        System.out.println("I got a  fresh bananer");
    }
}
public abstract class FruitPlate{
    // 抽象方法获取新鲜水果
    public abstract Fruit getFruit();
}
public class TestMethodOverrides {

    public static void main(String[] args) {
        ApplicationContext app = new ClassPathXmlApplicationContext("bean.xml");

        FruitPlate fp1 = (FruitPlate)app.getBean("fruitPlate1");
        FruitPlate fp2 = (FruitPlate)app.getBean("fruitPlate2");

        fp1.getFruit();
        fp2.getFruit();
    }

}



 









<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="apple" class="com.listao.methodOverrides.lookup.Apple" scope="singleton"/>
    <bean id="banana" class="com.listao.methodOverrides.lookup.Banana" scope="prototype" />

    <bean id="fruitPlate1" class="com.listao.methodOverrides.lookup.FruitPlate">
        <lookup-method name="getFruit" bean="apple"/>
    </bean>

    <bean id="fruitPlate2" class="com.listao.methodOverrides.lookup.FruitPlate">
        <lookup-method name="getFruit" bean="banana"/>
    </bean>
</beans>









 



 


11. <replace-method>

  • 替换方法体及其返回值
public class OriginalDog {

	public void sayHello() {
		System.out.println("Hello, I am a black dog...");
	}

	public void sayHello(String name) {
		System.out.println("Hello, I am a black dog, my name is " + name);
	}

}
public class ReplaceDog implements MethodReplacer {

	@Override
	public Object reimplement(Object obj, Method method, Object[] args) throws Throwable {
		System.out.println("Hello, I am a white dog...");

		Arrays.stream(args).forEach(str -> System.out.println("参数:" + str));
		return obj;
	}

}



 







public class TestMethodOverrides {

    public static void main(String[] args) {
        ApplicationContext app = new ClassPathXmlApplicationContext("bean.xml");
        OriginalDog originalDogReplaceMethod = app.getBean("originalDogReplaceMethod", OriginalDog.class);
        originalDogReplaceMethod.sayHello("结果被替换");
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="dogReplaceMethod" class="com.listao.methodOverrides.replace.ReplaceDog"/>

    <bean id="originalDogReplaceMethod" class="com.listao.methodOverrides.replace.OriginalDog">
        <replaced-method name="sayHello" replacer="dogReplaceMethod">
            <arg-type match="java.lang.String"></arg-type>
        </replaced-method>
    </bean>

</beans>








 
 
 



12. Supplier

public class CreateSupplier {

    public static User createUser(){
        return new User("张三");
    }
}
public class User {

    private String username;
}

public class UserBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        BeanDefinition user = beanFactory.getBeanDefinition("user");
        GenericBeanDefinition beanDefinition = (GenericBeanDefinition) user;
        beanDefinition.setInstanceSupplier(CreateSupplier::createUser);
        beanDefinition.setBeanClass(User.class);
    }

}






 




MyClassPathXmlApplicationContext ac = new MyClassPathXmlApplicationContext("applicationContext.xml");
User bean = ac.getBean(User.class);
System.out.println(bean.getUsername());
<bean id="user" class="com.listao.supplier.User"></bean>

<bean class="com.listao.supplier.UserBeanFactoryPostProcessor"></bean>

13. resolveBeforeInstantiation()

  • 给BeanPostProcessor的实现子类机会,去生成代理对象来替代对象
public class BeforeInstantiation {

    public void doSomeThing() {
        System.out.println("执行do some thing ...");
    }

}
public class MyMethodInterceptor implements MethodInterceptor {

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("目标方法前:" + method);
        Object o1 = methodProxy.invokeSuper(o, objects);
        System.out.println("目标方法后:" + method);
        return o1;
    }

}
public class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.print("beanName:" + beanName + "执行..postProcessBeforeInitialization\n");

        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.print("beanName:" + beanName + "执行..postProcessAfterInitialization\n");

        return bean;
    }

    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        System.out.print("beanName:" + beanName + "执行..postProcessAfterInstantiation\n");

        return false;
    }

    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {

        System.out.print("beanName:" + beanName + "执行..postProcessBeforeInstantiation\n");
        // 生成动态代理
        if (beanClass == BeforeInstantiation.class) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(beanClass);
            enhancer.setCallback(new MyMethodInterceptor());
            BeforeInstantiation beforeInstantiation = (BeforeInstantiation) enhancer.create();
            System.out.print("返回动态代理\n");
            return beforeInstantiation;
        }
        return null;
    }

    @Override
    public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
        System.out.print("beanName:" + beanName + "执行..postProcessProperties\n");
        return pvs;
    }

}
























 





















public static void main(String[] args) {
    ApplicationContext ac = new ClassPathXmlApplicationContext("resolveBeforeInstantiation.xml");
    BeforeInstantiation bean = ac.getBean(BeforeInstantiation.class);
    bean.doSomeThing();
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="beforeInstantiation"
          class="com.listao.postProcessor.bpp.resolveBeforeInstantiation.BeforeInstantiation"/>

    <bean id="myInstantiationAwareBeanPostProcessor"
          class="com.listao.postProcessor.bpp.resolveBeforeInstantiation.MyInstantiationAwareBPP"/>

</beans>

14. FactoryMethod

public class Person {
    private int id;
    private String name;
    private int age;
    private String gender;
}
public class PersonInstanceFactory {

    public Person getPerson(String name){
        Person person = new Person();
        person.setId(1);
        person.setName(name);
        return person;
    }
}
public class PersonStaticFactory {

    public static Person getPerson(String name){
        Person person = new Person();
        person.setId(1);
        person.setName(name);
        return person;
    }

}
public class Test {

    public static void main(String[] args) {
        MyClassPathXmlApplicationContext ac = new MyClassPathXmlApplicationContext("factoryMethod.xml");
    }
}



 


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="person5" class="com.listao.factoryMethod.PersonStaticFactory" factory-method="getPerson">
        <!-- constructor-arg:可以为方法指定参数 -->
        <constructor-arg value="lisi"/>
    </bean>

    <bean id="personInstanceFactory" class="com.listao.factoryMethod.PersonInstanceFactory"/>
    <!--
        factory-bean:指定使用哪个工厂实例
        factory-method:指定使用哪个工厂实例的方法
    -->
    <bean id="person6" class="com.listao.factoryMethod.Person" factory-bean="personInstanceFactory" factory-method="getPerson">
        <constructor-arg value="wangwu"/>
    </bean>
</beans>





 









 



15. 三级缓存

  • 没有动态代理,其实只需要二级缓存就足以解决循环依赖问题
  • 有了动态代理,所以必须要使用三级缓存来解决此问题
  1. 修改doCreatenBean()
      if (earlySingletonExposure) {
          if (logger.isTraceEnabled()) {
              logger.trace("Eagerly caching bean '" + beanName +
                      "' to allow for resolving potential circular references");
          }
          // 为避免后期循环依赖,可以在bean初始化完成前将创建实例的ObjectFactory加入工厂
          //addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

          //只保留二级缓存,不向三级缓存中存放对象
          earlySingletonObjects.put(beanName,bean);
          registeredSingletons.add(beanName);

      }
  1. 修改getSingleton()
    @Nullable
	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		Object singletonObject = this.singletonObjects.get(beanName);
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
				synchronized (this.singletonObjects) {
					singletonObject = this.earlySingletonObjects.get(beanName);
					return singletonObject;
				}
			}
		return singletonObject != null ? singletonObject:null;
	}
  1. 修改三级缓存的访问权限
public final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
public final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
public final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
public final Set<String> registeredSingletons = new LinkedHashSet<>(256);