- 一般情况下,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);
}
}
public class MyClassPathXmlApplicationContext extends ClassPathXmlApplicationContext {
public MyClassPathXmlApplicationContext(String... configLocations){
super(configLocations);
}
@Override
protected void initPropertySources() {
getEnvironment().setRequiredProperties("OS");
}
}
- 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);
}
}
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>
void ignoreDependencyType(Class<?> type);
void ignoreDependencyInterface(Class<?> ifc);
- 在日常的工作中,一些特殊案例需要自定义属性解析器来完成对应的属性解析工作
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>
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);
super.addBeanFactoryPostProcessor(new MyBeanFactoryPostProcessor());
super.customizeBeanFactory(beanFactory);
}
}
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 {
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>
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"/>
<property name="ooxx" value="131_邯郸市"/>
</bean>
<bean id="myConverter" class="com.listao.myConverter.MyConverter"/>
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<ref bean="myConverter"/>
</set>
</property>
</bean>
- 单例模式的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>
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>
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>
- 给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>
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 value="lisi"/>
</bean>
<bean id="personInstanceFactory" class="com.listao.factoryMethod.PersonInstanceFactory"/>
<bean id="person6" class="com.listao.factoryMethod.Person" factory-bean="personInstanceFactory" factory-method="getPerson">
<constructor-arg value="wangwu"/>
</bean>
</beans>
- 没有动态代理,其实只需要二级缓存就足以解决循环依赖问题
- 有了动态代理,所以必须要使用三级缓存来解决此问题
- 修改
doCreatenBean()
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
earlySingletonObjects.put(beanName,bean);
registeredSingletons.add(beanName);
}
- 修改
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;
}
- 修改三级缓存的访问权限
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);