Spring(6)spring进阶

716 阅读6分钟

blog.csdn.net/pan_junbiao… blog.csdn.net/pan_junbiao…

目录

1.bean的覆盖问题

在默认情况下spring允许bean的重名情况出现

当在同一个xml文件下,如果出现了重名的bean,则会报错

在不同的xml文件中出现了重名的bean,则后加载的bean会覆盖前面加载的同名bean

bean的加载过程代码如下

synchronized (this.beanDefinitionMap) {  
    //从beanDefinationMap中获得beanDefination
    Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);  
    //如果oldBeanDefinition存在,则查看是否允许覆盖,若不存在,则进行bean的实例化
    if (oldBeanDefinition != null) {  
    	//如果不允许覆盖,则直接报异常,否则进行覆盖
        if (!this.allowBeanDefinitionOverriding) {  
            throw new BeanDefinitionStoreException(beanDefinition  
                    .getResourceDescription(), beanName,  
                    "Cannot register bean definition ["  
                            + beanDefinition + "] for bean '"  
                            + beanName + "': There is already ["  
                            + oldBeanDefinition + "] bound.");  
        } else {  
            if (this.logger.isInfoEnabled()) {  
                this.logger  
                        .info("Overriding bean definition for bean '"  
                                + beanName + "': replacing ["  
                                + oldBeanDefinition + "] with ["  
                                + beanDefinition + "]");  
            }  
        }  
    } else {  
        this.beanDefinitionNames.add(beanName);  
        this.frozenBeanDefinitionNames = null;  
    }  
    this.beanDefinitionMap.put(beanName, beanDefinition);  
    resetBeanDefinition(beanName);  
}  

所以。如果不允许覆盖,需要将allowBeanDefinitionOverriding设置为false

  • 方法一:通过applicationContext的setAllowBeanDefinitionOverriding方法

注意:需要调用refresh重新刷新applicationContext

ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("配置文件1.xml");
applicationContext.setAllowBeanDefinitionOverriding(false);
applicationContext.refresh();

2.工厂模式生成bean

工厂模式生成bean,可以通过工厂中方法的返回值生成我们需要的bean

  • (一)使用xml注解的工厂模式生成bean

主要使用factory-method标签与factory-bean标签

/**
* 模拟一个静态工厂,创建业务层实现类
*/
public class StaticFactory {
	public static IAccountService createAccountService(){
	return new AccountServiceImpl();
	}
}
<!-- 此种方式是:
使用 StaticFactory 类中的静态方法 createAccountService 创建对象,并存入 spring 容器
id 属性:指定 bean 的 id,用于从容器中获取
class 属性:指定静态工厂的全限定类名
factory-method 属性:指定生产对象的静态方法
-->
<bean id="accountService"
class="com.itheima.factory.StaticFactory"
factory-method="createAccountService"></bean>

/**
* 模拟一个实例工厂,创建业务层实现类
* 此工厂创建对象,必须现有工厂实例对象,再调用方法
*/
public class InstanceFactory {
public IAccountService createAccountService(){
return new AccountServiceImpl();
}
}
<!-- 此种方式是:
先把工厂的创建交给 spring 来管理。
然后在使用工厂的 bean 来调用里面的方法
factory-bean 属性:用于指定实例工厂 bean 的 id。
factory-method 属性:用于指定实例工厂中创建对象的方法。
-->
<bean id="instancFactory" class="com.itheima.factory.InstanceFactory"></bean>
<bean id="accountService"
factory-bean="instancFactory"
factory-method="createAccountService"></bean>
  • (二)@Bean注解也可以将一个方法的返回值注册到spring容器中
@Configuration
public class AppConfig {
    @Bean
    public TransferService transferService() {
        return new TransferServiceImpl();
    }
}

3.FactoryBean

通过FactoryBean我们可以对一些复杂的bean在注册前进行初始化,或者将一些复杂的bean的创建过程包装起来,给bean的属性设置初始值等

public interface FactoryBean<T> {
    T getObject() throws Exception;
    Class<T> getObjectType();
    boolean isSingleton();
}

实例:

public class MyCarFactoryBean implements FactoryBean<Taxi> {
    private String make;
    private int year ;

    public void setMake(String m){ this.make =m ; }

    public void setYear(int y){ this.year = y; }

    @Override
    public Taxi getObject(){
        //模拟复杂bean的构建过程
        Taxi taxi = new Taxi();
        taxi.setTaxiNmae("factory");
        return taxi;
    }

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

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

public class Taxi {
    private String taxiNmae;

    public void setTaxiNmae(String taxiNmae) {
        this.taxiNmae = taxiNmae;
    }
    
	public void say() {
        System.out.println("I am a Taxi..."+taxiNmae);
    }
}

xml配置

    <bean id = "myCarFactoryBean" class = "com.qunar.spring入门.pojo.MyCarFactoryBean" >
        <property name = "make" value ="Honda"/>
        <property name = "year" value ="1984"/>
    </bean>
    <bean id = "car" class = "com.qunar.spring入门.pojo.Car" >
        <property name="taxi" ref="myCarFactoryBean"></property>
    </bean>

spring会自动的调用getObject方法返回目标对象

4.初始化bean的回调

初始化bean的回调,在spring完成bean的实例化和初始化之后,会自动执行的方法

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User2 {
    private Integer userId;
    private String name;
    private String password;

    void printInfo() {
        System.out.println("user2");
    }
}

(一)通过标签内部的init-method方法指定回调方法

<bean id = "user" class = "com.qunar.spring入门.pojo.User2" init-method="printInfo" ></bean>

(二)实现initializingBean接口,重写afterPropertiesSet方法

public class User2 implements InitializingBean {
    private Integer userId;
    private String name;
    private String password;
    
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("initializingBean:user2");
    }
}

(三)使用@Bean注解中的initMethod属性置顶初始化方法

 @Bean(initMethod = "prinfInfo")
    public User foo() {
        return new User();
    }

(四)使用@PostConstruct注解,指定回调方法

@PostConstruct
    void printInfo() {
        System.out.println("user2");
    }

5.销毁bean的回调

(一)通过标签内部的destory-method属性指定销毁方法

<bean id = "user" class = "com.qunar.spring入门.pojo.User2" destory-method="printInfo" ></bean>

(二)通过实现DisposableBean接口,重写destory方法

public class AnotherExampleBean implements DisposableBean {
 
    public void destroy() {
        // do some destruction work (like releasing pooled connections)
    }
}

(三)在@Bean注解中的destoryMethod属性指定销毁方法

@Bean(destroyMethod = "cleanup")
public Bar bar() {
    return new Bar();
}

(四)通过@PreDestory注解指定销毁方法

@PreDestroy
public void cleanup() {
 
}

6.Bean 继承

Spring bean也可以与java中的继承相似,子类bean可以继承父类bean的属性和方法,也可以重写这些方法

主要是通过 中的 parent 属性来指定父类bean

<bean id="user1" class="com.qunar.spring入门.pojo.User1">
        <property name="name" value="user1"></property>
        <property name="password" value="user1"></property>
    </bean>

    <bean id="user2" class="com.qunar.spring入门.pojo.User2" parent="user1">
        <property name="name" value="user2"></property>
    </bean>

7.方法的注入

主要是lookupMethod与replaceMethod两个标签, 可以对bean的返回结果或者是方法体进行拦截增强

查找方法注入:用于注入方法返回结果,也就是说能通过配置方式替换方法返回结果。即我们通常所说的lookup-method注入。

替换方法注入:可以实现方法主体或返回结果的替换,即我们通常所说的replaced-method注入。

spring方法的注入,主要有两个注解

8.ConversionService

9.BeanPostProcessor

也叫做后置处理器,通过beanPostProcessor可以在bean的实例化和依赖注入之后,进行相关方法的调用或者自定义逻辑的处理

BeanProcessor接口的源码

public interface BeanPostProcessor {
	//在自定义的init方法前执行
	Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
    //在自定义的init方法后执行
	Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}

-(一)BeanPostProcessor的使用

/**
 * 自定义BeanPostProcessor实现类
 * BeanPostProcessor接口的作用是:
 * 	 我们可以通过该接口中的方法在bean实例化、配置以及其他初始化方法前后添加一些我们自己的逻辑
*/
public class MyBeanPostProcessor implements BeanPostProcessor {

    /**
     * 实例化、依赖注入完毕,在调用显示的初始化之前完成一些定制的初始化任务
     * 注意:方法返回值不能为null
     * 如果返回null那么在后续初始化方法将报空指针异常或者通过getBean()方法获取不到bena实例对象
     * 因为后置处理器从Spring IoC容器中取出bean实例对象没有再次放回IoC容器中
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("before--实例化的bean对象:"+bean+"\t"+beanName);
        // 可以根据beanName不同执行不同的处理操作
        return bean;
    }

    /**
     * 实例化、依赖注入、初始化完毕时执行
     * 注意:方法返回值不能为null
     * 如果返回null那么在后续初始化方法将报空指针异常或者通过getBean()方法获取不到bena实例对象
     * 因为后置处理器从Spring IoC容器中取出bean实例对象没有再次放回IoC容器中
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("after...实例化的bean对象:"+bean+"\t"+beanName);
        // 可以根据beanName不同执行不同的处理操作
        return bean;
    }
}

public class User3 {

    private int id;

    private String name;

    private String beanName;

    public User3(){
        System.out.println("User 被实例化");
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        System.out.println("设置:"+name);
        this.name = name;
    }

    public String getBeanName() {
        return beanName;
    }

    public void setBeanName(String beanName) {
        this.beanName = beanName;
    }
    /**
     * 自定义的初始化方法
     */
    public void start(){
        System.out.println("User 中自定义的初始化方法");
    }

    @Override
    public String toString() {
        return "User3{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", beanName='" + beanName + '\'' +
                '}';
    }
}

xml的配置,将PostPostPrecessor添加进容器即可

 <bean class="com.qunar.spring入门.pojo.User3" id="user3" init-method="start">
        <property name="name" value="波波烤鸭" />
    </bean>

    <!-- 注册处理器 -->
    <bean class="com.qunar.spring入门.pojo.MyBeanPostProcessor"></bean>

执行结果:

ser 被实例化
设置:波波烤鸭
before--实例化的bean对象:User3{id=0, name='波波烤鸭', beanName='null'}	user3
User 中自定义的初始化方法
after...实例化的bean对象:User3{id=0, name='波波烤鸭', beanName='null'}	user3
User3{id=0, name='波波烤鸭', beanName='null'}

也可以设置多个BeanPostProcessor,会默认按照申明的顺序进行调用,也可以实现Ordered接口,实现 getOrder() 方法指定顺序,在Spring机制中可以指定后置处理器调用顺序,通过让BeanPostProcessor接口实现类实现Ordered接口getOrder方法,该方法返回一整数,默认值为 0,优先级最高,值越大优先级越低

public class MyBeanPostProcessor implements BeanPostProcessor,Ordered{
	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    	return bean;
	}

	@Override
	public int getOrder() {
		// TODO Auto-generated method stub
		return 10;
	}
}

10.三级缓存

11.循环依赖

12.@Autowired与@Resource的区别

13.spring cache缓存

14.spring事务的使用

15.bean的生命周期

16.BeanFactory与ApplicationContext的区别

cloud.tencent.com/developer/a…

17.内部bean

当一个bean仅被用作另一个bean的属性时,它能被声明为一个内部bean,为了定义inner bean,在Spring 的 基于XML的 配置元数据中,可以在 或 元素内使用 元素,内部bean通常是匿名的,它们的Scope一般是prototype。

代码示例:

<bean id="person2" class="com.itdjx.spring.dependency.injection.Person">
        <property name="name" value="李玉"/>
        <property name="age" value="23"/>
        <property name="sex" value="女"/>
        <property name="car" >
            <bean class="com.itdjx.spring.dependency.injection.Car">
                <constructor-arg value="Ferrari" index="0"/>
                <constructor-arg value="Italy" index="1"/>
                <constructor-arg value="22500000" type="double"/>
            </bean>
        </property>
    </bean>