🔺以下整理的学习笔记来自雷丰阳老师的spring讲解视频👏
@Bean注解
先创建一个实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Person {
private String name;
private Integer age;
}
配置一个Bean
// 告诉spring这是一个配置
@Configuration
public class BeanConfig {
@Bean
public Person person() {
return new Person("张山", 25);
}
}
在启动类中获取自定义的bean
public static void main(String[] args) {
// 获取自己定义的Bean
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeanConfig.class);
Person bean = applicationContext.getBean(Person.class);
System.out.println(bean);
// 获取Person在ioc容器中的名字
String[] names = applicationContext.getBeanNamesForType(Person.class);
Arrays.stream(names).forEach(System.out::println); SpringApplication.run(SpringannotationstudyApplication.class, args);
}
控制台打印
@ComponentScan注解
指定扫描哪些包下的注解
// 告诉spring这是一个配置
@Configuration
// includeFilters指定扫描哪些注解;
// excludeFilters忽略排除哪些注解
@ComponentScan(value = "com.yang", includeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class})
},useDefaultFilters = false)
public class BeanConfig {
@Bean
public Person person() {
return new Person("张山", 25);
}
}
写一个测试
@Test
public void Test(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeanConfig.class);
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
}
结果
@Scope注解
写一个配置类
@Configuration
public class BeanConfig2 {
/**
* @Scope注解默认是单实例的
* 下面是可以取得值:
* request
* session
* prototype 多实例的
* singleton 单实例的(默认的)
* @return
*/
@Scope(value = "prototype")
/**@Bean默认是单实例的*/
@Bean("person2")
public Person person(){
return new Person("李四",55);
}
}
写一个测试类
@Test
public void Test2(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeanConfig2.class);
String[] names = applicationContext.getBeanDefinitionNames();
for (String name : names) {
System.out.println(name);
}
Object person2 = applicationContext.getBean("person2");
Object person3 = applicationContext.getBean("person2");
// 判断是不是相等
System.out.println(person2 == person3);
}
结果为false
@lazy注解(使Bean懒加载)
单实例的Bean默认在容器启动时创建,现在我们想容器创建的时候不创建Bean对象
正常的加载:
@Configuration
public class BeanConfig2 {
/**@Lazy可以让容器创建时不创建这个Bean的对象*/
@Bean("person2")
public Person person(){
System.out.println("给容器中添加Person----");
return new Person("李四",55);
}
}
@Test
public void Test2(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeanConfig2.class);
System.out.println("ioc容器创建完成------");
}
测试结果:
懒加载:
@Configuration
public class BeanConfig2 {
/**@Lazy可以让容器创建时不创建这个Bean的对象*/
@Bean("person2")
public Person person(){
System.out.println("给容器中添加Person----");
return new Person("李四",55);
}
}
@Test
public void Test2(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeanConfig2.class);
System.out.println("ioc容器创建完成------");
}
测试结果:
首次用到Bean:
@Test
public void Test2(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeanConfig2.class);
Object person2 = applicationContext.getBean("person2");
测试结果:
以后每次获取也不创建了,因为是单实例的:
@Test
public void Test2(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeanConfig2.class);
System.out.println("ioc容器创建完成------");
Object person2 = applicationContext.getBean("person2");
// 获取第二次
Object person3 = applicationContext.getBean("person2");
System.out.println(person2 == person3);
}
test result:
工厂Bean
创建一个Spring定义的FactoryBean
public class PersonFactoryBean implements FactoryBean {
// 返回一个Person对象,这个对象会添加到容器中
@Override
public Object getObject() throws Exception {
System.out.println("PersonFactoryBean调用了getObject方法---");
return new Person();
}
@Override
public Class<?> getObjectType() {
return Person.class;
}
/** 是否是单例
* true:这个Bean是单例的,在容器中保存一份
* false:多实例,每次获取都会创建一个新的Bean
*/
@Override
public boolean isSingleton() {
return true;
}
}
将PersonFactoryBean 注册为Bean
@Configuration
public class BeanConfig2 {
// 看似在这里装载的PersonFactoryBean bean其实在①处Bean工厂调用getObject创建的对象
@Bean
public PersonFactoryBean personFactoryBean(){
return new PersonFactoryBean();
}
}
测试:
@Test
public void Test2(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeanConfig2.class);
// ①
Object personFactoryBean = applicationContext.getBean("personFactoryBean");
System.out.println("bean的类型为"+personFactoryBean.getClass());
}
test result:
如果想要获取Bean本身,在按照id获取时加一个前缀 & 就可以获取到了
@Test
public void Test2(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeanConfig2.class);
// ①
Object personFactoryBean = applicationContext.getBean("&personFactoryBean");
System.out.println("bean的类型为"+personFactoryBean.getClass());
}
test result:
自定义Bean的出初始和销毁
bean的生命周期就是
bean创建----初始化----销毁的过程
创建一个car类
public class Car {
// 容器调用无参构造创建对象
public Car() {
System.out.println("car的无参构造");
}
public void init(){
System.out.println("初始化Car");
}
public void destroy(){
System.out.println("销毁Car");
}
}
为car写一个配置类
@Configuration
public class CarBeanConfig {
@Bean
public Car car(){
return new Car();
}
}
测试:
@Test
public void Test3(){
// 创建ioc容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(CarBeanConfig.class);
System.out.println("容器创建完成---");
}
test result:
指定初始化和销毁的方法:
initMethod、destoryMethod
@Configuration
public class CarBeanConfig {
@Bean(initMethod = "init",destroyMethod = "destroy")
public Car car(){
return new Car();
}
}
在启动测试:
@Test
public void Test3(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(CarBeanConfig.class);
System.out.println("容器创建完成---");
}
test result:
容器关闭:
@Test
public void Test3(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(CarBeanConfig.class);
System.out.println("容器创建完成---");
applicationContext.close();
}
以上是单实例的Bean 在容器启动时创建对象
多实例的Bean在获取时创建对象
以下是多实例的自定义初始化:
@Configuration
public class CarBeanConfig {
// 修改为多实例的Bean
@Scope("prototype")
@Bean(initMethod = "init",destroyMethod = "destroy")
public Car car(){
return new Car();
}
}
创建容器看是否创建对象:
@Test
public void Test3(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(CarBeanConfig.class);
System.out.println("容器创建完成---");
// applicationContext.close();
// applicationContext.getBean("car");
}
result:
没有创建对象
获取Bean:
@Test
public void Test3(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(CarBeanConfig.class);
System.out.println("容器创建完成---");
applicationContext.getBean("car");
}
result:
多实例 容器不会管理这个Bean 容器不会调用这个销毁的方法
另一中方法初始化和销毁Bean
通过实现 InitializingBean, DisposableBean两个接口
@Component
public class Cat implements InitializingBean, DisposableBean {
public Cat() {
System.out.println("Cat的无参构造");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("afterPropertiesSet---------bean创建完成,属性赋值后调用");
}
@Override
public void destroy() throws Exception {
System.out.println("bean销毁方法");
}
}
换种方式创建Bean、添加@Component 把cat当成一个组件去扫描它
借用了CarBeanConfig 类
@ComponentScan("com.yang.entitys")
@Configuration
public class CarBeanConfig {
// @Scope("prototype")
@Bean(initMethod = "init",destroyMethod = "destroy")
public Car car(){
return new Car();
}
}
测试:
@Test
public void Test4(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(CarBeanConfig.class);
System.out.println("容器创建完成---");
applicationContext.getBean("cat");
}
test result:
@Conditional注解 可以根据配置条件选择性加载Bean
下面有两个Bean,现在想要如果是windows系统 把father添加到容器中如果是linux系统 把son添加到容器中
@Configuration
public class BeanConfig3 {
/**如果是windows系统 把father添加到容器中
* 如果是linux系统 把son添加到容器中*/
@Conditional({WindowsCondition.class})
@Bean("father")
public Person person1(){
return new Person("father",50);
}
@Conditional({LinuxCondition.class})
@Bean("son")
public Person person2(){
return new Person("son",20);
}
}
需要写两个类作为条件
/**
* 判断是否是windows系统
*
* @author : li
* @date : 2021-06-29 22:05
*/
@SuppressWarnings("all")
public class WindowsCondition implements Condition {
/**
* ConditionContext 判断条件能使用的上下文 (环境)
* AnnotatedTypeMetadata 注释信息
*/
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
// 能获取到ioc使用的beanFactory,能获取到的ioc使用的beanFactory就是创建对象 以及装配的工厂
ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();
// 获取到类加载器
ClassLoader classLoader = conditionContext.getClassLoader();
// 获取到bean定义的注册类
BeanDefinitionRegistry registry = conditionContext.getRegistry();
// 获取当前环境信息
Environment environment = conditionContext.getEnvironment();
String property = environment.getProperty("os.name");
// 判断是否是windows
if (property.contains("Windows")){
return true;
}
return false;
}
}
/**
* 判断是否是linux系统
*
* @author : li
* @date : 2021-06-29 22:08
*/
@SuppressWarnings("all")
public class LinuxCondition implements Condition {
/**
* ConditionContext 判断条件能使用的上下文 (环境)
* AnnotatedTypeMetadata 注释信息
*/
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
// 能获取到ioc使用的beanFactory,能获取到的ioc使用的beanFactory就是创建对象 以及装配的工厂
ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();
// 获取到类加载器
ClassLoader classLoader = conditionContext.getClassLoader();
// 获取到bean定义的注册类
BeanDefinitionRegistry registry = conditionContext.getRegistry();
// 获取当前环境信息
Environment environment = conditionContext.getEnvironment();
String property = environment.getProperty("os.name");
// 判断是否是windows
if (property.contains("Linux")){
return true;
}
return false;
}
}
test:
@Test
public void test5(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeanConfig3.class);
ConfigurableEnvironment environment = applicationContext.getEnvironment();
String property = environment.getProperty("os.name");
System.out.println("操作系统"+property);
String[] beanNamesForType = applicationContext.getBeanNamesForType(Person.class);
for (String s : beanNamesForType) {
System.out.println(s);
}
Map<String, Person> personMap = applicationContext.getBeansOfType(Person.class);
/*personMap.forEach((key,value) -> {
System.out.println(key+" : "+value);
});*/
System.out.println(personMap);
}
test result:
@Import
加入我们要导入一个自己写的类或者第三方的类,可以类上面加上@Import注解
自己写的:
public class Color {
}
配置类:
@Configuration
@Import({Color.class})
// @Import({Color.class,Red.class})
public class BeanConfig4 {
}
test:
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeanConfig4.class);
public void getName(AnnotationConfigApplicationContext applicationContext){
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String name : beanDefinitionNames) {
System.out.println(name);
}
}
@Test
public void testImport(){
getName(applicationContext);
}
test result:
ImportSelector 接口
自定义一个类实现Import Selector接口
/**
* importselector
* 自定义逻辑返回需要导入的组件
* @author : li
* @date : 2021-07-03 21:49
*/
public class MyImportSelector implements ImportSelector {
/**
* 返回值就是导入到容器中的组件全类名数组
* @param annotationMetadata 当前标注@Import注解的类的所有信息
* @return
*/
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
Set<String> annotationTypes = annotationMetadata.getAnnotationTypes();
System.out.println(annotationTypes);
return new String[]{"com.yang.entitys.Blue","com.yang.entitys.Yellow"};
}
}
然后结合@Import注解导入配置类
@Configuration
// Import导入组件 id是默认的全类名
@Import({Color.class, Red.class,MyImportSelector.class})
public class BeanConfig4 {
}
test:
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeanConfig4.class);
public void getName(AnnotationConfigApplicationContext applicationContext){
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String name : beanDefinitionNames) {
System.out.println(name);
}
}
@Test
public void testImport(){
getName(applicationContext);
// 获取Blue的信息
Blue bean = applicationContext.getBean(Blue.class);
System.out.println(bean);
}
ImportBeanDefinitionRegistrar接口 手动注册bean到容器中
定义一个类实现ImportBeanDefinitionRegistrar接口
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
/**
* AnnotationMetadata 当前类的注解信息
* BeanDefinitionRegistry BeanDefinition注册类
* 把所有添加到容器中的bean 调用
* BeanDefinitionRegistry.registerBeanDefinition手动注册进来
* @param importingClassMetadata
* @param registry
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 查询是否包含某个Bean定义
boolean blue = registry.containsBeanDefinition("com.yang.entitys.Blue");
boolean yellow = registry.containsBeanDefinition("com.yang.entitys.Yellow");
if (blue && yellow){
// 获取到一个bean
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Rainbow.class);
// 自定义名字注册bean
registry.registerBeanDefinition("rainbow",rootBeanDefinition);
}
}
}
配合import使用:
```java
@Configuration
// Import导入组件 id是默认的全类名
@Import({MyImportBeanDefinitionRegistrar.class})
public class BeanConfig4 {
}
test:
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeanConfig4.class);
public void getName(AnnotationConfigApplicationContext applicationContext){
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String name : beanDefinitionNames) {
System.out.println(name);
}
}
@Test
public void testImport(){
getName(applicationContext);
Blue bean = applicationContext.getBean(Blue.class);
System.out.println(bean);
}
test result:
属性@Value赋值
在实体类中用@Value赋值
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Person {
/**
* 使用@value赋值
* 1.基本数值
* 2.SPEL表达式 #{}
* 3.${};取出配置文件【properties】中的值(在运行环境变量中的的值)
*/
@Value("张三")
private String name;
@Value("#{20-2}")
private Integer age;
@Value("${person.nickName}")
private String nickname;
}
在resources文件夹下创建properties配置文件
person.nickName=小李子
写个配置类获取实体类的bean
/**
* property配置文件取值
*
* @author : li
* @date : 2021-07-04 11:59
* 使用@properSource读取外部配置文件中的K/V保存运行的环境变量中
*/
@PropertySource(value = {"classpath:/person.properties"})
@Configuration
public class MyConfigOfPropertyValue {
@Bean
public Person person(){
return new Person();
}
}
test:
@Test
public void TestProperty(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfigOfPropertyValue.class);
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
Person person = (Person) applicationContext.getBean("person");
System.out.println(person);
// 获取当前环境
ConfigurableEnvironment environment = applicationContext.getEnvironment();
// 从环境中取配置文件中的K
String property = environment.getProperty("person.nickName");
System.out.println(property);
applicationContext.close();
}
test result:
@Autowired 按照类型装配
@Qualifier(“xxx”)指定 需要装配的组件的id
如果没有装配好则会报错,那么可以使用:@Autowired(required=false)
@Primary 在Bean上加上此注解,默认首选装配这个Bean
Spring还支持@Resource(JSR250)和 Inject(JSR330)注解
@Resources默认按照名字装配,也可以指定名字@Resources(“name=xxx”)
@Resources不支持@Primary也不支持required=false
@Inject
需要导入依赖
<!-- https://mvnrepository.com/artifact/javax.inject/javax.inject -->
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
它和@Autowired功能一样,不过没有required=false功能
三者区别:
@Autowired是spring的、@Resources和@inject是Java规范的
AOP切面
导入依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>
定义一个逻辑类:
在业务逻辑运行时将日志进行打印
/**
* 除法
*
* @author : li
* @date : 2021-07-04 16:54
*/
@Slf4j
public class Division {
public int div(int a,int b){
log.info("接收参数[{}],[{}]方法开始执行计算",a,b);
return a/b;
}
}
定义一个日志切面类(切面里的方法动态感知Division类里的div方法运行到哪一步):
通知方法:
前置通知(@Before):在div方法运行之前运行
返回通知(@AfterReturning):执行完之后返回结果
后置通知()@After:在div方法运行之后运行,无论成功与否都调用
异常通知(@AfterThrowing):有异常打印一场信息
环绕通知(@Around):环绕通知,手动推进目标方法运行(joinPoind.procced)
给切面类的方法标注何时何地运行(通知注解),并告诉Spring哪一个是切面类,给切面类加注解@Aspects
/**
* 除法的日志切面
*
* @author : li
* @date : 2021-07-04 16:57
*/
@Aspect
@Slf4j
public class LogAspects {
/**
* 抽取公共的切入点表达式
* 1.本类引用 @Before("pointCut()")
* 外部类引用 @After("com.yang.config.LogAspects.pointCut()")
*/
@Pointcut("execution(public int com.yang.config.Division.*(..))")
public void pointCut(){}
@Before("pointCut()")
public void logStart(JoinPoint joinPoint){
Object[] pointArgs = joinPoint.getArgs();
System.out.println(joinPoint.getSignature().getName()+"除法运行-----参数列表是{"+ Arrays.asList(pointArgs)+"}");
}
@AfterReturning(value = "pointCut()",returning = "result")
public void logResult(Object result){
System.out.println("除法返回结果-------{"+result+"}");
MDC.clear();
}
@After("com.yang.config.LogAspects.pointCut()")
public void logEnd(JoinPoint joinPoint){
System.out.println(joinPoint.getSignature().getName()+"除法结束-----");
}
@AfterThrowing(value = "pointCut()",throwing = "exception")
public void logException(JoinPoint joinPoint,Exception exception){
System.out.println(joinPoint.getSignature().getName()+"除法异常-------返回结果{"+exception+"}");
}
@Around("pointCut()")
public Object timeAround(ProceedingJoinPoint joinPoint){
// 获取开始执行的时间
long startTime = System.currentTimeMillis();
// 定义返回对象,得到方法需要的参数
Object o = null;
try {
o = joinPoint.proceed();
} catch (Throwable throwable) {
log.info("统计[{}]执行耗时环绕通知出错",joinPoint.getSignature().getName(),throwable);
}
log.info("方法[{}]执行时间[{}]",joinPoint.getSignature().getName(),System.currentTimeMillis() - startTime);
return o;
}
}
ps:JoinPoint joinPoint在参数表中一定写在第一位,不然报错
将切面类和业务逻辑类都加入到容器中:
/**
* aop切面
*
* @author : li
* @date : 2021-07-04 16:52
*/
@Configuration
public class MyConfigOfAop {
/**
* 将Division类加入到容器中
* @return
*/
@Bean
public Division division(){
return new Division();
}
/**
* 将LogAspects类加入到容器中
* @return
*/
@Bean
public LogAspects logAspects(){
return new LogAspects();
}
}
添加开启切面的功能注解@EnableAspectJAutoProxy
/**
* 切面配置类
*
* @author : li
* @date : 2021-07-04 16:52
*/
@EnableAspectJAutoProxy
@Configuration
public class MyConfigOfAop {
/**
* 将Division类加入到容器中
* @return
*/
@Bean
public Division division(){
return new Division();
}
/**
* 将LogAspects类加入到容器中
* @return
*/
@Bean
public LogAspects logAspects(){
return new LogAspects();
}
}
test:
@Test
public void TestAop(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfigOfAop.class);
// 必须从容器中获取Bean,切面才生效
Division division = applicationContext.getBean(Division.class);
division.div(1,1);
}
normal test:
@Test
public void TestAop(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfigOfAop.class);
// 必须从容器中获取Bean,切面才生效
Division division = applicationContext.getBean(Division.class);
division.div(10,5);
}
normal test result:
exception test result:
执行顺序:
①:前置通知(@Before):在div方法运行之前运行
②:返回通知(@AfterReturning):执行完之后返回结果
③:后置通知()@After:在div方法运行之后运行,无论成功与否都调用
②:异常通知(@AfterThrowing):有异常打印一场信息
返回通知和异常通知只能有一个