注册Bean
xml方式绑定bean
新建一个base.xml
<bean id="person" class="com.enjoy.cap1.Person">
<property name="name" value="james"></property>
<property name="age" value="19"></property>
</bean>
新建一个实体类
private String name;
private Integer age;
public Person() {
super();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Person(String name, Integer age) {
super();
this.name = name;
this.age = age;
}
public Integer getAge() {
return age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
public void setAge(Integer age) {
this.age = age;
}
}
新建一个MainTest1
public class MainTest1 {
public static void main(String[] args) {
ApplicationContext app = new ClassPathXmlApplicationContext("base.xml");
Person person = (Person) app.getBean("person");
System.out.println(person);
}
}
显然我们现在不使用xml的方式,我们使用注解的方式
注解绑定bean
新建config类
@Configuration
public class MainConfig {
@Bean
public Person person() {
return new Person("sam", "20");
}
}
新建MainTest2
public class MainTest2 {
public static void main(String[] args) {
AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(MainConfig.class);
Person person = (Person) app.getBean("person");
System.out.println(person);
String[] beanNamesForType = app.getBeanNamesForType(Person.class);
for (String name : beanNamesForType) {
System.out.println(name);
}
}
}
ComponentScan扫描规则
新建一个MainConfig
@Configuration
@ComponentScan(value = "com.enjoy.cap2", includeFilters = {
@ComponentScan.Filter(type = FilterType.CUSTOM, classes = {SamTypeFilter.class})
}, useDefaultFilters = false)
public class MainConfig {
@Bean
public Person person() {
return new Person("sam", "20");
}
}
@ComponentScan value:指定要扫描的包
excludeFilters = Filter[] 指定扫描的时候按照什么规则排除那些组件
includeFilters = Filter[] 指定扫描的时候只需要包含哪些组件
useDefaultFilters = false 默认是true,扫描所有组件,要改成false
----扫描规则如下
FilterType.ANNOTATION:按照注解
FilterType.ASSIGNABLE_TYPE:按照给定的类型;比如按BookService类型
FilterType.ASPECTJ:使用ASPECTJ表达式
FilterType.REGEX:使用正则指定
FilterType.CUSTOM:使用自定义规则,自已写类,实现TypeFilter接口
自己实现过滤规则
public class SamTypeFilter implements TypeFilter {
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
String className = annotationMetadata.getClassName();
Resource resource = metadataReader.getResource();
ClassMetadata classMetadata = metadataReader.getClassMetadata();
System.out.println("---------->" + className);
if (className.contains("er")) {
return true;
}
return false;
}
}
编写一个测试类
public class Test {
@Test
public void test01() {
AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(MainConfig.class);
String[] beanNamesForType = app.getBeanDefinitionNames();
for(String name:beanNamesForType){
System.out.println(name);
}
}
}
Scope扫描规则
新建一个Config
@Configuration
public class MainConfig {
@Scope("prototype")
@Bean
public Person person() {
return new Person("sam", "20");
}
}
- prototype:多实例: IOC容器启动的时候,IOC容器启动并不会去调用方法创建对象, 而是每次获取的时候才会调用方法创建对象
- singleton:单实例(默认):IOC容器启动的时候会调用方法创建对象并放到IOC容器中,以后每次获取的就是直接从容器中拿(大Map.get)的同一个bean
- request: 主要针对web应用, 递交一次请求创建一个实例
- session:同一个session创建一个实例
编写一个测试类
public class Test {
@Test
public void test01() {
AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(MainConfig.class);
String[] beanNamesForType = app.getBeanDefinitionNames();
for (String name : beanNamesForType) {
System.out.println(name);
}
Person bean1 = (Person) app.getBean("person");
Person bean2 = (Person) app.getBean("person");
System.out.println(bean1 == bean2);
}
}
懒加载
新建Config
@Configuration
public class MainConfig {
@Lazy
@Bean
public Person person() {
System.out.println("开始加载Person--------------");
return new Person("sam", "20");
}
}
懒加载: 主要针对单实例bean:默认在容器启动的时候创建对象 懒加载:容器启动时候不创建对象, 仅当第一次使用(获取)bean的时候才创建被初始化
编写测试类
public class Test {
@Test
public void test01() {
AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(MainConfig.class);
System.out.println("容器加载完成--------------");
app.getBean("person");
}
}
Condition
条件注册bean通过实现Condition接口来进行雕件判断是否注册Bean
public class MacCondition implements Condition {
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();
Environment environment = conditionContext.getEnvironment();
String os = environment.getProperty("os.name");
System.out.println(os);
if (os.contains("Mac")) {
return true;
}
return false;
}
}
public class WinCondition implements Condition {
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();
Environment environment = conditionContext.getEnvironment();
String os = environment.getProperty("os.name");
System.out.println(os);
if (os.contains("Windows")) {
return true;
}
return false;
}
}
@Configuration
public class MainConfig {
@Bean
public Person person() {
System.out.println("开始加载Person--------------");
return new Person("person", "200");
}
@Conditional(MacCondition.class)
@Bean
public Person sam() {
System.out.println("开始加载Sam--------------");
return new Person("sam", "20");
}
@Bean
@Conditional(WinCondition.class)
public Person max() {
System.out.println("开始加载Max--------------");
return new Person("max", "21");
}
}
Import
常见的注册bean的方式有以下
- 1,@Bean: [导入第三方的类或包的组件],比如Person为第三方的类, 需要在我们的IOC容器中使用
- 2,包扫描+组件的标注注解(@ComponentScan: @Controller, @Service @Reponsitory @ Componet),一般是针对 我们自己写的类,使用这个
- 3,@Import:[快速给容器导入一个组件] 注意:@Bean有点简单
-
a,@Import(要导入到容器中的组件):容器会自动注册这个组件,bean 的 id为全类名 -
b,ImportSelector:是一个接口,返回需要导入到容器的组件的全类名数组 -
c,ImportBeanDefinitionRegistrar:可以手动添加组件到IOC容器, 所有Bean的注册可以使用BeanDifinitionRegistry
-
-
写JamesImportBeanDefinitionRegistrar实现ImportBeanDefinitionRegistrar接口即可 - 4,使用Spring提供的FactoryBean(工厂bean)进行注册
@Configuration
@Import(value = {Dog.class, Cat.class, SamImportBeanSelector.class, SamImportBeanDefinitionRegistrar.class})
public class MainConfig {
@Bean
public Person person() {
System.out.println("开始加载Person--------------");
return new Person("person", "200");
}
@Bean
public SamFactoryBean samFactoryBean() {
return new SamFactoryBean();
}
}
public class SamFactoryBean implements FactoryBean<Monkey> {
public Monkey getObject() throws Exception {
return new Monkey();
}
public Class<?> getObjectType() {
return Monkey.class;
}
public boolean isSingleton() {
return true;
}
}
public class SamImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
boolean b = beanDefinitionRegistry.containsBeanDefinition("com.enjoy.cap6.bean.Tiger");
boolean b2 = beanDefinitionRegistry.containsBeanDefinition("com.enjoy.cap6.bean.Fish");
if (b && b2) {
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Pig.class);
beanDefinitionRegistry.registerBeanDefinition("pig", rootBeanDefinition);
}
}
}
public class SamImportBeanSelector implements ImportSelector {
public String[] selectImports(AnnotationMetadata annotationMetadata) {
return new String[]{"com.enjoy.cap6.bean.Tiger", "com.enjoy.cap6.bean.Fish"};
}
}
public class Test {
@Test
public void test01() {
AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(MainConfig.class);
System.out.println("容器加载完成--------------");
Object samFactoryBean = app.getBean("samFactoryBean");
Object samFactoryBean2 = app.getBean("samFactoryBean");
System.out.println("bean的类型为:" + samFactoryBean.getClass());
System.out.println(samFactoryBean == samFactoryBean2);
System.out.println(app.getBean("&samFactoryBean"));
String[] beanNamesForType = app.getBeanDefinitionNames();
for (String name : beanNamesForType) {
System.out.println(name);
}
}
}
bean的生命周期
bean的生命周期是指 bean创建-初始化-销毁 的过程
bean的生命周期是由容器进行管理的,我们可以自定义bean的初始化和销毁的,在容器在bean 进行到当前生命周期时来调用自定义的初始化和销毁方法,一般有以下四种方法
指定初始化和销毁方法
之前在base.xml中指定init-method和destroy-method,但是现在可以用注解
public class Bike {
public Bike() {
System.out.println("Bike constructor..............");
}
public void init() {
System.out.println("Bike .....init.....");
}
public void destory() {
System.out.println("Bike.....destory");
}
}
在配置类里面通过@Bean(inintMethod="",destroyMethon="")
@Bean(initMethod = "init", destroyMethod = "destory")
public Bike bike() {
return new Bike();
}
-
单实例:当容器关闭的时候会自动调用destroy方法
@Test public void test01() { AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(Cap7MainConfigOfLifeCycle.class); System.out.println("IOC容器创建完成........"); app.close(); } -
多实例:容器值负责初始化,但不回管理bean,容器关闭不回调用销毁方法
实现InitializingBean和DisposableBean接口
通过实现以上两个接口啦模拟bean的初始化和销毁
- InitializingBean:afterPropertiesSet()方法,当beanfactory吧bean对象创建好,且吧bean的属性设置好了的时候,回调用这个方法相当于初始化方法
- DisposableBean:destroy()方法,当bean销毁时,会把单例bean进行销毁
@Component
public class Train implements InitializingBean, DisposableBean {
public Train() {
System.out.println("Train__________________constructor");
}
public void destroy() throws Exception {
System.out.println("Train___________________destory");
}
public void afterPropertiesSet() throws Exception {
System.out.println("Train____________________init");
}
}
@ComponentScan("com.enjoy.cap7.bean")
@Configuration
public class MainConfig {
//……
}
@Test
public void test() {
AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(Cap7MainConfigOfLifeCycle.class);
System.out.println("IOC容器创建完成........");
app.close();
}
JSR250的@PostConstrct和@PreDretroy
-
@PostConstruct在Bean创建完成且赋值完成后进行初始化,属于JDK规范注解
-
@PreDestroy 在Bean被移除之前进行通知,在容器销毁前进行亲历工作
@Component
public class Jeep {
public Jeep() {
System.out.println("Jeep.....constructor........");
}
@PostConstruct
public void init() {
System.out.println("Jeep.....@PostConstruct........");
}
@PreDestroy
public void destory() {
System.out.println("Jeep.....@PreDestroy......");
}
}
@ComponentScan("com.enjoy.cap7.bean")
@Configuration
public class MainConfig {
……
}
@Test
public void test() {
AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(Cap7MainConfigOfLifeCycle.class);
System.out.println("IOC容器创建完成........");
app.close();
}
BeanPostProcessor
BeanPostProcessor:bean的后置处理器,在bean初始化之前调用拦截,主要用于bean初始化之后进行一些处理工作:
- postProcessorBeforeInitialization(),在备bean初始化之前进行后置处理工作(在init-method之前)
- postProcessorAfterInitialization(),在bean初始化之后后置处理工作,例如(InitializingBean的afterPropertiesSet())
@Component
public class SameBeanPorcessor implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessBeforeInitialization______" + beanName + "____" + beanName);
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessAfterInitialization_______" + beanName + "____" + beanName);
return bean;
}
}
@ComponentScan("com.enjoy.cap7.bean")
@Configuration
public class MainConfig {
……
}
@Test
public void test01() {
AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(Cap7MainConfigOfLifeCycle.class);
System.out.println("IOC容器创建完成........");
app.close();
}
bean的生命周期时可控的
容器启动及BeanPostProcessor
beanPortProcessor原理:
AnnotationConfigApplicationContext()
->refresh()
-->finishBeanFactoryInitialization()
--->preInstantiateSingletons()
---->getBean()
----->doGetBean()
------>createBean()
------->doCreateBean()
--------->{
createBeanInstance(),
populateBean(),
initializeBean()
----------->{
applyBeanPostProcessorsBeforeInitialization(),
invokeInitMethods(),
applyBeanPostProcessorsAfterInitialization()
}
}
Spring底层对BeanPostPorcessor使用
Spring底层对BeanPostProcessor的使用, 包括bean的赋值, 注入其它组件, 生命周期注解功能,@Async, 等等
ApplicationContextAwareProcessor
此类帮助我们创建IOC容器,跟进ApplocationContextAwareProcessor我们发现,这个后置处理器就是判断我们的 bean是否实现了我们的ApplicationContextAware接口,并处理相应的逻辑,其实所有的后置处理器原理均是如此
@Component
public class Train implements InitializingBean, DisposableBean {
public Train() {
System.out.println("Train......constructor............");
}
//当我们bean销毁时,调用此方法
@Override
public void destroy() throws Exception {
System.out.println("Train......destory......");
//logger.error
}
//当我们的bean属性赋值和初始化完成时调用
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("Train.......afterPropertiesSet()...");
}
}
我们分析一下ApplicationContextAwareProcessor里面的方法
@Override
@Nullable
public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
AccessControlContext acc = null;
if (System.getSecurityManager() != null &&
(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)) {
acc = this.applicationContext.getBeanFactory().getAccessControlContext();
}
if (acc != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
invokeAwareInterfaces(bean);
return null;
}, acc);
}
else {
invokeAwareInterfaces(bean);
}
return bean;
}
-
在创建plane对象,还没有初始化之前,先判断是不是实现了ApplicationContextAware接口,如果是的话就调用invokeAwareInterfaces();并且吧bean注入
-
进入invokeAwareInterfaces(),判断是哪个类型的Aware如果是ApplicationContextAware, 就将当前的bean转成ApplicationContextAware类型, 调用setApplicationContext(), 把IOC容器注入到Plane里去;
private void invokeAwareInterfaces(Object bean) { if (bean instanceof Aware) { if (bean instanceof EnvironmentAware) { ((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment()); } if (bean instanceof EmbeddedValueResolverAware) { ((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver); } if (bean instanceof ResourceLoaderAware) { ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext); } if (bean instanceof ApplicationEventPublisherAware) { ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext); } if (bean instanceof MessageSourceAware) { ((MessageSourceAware) bean).setMessageSource(this.applicationContext); } if (bean instanceof ApplicationContextAware) { ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext); } } }
BeanValidationPostProcessor分析
在对象创建完,给bean赋值之后,在web用的特别多,把页面提交的值进行校验
//初始化之前校验
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (!this.afterInitialization) {
doValidate(bean);
}
return bean;
}
//初始化之后校验
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (this.afterInitialization) {
doValidate(bean);
}
return bean;
}
InitDestroyAnnotationBeanPostProcessor
用于处理@PostConstruct和@PreDestroy
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
try {
metadata.invokeInitMethods(bean, beanName);
}
catch (InvocationTargetException ex) {
throw new BeanCreationException(beanName, "Invocation of init method failed", ex.getTargetException());
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Failed to invoke init method", ex);
}
return bean;
}
public void invokeInitMethods(Object target, String beanName) throws Throwable {
Collection<LifecycleElement> checkedInitMethods = this.checkedInitMethods;
Collection<LifecycleElement> initMethodsToIterate =
(checkedInitMethods != null ? checkedInitMethods : this.initMethods);
if (!initMethodsToIterate.isEmpty()) {
boolean debug = logger.isDebugEnabled();
for (LifecycleElement element : initMethodsToIterate) {
if (debug) {
logger.debug("Invoking init method on bean '" + beanName + "': " + element.getMethod());
}
element.invoke(target);
}
}
}
@Value赋值
public class Bird {
@Value("sam")
private String name;
@Value("#{20-1}")
private Integer age;
@Value("${bird.color}")
private String color;
@Override
public String toString() {
return "Bird{" +
"name='" + name + '\'' +
", age=" + age +
", color='" + color + '\'' +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
@Configuration
@PropertySource(value = "classpath:/test.properties")
public class MainConfig {
@Bean
public Bird bird() {
return new Bird();
}
}
public class Cap8Test {
@Test
public void test01() {
AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(MainConfig.class);
System.out.println("容器加载完成--------------");
Bird bird = (Bird) app.getBean("bird");
System.out.println(bird);
String[] beanDefinitionNames = app.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
ConfigurableEnvironment environment = app.getEnvironment();
System.out.println(environment.getProperty("bird.color"));
app.close();
}
}
自动装配
自动装配:sping利用依赖注入(DI)完成对IOC容器中的各个组件的依赖关系赋值
代码如下:
@Controller
public class TestController {
@Autowired
private TestService testService;
}
@Service
public class TestService {
@Autowired
private TestDao testDao;
public void printIn() {
System.out.println("Service...dao...." + testDao);
}
}
@Repository
public class TestDao {
private String flag = "1";
public String getFlag() {
return flag;
}
public void setFlag(String flag) {
this.flag = flag;
}
@Override
public String toString() {
return "TestDao...... [flag=" + flag + "]";
}
}
@Configuration
@ComponentScan({"com.enjoy.cap9.controller", "com.enjoy.cap9.service", "com.enjoy.cap9.dao", "com.enjoy.cap9.bean"})
public class MainConfig {
//……
}
public class Cap9Test {
@Test
public void test01() {
AnnotationConfigApplicationContext app = new
AnnotationConfigApplicationContext(MainConfig.class);
System.out.println("容器加载完成--------------");
TestService bean = app.getBean(TestService.class);
bean.printIn();
TestDao bean1 = app.getBean(TestDao.class);
System.out.println(bean1);
}
}
@Autowired表示默认优先按类型去容器中找对应的组件,相当于app.getBean(TestDao.class)去容器获取id为testDao的bean, 并注入到TestService的bean中;
注意事项
-
Spring
- 如果容器中有多个testDao要加载哪一个呢?
可以使用@Autowired和@Qualifier来指定使用哪个bean
- 如果容器中没有testDao呢?
会报错,因为@Autowired里面的属性required默认为true就是必须要找到加载的bean
-
@Primary指定的bean是如何加载的呢
-
@Primary是针对指所有testDao bean的默认值,除非@Qualifier指定了加载哪个,意思就是@Qualifier指定的bean不回被@Priamry修改
-
JSR250
- @Resource
- 不能支持@Primary
- 不能支持@Autowired(required=false)的情况
- @Resource
-
JSR330
- @Inject
- 支持@Parimary
- 不能支持@Autowired(required=false)的情况
- 非Spring框架支持,需要映入javax.inject 依赖
- @Inject
@Autowaired可以在构造函数,构造函数参数中使用
@Component
public class Sun {
private Moon moon;
@Autowired
public Sun(Moon moon) {
//public Sun(@Autowaired Moon moon) {
this.moon = moon;
}
public Moon getMoon() {
return moon;
}
public void setMoon(Moon moon) {
this.moon = moon;
}
@Override
public String toString() {
return "Sun [moon=" + moon + "]";
}
}
Aware注入spring底层组件原理
如果我们组件想要使用Spring底层的组件(ApplicationContext,beanFactory),可以实现xxxAware接口,回调用接口规定的方法注入到相关组件
我们找到Aware接口,可以查看这个接口的实现类
Aware (org.springframework.beans.factory)
ApplicationEventPublisherAware (org.springframework.context) //注入事件派发器
NotificationPublisherAware (org.springframework.jmx.export.notification)
MessageSourceAware (org.springframework.context)//资源国际化
EnvironmentAware (org.springframework.context)//运行环境
BeanFactoryAware (org.springframework.beans.factory)
ResourceLoaderAware (org.springframework.context)//资源加载器
EmbeddedValueResolverAware (org.springframework.context)
ImportAware (org.springframework.context.annotation)
LoadTimeWeaverAware (org.springframework.context.weaving)
BeanClassLoaderAware (org.springframework.beans.factory)//类加载器
BeanNameAware (org.springframework.beans.factory)//bean名称
ApplicationContextAware (org.springframework.context)
可以通过实现以上的aware来获取相关的组件
@Component
public class Light implements ApplicationContextAware, BeanNameAware, EmbeddedValueResolverAware {
private ApplicationContext applicationContext;
public void setBeanName(String s) {
System.out.println("当前bean的name为" + s);
}
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("传入的IOC容器: " + applicationContext);
this.applicationContext = applicationContext;
}
public void setEmbeddedValueResolver(StringValueResolver stringValueResolver) {
String s = stringValueResolver.resolveStringValue("你好${os.name}, 计算#{3*8}");
System.out.println("解析的字符串为---" + s);
}
}
通过以上的方法可以把Spring底层的组件组入到自定义bean中,ApplicationContextAware是通过ApplicationContextAwareProcessor来处理的,其他的也一样,XXXAware也是有相关的Processor来处理,也就是后置处理器来处理的。