Spring注解驱动开发
一、使用配置文件进行bean的注册
1. 创建bean对象
package com.leon.spring_annotation.bean;
import java.util.Objects;
/**
* ClassName:Person
* Package:com.leon.spring_annotation.bean
* Description:
*
* @Author: leon
* @Version: 1.0
*/
public class Person {
private String name;
private Integer age;
public Person() {
}
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
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;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return Objects.equals(name, person.name) && Objects.equals(age, person.age);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
2.创建配置文件,然后再进行bean的定义
<?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 class="com.leon.spring_annotation.bean.Person" id="person">
<property name="name" value="tom"/>
<property name="age" value="20"/>
</bean>
</beans>
3.在测试类中获取注册的bean
package com.leon.spring_annotation.test;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* ClassName:PersonTest
* Package:com.leon.spring_annotation.test
* Description:
*
* @Author: leon
* @Version: 1.0
*/
public class PersonTest {
private ApplicationContext applicationContext;
@Before
public void init(){
applicationContext = new ClassPathXmlApplicationContext("beans.xml");
}
// 原始配置文件配置使用
@Test
public void beanXmlTest(){
Object person = applicationContext.getBean("person");
System.out.println(person);
}
}
二、注解方式注册bean
1.@Configuration+@Bean方式注册Bean
1.1创建BeanConfig.java配置类
package com.leon.spring_annotation.config;
import com.leon.spring_annotation.bean.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* ClassName:BeanConfig
* Package:com.leon.spring_annotation.config
* Description:
*
* @Author: leon
* @Version: 1.0
*/
// @Configuration 被标注的类,表示是一个配置类,其本身也会被加载到容器中
@Configuration
public class BeanConfig {
// 表示注入的是一个bean对象,其中返回的对象会被注册到容器中
// 方法名是该bean对象在容器中的名称或者可以理解为id
// 还可以通过@Bean注解的value值设置Bean的名称或者可以理解为id
@Bean("person")
public Person person01(){
return new Person("jack",22);
}
}
1.2 进行测试
@Test
public void annotationBeanTest(){
Object person = applicationContext.getBean("person");
// 获取容器中Bean的id
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String name : beanDefinitionNames) {
System.out.println("==========>"+name);
}
System.out.println(person);
}
2. @ComponentScan注解注册Bean
2.1 在配置类中进行配置
package com.leon.spring_annotation.config;
import com.leon.spring_annotation.bean.Person;
import com.leon.spring_annotation.filter.MyFilter;
import com.leon.spring_annotation.service.UserService;
import org.springframework.context.annotation.*;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;
/**
* ClassName:BeanConfig
* Package:com.leon.spring_annotation.config
* Description:
*
* @Author: leon
* @Version: 1.0
*/
// @Configuration 被标注的类,表示是一个配置类,其本身也会被加载到容器中
@Configuration
// @ComponentScan 表示要扫描哪个包下的类
// @ComponentScan 可以重复使用多次,前提得是JDK8以上
/*
excludeFilters 表示排除哪些Bean不注册到容器中
@ComponentScan.Filter 表示过滤注解,其中的type属性表示过滤规则,而Class则是哪些需要被过滤的
*/
/*
includeFilters 表示只包含哪些Bean进行注册
@ComponentScan.Filter 表示过滤注解,其中的type属性表示过滤规则,而Class则是哪些需要被过滤的
useDefaultFilters = false 表示取消默认过滤规则,只有取消使用默认规则includeFilters的规则才会生效
*/
//@ComponentScan(value = "com.leon.spring_annotation",includeFilters = {
// @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class})
//},useDefaultFilters = false)
// 如果使用的不是JDK8以上,则可以使用
@ComponentScans(value = {
@ComponentScan(value = "com.leon.spring_annotation",includeFilters = {
//@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class}),
//@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,classes = {UserService.class})
@ComponentScan.Filter(type = FilterType.CUSTOM,classes = {MyFilter.class})
},useDefaultFilters = false)
})
/*
* type = FilterType.ANNOTATION 按照注解的方式
* type = FilterType.ASSIGNABLE_TYPE 按照给定的类型
* type = FilterType.ASPECTJ 根据ASPECTJ表达式 【使用较少】
* type = FilterType.REGEX 根据正则表达式指定
* type = FilterType.CUSTOM 使用自定义规则
* */
public class BeanConfig {
// 表示注入的是一个bean对象,其中返回的对象会被注册到容器中
// 方法名是该bean对象在容器中的名称或者可以理解为id
// 还可以通过@Bean注解的value值设置Bean的名称或者可以理解为id
@Bean("person")
public Person person01(){
return new Person("jack",22);
}
}
2.2 创建MyFilter.java
package com.leon.spring_annotation.filter;
import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;
import java.io.IOException;
/**
* ClassName:MyFiletr
* Package:com.leon.spring_annotation.filter
* Description:
*
* @Author: leon
* @Version: 1.0
*/
public class MyFilter implements TypeFilter {
/**
* @param metadataReader 读取到的当前正在扫描的类的信息
* @param metadataReaderFactory 可以获取到其他任何类的信息
* @return
* @throws IOException
*/
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
// 获取当前类的注解信息
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
// 获取当前正在扫描的类的信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();
//获取当前类资源(类的路径)
Resource resource = metadataReader.getResource();
// 获取当前正在扫描的类的类名
String className = classMetadata.getClassName();
System.out.println("=========><"+className);
if(className.contains("er")){
return true;
}
return false;
}
}
2.3 进行测试
@Test
//@SuppressWarnings("resource")
public void componentScanTest(){
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String name : beanDefinitionNames) {
System.out.println("==========>"+name);
}
}
3. @Scope设置Bean的作用域
3.1 创建BeanConfig02.java
package com.leon.spring_annotation.config;
import com.leon.spring_annotation.bean.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
/**
* ClassName:BeanConfig02
* Package:com.leon.spring_annotation.config
* Description:
*
* @Author: leon
* @Version: 1.0
*/
@Configuration
public class BeanConfig02 {
// 在默认情况下Bean是单例的,也就是每次请求的Bean对象都是同一个对象
// 使用@Scope注解来设置作用域,默认是singleton
/*
* prototype 多实例:IOC容器启动并不会调用方法创建对象放到IOC容器中,每次获取的时候才会调用方法创建对象
* singleton 单实例的(默认):IOC容器启动会调用方法创建对象放到IOC容器中,后面每次获取就是直接从容器(map.get())中获取
* request 同一次请求创建一个实例
* session 通一个Session创建一个实例
* */
@Bean("lisi")
@Scope("prototype")
public Person person(){
System.out.println("===========new Person(\"lisi\",23)");
return new Person("lisi",23);
}
}
3.2 进行测试
package com.leon.spring_annotation.test;
import com.leon.spring_annotation.config.BeanConfig;
import com.leon.spring_annotation.config.BeanConfig02;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
* ClassName:PersonTest2
* Package:com.leon.spring_annotation.test
* Description:
*
* @Author: leon
* @Version: 1.0
*/
public class PersonTest2 {
private ApplicationContext applicationContext;
@Before
public void init(){
// 通注解注册bean
applicationContext= new AnnotationConfigApplicationContext(BeanConfig02.class);
}
@Test
public void scopeTest(){
Object person = applicationContext.getBean("lisi");
Object person2 = applicationContext.getBean("lisi");
System.out.println(person==person2);
}
}
4. @Lazy注解懒加载
4.1 在BeanConfig02.java设置注解
package com.leon.spring_annotation.config;
import com.leon.spring_annotation.bean.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
/**
* ClassName:BeanConfig02
* Package:com.leon.spring_annotation.config
* Description:
*
* @Author: leon
* @Version: 1.0
*/
@Configuration
public class BeanConfig02 {
// 在默认情况下Bean是单例的,也就是每次请求的Bean对象都是同一个对象
// 使用@Scope注解来设置作用域,默认是singleton
/*
* prototype 多实例:IOC容器启动并不会调用方法创建对象放到IOC容器中,每次获取的时候才会调用方法创建对象
* singleton 单实例的(默认):IOC容器启动会调用方法创建对象放到IOC容器中,后面每次获取就是直接从容器(map.get())中获取
* request 同一次请求创建一个实例
* session 通一个Session创建一个实例
* */
@Bean("lisi")
//@Scope("prototype")
@Lazy
/*
* @Lazy懒加载:
* 单实例Bean在默认情况下,在容器启动的时候进行创建
* 懒加载:容器启动不创建对象。第一次使用(获取)Bean时,进行创建,并进行初始化。此时对象仍然是单例的
* */
public Person person(){
System.out.println("===========new Person(\"lisi\",23)");
return new Person("lisi",23);
}
}
4.2 进行测试
@Test
public void lazyTest(){
Object person = applicationContext.getBean("lisi");
Object person2 = applicationContext.getBean("lisi");
}
5. @Conditional注解的使用
5.1 设置BeanConfig02.java
package com.leon.spring_annotation.config;
import com.leon.spring_annotation.bean.Person;
import com.leon.spring_annotation.conditional.LinuxCondition;
import com.leon.spring_annotation.conditional.WindowsCondition;
import org.springframework.context.annotation.*;
/**
* ClassName:BeanConfig02
* Package:com.leon.spring_annotation.config
* Description:
*
* @Author: leon
* @Version: 1.0
*/
@Configuration
// 类中的组件统一设置,满足当前条件,这个类中配置的所有Bean注册才能生效
@Conditional({WindowsCondition.class})
public class BeanConfig02 {
// 在默认情况下Bean是单例的,也就是每次请求的Bean对象都是同一个对象
// 使用@Scope注解来设置作用域,默认是singleton
/*
* prototype 多实例:IOC容器启动并不会调用方法创建对象放到IOC容器中,每次获取的时候才会调用方法创建对象
* singleton 单实例的(默认):IOC容器启动会调用方法创建对象放到IOC容器中,后面每次获取就是直接从容器(map.get())中获取
* request 同一次请求创建一个实例
* session 通一个Session创建一个实例
* */
@Bean("lisi")
//@Scope("prototype")
@Lazy
/*
* @Lazy懒加载:
* 单实例Bean在默认情况下,在容器启动的时候进行创建
* 懒加载:容器启动不创建对象。第一次使用(获取)Bean时,进行创建,并进行初始化。此时对象仍然是单例的
* */
public Person person(){
System.out.println("===========new Person(\"lisi\",23)");
return new Person("lisi",23);
}
/*
* @Conditional:按照一定的条件进行判断,满足条件给容器中注册Bean
*
*
* */
@Bean("bill")
@Conditional({WindowsCondition.class})
public Person bill(){
return new Person("bill",62);
}
@Bean("linus")
@Conditional({LinuxCondition.class})
public Person linus(){
return new Person("linus",48);
}
}
5.2 创建Condition接口实现类
package com.leon.spring_annotation.conditional;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;
/**
* ClassName:LinuxCondition
* Package:com.leon.spring_annotation.conditional
* Description:
*
* @Author: leon
* @Version: 1.0
*/
public class LinuxCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
// 获取IOC使用的BeanFactory
ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();
// 获取类加载器
ClassLoader classLoader = conditionContext.getClassLoader();
// 获取程序运行环境
Environment environment = conditionContext.getEnvironment();
// 获取到Bean定义的注册类
BeanDefinitionRegistry registry = conditionContext.getRegistry();
String property = environment.getProperty("os.name");
// 判断是否是Windows系统
if(property.contains("Linux")){
return true;
}
return false;
}
}
```java
package com.leon.spring_annotation.conditional;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;
/**
* ClassName:WindowsConditional
* Package:com.leon.spring_annotation.conditional
* Description:
*
* @Author: leon
* @Version: 1.0
*/
public class WindowsCondition implements Condition {
/**
* @param conditionContext 判断条件使用的上下文(环境)
* @param annotatedTypeMetadata 注释信息
* @return
*/
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
// 获取IOC使用的BeanFactory
ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();
// 获取类加载器
ClassLoader classLoader = conditionContext.getClassLoader();
// 获取程序运行环境
Environment environment = conditionContext.getEnvironment();
// 获取到Bean定义的注册类
BeanDefinitionRegistry registry = conditionContext.getRegistry();
String property = environment.getProperty("os.name");
// 可以判断容器中的Bean注册情况,也可以给容器中注册Bean
boolean person = registry.containsBeanDefinition("person");
// 判断是否是Windows系统
if(property.contains("Windows")){
return true;
}
return false;
}
}
5.3 进行测试
@Test
public void conditionalTest(){
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
//获取运行环境
Environment environment = applicationContext.getEnvironment();
String property = environment.getProperty("os.name");
System.out.println(property);
for (String name : beanDefinitionNames) {
System.out.println("-------<>"+name);
}
Map<String, Person> beansOfType = applicationContext.getBeansOfType(Person.class);
System.out.println(beansOfType);
}
6.@Import注解注册Bean
6.1 设置BeanConfig02.java
package com.leon.spring_annotation.config;
import com.leon.spring_annotation.bean.Color;
import com.leon.spring_annotation.bean.Person;
import com.leon.spring_annotation.bean.Red;
import com.leon.spring_annotation.conditional.LinuxCondition;
import com.leon.spring_annotation.conditional.WindowsCondition;
import com.leon.spring_annotation.factory.ColorFactoryBean;
import com.leon.spring_annotation.importbeandefinitionregistrar.MyImportBeanDefinitionRegistrar;
import com.leon.spring_annotation.importselector.MyImportSelector;
import org.springframework.context.annotation.*;
/**
* ClassName:BeanConfig02
* Package:com.leon.spring_annotation.config
* Description:
* 给容器中注册组件:
* 1. 包扫描+组件标注注解(@Controller/@service/@Repository/@Component)[自己定义的组件]
* 2. @Bean[导入的第三方包里面的组件]
* 3. @Import[快速给容器导入一个组件]
* ①@Import(要导入到容器中的组件):容器中就会自动注册这个组件,id默认是全类名
* ②ImportSelector:返回需要导入的组件的全类名数组
* ③ImportBeanDefinitionRegistrar:手动注册Bean到容器中
* 4. 使用Spring提供的FactoryBean(工厂Bean)
* ①默认获取的是工厂Bean调用getObject创建的对象
* ②要获取工厂Bean本身,我们需要给ID前加一个&,例如:&colorFactoryBean
* ③使用FactoryBean接口实现类的Class对象也可以获取到工厂Bean,例如:ColorFactoryBean.class
* @Author: leon
* @Version: 1.0
*/
@Configuration
// 类中的组件统一设置,满足当前条件,这个类中配置的所有Bean注册才能生效
//@Conditional({WindowsCondition.class})
@Import({Color.class, Red.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class})
// @Import导入的组件默认的id是组件的全类名
public class BeanConfig02 {
// 在默认情况下Bean是单例的,也就是每次请求的Bean对象都是同一个对象
// 使用@Scope注解来设置作用域,默认是singleton
/*
* prototype 多实例:IOC容器启动并不会调用方法创建对象放到IOC容器中,每次获取的时候才会调用方法创建对象
* singleton 单实例的(默认):IOC容器启动会调用方法创建对象放到IOC容器中,后面每次获取就是直接从容器(map.get())中获取
* request 同一次请求创建一个实例
* session 通一个Session创建一个实例
* */
@Bean("lisi")
//@Scope("prototype")
@Lazy
/*
* @Lazy懒加载:
* 单实例Bean在默认情况下,在容器启动的时候进行创建
* 懒加载:容器启动不创建对象。第一次使用(获取)Bean时,进行创建,并进行初始化。此时对象仍然是单例的
* */
public Person person(){
System.out.println("===========new Person(\"lisi\",23)");
return new Person("lisi",23);
}
/*
* @Conditional:按照一定的条件进行判断,满足条件给容器中注册Bean
*
*
* */
@Bean("bill")
@Conditional({WindowsCondition.class})
public Person bill(){
return new Person("bill",62);
}
@Bean("linus")
@Conditional({LinuxCondition.class})
public Person linus(){
return new Person("linus",48);
}
@Bean
public ColorFactoryBean colorFactoryBean(){
return new ColorFactoryBean();
}
}
6.2创建MyImportSelector.java
package com.leon.spring_annotation.importselector;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
/**
* ClassName:MyImportSelector
* Package:com.leon.spring_annotation.importselector
* Description:
* 自定义逻辑返回需要导入的组件
* @Author: leon
* @Version: 1.0
*/
public class MyImportSelector implements ImportSelector {
/**
* @param annotationMetadata
* @return 就是要导入到容器中的组件全类名
*/
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
// 可以返回空数组,但是不能返回NULL
return new String[]{"com.leon.spring_annotation.bean.Blue","com.leon.spring_annotation.bean.Yellow"};
}
}
6.3创建MyImportBeanDefinitionRegistrar.java
package com.leon.spring_annotation.importbeandefinitionregistrar;
import com.leon.spring_annotation.bean.RainBow;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
/**
* ClassName:MyImportBeanDefinitionRegister
* Package:com.leon.spring_annotation.importbeandefinitionregister
* Description:
*
* @Author: leon
* @Version: 1.0
*/
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
/**
* @param annotationMetadata 当前类的注解信息
* @param beanDefinitionRegistry BeanDefinition注册类:把所有需要添加到容器中的bean:调用BeanDefinitionRegistry.registerBeanDefinition手工注册进来
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
// 判断是否有id为red的bean对象
boolean red = beanDefinitionRegistry.containsBeanDefinition("com.leon.spring_annotation.bean.Red");
// 判断是否有id为blue的bean对象
boolean blue = beanDefinitionRegistry.containsBeanDefinition("com.leon.spring_annotation.bean.Blue");
// 判断两个条件是否满足
if(red && blue){
// 指定bean定义信息,(Bean的类型,Bean的作用域....)
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(RainBow.class);
// 注册一个bean,指定bean的id
beanDefinitionRegistry.registerBeanDefinition("rainBow",rootBeanDefinition);
}
}
}
6.4 进行测试
@Test
public void importTest(){
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
Blue bean = applicationContext.getBean(Blue.class);
System.out.println(bean);
}
7.@BeanFactory注解使用
7.1 设置BeanConfig02.java
package com.leon.spring_annotation.config;
import com.leon.spring_annotation.bean.Color;
import com.leon.spring_annotation.bean.Person;
import com.leon.spring_annotation.bean.Red;
import com.leon.spring_annotation.conditional.LinuxCondition;
import com.leon.spring_annotation.conditional.WindowsCondition;
import com.leon.spring_annotation.factory.ColorFactoryBean;
import com.leon.spring_annotation.importbeandefinitionregistrar.MyImportBeanDefinitionRegistrar;
import com.leon.spring_annotation.importselector.MyImportSelector;
import org.springframework.context.annotation.*;
/**
* ClassName:BeanConfig02
* Package:com.leon.spring_annotation.config
* Description:
* 给容器中注册组件:
* 1. 包扫描+组件标注注解(@Controller/@service/@Repository/@Component)[自己定义的组件]
* 2. @Bean[导入的第三方包里面的组件]
* 3. @Import[快速给容器导入一个组件]
* ①@Import(要导入到容器中的组件):容器中就会自动注册这个组件,id默认是全类名
* ②ImportSelector:返回需要导入的组件的全类名数组
* ③ImportBeanDefinitionRegistrar:手动注册Bean到容器中
* 4. 使用Spring提供的FactoryBean(工厂Bean)
* ①默认获取的是工厂Bean调用getObject创建的对象
* ②要获取工厂Bean本身,我们需要给ID前加一个&,例如:&colorFactoryBean
* ③使用FactoryBean接口实现类的Class对象也可以获取到工厂Bean,例如:ColorFactoryBean.class
* @Author: leon
* @Version: 1.0
*/
@Configuration
// 类中的组件统一设置,满足当前条件,这个类中配置的所有Bean注册才能生效
//@Conditional({WindowsCondition.class})
@Import({Color.class, Red.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class})
// @Import导入的组件默认的id是组件的全类名
public class BeanConfig02 {
// 在默认情况下Bean是单例的,也就是每次请求的Bean对象都是同一个对象
// 使用@Scope注解来设置作用域,默认是singleton
/*
* prototype 多实例:IOC容器启动并不会调用方法创建对象放到IOC容器中,每次获取的时候才会调用方法创建对象
* singleton 单实例的(默认):IOC容器启动会调用方法创建对象放到IOC容器中,后面每次获取就是直接从容器(map.get())中获取
* request 同一次请求创建一个实例
* session 通一个Session创建一个实例
* */
@Bean("lisi")
//@Scope("prototype")
@Lazy
/*
* @Lazy懒加载:
* 单实例Bean在默认情况下,在容器启动的时候进行创建
* 懒加载:容器启动不创建对象。第一次使用(获取)Bean时,进行创建,并进行初始化。此时对象仍然是单例的
* */
public Person person(){
System.out.println("===========new Person(\"lisi\",23)");
return new Person("lisi",23);
}
/*
* @Conditional:按照一定的条件进行判断,满足条件给容器中注册Bean
*
*
* */
@Bean("bill")
@Conditional({WindowsCondition.class})
public Person bill(){
return new Person("bill",62);
}
@Bean("linus")
@Conditional({LinuxCondition.class})
public Person linus(){
return new Person("linus",48);
}
@Bean
public ColorFactoryBean colorFactoryBean(){
return new ColorFactoryBean();
}
}
7.2 创建ColorFactoryBean.java
package com.leon.spring_annotation.factory;
import com.leon.spring_annotation.bean.Color;
import org.springframework.beans.factory.FactoryBean;
/**
* ClassName:ColorFactoryBean
* Package:com.leon.spring_annotation.factory
* Description:
*
* @Author: leon
* @Version: 1.0
*/
// 创建一个Spring定义的FactoryBean
public class ColorFactoryBean implements FactoryBean<Color> {
// 用于返回Color对象,这个对象会添加到容器中
@Override
public Color getObject() throws Exception {
return new Color();
}
@Override
public Class<?> getObjectType() {
return Color.class;
}
// 是否为单例
// true是单例,在容器中只保存一份
// false是多例,每次调用创建新对象
@Override
public boolean isSingleton() {
return true;
}
}
7.3 进行测试
@Test
public void factoryBeanTest(){
Object bean = applicationContext.getBean("colorFactoryBean");
Object bean2 = applicationContext.getBean("colorFactoryBean");
Object bean3 = applicationContext.getBean("&colorFactoryBean");
ColorFactoryBean bean4 = applicationContext.getBean(ColorFactoryBean.class);
System.out.println(bean.getClass());
System.out.println(bean3.getClass());
System.out.println("===---"+bean4.getClass());
System.out.println(bean==bean2);
}
三、Bean的生命周期
1.bean的初始化方法和销毁的处理方法的指定
- 指定初始化和销毁方法:
- 通过@Bean指定init-method和destroy-method
1.1 创建Car.java类
package com.leon.spring_annotation.bean;
/**
* ClassName:Car
* Package:com.leon.spring_annotation.bean
* Description:
*
* @Author: leon
* @Version: 1.0
*/
public class Car {
public Car(){
System.out.println("Car ------ Constructor");
}
//初始化方法,在构造器之后调用
public void init(){
System.out.println("Car ------ init");
}
//销毁方法,在容器关闭时进行调用
public void destroy(){
System.out.println("Car ------ destroy");
}
}
1.2 在注入bean时通过@Bean注解中的两个属性指定初始化方法和销毁方法
//@Scope("prototype")
// initMethod指定初始化方法,destroyMethod指定销毁方法
@Bean(initMethod = "init",destroyMethod = "destroy")
public Car car(){
return new Car();
}
2.通过让Bean实现InitializingBean(定义初始化逻辑),DisposableBean(定义销毁逻辑)
2.1 创建Cat.java类,实现InitializingBean接口中的afterPropertiesSet方法和DisposableBean接口中的destroy方法
package com.leon.spring_annotation.bean;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
/**
* ClassName:Cat
* Package:com.leon.spring_annotation.bean
* Description:
*
* @Author: leon
* @Version: 1.0
*/
public class Cat implements InitializingBean, DisposableBean {
public Cat() {
System.out.println("cat --- constructor");
}
//销毁方法,在容器关闭之后调用
@Override
public void destroy() throws Exception {
System.out.println("cat --- destroy");
}
//初始化方法,在构造器之后调用
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("cat --- afterPropertiesSet");
}
}
2.2 使用@ComponentScan引入bean
@ComponentScan(value = "com.leon.spring_annotation.bean")
3. JSR250
可以使用JSR250:
@PostConstruct: 在Bean创建完成并且属性赋值完成,来执行初始化方法
@PreDestroy: 在容器销毁Bean之前通知进行清理工作
注意,如果使用的是jdk8之上的jdk是需要导入javax.annotation依赖
<!-- Java EE 8 更早的版本 --> <dependency> <groupId>javax.annotation</groupId> <artifactId>javax.annotation-api</artifactId> <version>1.3.2</version> </dependency> <!-- Java EE 9 更高的版本 --> <dependency> <groupId>jakarta.annotation</groupId> <artifactId>jakarta.annotation-api</artifactId> <version>2.1.1</version> </dependency>
3.1 创建Dog.java类,在初始化方法和销毁方法上标注PostConstruct和PreDestroy
package com.leon.spring_annotation.bean;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
/**
* ClassName:Dog
* Package:com.leon.spring_annotation.bean
* Description:
*
* @Author: leon
* @Version: 1.0
*/
@Component
public class Dog implements ApplicationContextAware {
private ApplicationContext applicationContext;
public Dog(){
System.out.println("Dog |---| constructor");
}
@PostConstruct
public void init(){
System.out.println("Dog |---| init");
}
@PreDestroy
public void destroy(){
System.out.println("Dog |---| destroy");
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
@Override
public String toString() {
return "Dog{" +
"applicationContext=" + applicationContext +
'}';
}
}
3.2 在配置类上标注要扫描的类
@ComponentScan(value = "com.leon.spring_annotation.bean",includeFilters = {
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,classes = {Dog.class}),
},useDefaultFilters = false)
3.3 在pom为文件中导入依赖
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
4. 使用BeanPostProcessor:Bean的后置处理器
- 在Bean初始化前后进行一些处理工作:
- BeanPostProcessor后置处理器的前置方法-postProcessBeforeInitialization:在初始化方法之前执行
- BeanPostProcessor后置处理器的后置方法-postProcessAfterInitialization:在初始化方法之后执行
4.1 创建MyBeanPostProcessor.java并实现BeanPostProcessor接口
package com.leon.spring_annotation.processor;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
/**
* ClassName:MyBeanPostProcessor
* Package:com.leon.spring_annotation.processor
* Description:
*
* @Author: leon
* @Version: 1.0
*/
// MyBeanPostProcessor实现BeanPostProcessor之后,该类就是一个后置处理器了
// 初始化前后进行处理工作
// 将后置处理器加入到容器中
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessBeforeInitialization----------object===>"+bean+"String ===>"+beanName);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessAfterInitialization----------object===>"+bean+"String ===>"+beanName);
return bean;
}
}
4.2 在配置类上标注要扫描的类
@Configuration
@ComponentScan(value = "com.leon.spring_annotation.bean",includeFilters = {
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,classes = {Dog.class}),
},useDefaultFilters = false)
@ComponentScan(value = {"com.leon.spring_annotation.processor"})
public class BeanConfigLifeCycle {
//@Scope("prototype")
// initMethod指定初始化方法,destroyMethod指定销毁方法
@Bean(initMethod = "init",destroyMethod = "destroy")
public Car car(){
return new Car();
}
@Bean
public Cat cat(){
return new Cat();
}
}
5.进行测试
package com.leon.spring_annotation.test;
import com.leon.spring_annotation.config.BeanConfig02;
import com.leon.spring_annotation.config.BeanConfigLifeCycle;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
* ClassName:BeanLifeCycleTest
* Package:com.leon.spring_annotation.test
* Description:
*
* @Author: leon
* @Version: 1.0
*/
public class BeanLifeCycleTest {
private AnnotationConfigApplicationContext applicationContext;
@Before
public void init(){
// 通注解注册bean
applicationContext= new AnnotationConfigApplicationContext(BeanConfigLifeCycle.class);
System.out.println("========================容器创建成功========================");
}
@Test
public void initAndDestroyTest(){
Object bean = applicationContext.getBean("car");
applicationContext.close();
}
@Test
public void InitializingBeanAndDisposableBeanTest(){
Object bean = applicationContext.getBean("cat");
Object dog = applicationContext.getBean("dog");
System.out.println("==========================applicationContext===="+applicationContext);
System.out.println(dog);
applicationContext.close();
}
}
6. 总结
6.1 bean的生命周期:
- Bean创建—>初始化—>销毁的过程
- 容器管理Bean的生命周期:
- 我们可以自定义初始化方法和销毁方法:容器在bean进行到当前生命周期的时候来调用我们自定义的初始化和销毁
6.2 bean的生命周期完整的过程
- 构造(创建对象)
- 单实例:在容器启动时创建对象
- 多实例:每次在获取的时候创建对象
- BeanPostProcessor后置处理器的前置方法-postProcessBeforeInitialization
- 初始化:
- 对象创建完成,并赋值好,调用初始化方法
- BeanPostProcessor后置处理器的后置方法-postProcessAfterInitialization
- 销毁:
- 单实例:在容器关闭时,销毁对象
- 多实例:对象是多实例的情况下,容器会创建Bean对象,但是容器是不会销毁对象的,这些多实例对象容器也不会参与管理
6.3 BeanPostProcessor原理
- 遍历得到容器中所有放入BeanPostProcessor:一个一个执行beforeInitialization,一但返回NULL,则跳出for循环,不会执行后面的BeanPostProcessor.postProcessBeforeInitialization(result, beanName)
BeanPostProcessor原理
{
populateBean(beanName, mbd, instanceWrapper); //给Bean进行属性赋值
initializeBean{
this.applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
this.invokeInitMethods(beanName, wrappedBean, mbd); 执行初始化方法
this.applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
}
- Spring底层对BeanPostProcessor的使用:
- 如bean的注入,组件的注入,自动装配(依赖注入),类型的自动转换等底层都使用的是BeanPostProcessor这个接口
四、@Value属性赋值
1. SpEL表达式 #{...}
- 特点
- 使用
#{}
语法(花括号前有井号) - 在运行时进行动态计算
- 可以引用bean、调用方法、执行数学运算等
- 支持逻辑表达式和条件操作
2. 属性占位符 ${...}
- 特点
- 使用
${}
语法(花括号前有美元符号) - 直接引用配置属性,不进行计算
- 通常从
.properties
文件、.yml
文件或环境变量中获取值 - 可以提供默认值
3. 在Person.java的属性上设置
package com.leon.spring_annotation.bean;
import org.springframework.beans.factory.annotation.Value;
import java.util.Objects;
/**
* ClassName:Person
* Package:com.leon.spring_annotation.bean
* Description:
*
* @Author: leon
* @Version: 1.0
*/
public class Person {
@Value(value = "jack")
private String name;
@Value(value = "#{100-80}")
private Integer age;
@Value(value = "#{${person.happy} == true ? '羽毛球':'打篮球'}")
private String happy;
@Value(value = "#{T(Math).random()*100+1-1}")
private Integer id;
public Person() {
}
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
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;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", happy='" + happy + '\'' +
", id=" + id +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getHappy() {
return happy;
}
public void setHappy(String happy) {
this.happy = happy;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return Objects.equals(name, person.name) && Objects.equals(age, person.age);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
3.1 注意事项
需要在配置类上标注@PropertySource注解,添加获取的配置类的位置
package com.leon.spring_annotation.config; import com.leon.spring_annotation.bean.Person; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import java.beans.BeanProperty; /**
ClassName:FiledPropertiesValue
Package:com.leon.spring_annotation.config
Description:
*@Author: leon
@Version: 1.0
*/
@PropertySource(value = “classpath:/person.properties”,encoding = “utf-8”)
@Configuration
public class FiledPropertiesValue {@Bean
public Person person(){return new Person();
}
}
五、自动装配
1.自动装配注解@Autowried和@Qualifier以及@Primary
1.1 配置UserService.java
package com.leon.spring_annotation.service;
import com.leon.spring_annotation.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
/**
* ClassName:UserService
* Package:com.leon.spring_annotation.service
* Description:
*
* @Author: leon
* @Version: 1.0
*/
@Service
public class UserService {
/**
* @Autowired
* 容器中只有个要装配的对象
* 1.默认是使用类型进行装配
* 如果容器中有多个相同的装配对象:
* 1. 使用属性名进行装配
* required属性
* 默认是true,则容器中必须要有装配的对象,否则报错
* 设置为false,则容器中没有要装配的对象也可以,成员属性则为null
*
* @Qualifier("userDao")
* 指定哪个id的对象进行装配,这里则表示bean的id为userDao的对象为装配对象
*
*/
@Qualifier("userDao")
@Autowired(required = false)
private UserDao userDao;
public void print(){
System.out.println("userDao:"+userDao);
}
}
1.2 设置UserDao.java
package com.leon.spring_annotation.dao;
import org.springframework.stereotype.Repository;
/**
* ClassName:UserDao
* Package:com.leon.spring_annotation.dao
* Description:
*
* @Author: leon
* @Version: 1.0
*/
@Repository
public class UserDao {
private int id = 1 ;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Override
public String toString() {
return "UserDao{" +
"id=" + id +
'}';
}
}
1.3 配置配置类
package com.leon.spring_annotation.config;
import com.leon.spring_annotation.dao.UserDao;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
/**
* ClassName:AutowriedConfig
* Package:com.leon.spring_annotation.config
* Description:
*
* @Author: leon
* @Version: 1.0
*/
@Configuration
@ComponentScan(value = {"com.leon.spring_annotation.dao","com.leon.spring_annotation.service"})
public class AutowriedConfig {
/*
* @Primary
* 1.表示该bean在自动装配时,优先级比普通的bean更高
* 2.但是如果被装配的字段使用了@Qualifier("userDao")来指定要装配的bean,
* 则@Primary失效
* */
@Primary
@Bean("userDao2")
public UserDao userDao(){
UserDao userDao = new UserDao();
userDao.setId(2);
return userDao;
}
}
1.4 进行测试
package com.leon.spring_annotation.test;
import com.leon.spring_annotation.config.AutowriedConfig;
import com.leon.spring_annotation.dao.UserDao;
import com.leon.spring_annotation.service.UserService;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
* ClassName:FiledPropertiesValueTest
* Package:com.leon.spring_annotation.test
* Description:
*
* @Author: leon
* @Version: 1.0
*/
public class AutoWriedTest {
private ApplicationContext applicationContext;
@Before
public void init(){
applicationContext = new AnnotationConfigApplicationContext(AutowriedConfig.class);
}
@Test
public void filedPropertiesValueTest(){
UserService bean = applicationContext.getBean(UserService.class);
bean.print();
UserDao bean1 = applicationContext.getBean("userDao2", UserDao.class);
System.out.println(bean1);
}
}
2. 自动装配@Resource和@Inject
2.1 修改UserService.java
package com.leon.spring_annotation.service;
import com.leon.spring_annotation.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.inject.Inject;
/**
* ClassName:UserService
* Package:com.leon.spring_annotation.service
* Description:
*
* @Author: leon
* @Version: 1.0
*/
@Service
public class UserService {
/**
* @Autowired
* 容器中只有个要装配的对象
* 1.默认是使用类型进行装配
* 如果容器中有多个相同的装配对象:
* 1. 使用属性名进行装配
* required属性
* 默认是true,则容器中必须要有装配的对象,否则报错
* 设置为false,则容器中没有要装配的对象也可以,成员属性则为null
*
* @Qualifier("userDao")
* 指定哪个id的对象进行装配,这里则表示bean的id为userDao的对象为装配对象
*
*/
//@Qualifier("userDao")
//@Autowired(required = false)
/*
* @Resource
* 如果没有使用name属性进行指定的情况下
* 1.默认是按照属性名称来进行自动装配的
* 2.如果按照属性名称找不到则按照类型进行装配
* 使用name 属性的情况下则按照name的指定进行装配
* 在特殊情况下@Resoucre也是支持Primary注解的,但这是Spring扩展而不是JSR250的扩展
* */
//@Resource
/*
* @Inject
* 和Autowried注解一样的功能,但是没有required属性值
* */
@Inject
private UserDao userDao;
public void print(){
System.out.println("userDao:"+userDao);
}
}
2.2 进行测试
package com.leon.spring_annotation.test;
import com.leon.spring_annotation.config.AutowriedConfig;
import com.leon.spring_annotation.dao.UserDao;
import com.leon.spring_annotation.service.UserService;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
* ClassName:FiledPropertiesValueTest
* Package:com.leon.spring_annotation.test
* Description:
*
* @Author: leon
* @Version: 1.0
*/
public class AutoWriedTest {
private ApplicationContext applicationContext;
@Before
public void init(){
applicationContext = new AnnotationConfigApplicationContext(AutowriedConfig.class);
}
@Test
public void filedPropertiesValueTest(){
UserService bean = applicationContext.getBean(UserService.class);
bean.print();
UserDao bean1 = applicationContext.getBean("userDao2", UserDao.class);
System.out.println(bean1);
}
}
3. @Autowried注解在构造器、参数、方法、属性上的使用
- 标注在方法上:
- 在set方法上标注
- @Bean+方法参数,默认从容器中获取,默认不写@Autowried效果也是一样,都能实现自动装配
- 标在构造器上:
- 如果类中只有一个有参构造器,这个有参构造器的@Autowried可以省略,参数位置的组件还是可以从容器中获取
- 放在参数位置
4.自动装配-Aware注入Spring底层组件&原理
4.1 实践代码
package com.leon.spring_annotation.bean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.util.StringValueResolver;
/**
* ClassName:Red
* Package:com.leon.spring_annotation.bean
* Description:
* 自定义组件想要使用Spring容器底层的一些组件(如:ApplicationContext,BeanFactory等)
* 自定义组件实现XXXAware接口,在创建对象时,会调用接口规定的方法注入相关组件
* 把Spring底层一些组件注入到自定义的Bean中,
* 每个xxxAware接口都有对应的xxxProcessor
* ApplicationContextAware==>ApplicationContextAwareProcessor
* @Author: leon
* @Version: 1.0
*/
public class Red implements ApplicationContextAware, BeanNameAware, EmbeddedValueResolverAware {
@Override
public void setBeanName(String name) {
System.out.println("BeanNameAware setBeanName: " + name);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("ApplicationContextAware setApplicationContext: " + applicationContext);
}
@Override
public void setEmbeddedValueResolver(StringValueResolver stringValueResolver) {
String s = stringValueResolver.resolveStringValue("我是${os.name},是#{2020}年生产的");
System.out.println("EmbeddedValueResolver setEmbeddedValueResolver: " + s);
}
}
5. @Proflie注解
5.1 创建配置类
package com.leon.spring_annotation.config;
import com.leon.spring_annotation.bean.Yellow;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.context.annotation.PropertySource;
import org.springframework.util.StringValueResolver;
import javax.sql.DataSource;
import java.beans.PropertyVetoException;
/**
* ClassName:ProfileConfig
* Package:com.leon.spring_annotation.config
* Description:
* Profile
* Spring为我们提供的可以根据当前环境,动态的激活和切换一系列组件的功能
* @Profile:指定组件在哪个个环境的情况下才能被注册到容器中,指定,任何环境下都能注册这个组件
* 1.加了环境标识的bean,只有这个环境被激活的时候才能注册到容器中。默认是default
* 2.写在配置类上,只有是指定的环境的时候,整个配置类里面的所有配置才能生效
* 3.没有标注环境标识的bean在任何环境下都是加载的
* @Author: leon
* @Version: 1.0
*/
//@Profile("test")
@PropertySource("classpath:/person.properties")
@Configuration
public class ProfileConfig implements EmbeddedValueResolverAware {
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
private String driverClassName;
@Profile("test")
@Bean
public Yellow yellow(){
return new Yellow();
}
@Profile("test")
@Bean
public DataSource testDataSource() throws Exception {
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
// 设置数据用户名
comboPooledDataSource.setUser(username);
// 设置数据库密码
comboPooledDataSource.setPassword(password);
// 设置连接驱动
comboPooledDataSource.setDriverClass(driverClassName);
// 设置数据库url
comboPooledDataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
return comboPooledDataSource;
}
@Profile("dev")
@Bean
public DataSource devDataSource() throws Exception {
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
// 设置数据用户名
comboPooledDataSource.setUser(username);
// 设置数据库密码
comboPooledDataSource.setPassword(password);
// 设置连接驱动
comboPooledDataSource.setDriverClass(driverClassName);
// 设置数据库url
comboPooledDataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
return comboPooledDataSource;
}
@Profile("pro")
@Bean
public DataSource proDataSource() throws Exception {
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
// 设置数据用户名
comboPooledDataSource.setUser(username);
// 设置数据库密码
comboPooledDataSource.setPassword(password);
// 设置连接驱动
comboPooledDataSource.setDriverClass(driverClassName);
// 设置数据库url
comboPooledDataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
return comboPooledDataSource;
}
@Override
public void setEmbeddedValueResolver(StringValueResolver stringValueResolver) {
driverClassName = stringValueResolver.resolveStringValue("${jdbc.driver}");
}
}
5.2 进行测试
package com.leon.spring_annotation.test;
import com.leon.spring_annotation.config.AutowriedConfig;
import com.leon.spring_annotation.config.ProfileConfig;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.env.Environment;
import javax.sql.DataSource;
/**
* ClassName:ProfileTest
* Package:com.leon.spring_annotation.test
* Description:
*
* @Author: leon
* @Version: 1.0
*/
public class ProfileTest {
private AnnotationConfigApplicationContext applicationContext;
@Before
public void init(){
//applicationContext = new AnnotationConfigApplicationContext(ProfileConfig.class);
/*
* 修改环境
* 1.使用命令行动态参数:在虚拟机参数位置加载:-Dspring.profiles.active=test
* 2.通过AnnotationConfigApplicationContext自定义创建对象时,来设置环境
* */
// 创建ApplicationCentext对象
applicationContext = new AnnotationConfigApplicationContext();
// 设置环境
applicationContext.getEnvironment().setActiveProfiles("dev");
// 注册容器
applicationContext.register(ProfileConfig.class);
// 刷新容器
applicationContext.refresh();
}
@Test
public void test(){
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
Object bean = applicationContext.getBean(DataSource.class);
System.out.println(bean);
}
}
六、AOP
1. 创建AOP类
package com.leon.spring_annotation.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;
/**
* ClassName:LogAspect
* Package:com.leon.spring_annotation.aop
* Description:
* AOP:动态代理
* 指在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的编程方式
* @Aspect标注的类表示是一个切面类
* @Pointcut表示切入点,将切点集中到一起,方便管理和引用
* @Before表示前置通知:在目标方法执行之前执行
* @After表示后置通知:在目标方法执行之后执行
* @AfterReturning表示返回通知:在目标方法执行之后执行
* @AfterThrowing表示异常通知:在目标方法发生异常时执行
* @Around表示环绕通知:这需要手动执行目标方法
* @Author: leon
* @Version: 1.0
*/
@Aspect
@Component
public class LogAspect {
@Pointcut("execution(public double com.leon.spring_annotation.aop.MathematicsCalculate.*(..))")
public void pointcut(){}
/*
* JoinPoint必须写在参数列表的第一个位置,不然会报错
* 可以通过JoinPoint获取切入点的信息
* */
@Before("pointcut()")
public void before(JoinPoint joinPoint){
Signature signature = joinPoint.getSignature();
System.out.println("目标方法:"+signature.getName()+"参数列表"+ Arrays.toString(joinPoint.getArgs()) +"LogAspect--->before");
}
@After("pointcut()")
public void after(JoinPoint joinPoint){
System.out.println("LogAspect--->after");
}
/*
* 通过returning来指定哪个形参来接收返回结果的
* */
@AfterReturning(value = "pointcut()",returning = "result")
public void afterReturn(JoinPoint joinPoint,Object result){
System.out.println("LogAspect--->afterReturn="+result);
}
/*
* 通过throwing来指定哪个形参来接收异常结果的
* */
@AfterThrowing(value = "pointcut()",throwing = "e")
public void afterThrowing(JoinPoint joinPoint,Exception e){
System.out.println("LogAspect--->afterThrowing="+e.getMessage());
}
}
2. 目标对象
package com.leon.spring_annotation.aop;
import org.springframework.stereotype.Component;
/**
* ClassName:MathematicsCalculate
* Package:com.leon.spring_annotation.aop
* Description:
*
* @Author: leon
* @Version: 1.0
*/
@Component
public class MathematicsCalculate {
public double calculate(int a, int b) {
System.out.println("Mathematics Calculate--->"+a/b);
return a/b;
}
}
3. 配置类
package com.leon.spring_annotation.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
/**
* ClassName:AopConfig
* Package:com.leon.spring_annotation.config
* Description:
* @EnableAspectJAutoProxy启用AOP注解功能
* Spring很多的@EnableXXX,都是用于启动某些功能的
* @Author: leon
* @Version: 1.0
*/
@EnableAspectJAutoProxy
@Configuration
@ComponentScan("com.leon.spring_annotation.aop")
public class AopConfig {
}
4. 进行测试
package com.leon.spring_annotation.test;
import com.leon.spring_annotation.aop.MathematicsCalculate;
import com.leon.spring_annotation.bean.Boos;
import com.leon.spring_annotation.bean.Car;
import com.leon.spring_annotation.bean.Color;
import com.leon.spring_annotation.config.AopConfig;
import com.leon.spring_annotation.config.AutowriedConfig;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
* ClassName:FiledPropertiesValueTest
* Package:com.leon.spring_annotation.test
* Description:
*
* @Author: leon
* @Version: 1.0
*/
public class AopConfigTest {
private ApplicationContext applicationContext;
@Before
public void init(){
applicationContext = new AnnotationConfigApplicationContext(AopConfig.class);
}
@Test
public void filedPropertiesValueTest(){
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
MathematicsCalculate bean = applicationContext.getBean(MathematicsCalculate.class);
bean.calculate(10,5);
}
}
七、声明式事务以及编程式事务
1. 声明式事务
1.1 在配置类中添加基本配置
@Configuration
/*
* @EnableTransactionManagement开启基于注解的事务管理
* */
@EnableTransactionManagement
@Import({UserService.class, UserDao.class})
public class TxConfig {
/*
* 使用连接池技术
* */
@Bean
public DataSource dataSource() throws Exception {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl("jdbc:mysql://localhost:3306/test?allowPublicKeyRetrieval=true&useSSL=false");
dataSource.setUsername("root");
dataSource.setPassword("root");
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
return dataSource;
}
/*
* 注入JdbcTemplate简化数据库操作流程
* */
@Bean
public JdbcTemplate jdbcTemplate() throws Exception {
return new JdbcTemplate(dataSource());
}
/*
* 需要添加DataSourceTransactionManager事务管理器
* */
@Bean
public PlatformTransactionManager transactionManager() throws Exception {
return new DataSourceTransactionManager(dataSource());
}
1.2 在User Dao.java中添加数据库操作
public void insertUser() {
String sql = "insert into user(`name`,`age`) VALUES(?,?)";
String username = new Random().nextInt(10000) + "";
jdbcTemplate.update(sql, username, 12);
}
1.3在UserService.java添加数据库操作
/*
* @Transactional 表示开启声明式事务,如果事务内部发生异常则将数据进行回滚,没有则提交事务
* */
@Transactional
public void insert(){
userDao.insertUser();
System.out.println("UserService--->insert 插入数据完成");
System.out.println(8/0);
}
1.4 测试
package com.leon.spring_annotation.test;
import com.leon.spring_annotation.aop.MathematicsCalculate;
import com.leon.spring_annotation.config.AopConfig;
import com.leon.spring_annotation.config.TxConfig;
import com.leon.spring_annotation.service.UserService;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
* ClassName:FiledPropertiesValueTest
* Package:com.leon.spring_annotation.test
* Description:
*
* @Author: leon
* @Version: 1.0
*/
public class TxConfigTest {
private ApplicationContext applicationContext;
@Before
public void init(){
applicationContext = new AnnotationConfigApplicationContext(TxConfig.class);
}
@Test
public void filedPropertiesValueTest(){
UserService bean = applicationContext.getBean(UserService.class);
bean.insert();
}
}
2.编程式事务
2.1 在配置类中添加组件
/*
* 添加编程式事务处理对象
* */
@Bean
public TransactionTemplate transactionTemplate(PlatformTransactionManager transactionManager) throws Exception {
return new TransactionTemplate(transactionManager);
}
2.2 在UserService.java添加数据库操作语句
public void insertUser(){
// 使用编程式事务
transactionTemplate.execute(new TransactionCallbackWithoutResult(){
@Override
protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
try {
userDao.insertUser();
System.out.println("UserService--->insert 插入数据完成");
System.out.println(8/0);
} catch (Exception e) {
// 回滚事务
transactionStatus.setRollbackOnly();
throw new RuntimeException(e);
}
}
});
}
八、Spring的扩展原理
1. BeanFactoryPostProcessor原理
1.1 创建BeanFactoryPostProcessor接口实现类
package com.leon.spring_annotation.expand;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;
import java.util.Arrays;
/**
* ClassName:MyBeanFactoryPostProcessor
* Package:com.leon.spring_annotation.expand
* Description:
* BeanFactoryPostProcessor:BeanFactory的后置处理器
* 1.在BeanFactory标准初始化之后调用,来定制和修改BeanFactory的内容
* 2.所有的bean定义已经保存加载到BeanFactory,但是Bean的实例还未创建
* BeanFactoryPostProcessor执行过程
* 1. ioc容器创建对象
* 2.this.invokeBeanFactoryPostProcessors(beanFactory);
* 1) 如何找到所有的BeanFactoryPostProcessor 并执行他们的方法
* ① 直接在BeanFactory中找到所有类型是BeanFactoryPostProcessor的组件(实现了BeanFactoryPostProcessor接口的对象),并执行他们的方法
* ② 在初始化创建其他逐渐前面执行
*
* @Author: leon
* @Version: 1.0
*/
@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// 获取容器中定义的bean的数量
int beanDefinitionCount = beanFactory.getBeanDefinitionCount();
// 获取容器中所有bean的名称
String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
System.out.println("beanDefinitionCount: " + beanDefinitionCount);
System.out.println("beanDefinitionNames: " + Arrays.toString(beanDefinitionNames));
}
}
1.2 创建配置类
package com.leon.spring_annotation.config;
import com.leon.spring_annotation.bean.Car;
import com.leon.spring_annotation.dao.UserDao;
import com.leon.spring_annotation.service.UserService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
* ClassName:ExpandConfig
* Package:com.leon.spring_annotation.config
* Description:
*
* @Author: leon
* @Version: 1.0
*/
@Configuration
@ComponentScan("com.leon.spring_annotation.expand")
@Import({UserService.class})
public class ExpandConfig {
@Bean
public Car car(){
return new Car();
}
}
1.3 测试
package com.leon.spring_annotation.test;
import com.leon.spring_annotation.config.ExpandConfig;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
* ClassName:ExpandConfigTest
* Package:com.leon.spring_annotation.test
* Description:
*
* @Author: leon
* @Version: 1.0
*/
public class ExpandConfigTest {
private AnnotationConfigApplicationContext applicationContext;
@Before
public void init(){
applicationContext = new AnnotationConfigApplicationContext(ExpandConfig.class);
}
@Test
public void beanFactoryPostProcessor(){
applicationContext.publishEvent(new ApplicationEvent(new String("发布事件")) {});
applicationContext.close();
}
}
2.BeanDefinitionRegistryPostProcessor原理
2.1创建BeanDefinitionRegistryPostProcessor实现类
package com.leon.spring_annotation.expand;
import com.leon.spring_annotation.bean.Yellow;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.stereotype.Component;
/**
* ClassName:MyBeanDefinitionRegistryPostProcessor
* Package:com.leon.spring_annotation.expand
* Description:
* BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor
* 1.postProcessBeanDefinitionRegistry(),在所有bean定义信息将要被加载,bean实例还未被创建
* 2.优于BeanFactoryPostProcessor执行,先执行postProcessBeanDefinitionRegistry,在执行postProcessBeanFactory,
* 可以利用BeanDefinitionRegistry给容器中再添加一些组件
* 原理:
* 1. ioc创建对象
* 2. refresh()-->this.invokeBeanFactoryPostProcessors(beanFactory);
* 3. 从容器中获取所有的BeanDefinitionRegistryPostProcessor组件,
* ①依次触发苏哦有的postProcessBeanDefinitionRegistry()方法
* ②再来触发postProcessBeanFactory()方法
* 4. 再来从容器中找到BeanFactoryPostProcessor组件,然后依次触发postProcessBeanFactory()方法
* @Author: leon
* @Version: 1.0
*/
@Component
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
System.out.println("MyBeanDefinitionRegistryPostProcessor -- postProcessBeanDefinitionRegistry"+registry.getBeanDefinitionCount());
// 使用RootBeanDefinition定义bean
//RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Yellow.class);
// 使用BeanDefinitionBuilder
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(Yellow.class);
registry.registerBeanDefinition("yellow",beanDefinitionBuilder.getBeanDefinition());
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("MyBeanDefinitionRegistryPostProcessor -- postProcessBeanFactory"+beanFactory.getBeanDefinitionCount());
}
}
2.2 配置配置类
package com.leon.spring_annotation.config;
import com.leon.spring_annotation.bean.Car;
import com.leon.spring_annotation.dao.UserDao;
import com.leon.spring_annotation.service.UserService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
* ClassName:ExpandConfig
* Package:com.leon.spring_annotation.config
* Description:
*
* @Author: leon
* @Version: 1.0
*/
@Configuration
@ComponentScan("com.leon.spring_annotation.expand")
@Import({UserService.class})
public class ExpandConfig {
@Bean
public Car car(){
return new Car();
}
}
2.3 测试
package com.leon.spring_annotation.test;
import com.leon.spring_annotation.config.ExpandConfig;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
* ClassName:ExpandConfigTest
* Package:com.leon.spring_annotation.test
* Description:
*
* @Author: leon
* @Version: 1.0
*/
public class ExpandConfigTest {
private AnnotationConfigApplicationContext applicationContext;
@Before
public void init(){
applicationContext = new AnnotationConfigApplicationContext(ExpandConfig.class);
}
@Test
public void beanFactoryPostProcessor(){
applicationContext.publishEvent(new ApplicationEvent(new String("发布事件")) {});
applicationContext.close();
}
}
3.ApplicationListener
3.1 创建ApplicationListener实现类
package com.leon.spring_annotation.expand;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
/**
* ClassName:MyApplicationListener
* Package:com.leon.spring_annotation.expand
* Description:
* ApplicationListener:监听容器中发布的事件。事件驱动模型开发
* 1.public interface ApplicationListener<E extends ApplicationEvent>监听ApplicationEvent及其下面的子类
* 步骤:
* 1.写一个监听器来监听某个事件(ApplicationEvent及其子类)
* 使用@EventListener注解标注在方法之上
* 2.把监听器加入到容器中
* 3.只要容器中有相关事件的发布,就能监听到这个事件
* ①ContextRefreshedEvent 容器刷新事件
* ②ContextClosedEvent 容器关闭事件
* 4.发布一个事件:applicationContext。publishEvent();
* 原理:
* 1.ContextRefreshedEvent事件
* 1)创建IOC容器对象,进行容器刷新this.refresh()
* 2)this.finishRefresh();,容器刷新完成
* 3)this.publishEvent((ApplicationEvent)(new ContextRefreshedEvent(this)));
* 2.自己发布的事件
* 3.ContextClosedEvent事件
* 事件发布流程:
* 1.获取事件的多播器(派发器),this.getApplicationEventMulticaster()
* 2.multicastEvent派发事件
* 3.获取到所有的ApplicationListener
* 1) 如果有Executor,可以支持使用Executor进行异步派发
* 2)否则,同步的方式直接执行Listener方法:this.invokeListener(listener, event);
* 拿到Listener回调onApplicationEvent方法
* 事件多播器(派发器)
* 1.容器创建对象:this.refresh()
* 2. this.initApplicationEventMulticaster();:初始化ApplicationEventMulticaster
* 1)先去容器中找有没有id="applicationEventMulticaster"的组件,beanFactory.containsLocalBean("applicationEventMulticaster")
* 2)如果有则直接从容器中获取this.applicationEventMulticaster = (ApplicationEventMulticaster)beanFactory.getBean("applicationEventMulticaster", ApplicationEventMulticaster.class);
* 3)如果没有则创建一个简单的派发器,this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
* 并添加进容器中,因而就可以在其他组件要派发事件时,只需要自动注入ApplicationEventMulticaster()
* 容器中有那些监听器
* 1.创建容器,this.refresh()
* 2.this.registerListeners();
* 1) 获取监听器的名称,String[] listenerBeanNames = this.getBeanNamesForType(ApplicationListener.class, true, false);
* 2) 从容器中拿到所有的监听器,把他们注册到ApplicationEventMulticaster()中 this.getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
* @EventListener原理:使用EventListenerMethodProcessor处理器来解析方法上的@EventListener
* SmartInitializingSingleton原理-->afterSingletonInstantiated();
* 1. IOC容器创建对象this.refresh()
* 2. finishBeanFactoryInitialization(beanFactory),初始化剩下的单实例bean
* 1) 先创建所有的单实例bean:getBean()
* 2) 获取所有创建好的单实例bean,判断是否是SmartInitializingSingleton类型的
* 如果是就调用afterSingletonInstantiated()方法
* @Author: leon
* @Version: 1.0
*/
@Component
public class MyApplicationListener implements ApplicationListener<ApplicationEvent> {
@Override
public void onApplicationEvent(ApplicationEvent applicationEvent) {
System.out.println("MyApplicationListener========>监听到的事件为"+applicationEvent);
}
}
3.2 设置UserService方法
@EventListener
public void listenerEvent(ApplicationEvent event){
System.out.println("UserService--->监听事件"+event);
}
3.3 配置配置类
package com.leon.spring_annotation.config;
import com.leon.spring_annotation.bean.Car;
import com.leon.spring_annotation.dao.UserDao;
import com.leon.spring_annotation.service.UserService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
* ClassName:ExpandConfig
* Package:com.leon.spring_annotation.config
* Description:
*
* @Author: leon
* @Version: 1.0
*/
@Configuration
@ComponentScan("com.leon.spring_annotation.expand")
@Import({UserService.class})
public class ExpandConfig {
@Bean
public Car car(){
return new Car();
}
}
3.4 测试
package com.leon.spring_annotation.test;
import com.leon.spring_annotation.config.ExpandConfig;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
* ClassName:ExpandConfigTest
* Package:com.leon.spring_annotation.test
* Description:
*
* @Author: leon
* @Version: 1.0
*/
public class ExpandConfigTest {
private AnnotationConfigApplicationContext applicationContext;
@Before
public void init(){
applicationContext = new AnnotationConfigApplicationContext(ExpandConfig.class);
}
@Test
public void beanFactoryPostProcessor(){
applicationContext.publishEvent(new ApplicationEvent(new String("发布事件")) {});
applicationContext.close();
}
}