提交 9d679fb2 编写于 作者: 小傅哥's avatar 小傅哥

feat:Spring 注入案例 By 小傅哥

上级
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
*.iws
*.iml
*.ipr
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/
### Mac OS ###
.DS_Store
/data/
<?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">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.bugstack</groupId>
<artifactId>xfg-dev-tech-spring-dependency-injection</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.12</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.28</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.9</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.4</version>
</dependency>
</dependencies>
<build>
<finalName>xfg-dev-tech-app</finalName>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>**/**</include>
</includes>
</resource>
</resources>
<testResources>
<testResource>
<directory>src/test/resources</directory>
<filtering>true</filtering>
<includes>
<include>**/**</include>
</includes>
</testResource>
</testResources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.6</version>
<configuration>
<skipTests>true</skipTests>
<testFailureIgnore>false</testFailureIgnore>
<includes>
<include>**/*Test.java</include>
</includes>
</configuration>
</plugin>
</plugins>
</build>
</project>
\ No newline at end of file
package cn.bugstack.xfg.dev.tech;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ImportResource;
import org.springframework.context.annotation.PropertySource;
import org.springframework.scheduling.annotation.EnableScheduling;
@Slf4j
@SpringBootApplication
@Configurable
@PropertySource("classpath:properties/application.properties")
@ImportResource("classpath:spring/spring.xml")
@EnableScheduling
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
}
package cn.bugstack.xfg.dev.tech.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import java.util.ArrayList;
import java.util.List;
@Slf4j
@Configuration
@EnableConfigurationProperties(AutoConfigProperties.class)
public class AutoConfig {
@Bean("redisson01")
// 当 Spring 应用上下文中不存在某个特定类型的 Bean 时,才会创建和配置标注了 @ConditionalOnMissingBean 的 Bean 对象
@ConditionalOnMissingBean
public String redisson01() {
return "模拟的 Redis 客户端 01";
}
@Bean("redisson02")
// 当 Spring 应用上下文中不存在某个特定类型的 Bean 时,才会创建和配置标注了 @ConditionalOnMissingBean 的 Bean 对象
@ConditionalOnMissingBean
public String redisson02() {
return "模拟的 Redis 客户端 02";
}
@Bean
@Conditional(BeanCreateCondition.class)
public List<String> whitelistedUsers() {
return new ArrayList<String>() {{
add("user001");
add("user002");
add("user003");
}};
}
@Bean
@ConditionalOnProperty(value = "sdk.config.enabled", havingValue = "true", matchIfMissing = false)
public String createTopic(@Qualifier("redisson01") String redisson, AutoConfigProperties properties) {
log.info("redisson {} {} {}", redisson, properties.getApiHost(), properties.getApiSecretKey());
return redisson;
}
}
package cn.bugstack.xfg.dev.tech.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
@Data
@ConfigurationProperties(prefix = "sdk.config", ignoreInvalidFields = true)
public class AutoConfigProperties {
/** 状态;open = 开启、close 关闭 */
private boolean enable;
/** 转发地址 */
private String apiHost;
/** 可以申请 sk-*** */
private String apiSecretKey;
}
package cn.bugstack.xfg.dev.tech.config;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
public class BeanCreateCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String active = System.getProperty("isOpenWhitelistedUsers");
return null != active && active.equals("true");
}
}
package cn.bugstack.xfg.dev.tech.domain;
public interface IAwardService {
void doDistributeAward(String userId);
}
package cn.bugstack.xfg.dev.tech.domain;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class SpringBeanTest {
public SpringBeanTest() {
log.info("我是通过 Spring 配置文件实例化的 Bean 对象");
}
}
package cn.bugstack.xfg.dev.tech.domain.impl;
import cn.bugstack.xfg.dev.tech.domain.IAwardService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;
@Slf4j
@Service
// 用于根据配置环境实例化 Bean 对象
@Profile({"prod", "test"})
@Lazy
public class AliPayAwardService implements IAwardService {
public AliPayAwardService() {
log.info("如一些支付场景,必须指定上线后才能实例化");
}
@Override
public void doDistributeAward(String userId) {
log.info("红包奖励 {}", userId);
}
}
package cn.bugstack.xfg.dev.tech.domain.impl;
import cn.bugstack.xfg.dev.tech.domain.IAwardService;
public class NullAwardService implements IAwardService {
@Override
public void doDistributeAward(String userId) {
}
}
package cn.bugstack.xfg.dev.tech.domain.impl;
import cn.bugstack.xfg.dev.tech.domain.IAwardService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Primary;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Service;
/**
* OpenAI 可用的对话额度
*/
@Slf4j
@Service("openai_model")
// Primary 首选 Bean 对象标记
@Primary
@Order(1)
public class OpenAIModelAwardService implements IAwardService {
@Override
public void doDistributeAward(String userId) {
log.info("发奖服务,OpenAI 模型奖励 {}", userId);
}
}
package cn.bugstack.xfg.dev.tech.domain.impl;
import cn.bugstack.xfg.dev.tech.domain.IAwardService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Service;
/**
* OpenAI 用户对话额度奖励
*/
@Slf4j
@Service("openai_use_count")
@Order(2)
public class OpenAIUseCountAwardService implements IAwardService {
@Override
public void doDistributeAward(String userId) {
log.info("发奖服务,OpenAI 用户额度奖励 {}", userId);
}
}
package cn.bugstack.xfg.dev.tech.domain.impl;
import cn.bugstack.xfg.dev.tech.domain.IAwardService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Service;
/**
* 用户积分奖品
*/
@Slf4j
@Service("user_credit_random")
@Order(3)
public class UserCreditRandomAwardService implements IAwardService {
@Override
public void doDistributeAward(String userId) {
log.info("发奖服务,用户积分奖励 {}", userId);
}
}
package cn.bugstack.xfg.dev.tech.domain.rule;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
@Scope("prototype")
public class LogicChain {
}
package cn.bugstack.xfg.dev.tech.infrastructure;
import org.springframework.stereotype.Repository;
@Repository
public class AwardRepository {
}
package cn.bugstack.xfg.dev.tech.trigger.http;
import cn.bugstack.xfg.dev.tech.domain.IAwardService;
import cn.bugstack.xfg.dev.tech.types.Response;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.DependsOn;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.List;
import java.util.Map;
@Slf4j
@RestController()
@CrossOrigin(origins = "*")
@DependsOn({"openai_model", "openai_use_count", "user_credit_random"})
public class AwardController {
private final List<IAwardService> awardServices;
private final Map<String, IAwardService> awardServiceMap;
public AwardController(List<IAwardService> awardServices, Map<String, IAwardService> awardServiceMap) {
this.awardServices = awardServices;
this.awardServiceMap = awardServiceMap;
}
public Response<String> distributeAward(@RequestParam String userId, @RequestParam String awardKey) {
try {
log.info("发放奖品服务 userId:{} awardKey:{}", userId, awardKey);
awardServiceMap.get(awardKey);
return Response.<String>builder()
.code("0000")
.info("调用成功")
.data("发奖完成")
.build();
} catch (Exception e) {
return Response.<String>builder()
.code("0001")
.info("调用失败")
.build();
}
}
@PostConstruct
public void init() {
log.info("对象初始化完成 {}", this);
}
@PreDestroy
public void destroy() {
log.info("对象销毁完成 {}", this);
}
}
package cn.bugstack.xfg.dev.tech.trigger.job;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class SendAwardJob {
@Scheduled(cron = "0/5 * * * * ?")
public void inspection(){
log.info("巡检任务");
}
}
package cn.bugstack.xfg.dev.tech.types;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Response<T> implements Serializable {
private String code;
private String info;
private T data;
}
server:
port: 8090
config:
userId: xiaofuge
sdk:
config:
enabled: false
apiHost: https://open.bigmodel.cn/
apiSecretKey: d570f7c5d289cdac2abdfdc562e39f3f.trqz1dH8ZK6ED7Pg
logging:
level:
root: info
config: classpath:logback-spring.xml
\ No newline at end of file
server:
port: 8090
logging:
level:
root: info
config: classpath:logback-spring.xml
\ No newline at end of file
server:
port: 8090
logging:
level:
root: info
config: classpath:logback-spring.xml
\ No newline at end of file
spring:
config:
name: xfg-dev-tech-spring-dependency-injection
profiles:
active: dev
<?xml version="1.0" encoding="UTF-8"?>
<!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出 -->
<configuration scan="true" scanPeriod="10 seconds">
<contextName>logback</contextName>
<!-- name的值是变量的名称,value的值时变量定义的值。通过定义的值会被插入到logger上下文中。定义变量后,可以使“${}”来使用变量。 -->
<springProperty scope="context" name="log.path" source="logging.path"/>
<!-- 日志格式 -->
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
<conversionRule conversionWord="wex"
converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
<conversionRule conversionWord="wEx"
converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
<!-- 输出到控制台 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<!-- 此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息 -->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>info</level>
</filter>
<encoder>
<pattern>%d{yy-MM-dd.HH:mm:ss.SSS} [%-16t] %-5p %-22c{0}%X{ServiceId} -%X{trace-id} %m%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!--输出到文件-->
<!-- 时间滚动输出 level为 INFO 日志 -->
<appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文件的路径及文件名 -->
<file>./data/log/log_info.log</file>
<!--日志文件输出格式-->
<encoder>
<pattern>%d{yy-MM-dd.HH:mm:ss.SSS} [%-16t] %-5p %-22c{0}%X{ServiceId} -%X{trace-id} %m%n</pattern>
<charset>UTF-8</charset>
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 每天日志归档路径以及格式 -->
<fileNamePattern>./data/log/log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!--日志文件保留天数-->
<maxHistory>15</maxHistory>
<totalSizeCap>10GB</totalSizeCap>
</rollingPolicy>
</appender>
<!-- 时间滚动输出 level为 ERROR 日志 -->
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文件的路径及文件名 -->
<file>./data/log/log_error.log</file>
<!--日志文件输出格式-->
<encoder>
<pattern>%d{yy-MM-dd.HH:mm:ss.SSS} [%-16t] %-5p %-22c{0}%X{ServiceId} -%X{trace-id} %m%n</pattern>
<charset>UTF-8</charset>
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>./data/log/log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!-- 日志文件保留天数【根据服务器预留,可自行调整】 -->
<maxHistory>7</maxHistory>
<totalSizeCap>5GB</totalSizeCap>
</rollingPolicy>
<!-- WARN 级别及以上 -->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>WARN</level>
</filter>
</appender>
<!-- 异步输出 -->
<appender name="ASYNC_FILE_INFO" class="ch.qos.logback.classic.AsyncAppender">
<!-- 队列剩余容量小于discardingThreshold,则会丢弃TRACT、DEBUG、INFO级别的日志;默认值-1,为queueSize的20%;0不丢失日志 -->
<discardingThreshold>0</discardingThreshold>
<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
<queueSize>8192</queueSize>
<!-- neverBlock:true 会丢失日志,但业务性能不受影响 -->
<neverBlock>true</neverBlock>
<!--是否提取调用者数据-->
<includeCallerData>false</includeCallerData>
<appender-ref ref="INFO_FILE"/>
</appender>
<appender name="ASYNC_FILE_ERROR" class="ch.qos.logback.classic.AsyncAppender">
<!-- 队列剩余容量小于discardingThreshold,则会丢弃TRACT、DEBUG、INFO级别的日志;默认值-1,为queueSize的20%;0不丢失日志 -->
<discardingThreshold>0</discardingThreshold>
<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
<queueSize>1024</queueSize>
<!-- neverBlock:true 会丢失日志,但业务性能不受影响 -->
<neverBlock>true</neverBlock>
<!--是否提取调用者数据-->
<includeCallerData>false</includeCallerData>
<appender-ref ref="ERROR_FILE"/>
</appender>
<!-- 开发环境:控制台打印 -->
<springProfile name="dev">
<logger name="com.nmys.view" level="debug"/>
</springProfile>
<root level="info">
<appender-ref ref="CONSOLE"/>
<!-- 异步日志-INFO -->
<appender-ref ref="ASYNC_FILE_INFO"/>
<!-- 异步日志-ERROR -->
<appender-ref ref="ASYNC_FILE_ERROR"/>
</root>
</configuration>
\ No newline at end of file
app.name=xfg-dev-tech-spring-dependency-injection
app.version=1.0.0
\ No newline at end of file
<?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 id="exampleBean" class="cn.bugstack.xfg.dev.tech.domain.SpringBeanTest"/>
</beans>
\ No newline at end of file
package cn.bugstack.xfg.dev.tech.test;
import cn.bugstack.xfg.dev.tech.domain.IAwardService;
import cn.bugstack.xfg.dev.tech.domain.impl.AliPayAwardService;
import cn.bugstack.xfg.dev.tech.domain.impl.NullAwardService;
import cn.bugstack.xfg.dev.tech.domain.impl.OpenAIModelAwardService;
import cn.bugstack.xfg.dev.tech.domain.impl.OpenAIUseCountAwardService;
import cn.bugstack.xfg.dev.tech.domain.rule.LogicChain;
import cn.bugstack.xfg.dev.tech.trigger.http.AwardController;
import cn.bugstack.xfg.dev.tech.types.Response;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.scheduling.annotation.Async;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.CountDownLatch;
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest
public class ApiTest {
@Value("${config.userId}")
private String userId;
@Resource
private AwardController awardController;
@Resource
private OpenAIModelAwardService openAIModelAwardService;
@Autowired(required = true)
private OpenAIUseCountAwardService openAIUseCountAwardService;
@Autowired(required = false)
private NullAwardService nullAwardService;
@Autowired(required = false)
private AliPayAwardService aliPayAwardService;
@Resource
private IAwardService awardService;
@Autowired(required = false)
@Qualifier("whitelistedUsers")
private List<String> whitelistedUsers;
@Autowired
private Environment env;
@Resource
private ApplicationContext applicationContext;
static {
// BeanCreateCondition 会检测这个值,确定是否创建对象
System.setProperty("isOpenWhitelistedUsers", "false");
}
@Test
public void test_awardService_primary() {
log.info("测试结果 {}", awardService.getClass());
}
@Test
public void test_environment() {
log.info("应用信息 {} {}", env.getProperty("app.name"), env.getProperty("app.version"));
}
@Test
public void test_prototype() {
log.info("测试结果: {}", applicationContext.getBean(LogicChain.class).hashCode());
log.info("测试结果: {}", applicationContext.getBean(LogicChain.class).hashCode());
}
@Test
public void test_condition() {
// isOpenWhitelistedUsers 可以分别设置 true、false 验证,ture 的时候,会实例化
log.info("测试结果: {}", JSON.toJSONString(whitelistedUsers));
}
@Test
public void test_award() {
Response<String> response = awardController.distributeAward("xiaofuge", "user_credit_random");
log.info("测试结果: {}", JSON.toJSONString(response));
}
@Test
public void test_async() throws InterruptedException {
async();
new CountDownLatch(1).await();
}
@Async
private void async() {
System.out.println("异步方法开始执行: " + Thread.currentThread().getName());
try {
Thread.sleep(5000); // 模拟耗时操作
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("异步方法执行完成: " + Thread.currentThread().getName());
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册