精通Spring主要看有沒有掌握好Spring的那些擴展點,以及如何使用他們。
使用BeanPostProcessor自定義Bean
如果您想在Spring容器完成實例化,配置和初始化bean之后實現(xiàn)一些自定義邏輯,則可以插入一個或多個自定義BeanPostProcessor實現(xiàn)。這些實現(xiàn)成為后置處理器。
BeanPostProcessor接口包含兩個回調(diào)方法。當實現(xiàn)此接口類通過容器注冊為后處理器時,由Spring容器實例的Bean,Spring容器會在bean 的init方法執(zhí)行前回調(diào)postProcessBeforeInitialization方法,然后會在bean初始化之后回調(diào)postProcessAfterInitialization方法。后置處理器可以對這些Bean做任何自定義操作。一些Spring Aop 的基礎實現(xiàn)類就是通過實現(xiàn)BeanPostProcessor從而提供代理包裝邏輯 。
Spring容器能夠自動檢測任何實現(xiàn)了BeanPostProcessor接口的Bean.容器會自動將這些bean注冊成后置處理器以便后續(xù)調(diào)用。
下面的示例演示如何在ApplicationContext中編寫,注冊和使用BeanPostProcessor實例(Spring AOP的實現(xiàn)方式就是如下)。
public interface Greeting {
void sayHello();
}
public class StudentImpl implements Greeting {
private String name;
@Override
public void sayHello() {
System.out.println("Hello World,"+this.name);
}
public void init(){
this.name="student";
}
@Override
public String toString() {
return "HelloWorldImpl{" +
"name='" + name + '\'' +
'}';
}
}
public class TeacherImpl implements Greeting {
private String name;
@Override
public void sayHello() {
System.out.println("Hello World,"+this.name);
}
public void init(){
this.name="teacher";
}
@Override
public String toString() {
return "TeacherImpl{" +
"name='" + name + '\'' +
'}';
}
}
public class HelloWorldBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return Proxy.newProxyInstance(bean.getClass().getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("BeanPostProcessor織入,Spring AOP 實現(xiàn)原理");
return method.invoke(bean, args);
}
});
}
}
<?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">
<!-- Root Context: defines shared resources visible to all other web components -->
<bean id="student" class="com.gethin.extension.example1.StudentImpl" init-method="init" />
<bean id="teacher" class="com.gethin.extension.example1.TeacherImpl" init-method="init" />
<bean id="hellWorldBeanPostPostProcessor" class="com.gethin.extension.example1.HelloWorldBeanPostProcessor"/>
</beans>
執(zhí)行入口:
public class Main {
public static void main(String args[]) {
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("example1.xml");
Greeting student= (Greeting) applicationContext.getBean("student");
student.sayHello();
Greeting teacher= (Greeting) applicationContext.getBean("teacher");
teacher.sayHello();
}
}
上面程序執(zhí)行的結(jié)果如下:
BeanPostProcessor織入,Spring AOP 實現(xiàn)原理
Hello World,student
BeanPostProcessor織入,Spring AOP 實現(xiàn)原理
Hello World,teacher
使用BeanFactoryPostProcessor自定義配置元數(shù)據(jù)
BeanFactoryPostProcessor跟BeanPostProcessor有點相似,但是有一個很明顯的區(qū)別,BeanFactoryPostProcessor 主要是作用于Bean的配置元數(shù)據(jù)。Spring IoC容器允許BeanFactoryPostProcessor讀取配置元數(shù)據(jù),并有可以在容器實例化除BeanFactoryPostProcessor實例之外的任何bean之前更改配置元數(shù)據(jù)。
下面示例,如何通過BeanFactoryPostProcessor動態(tài)注冊Bean進去。
public class User {
private String id;
private String name;
//getter,setter省略
}
/**
* Created with IntelliJ IDEA.
*
* @author: gethin
* @Date: 2020/4/5 21:04
* @description: BeanFactoryPostProcessor 實現(xiàn)自動注冊User的bean上去
*/
public class HelloWorldBeanPostFactoryProcesser implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
DefaultListableBeanFactory defaultListableBeanFactory= (DefaultListableBeanFactory) configurableListableBeanFactory;
BeanDefinitionBuilder beanDefinitionBuilder=BeanDefinitionBuilder.genericBeanDefinition(User.class);
beanDefinitionBuilder.addPropertyValue("id", new Integer(1));
beanDefinitionBuilder.addPropertyValue("name", "gethin");
defaultListableBeanFactory.registerBeanDefinition("user",beanDefinitionBuilder.getBeanDefinition());
}
}
<?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="helloWorldBeanPostFactoryProcesser" class="com.gethin.extension.example2.HelloWorldBeanPostFactoryProcesser"/>
</beans>
程序入口,獲取User并輸出。
/**
* Created with IntelliJ IDEA.
*
* @author: gethin
* @Date: 2020/3/15 22:13
* @description:
*/
public class Main {
public static void main(String args[]) {
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("example2.xml");
User user= (User) applicationContext.getBean("user");
System.out.println(user.toString());
}
}
程序輸出結(jié)果,如下。
User{id='1', name='gethin'}
使用FactoryBean自定義實例化邏輯
您可以為本身就是工廠的對象實現(xiàn)FactoryBean接口。
FactoryBean接口是可插入Spring IoC容器的實例化邏輯的一點。
如果您有復雜的初始化代碼,可以用Java更好地表達,則可以創(chuàng)建自己的FactoryBean,在該類中編寫復雜的初始化,然后將自定義FactoryBean插入容器。
FactoryBean接口提供了三種方法:
- Object getObject():返回此工廠創(chuàng)建的對象的實例。
實例可以共享,具體取決于該工廠是否返回單例或原型。 - boolean isSingleton():如果此FactoryBean返回單例,則返回true,否則返回false。
- getObjectType():返回由getObject()方法返回的對象類型;如果類型未知,則返回null。
Mybatis與Spring整合,Mybatis的mapper對象也是通過FactoryBean來實例化的。下面通過簡單的例子,實現(xiàn)這一原理。
定義自己的Mapper注解。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Mapper {
}
創(chuàng)建模擬的Mapper接口類,PersonMapper和UserMapper都是接口,沒有具體實現(xiàn)。
@Mapper
public interface PersonMapper {
void getPerson();
}
創(chuàng)建MapperFatoryBean實現(xiàn)FatoryBean,重寫3個方法實現(xiàn)MapperBean的特殊實例方式(通過JDK代理實例)。
@Component
public class MapperFactoryBean<T> implements FactoryBean<T> {
private Class<T> mapperInterface;
@Override
public T getObject() throws Exception {
return (T)Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, (proxy, method, args) -> {
System.out.println( mapperInterface.getSimpleName() +"的代理類對象");
return null;
});
}
@Override
public Class<?> getObjectType() {
return mapperInterface;
}
@Override
public boolean isSingleton() {
return true;
}
public void setMapperInterface(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
}
/**
* Created with IntelliJ IDEA.
*
* @author: gethin
* @Date: 2020/4/6 1:06
* @description: 掃描包并進行Bean的注冊
*/
public class MapperScanner extends ClassPathBeanDefinitionScanner {
public MapperScanner(BeanDefinitionRegistry registry) {
super(registry);
addIncludeFilter(new AnnotationTypeFilter(Mapper.class));
}
@Override
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Set<BeanDefinitionHolder> mapperBeanDefinitionHolders = super.doScan(basePackages);
for (BeanDefinitionHolder mapperBeanDefinitionHolder : mapperBeanDefinitionHolders) {
GenericBeanDefinition mapperBeanDefinition= (GenericBeanDefinition) mapperBeanDefinitionHolder.getBeanDefinition();
MutablePropertyValues propertyValues = mapperBeanDefinition.getPropertyValues();
propertyValues.add("mapperInterface", mapperBeanDefinition.getBeanClassName());
mapperBeanDefinition.setBeanClass(MapperFactoryBean.class);
}
return mapperBeanDefinitionHolders;
}
@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
AnnotationMetadata metadata = beanDefinition.getMetadata();
return (metadata.isInterface() && metadata.isIndependent());
}
}
MapperBean通過實現(xiàn)BeanFactoryPostProcessor,將FactoryBean注冊到容器中去。
/**
* Created with IntelliJ IDEA.
*
* @author: gethin
* @Date: 2020/4/6 1:00
* @description:
*/
public class MapperBean implements BeanFactoryPostProcessor {
private String package2Scan;
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
MapperScanner mapperScanner=new MapperScanner((BeanDefinitionRegistry) configurableListableBeanFactory);
mapperScanner.doScan(package2Scan);
}
public void setPackage2Scan(String package2Scan) {
this.package2Scan = package2Scan;
}
}
程序執(zhí)行入口,mapper接口方法執(zhí)行。
/**
* Created with IntelliJ IDEA.
*
* @author: gethin
* @description:
*/
public class Main {
public static void main(String args[]) {
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("example3.xml");
UserMapper userMapper= applicationContext.getBean(UserMapper.class);
userMapper.getUser(1);
PersonMapper personMapper=applicationContext.getBean(PersonMapper.class);
personMapper.getPerson();
}
}
程序輸出,mapper接口執(zhí)行成功。
UserMapper的代理類對象
PersonMapper的代理類對象
總結(jié)
Spring IOC提供的拓展接口有以下3個,其他很多擴展也都是在此基礎上進行提供。
- BeanPostProcessor
- BeanFactoryPostProcessor
- FactoryBean