目录
先看下脑图
方式1:@Autowire
@Component public class Demo1 { @Autowired private ApplicationContext applicationContext; public void show() { System.out.println("Demo1: " + applicationContext); } }
测试
public class AppDemo { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.springboot.demo2.test"); context.getBean(Demo1.class).show(); } }
运行结果,显示已经拿到ApplicationContext 了:
方式2:构造方法
spring4.3新特性:构造方法,注入 只能有一个构造函数,此时,如如果有多个spring会使用无参的构造函数
@Component public class Demo2 { private ApplicationContext applicationContext; //4.3+的新特性 //只能有一个构造函数 public Demo2(ApplicationContext applicationContext) { this.applicationContext = applicationContext; } public void show() { System.out.println("Demo2: " + applicationContext.getClass()); } }
测试:
public class AppDemo { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.springboot.demo2.test"); // context.getBean(Demo1.class).show(); context.getBean(Demo2.class).show(); } }
运行结果,显示已经拿到ApplicationContext 了:
方式3:实现ApplicationContextAware
1.获取方式demo
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; @Component public class Demo3 implements ApplicationContextAware { private ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) { this.applicationContext = applicationContext; } public void show() { System.out.println("Demo3: " + applicationContext); } }
测试:
import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class AppDemo { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.springboot.demo2.test"); // context.getBean(Demo1.class).show(); // context.getBean(Demo2.class).show(); context.getBean(Demo3.class).show(); } }
运行结果,显示已经拿到ApplicationContext 了:
2.获取原理实现BeanPostProcessor接口
①BeanPostProcessor是啥?有啥用?
作用就是可以在spring装配和初始bean的过程中,搞点小动作。
他有2个方法:
postProcessBeforeInitialization:在bean依赖装配(属性设置完)完成之后触发,这里可以指定的bean做一些处理,比如说,返回该对象的代理对象。
postProcessAfterInitialization:bean init方法执行之后触发。
用代码说明:
我们自己实现这个BeanPostProcessor接口:
/** * EchoBeanPostProcessor 会在每个bean初始化的时候,调用一次 * 两个方法不能返回null,否则,从容器中获取不到 */ @Component public class EchoBeanPostProcessor implements BeanPostProcessor { /** * 实在bean依赖装配(属性设置完)完成之后触发 * * 这里可以指定的bean做一些处理,比如说,返回该对象的代理对象 * * @param bean * @param beanName * @return * @throws BeansException */ @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("=========postProcessBeforeInitialization======before======" + bean.getClass()); return bean; } /** * 是在bean init方法执行之后触发 * * @param bean * @param beanName * @return * @throws BeansException */ @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("=========postProcessAfterInitialization====post========" + bean.getClass()); return bean; } }
这个类被spring容器管理后,会影响每个bean的装配加载初始化,就像过滤器filter对应于每个请求一样,可以理解为spring装配完bean后加了个bean的后置处理器。
测试一波:
public class App { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.springboot.demo2"); context.getBean(Book.class).show(); context.getBean(Bank.class).show(); } }
看见这2个bean装载后,调用自己的方法前(使用前),调用了我们自己的逻辑。
②利用BeanPostProcessor做个代理对象的小demo
我们来实现一个功能: 当User对象装载时,加入我们的日志信息:
User
@Component public class User { @PostConstruct public void init() { System.out.println("user init2"); } private ApplicationContext applicationContext; public void show() { System.out.println("User: " + applicationContext); } @Autowired public void setApplicationContext(ApplicationContext applicationContext) { this.applicationContext = applicationContext; System.out.println("========set==========="); } }
子类LoginUser
public class LogUser extends User{ public void show() { System.out.println("log start .."); super.show(); System.out.println("log end .."); } }
添加我们的逻辑判断,如果是User装载,就调用LoginUser的方法:
@Component public class EchoBeanPostProcessor implements BeanPostProcessor { /** * 实在bean依赖装配(属性设置完)完成之后触发 * * 这里可以指定的bean做一些处理,比如说,返回该对象的代理对象 * * @param bean * @param beanName * @return * @throws BeansException */ @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof User) { return new LogUser(); } System.out.println("=========postProcessBeforeInitialization======before======" + bean.getClass()); return bean; } /** * 是在bean init方法执行之后触发 * * @param bean * @param beanName * @return * @throws BeansException */ @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("=========postProcessAfterInitialization====post========" + bean.getClass()); return bean; } }
测试:
public class App { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.springboot.demo2"); context.getBean(User.class).show(); } }
结果显示:
①在bean设置完属性后开始调用postProcessBeforeInitialization,
②在这个方法中,直接返回了loginUser,然后User替换成LoginUser后,
③执行loginUser初始化后,
④调用postProcessAfterInitialization,
⑤ 调用了LoginUser的show方法。
②方式3通过BeanPostProcessor就获取applcationContext具体做法:
这个得读源码:
方式3:从外观上看是实现了ApplicationContextAware就可以获取applcationContext了。
那么我们debug起来:
在AnnotationConfigApplicationContext源码中
进入这个类:
ApplicationContextAwareProcessor
发现1,实现了beanPostProcessor接口,
发现2:这个类下面的代码就是最终答案:在这里判断如果实现了ApplicationContextWare,就注入了ApplicationContext这个属性。
方式4:自己实现获取
我们了解了这个原理,就可自己实现个ApplicationWare的功能,然后我们也可以获取ApplicationContext对象。
import org.springframework.context.ApplicationContext; public interface SpringContentAware { public void setApplicationContext(ApplicationContext applicationContext); }
import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Component; @Component public class ContextBeanPostProcessor implements BeanPostProcessor { @Autowired private ApplicationContext applicationContext; @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof SpringContentAware){ SpringContentAware sca = (SpringContentAware) bean; sca.setApplicationContext(applicationContext); } return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; } }
测试下我们写的这个SpringContentAware 能否拿到applicationContext:
@Component public class Dog implements SpringContentAware { private ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) { this.applicationContext = applicationContext; } public void show() { System.out.println("Dog: " + applicationContext); } }
test类测试:
public class App { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.springboot.demo2"); context.getBean(Dog.class).show(); } }
运行结果,显示已经拿到了: