文章目录
- 1. 项目搭建过程
- 1. pom 依赖
- 2. 在没有配置数据库相关时不要引入依赖包,如spring-boot-starter-data-jpa
- 2. spring bean 默认生成策略的正确使用
- 1. 代码示例
- 2. 单元测试
- 3. 工具类 [参考](#test2)
- 4. 报错信息
- 5. 分析
- 6. 使用说明
- 2. 使用了@Autowired 注解,任然是空指针
- 1. 不理解Spring的自动装配规则,错误的是用new是很常见的
- 2. 没有理解spring 的扫描机制
- 3. 不使用自动注入还会获取上下文么?
- 1. 应用上下文的理解
- 2. Spring 核心是容器,但容器不是唯一
- 1. 不常用的容器实现 -- BeanFactory
- 2. 高级实现,继承BeanFactory派生的应用上下文 -- ApplicationContext
- 3. 获取应用上下文(ApplicationContext)的四种方式
- 1. ApplicationContextInitializer: 容器创建之后的回调
- 2. ApplicationListener : 观察者模式的典型应用
- 3. 获取应用上下文 ApplicationContextAware : Spring 的Aware接口
- 4. 多线程下Spring Bean 的数据不符合预期怎么办
- 1. 单例
- 2. coding
- 3. spring 单利Bean 的 特点
- 1. 优点
- 2. 劣势
- 3. 默认单例的理由
- 5. 经常报存在多个可用Bean 异常
- 1. 与Spring bean 相关的注解
- 1. @Autowired
- 2. @Qualifiler
- 3. @Resource
- 4. @Primary
1. 项目搭建过程
1. pom 依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>learning</artifactId>
<groupId>com.maidou</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-escape</artifactId>
<name>spring-escape</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<!-- 依赖声明 -->
<dependencyManagement>
<dependencies>
<!-- SpringBoot的依赖配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.6.4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.6.4</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>2.6.4</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.21</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.8.1</version>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>3.7.1</version>
</plugin>
<plugin>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>3.0.0</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
2. 在没有配置数据库相关时不要引入依赖包,如spring-boot-starter-data-jpa
2. spring bean 默认生成策略的正确使用
1. 代码示例
package com.maidou.spring.escape.service;
import org.springframework.stereotype.Service;
@Service
public class ORderService {
public void getOrder() {
System.out.println("this is order");
}
}
2. 单元测试
package com.maidou.spring.escape.service;
import com.maidou.spring.escape.utils.ApplicationUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import static org.junit.Assert.*;
@SpringBootTest
@RunWith(SpringRunner.class)
public class ORderServiceTest {
@Test
public void getOrder() {
ORderService oRderService = (ORderService)ApplicationUtils.getBean("oRderService");
oRderService.getOrder();
}
}
3. 工具类 参考
package com.maidou.spring.escape.utils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
* <h1>应用上下文工具类</h1>
* */
@Slf4j
@Component
public class ApplicationUtils implements ApplicationContextAware {
private static ApplicationContext applicationContext = null;
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
if (ApplicationUtils.applicationContext == null) {
ApplicationUtils.applicationContext = applicationContext;
}
}
public static ApplicationContext getApplicationContext() {
return ApplicationUtils.applicationContext;
}
/**
* <h2>通过 name 获取 Bean</h2>
* */
public static Object getBean(String name) {
return getApplicationContext().getBean(name);
}
/**
* <h2>通过 class 获取 Bean</h2>
* */
public static <T> T getBean(Class<T> clazz) {
return getApplicationContext().getBean(clazz);
}
/**
* <h2>通过 name + class 获取 Bean</h2>
* */
public static <T> T getBean(String name, Class<T> clazz) {
return getApplicationContext().getBean(name, clazz);
}
}
4. 报错信息
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'oRderService' available
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:874)
at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1344)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:309)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
5. 分析
AnnotationBeanNameGenerator -> generateBeanName -> Introspector.decapitalize(shortClassName)
public static String decapitalize(String name) {
if (name == null || name.length() == 0) {
return name;
}
// 第一个字母和第二个字母都是大写的话本名输出
if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&
Character.isUpperCase(name.charAt(0))){
return name;
}
char chars[] = name.toCharArray();
chars[0] = Character.toLowerCase(chars[0]);
return new String(chars);
}
6. 使用说明
- 避免首字母和第二个字母都是大写,否则全名输出 ORderService
- 注解的时候主动指定名称 @Service(“oRderService”)
- 通过类型的方式获取对象信息
2. 使用了@Autowired 注解,任然是空指针
1. 不理解Spring的自动装配规则,错误的是用new是很常见的
-
属性对象注入了,但是,当前类没有被标记为new
-
当前类标记为spring bean,且属性对象也注入了,但是,确实用了new去过去了对象
解决方式 : 使用bean 的整个过程中,都应该被Spring容器所管理
2. 没有理解spring 的扫描机制
-
创建外部类
import org.springframework.stereotype.Service; @Service public class Outer { public void test() { System.out.println("outer test"); } }
-
单元测试
@Test public void testOuter() { assert ApplicationUtils.getApplicationContext().containsBean("outer"); ((Outer)ApplicationUtils.getBean("outer")).test(); }
-
错误
java.lang.AssertionError at com.maidou.spring.escape.service.ORderServiceTest.testOuter(ORderServiceTest.java:26) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:74) at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:84) at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75) at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
-
解决方式 启动类增加扫描包信息
@ComponentScan(value = {"com.maidou.spring.escape","com.maidou.spring.outer"})
-
@ComponentScan 参数说明
- vaule
- includeFilters
- excludeFilters
- lazyInit
3. 不使用自动注入还会获取上下文么?
1. 应用上下文的理解
Spring的核心是管理对象,且并不只是帮我们创建对象,他负责了对象整个生命的管理,创建、装配、销毁
他是IOC容器
应用上下文可以认为是Spring容器的一种实现,也就是用于操作容器类对象
把需要管理的对象放入容器中,取得容器中的Bean
2. Spring 核心是容器,但容器不是唯一
1. 不常用的容器实现 – BeanFactory
1. 最简单的容器
2. 提供基本的DI功能
2. 高级实现,继承BeanFactory派生的应用上下文 – ApplicationContext
1. 解析配置文件
2. 注册管理Bean
3. 获取应用上下文(ApplicationContext)的四种方式
1. ApplicationContextInitializer: 容器创建之后的回调
定义在org.springframwork.context包下,两步实现
package com.maidou.spring.escape.application_context;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
* <h1>保存应用上下文</h1>
* */
@Slf4j
public class ApplicationContextStore {
private static ApplicationContext applicationContext = null;
public static void setApplicationContext(ApplicationContext applicationContext) {
log.info("coming in applicationContextStore");
if (ApplicationContextStore.applicationContext == null) {
ApplicationContextStore.applicationContext = applicationContext;
}
}
public static ApplicationContext getApplicationContext() {
return ApplicationContextStore.applicationContext;
}
}
获取应用上下文
package com.maidou.spring.escape.application_context;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
/**
* @Author maicheng
* @Description : 第一种方式获取应用上下文
* @Date 13:13 2022/10/22
**/
@Slf4j
public class UserInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
assert ApplicationContextStore.getApplicationContext() == null;
ApplicationContextStore.setApplicationContext(applicationContext);
assert ApplicationContextStore.getApplicationContext() != null;
log.info("get UserInitializer");
}
}
注册到启动类上
public static void main(String[] args) {
// SpringApplication.run(App.class, args);
SpringApplication application = new SpringApplication(App.class);
application.addInitializers(new UserInitializer());
application.run(args);
}
2. ApplicationListener : 观察者模式的典型应用
-
观察者的典型应用(内置事件)
ApplicationContexntEvent
- ContextRefreshedEvent
- ContextStartedEvent
- ContextStoppedEvent
- ContextClosedEvent
-
coding
package com.maidou.spring.escape.application_context; import lombok.extern.slf4j.Slf4j; import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.stereotype.Component; /** * @Author maicheng * @Description * @Date 13:28 2022/10/22 **/ @Slf4j @Component public class UserListener implements ApplicationListener<ContextRefreshedEvent> { @Override public void onApplicationEvent(ContextRefreshedEvent event) { assert ApplicationContextStore.getApplicationContext() == null; ApplicationContextStore.setApplicationContext(event.getApplicationContext()); assert ApplicationContextStore.getApplicationContext() != null; log.info("get UserListener"); } }
-
注释掉 注册的UserInitializer
3. 获取应用上下文 ApplicationContextAware : Spring 的Aware接口
- BeanNameAware
- BeanFactoryAware
- ApplicationContextAware
- ResourseLoaderAware
coding
package com.maidou.spring.escape.application_context;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
* @Author maicheng
* @Description :第三种应用上下文的方式
* @Date 13:44 2022/10/22
**/
@Component
@Slf4j
public class UserAware implements ApplicationContextAware {
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
assert ApplicationContextStore.getApplicationContext() == null;
ApplicationContextStore.setApplicationContext(applicationContext);
assert ApplicationContextStore.getApplicationContext() != null;
log.info("get UserAware");
}
}
启动类原始启动 参考
4. 多线程下Spring Bean 的数据不符合预期怎么办
1. 单例
2. coding
package com.maidou.spring.escape.singleton_;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
@Slf4j
@Service
public class DefaultSingleton {
private List<String> list = null;
@PostConstruct
public void init() {
log.info("Coming in DefaultSingleton");
this.list = new ArrayList<>(100);
}
public void add(String result) {
list.add(result);
}
public int getSize() {
return list.size();
}
public List<String> getList() {
return this.list;
}
}
测试
package com.maidou.spring.escape.singleton_;
import com.maidou.spring.escape.utils.ApplicationUtils;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import static org.junit.Assert.*;
@SpringBootTest
@RunWith(SpringRunner.class)
@Slf4j
public class DefaultSingletonTest {
@Test
public void init() {
DefaultSingleton bean = ApplicationUtils.getBean(DefaultSingleton.class);
DefaultSingleton bean1 = ApplicationUtils.getBean(DefaultSingleton.class);
assert bean.hashCode() == bean1.hashCode();
bean.add("11111");
bean.add("222222");
log.info("bean info : {}", bean.getList());
bean1.add("33333");
log.info("bean info : {}", bean.getList());
}
}
映射成多个对象 改变scope 模式
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
3. spring 单利Bean 的 特点
1. 优点
- 减少新生成实例的消耗
- 减少JVM垃圾回收
- 可以快速获取到Bean
2. 劣势
线程不安全
3. 默认单例的理由
- 少创建实例
- 垃圾回收便捷
- 使用缓存快速获取
5. 经常报存在多个可用Bean 异常
1. 与Spring bean 相关的注解
1. @Autowired
属于Spring 框架,默认使用类型(byType)进行注入
2. @Qualifiler
结合@Autowired一起使用,自动注入策略由byType变成byName
3. @Resource
JAVAEE 自带的注解,默认按byName自动注入
4. @Primary
存在多个类型的相同类型的Bean,则 @Primary用于定义首选项