@Configuration和@Bean
@Configuration 注解表示吧一个类声明为一个配置类
@Bean 注解相当于在容器中注册一个 bean
新建一个Person类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 package com.szx.bean;public class Person { String name; Integer age; @Override public String toString () { return "Person{" + "name='" + name + '\'' + ", 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; } public Person (String name, Integer age) { this .name = name; this .age = age; } public Person () { } }
通过配置类注册一个Person bean
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package com.szx.config;import com.szx.bean.Person;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configuration public class BeanConfig { @Bean("person01") public Person person () { return new Person("张三" ,16 ); } }
实例化 AnnotationConfigApplicationContext 得到 ioc 容器
1 2 3 4 5 6 7 8 9 10 11 public class BeanTest { public static void main (String[] args) { AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext(BeanConfig.class); Person person = ioc.getBean(Person.class); System.out.println(person); Person person01 = ioc.getBean("person01" , Person.class); System.out.println(person01); } }
@ComponentScan 自动扫描 基本使用方法 通过 xml 方式配置自动扫描时需要用到 component-scan
1 2 <context:component-scan base-package ="com.szx" > </context:component-scan >
同样也可以使用注解的方式来配置自动扫描指定包下面的所有类
value 指定扫描那些包下面的类
includeFilters 设置只扫描声明了那个注解的类,但是要同时声明 useDefaultFilters = false
@ComponentScan.Filter 设置过滤方式
type = FilterType.ANNOTATION 根据注解扫描
classes = Controller.class 扫描Controller注解
useDefaultFilters 默认的扫描
excludeFilters 设置不扫描声明了那个注解的类,它的参数和 includeFilters 相同
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Configuration @ComponentScan( // 设置自动扫描那些包下面的类 value = "com.szx", // 设置只扫描声明了Controller注解的类 includeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = Controller.class) }, // 取消默认的扫描方式 useDefaultFilters = false ) public class MainConfig {}
编写测试方法,查询当前ioc容器中的bean
1 2 3 4 5 6 7 8 9 @Test public void test () { AnnotationConfigApplicationContext mainioc = new AnnotationConfigApplicationContext(MainConfig.class); String[] beanNames = mainioc.getBeanDefinitionNames(); for (String beanName : beanNames) { System.out.println(beanName); } }
因为设置了 includeFilters 只扫描 Controller 注解,所以结果如下
自定义@ComponentScan.Filter过滤规则 type 属性的几个值分别对应的含义
ANNOTATION 根据注解过滤
ASSIGNABLE_TYPE 根据指定的类型过滤
ASPECTJ 根据ASPECTJ表达式
REGEX 根据正则表达式过滤
CUSTOM 自定义过滤
根据指定的类型过滤 ASSIGNABLE_TYPE ,示例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 @ComponentScan( // 设置自动扫描那些包下面的类 value = "com.szx", // 设置只扫描声明了Controller注解的类 includeFilters = { // 设置ASSIGNABLE_TYPE指定Book类型扫描 @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,classes = Book.class) }, // 取消默认的扫描方式 useDefaultFilters = false ) public class MainConfig {}
扫描结果
自定义扫描规则 CUSTOM
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Configuration @ComponentScan( // 设置自动扫描那些包下面的类 value = "com.szx", // 设置只扫描声明了Controller注解的类 includeFilters = { // 自定义扫描规则 CUSTOM @ComponentScan.Filter(type = FilterType.CUSTOM ,classes = MyTypeFilter.class) }, // 取消默认的扫描方式 useDefaultFilters = false ) public class MainConfig {}
需要新建一个 MyTypeFilter 类并实现 TypeFilter 接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 package com.szx.config;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;public class MyTypeFilter implements TypeFilter { @Override public boolean match (MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata(); Resource resource = metadataReader.getResource(); ClassMetadata classMetadata = metadataReader.getClassMetadata(); String className = classMetadata.getClassName(); if (className.contains("er" )){ return true ; } return false ; } }
运行测试方法,查看当前ioc容器中的bena
@Scope 调整作用域 @Scope 作用域有两个值
singleton 单例的(默认值) 初始化ioc容器时会调用bean的初始化方法
prototype 多例的,初始化ioc容器时不会调用bean中的初始化方法
首先新建一个配置类
1 2 3 4 5 6 7 8 9 10 11 12 @Configuration public class MainConfig2 { @Scope @Bean public Person person () { System.out.println("初始化person类bean" ); return new Person("李四" ,18 ); } }
然后添加测试方法,从ioc容器中重复的取两边person,此时判断两个引用地址为用一个,判断返回 true
1 2 3 4 5 6 7 @Test public void test2 () { AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext(MainConfig2.class); Person person = ioc.getBean("person" , Person.class); Person person2 = ioc.getBean("person" , Person.class); System.out.println(person == person2); }
然后将配置类中的 @Scope 设置 prototype
1 2 3 4 5 @Scope("prototype") @Bean public Person person () { return new Person("李四" ,18 ); }
然后再次执行测试方法会返回 false
其次当我们在初始化容器时两个状态也会有不同的情况,例如将测试方法中的 getBean 注释掉,只初始化一下 ioc 容器
1 2 3 4 5 6 7 @Test public void test2 () { AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext(MainConfig2.class); }
然后修改配置类,在 new Person 之前打印一句话
1 2 3 4 5 6 7 8 9 10 11 12 @Configuration public class MainConfig2 { @Scope("prototype") @Bean public Person person () { System.out.println("实例化 Person" ); return new Person("李四" ,18 ); } }
此时的作用域为 prototype,执行测试方法观察打印。发现控制台并没有任何打印,这说明在初始化ioc容器时并没有初始化bean
然后将测试方法中的 getBean 方法放开
然后将作用域改为 singleton,再来观察控制台输出
调用测试方法
会发现当作用域为singleton时在初始化ioc容器时就会实例化一下Person,当调用 getBean 时不会重复实例化
@Lazy 对象懒加载 设置对象懒加载,在初始化容器阶段不实例化对象,只在第一次获取时实例化,只针对单例有效
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package com.szx.config;import com.szx.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;@Configuration public class MainConfig2 { @Lazy @Bean public Person person () { System.out.println("实例化 Person" ); return new Person("李四" ,18 ); } }
调用测试方法,重复获取两次,判断结果为 true
1 2 3 4 5 6 7 @Test public void test2 () { AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext(MainConfig2.class); Person person = ioc.getBean("person" , Person.class); Person person2 = ioc.getBean("person" , Person.class); System.out.println(person == person2); }
@Conditional 自定义条件注册bean @Conditional 注解可以自定义根据条件注册当前bean
添加配置类,根据操作环境注册不同的bean,@Conditional 接收一个Condition接口实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 package com.szx.config;import com.szx.bean.Person;import com.szx.conditional.LinuxConditional;import com.szx.conditional.WindowConditional;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Conditional;import org.springframework.context.annotation.Configuration;@Configuration public class MainConfig3 { @Bean public Person lisi () { return new Person("lisi" ,18 ); } @Conditional(WindowConditional.class) @Bean public Person bill () { return new Person("bill" ,60 ); } @Conditional(LinuxConditional.class) @Bean public Person linus () { return new Person("linus" ,43 ); } }
添加 WindowConditional
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 package com.szx.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;public class WindowConditional implements Condition { @Override public boolean matches (ConditionContext context, AnnotatedTypeMetadata annotatedTypeMetadata) { ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); ClassLoader classLoader = context.getClassLoader(); Environment environment = context.getEnvironment(); BeanDefinitionRegistry registry = context.getRegistry(); String osName = environment.getProperty("os.name" ); System.out.println(osName); if (osName.contains("Window" )){ return true ; } return false ; } }
添加 LinuxConditional
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 package com.szx.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;public class LinuxConditional implements Condition { @Override public boolean matches (ConditionContext context, AnnotatedTypeMetadata annotatedTypeMetadata) { ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); ClassLoader classLoader = context.getClassLoader(); Environment environment = context.getEnvironment(); BeanDefinitionRegistry registry = context.getRegistry(); String osName = environment.getProperty("os.name" ); System.out.println(osName); if (osName.contains("linux" )){ return true ; } return false ; } }
编写测试方法查看当前ioc容器中的bean
1 2 3 4 5 6 7 8 @Test public void test3 () { AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext(MainConfig3.class); String[] names = ioc.getBeanNamesForType(Person.class); for (String name : names) { System.out.println(name); } }
运行测试方法,返回如下,因为操作系统是 Windows,所以 linus 不会被注册
@Import 快速注册bean 基本用法 除了使用 @Bean 的方式注册 bean 之外,也可以在类上使用 @Import 快速注册 bean
使用方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 package com.szx.config;import com.szx.bean.Color;import com.szx.bean.Order;import com.szx.bean.Person;import com.szx.conditional.LinuxConditional;import com.szx.conditional.WindowConditional;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Conditional;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Import;@Import({Color.class, Order.class}) @Configuration public class MainConfig3 { @Bean public Person lisi () { return new Person("lisi" ,18 ); } @Conditional(WindowConditional.class) @Bean public Person bill () { return new Person("bill" ,60 ); } @Conditional(LinuxConditional.class) @Bean public Person linus () { return new Person("linus" ,43 ); } }
调用测试方法获取当前ioc容器中的所有bean
1 2 3 4 5 6 7 8 @Test public void test3 () { AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext(MainConfig3.class); String[] names = ioc.getBeanDefinitionNames(); for (String name : names) { System.out.println(name); } }
使用 ImportSelector 导入类 ImportSelector 是一个接口,返回需要导入类的全类名组成的字符串数组。可以在 @Import 注解上传入 ImportSelector 实现类来完成导入
添加 ImportSelector 实现类 MyImportSelector
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package com.szx.config;import org.springframework.context.annotation.ImportSelector;import org.springframework.core.type.AnnotationMetadata;public class MyImportSelect implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { return new String[]{"com.szx.bean.Yellor" ,"com.szx.bean.Blue" }; } }
然后在配置类上添加 @Import 注解
执行测试方法,查看当前ioc容器中的bean
1 2 3 4 5 6 7 8 9 10 @Test public void test3 () { AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext(MainConfig3.class); String[] names = ioc.getBeanDefinitionNames(); for (String name : names) { System.out.println(name); } Blue blue = ioc.getBean("com.szx.bean.Blue" , Blue.class); System.out.println(blue); }
ImportBeanDefinitionRegistrar 手动注册bean ImportBeanDefinitionRegistrar 是一个接口,重写其 registerBeanDefinitions 方法,方法有两个参数
AnnotationMetadata 当前类的注解信息
BeanDefinitionRegistry BeanDefinition的注册类
首先编写 ImportBeanDefinitionRegistrar 实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package com.szx.config;import com.szx.bean.Rainbow;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;public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions (AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { boolean b = registry.containsBeanDefinition("com.szx.bean.Blue" ); boolean b1 = registry.containsBeanDefinition("com.szx.bean.Yellor" ); if (b && b1){ RootBeanDefinition beanDefinition = new RootBeanDefinition(Rainbow.class); registry.registerBeanDefinition("rainbow" ,beanDefinition); } } }
然后再配置类中的 @Import 注解上添加 MyImportBeanDefinitionRegistrar.class
执行测试方法查看当前ioc容器中的bean
1 2 3 4 5 6 7 8 9 10 @Test public void test3 () { AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext(MainConfig3.class); String[] names = ioc.getBeanDefinitionNames(); for (String name : names) { System.out.println(name); } Blue blue = ioc.getBean("com.szx.bean.Blue" , Blue.class); System.out.println(blue); }
通过 FactoryBean 工厂bean创建对象 FactoryBean 是一个接口,首先创建这个接口的实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package com.szx.bean;import org.springframework.beans.factory.FactoryBean;public class ColorFactoryBean implements FactoryBean <Color > { @Override public Color getObject () throws Exception { System.out.println("创建Color实例" ); return new Color(); } @Override public Class<?> getObjectType() { return Color.class; } @Override public boolean isSingleton () { return true ; } }
在配置类中注册
1 2 3 4 5 6 @Bean public ColorFactoryBean colorFactoryBean () { return new ColorFactoryBean(); }
首先调用测试方法查看当前ioc容器中的所有bean,可以看到存在 colorFactoryBean 对象
获取 colorFactoryBean 并打印这个对象的类型,获取工厂bean本身时需要在bean id前面添加 &
1 2 3 4 5 6 7 8 9 10 11 12 13 @Test public void test4 () throws Exception { AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext(MainConfig3.class); Object colorFactoryBean = ioc.getBean("colorFactoryBean" ); Class<?> aClass = colorFactoryBean.getClass(); System.out.println("aClass = " + aClass); Object bean = ioc.getBean("&colorFactoryBean" ); System.out.println(bean.getClass()); }
执行运行结果
@Bean 指定初始化和销毁方法 首先新建两个普通类,并分别定义 init 和 destroy 方法
新建 Car
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package com.szx.bean;public class Car { public Car () { System.out.println("car constructor..." ); } public void init () { System.out.println("car init..." ); } private void destroy () { System.out.println("car destroy..." ); } }
新建 Emp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package com.szx.bean;public class Emp { public Emp () { System.out.println("emp constructor ..." ); } public void init () { System.out.println("emp init..." ); } public void destroy () { System.out.println("emp destroy ..." ); } }
新建一个配置类,在这个配置类中注册 car 和 emp 两个 bean,其中emp为多例 bean
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package com.szx.config;import com.szx.bean.Car;import com.szx.bean.Emp;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Scope;@Configuration public class MainConfig4 { @Bean(initMethod = "init",destroyMethod = "destroy") public Car car () { return new Car(); } @Scope("prototype") @Bean(initMethod = "init",destroyMethod = "destroy") public Emp emp () { return new Emp(); } }
编写测试方法,查看两个对象的初始化方法和销毁方法调用时机
1 2 3 4 5 6 7 8 @Test public void test1 () { AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext(MainConfig4.class); ioc.close(); }
首先只初始化容器和关闭容器,查看运行结果,可以看到多例bean emp的构造方法和init以及destroy方法都没有执行
然后现在加上获取 emp bean的方法
1 2 3 4 5 6 7 8 9 10 11 12 @Test public void test1 () { AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext(MainConfig4.class); ioc.getBean("emp" ); ioc.close(); }
此时只调用了 emp 对象的构造方法和 init 方法,destroy 方法并没有被调用
总结:
单例bean
ioc容器创建时就调用对象的构造方法和init方法
ioc容器关闭时会调用对象的destroy方法
多例bean
ioc容器创建时不会调用对象的构造方法和init方法
获取这个bean对象时才调用构造方法和init方法
ioc容器关闭不会调用多例bean对象的destroy方法
nitializingBean, DisposableBean 接口的使用 让一个类实现这两个接口即可实现初始化方法和销毁方法
创建接口实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package com.szx.bean;import org.springframework.beans.factory.DisposableBean;import org.springframework.beans.factory.InitializingBean;import org.springframework.context.annotation.Lazy;import org.springframework.context.annotation.Scope;import org.springframework.stereotype.Component;@Component public class Dep implements InitializingBean , DisposableBean { public Dep () { System.out.println("dep constructor ..." ); } @Override public void afterPropertiesSet () throws Exception { System.out.println("dep init ..." ); } @Override public void destroy () throws Exception { System.out.println("dep destroy ..." ); } }
在配置类中添加 @ComponentScan("com.szx.bean")
注解自动扫描
执行测试方法查看初始化方法的打印
1 2 3 4 5 6 7 @Test public void test1 () { AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext(MainConfig4.class); ioc.close(); }
运行效果
通过@PostConstruct和@PreDestroy指定初始化和销毁方法
@PostConstruct 注解指定一个方法为属性赋值后调用的初始化方法
@PreDestroy 注解指定一个方法为对象销毁调用的销毁方法
新建一个 Dog 类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 package com.szx.bean;import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;import javax.annotation.PreDestroy;@Component public class Dog { 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..." ); } }
运行测试方法查询执行效果
1 2 3 4 5 6 7 @Test public void test1 () { AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext(MainConfig4.class); ioc.close(); }
BeanPostProcessor后置处理器 BeanPostProcessor 是一个接口,要重写这个接口的两个方法,分别表示对象初始化之前调用,对象初始化之后调用
postProcessBeforeInitialization 对象初始化之前调用
postProcessAfterInitialization 对象初始化之后调用
创建实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package com.szx.bean;import org.springframework.beans.BeansException;import org.springframework.beans.factory.config.BeanPostProcessor;import org.springframework.lang.Nullable;import org.springframework.stereotype.Component;@Component public class MyBeanPostProcess implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization (Object bean, String beanName) throws BeansException { System.out.println(beanName + "初始化之前.." ); return bean; } @Override public Object postProcessAfterInitialization (Object bean, String beanName) throws BeansException { System.out.println(beanName + "初始化之后.." ); return bean; } }
执行测试方法,查看运行效果
1 2 3 4 5 6 7 @Test public void test1 () { AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext(MainConfig4.class); ioc.close(); }
@Value注解给属性赋值 @Value 添加对象的属性上
可以是基本数据类型
可以是SpEL表达式,例如 #{15-6}
可以是 ${}
,读取外部配置文件
新建一个配置类,里面注册一个 Person Bean
1 2 3 4 5 6 7 @Configuration public class MainConfig5 { @Bean public Person person () { return new Person(); } }
然后打印这个ioc容器中的bean
1 2 3 4 5 6 7 8 @Test public void test1 () { AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext(MainConfig5.class); String[] beanNames = ioc.getBeanDefinitionNames(); for (String beanName : beanNames) { System.out.println(beanName); } }
现在来获取 person bean 查看属性值,可以看到现在都是 null
接着通过 @Value 注解对属性进行赋值
1 2 3 4 5 6 7 8 9 public class Person { @Value("张三") String name; @Value("#{15-6}") Integer age; }
再来执行获取 person 方法,查看打印的对象值
@PropertySource 读取配置文件为属性赋值 首先新建一个配置文件 person.properties
然后在配置文件中使用 @PropertySource 配置数据源
value 是一个字符串数组,可以设置多个数据源
encoding 设置读取配置文件的编码方式,使用 utf-8 解决读取中文乱码问题
1 2 3 4 5 6 7 8 @PropertySource(value = {"classpath:person.properties"},encoding = "utf-8") @Configuration public class MainConfig5 { @Bean public Person person () { return new Person(); } }
然后再 Person 类中使用 ${person.nickName}
的方式为 nickName 属性赋值
1 2 @Value("${person.nickName}") String nickName;
执行测试方法,查看打印结果
1 2 3 4 5 6 @Test public void test1 () { AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext(MainConfig5.class); Person person = ioc.getBean("person" , Person.class); System.out.println(person); }
自动装配 @AutoWrite 新建一个 Bookdao
1 2 3 4 5 6 7 8 9 10 11 12 @Component public class BookDao { String bookName = "book1" ; public String getBookName () { return bookName; } public void setBookName (String bookName) { this .bookName = bookName; } }
然后新建 BookServer,使用 @Autowired 自动装配 bookDao
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Service public class BookServer {  { } public BookDao getBookDao () { return bookDao; } public void setBookDao (BookDao bookDao) { this .bookDao = bookDao; } }
新建配置类 MainConfig6
1 2 3 4 5 @ComponentScan(value = {"com.szx.dao","com.szx.server"}) @Configuration public class MainConfig6 { }
添加测试方法,通过 BookServer 获取 bookDao 中的 bookName
1 2 3 4 5 6 7 8 9 public class AutowiredTest { @Test public void test1 () { AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext(MainConfig6.class); BookServer bookServer = ioc.getBean("bookServer" , BookServer.class); BookDao bookDao = bookServer.getBookDao(); System.out.println(bookDao.getBookName()); } }
打印结果为book1
@Qualifier 使用 @AutoWrite 注解默认按照组件类型去容器中获取,如果容器中有两个相同类型的组件,再将属性的名称作为 id 去容器中查找
修改配置类,在配置类注册另外一个 BookDao 类型的组件,名称为 bookDao2
1 2 3 4 5 6 7 8 9 10 11 @ComponentScan(value = {"com.szx.dao","com.szx.server"}) @Configuration public class MainConfig6 { @Bean public BookDao bookDao2 () { BookDao bookDao = new BookDao(); bookDao.setBookName("book2" ); return bookDao; } }
运行测试方法,此时仍然打印 book1
如果才能打印 book2 呢?可以使用 @Qualifier 注解
在 BookServer 中添加 @Qualifier(“bookDao2”) 表示自动装配容器中名称为 bookDao2 的对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package com.szx.server;import com.szx.dao.BookDao;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Qualifier;import org.springframework.stereotype.Service;@Service public class BookServer { @Qualifier("bookDao2") @Autowired BookDao bookDao; public BookServer () { } public BookDao getBookDao () { return bookDao; } public void setBookDao (BookDao bookDao) { this .bookDao = bookDao; } }
执行测试方法
1 2 3 4 5 6 7 @Test public void test1 () { AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext(MainConfig6.class); BookServer bookServer = ioc.getBean("bookServer" , BookServer.class); BookDao bookDao = bookServer.getBookDao(); System.out.println(bookDao.getBookName()); }
@Primary 在bean对象上添加 @Primary 注解表示Spring自动装配时默认使用的首选 bean,此时就和 bean 名称无关
将 BookServer 中的 @Qualifier(“bookDao2”) 去掉,只留一个 @AutoWrite,此时正常情况下会装配 bookDao 对象
1 2 @Autowired BookDao bookDao;
但是我们在配置类中注册的 bookDao2 上添加 @Primary 注解。此时就会装配 bookDao2
1 2 3 4 5 6 7 @Primary @Bean public BookDao bookDao2 () { BookDao bookDao = new BookDao(); bookDao.setBookName("book2" ); return bookDao; }
运行测试方法,打印的是 book2
@AutoWrite自动装配的位置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package com.szx.bean;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Qualifier;import org.springframework.stereotype.Component;@Component public class Boos { Yellor yellor; public Boos () { } @Autowired public Boos (Yellor yellor) { this .yellor = yellor; } @Override public String toString () { return "Boos{" + "yellor=" + yellor + '}' ; } }
自动装配Aware注入Spring底层组件 我们可以在自定义组件中注入Spring底层的一些组件,例如在组件中注入 applicationContext
实现 ApplicationContextAware 接口,然后重写 setApplicationContext 方法,这个方法会以回调的方式自动执行
1 2 3 4 5 6 7 8 9 10 @Component public class MyAware implements ApplicationContextAware { ApplicationContext applicationContext; @Override public void setApplicationContext (ApplicationContext applicationContext) throws BeansException { this .applicationContext = applicationContext; System.out.println("applicationContext = " + applicationContext); } }
运行测试代码注册ioc容器,查看打印结果
1 2 3 4 5 @Test public void test1 () { AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext(MainConfig6.class); System.out.println(ioc); }
@Profile @Profile 注解可以根据环境自动切换装配不同的bean,例如配置的数据库引用地址。在开发环境我们连接的是 dev 库,生产环境下我们连接 produce 库。
环境搭建 首先新建数据库,分别新建 annotation_dev
和 annotation_product
添加依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 <dependency > <groupId > c3p0</groupId > <artifactId > c3p0</artifactId > <version > 0.9.1.2</version > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 8.0.28</version > </dependency >
然后新建 jdbc.properties 配置文件
1 2 3 4 5 jdbc.name =root jdbc.pwd =abc123 jdbc.driver =com.mysql.jdbc.Driver jdbc.devUrl =jdbc:mysql://127.0.0.1:3306/annotation_dev jdbc.proUrl =jdbc:mysql://127.0.0.1:3306/annotation_product
然后新建配置类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 package com.szx.config;import com.mchange.v2.c3p0.ComboPooledDataSource;import org.springframework.beans.factory.annotation.Value;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.PropertySource;import javax.sql.DataSource;import java.beans.PropertyVetoException;@PropertySource("classpath:jdbc.properties") @Configuration public class MainConfig7 { @Value("${jdbc.name}") String name; @Value("${jdbc.pwd}") String pwd; @Value("${jdbc.devUrl}") String devUrl; @Value("${jdbc.proUrl}") String proUrl; @Value("${jdbc.driver}") String driver; @Bean public DataSource dataSourceDev () throws PropertyVetoException { ComboPooledDataSource dataSource = new ComboPooledDataSource(); dataSource.setUser(name); dataSource.setPassword(pwd); dataSource.setJdbcUrl(devUrl); dataSource.setDriverClass(driver); return dataSource; } @Bean public DataSource dataSourceProduct () throws PropertyVetoException { ComboPooledDataSource dataSource = new ComboPooledDataSource(); dataSource.setUser(name); dataSource.setPassword(pwd); dataSource.setJdbcUrl(devUrl); dataSource.setDriverClass(proUrl); return dataSource; } }
根据环境配置bean 首先在bean对象上添加 @Profile 注解,@Profile 注解可以自定义一个环境名称,和然后会自动根据当前环境自动装配相对应的bena。如果bean没有指定环境,则默认所有环境都进行装配
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 @Bean public Yellor yellor () { return new Yellor(); } @Profile("dev") @Bean public DataSource dataSourceDev () throws PropertyVetoException { ComboPooledDataSource dataSource = new ComboPooledDataSource(); dataSource.setUser(name); dataSource.setPassword(pwd); dataSource.setJdbcUrl(devUrl); dataSource.setDriverClass(driver); return dataSource; } @Profile("product") @Bean public DataSource dataSourceProduct () throws PropertyVetoException { ComboPooledDataSource dataSource = new ComboPooledDataSource(); dataSource.setUser(name); dataSource.setPassword(pwd); dataSource.setJdbcUrl(devUrl); dataSource.setDriverClass(proUrl); return dataSource; }
有两种方法:
方式一:设置启动参数 设置启动参数
添加启动配置,固定语句:-Dspring.profiles.active=product,product 就是要设置的环境名称
执行测试方法
1 2 3 4 5 6 7 8 @Test public void test1 () { AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext(MainConfig7.class); String[] beanNames = ioc.getBeanDefinitionNames(); for (String beanName : beanNames) { System.out.println(beanName); } }
通过图中可以看到 yellow 没有设置 @Profile,所以会被打印,但是 DataSource 类型的 bean 只打印了 dataSoruceProduct
修改启动参数 -Dspring.profiles.active=dev
方式二:通过代码方式设置启动环境 操作步骤:
使用空参构造器创建一个ioc容器
设置需要激活的环境,可以设置多个
注册配置类
刷新容器
示例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Test public void test2 () { AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext(); ioc.getEnvironment().setActiveProfiles("dev" ); ioc.register(MainConfig7.class); ioc.refresh(); String[] beanNames = ioc.getBeanDefinitionNames(); for (String beanName : beanNames) { System.out.println(beanName); } }
运行效果
@Profile设置位置 @Profile 注解除了可以设置 bean 上,也可以直接设置在配置类上,设置在配置类上表示当前配置类中的所有 bean 都会根据环境自动注册
例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 package com.szx.config;import com.mchange.v2.c3p0.ComboPooledDataSource;import com.szx.bean.Yellor;import org.springframework.beans.factory.annotation.Value;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 javax.sql.DataSource;import java.beans.PropertyVetoException;@Profile("dev") @PropertySource("classpath:jdbc.properties") @Configuration public class MainConfig7 { @Value("${jdbc.name}") String name; @Value("${jdbc.pwd}") String pwd; @Value("${jdbc.devUrl}") String devUrl; @Value("${jdbc.proUrl}") String proUrl; @Value("${jdbc.driver}") String driver; @Bean public Yellor yellor () { return new Yellor(); } @Bean public DataSource dataSourceDev () throws PropertyVetoException { ComboPooledDataSource dataSource = new ComboPooledDataSource(); dataSource.setUser(name); dataSource.setPassword(pwd); dataSource.setJdbcUrl(devUrl); dataSource.setDriverClass(driver); return dataSource; } @Bean public DataSource dataSourceProduct () throws PropertyVetoException { ComboPooledDataSource dataSource = new ComboPooledDataSource(); dataSource.setUser(name); dataSource.setPassword(pwd); dataSource.setJdbcUrl(devUrl); dataSource.setDriverClass(proUrl); return dataSource; } }
然后修改测试方法,将环境设置为 product,但是上面的配置类添加了 @Profile(“dev”) 注解,表示只有在 dev 环境下才会注册,所以运行结果中就不会有任何 bean
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Test public void test2 () { AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext(); ioc.getEnvironment().setActiveProfiles("product" ); ioc.register(MainConfig7.class); ioc.refresh(); String[] beanNames = ioc.getBeanDefinitionNames(); for (String beanName : beanNames) { System.out.println(beanName); } }
运行结果
AOP功能测试 aop 指的是在程序运行期间动态的将某段代码切入到指定方法指定位置运行的编程方式。
设置步骤
导入AOP依赖 spring-aspects
定义一个业务逻辑类(MathFuns),在业务逻辑类中方法运行时将日志进行打印
定义一个日志切面类(LogAspects),切面类里面的方法需要动态感知 MathFuns 类中方法的运行进度,进行通知
通知方法:
前置通知 @Before,方法开始调用之前执行
后置通知 @After,方法运行结束后执行,无论方法执行成功还是失败都会执行
成功返回通知 @AfterReturning,方法成功运行后执行
异常返回通知 @AfterThrowing,方法运行出错后执行
环绕通知
给切面类中的目标方法添加不同的注解
将切面类和逻辑类都添加到ioc容器中
告诉Spring那个类才是切面类,添加 @Aspects 注解来表示该类是一个切面类
在配置类上添加 @EnableAspectJAutoProxy 注解开启基于注解的aop模式
代码演示 第一步:导入AOP依赖 spring-aspects
1 2 3 4 5 6 <dependency > <groupId > org.springframework</groupId > <artifactId > spring-aspects</artifactId > <version > 5.2.22.RELEASE</version > </dependency >
第二步:定义一个业务逻辑类(MathFuns),在业务逻辑类中方法运行时将日志进行打印
1 2 3 4 5 6 7 public class MathFuns { public int div (int i,int j) { System.out.println("MathFuns.div 自己运行中.." ); return i / j; } }
第三步:定义一个日志切面类(LogAspects),切面类里面的方法需要动态感知 MathFuns 类中方法的运行进度,进行通知
第四步:给切面类中的目标方法添加不同的注解
第六步:告诉Spring那个类才是切面类,添加 @Aspects 注解来表示该类是一个切面类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 package com.szx.aop;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.annotation.*;import java.lang.reflect.Array;import java.util.Arrays;import java.util.List;@Aspect public class LogAspects { @Pointcut(value = "execution(* com.szx.aop.MathFuns.*(..))") public void pointcut () {} @Before(value = "pointcut()") public void logStart (JoinPoint joinPoint) { String className = joinPoint.getSignature().getDeclaringTypeName(); String methodName = joinPoint.getSignature().getName(); Object[] args = joinPoint.getArgs(); List<Object> argList = Arrays.asList(args); System.out.println(className + "." + methodName + "方法开始调用,参数为:" + argList); } @AfterReturning(value = "pointcut()",returning = "res") public void logSucReturn (JoinPoint joinPoint,Object res) { String className = joinPoint.getSignature().getDeclaringTypeName(); String methodName = joinPoint.getSignature().getName(); System.out.println(className + "." + methodName + "方法正常返回,返回结果:" + res); } @AfterThrowing(value = "pointcut()",throwing = "error") public void logErrReturn (JoinPoint joinPoint,Exception error) { String className = joinPoint.getSignature().getDeclaringTypeName(); String methodName = joinPoint.getSignature().getName(); System.out.println(className + "." + methodName + "方法发生错误,错误信息:" + error); } @After(value = "pointcut()") public void logEnd (JoinPoint joinPoint) { String className = joinPoint.getSignature().getDeclaringTypeName(); String methodName = joinPoint.getSignature().getName(); System.out.println(className + "." + methodName + "方法结束" ); } }
第五步:将切面类和逻辑类都添加到ioc容器中
第七步:在配置类上添加 @EnableAspectJAutoProxy 注解开启基于注解的aop模式
1 2 3 4 5 6 7 8 9 10 11 12 13 @EnableAspectJAutoProxy @Configuration public class MainConfig8_AOP { @Bean public MathFuns mathFuns () { return new MathFuns(); } @Bean public LogAspects logAspects () { return new LogAspects(); } }
测试 添加测试方法,从ioc容器中获取 mathFuns 类
1 2 3 4 5 6 @Test public void test () { AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext(MainConfig8_AOP.class); MathFuns mathFuns = ioc.getBean(MathFuns.class); mathFuns.div(1 , 1 ); }
运行结果:
如果我们手动的让方法发生错误,将除数设置为0
1 2 3 4 5 6 @Test public void test () { AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext(MainConfig8_AOP.class); MathFuns mathFuns = ioc.getBean(MathFuns.class); mathFuns.div(1 , 0 ); }
运行结果:
声明式事务 环境搭建 首先导入相关依赖
spring 核心包
jdbc
c3p0数据库连接池
mysql驱动
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <dependency > <groupId > org.springframework</groupId > <artifactId > spring-context</artifactId > <version > 5.2.22.RELEASE</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-jdbc</artifactId > <version > 5.2.22.RELEASE</version > </dependency > <dependency > <groupId > c3p0</groupId > <artifactId > c3p0</artifactId > <version > 0.9.1.2</version > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 8.0.28</version > </dependency >
新建 TxConfig 配置类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 package com.szx.tx;import com.mchange.v2.c3p0.ComboPooledDataSource;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;import org.springframework.jdbc.core.JdbcTemplate;import javax.sql.DataSource;import java.beans.PropertyVetoException;@ComponentScan("com.szx.tx") @Configuration public class TxConfig { @Bean public DataSource dataSource () throws PropertyVetoException { ComboPooledDataSource dataSource = new ComboPooledDataSource(); dataSource.setUser("root" ); dataSource.setPassword("abc123" ); dataSource.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/txdata" ); dataSource.setDriverClass("com.mysql.jdbc.Driver" ); return dataSource; } @Bean public JdbcTemplate jdbcTemplate () throws PropertyVetoException { JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource()); return jdbcTemplate; } }
新建 TxDao
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package com.szx.tx;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.jdbc.core.JdbcTemplate;import org.springframework.stereotype.Component;@Component public class TxDao { @Autowired JdbcTemplate jdbcTemplate; public void addUser () { String sql = "INSERT INTO users VALUES (null,?,?);" ; jdbcTemplate.update(sql,"lisi" ,21 ); } }
新建 TxServlet
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package com.szx.tx;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;@Service public class TxServlet { @Autowired TxDao txDao; public void addUser () { txDao.addUser(); } }
此时基本环境搭建配置完成,但是还没有引入事务相关注解
添加事务注解 第一步,在配置类上添加 @EnableTransactionManagement 注解开启声明式注解
第二步:在配置类中注册 PlatformTransactionManager 类型的bean
1 2 3 4 @Bean public PlatformTransactionManager transactionManager () throws PropertyVetoException { return new DataSourceTransactionManager(dataSource()); }
第三步:在要实现添加事务的方法上添加 @Transactional 注解
1 2 3 4 5 @Transactional public void addUser () { txDao.addUser(); Integer i = 10 /0 ; }