了解IOC和DI
Ioc(Inversion of Control)控制反转
在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。理解好Ioc的关键是要明白以下两点:
- 谁控制谁,控制什么?是由Ioc容器来控制对象的创建,而不是传统的在对象内部通过new进行创建对象。主要控制的就是外部资源的获取(不仅仅是java对象还包括文件等)
- 为何是反转,哪些方面反转了?反转是指由容器来帮忙创建及注入依赖对象,而不是像传统开发在内部通过new主动创建依赖,所反转的是依赖对象的获取过程被反转了。
IoC是一个重要的面向对象编程的法则,指导如何设计出松耦合、更优良的程序。传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了IoC容器后,
把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是松散耦合。
DI(Dependency Injection)依赖注入
组件之间的依赖关系由容器在运行期决定,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的主要目的为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。 理解DI的关键是要理解以下内容:
- 谁依赖于谁?当然是应用程序依赖于IoC容器;
- 为什么需要依赖?应用程序需要IoC容器来提供对象需要的外部资源;
- 谁注入谁?很明显是IoC容器注入应用程序某个对象,应用程序依赖的对象;
- 注入了什么?就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)。;
IoC和DI本质上是对同一个概念的不同角度描述。这里有一个关键的组织者就是IoC容器,Ioc容器具有依赖注入功能的容器,IoC容器负责实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。应用程序无需直接在代码中new相关的对象,应用程序由IoC容器进行组装。它的存在可以让服务消费者不直接依赖于服务提供者的组件设计方式,符合减少类与类之间依赖的设计原则。
Bean作用域
在面向对象程序设计中一般指对象或变量之间的可见范围。而在Spring容器中是指其创建的Bean对象相对于其他Bean对象的请求可见范围。 Spring提供“singleton”和“prototype”两种基本作用域,另外提供“request”、“session”、“global session”三种web作用域。
application配置文件特殊用法
使用配置中的变量
可以使用${}
直接使用spring配置文件中的变量,比如:
spring.application.name = springdemoapiGroupName = ${spring.application.name}
使用随机数
要是应对重复的可能性,比如端口号。这样也方便注册中心的管理。
#10之间的随机数
server.port=${random.int(10)}
#10~20之间的随机数
server.port=${random.int[10,20]}
多模块配置文件
SpringBoot从2.4.x版本开始支持了通过spring.config.import属性导入多种途径的配置文件。spring.config.import属性允许我们在主配置文件中引入其他配置文件,这使得配置文件更加模块化,易于维护和管理。之后就可以用@ConfigurationProperties或@Value来注入使用了。
- classpath: 导入classpath下的配置文件
spring:config:import:# 导入src/main/resource/default.properties配置文件- classpath:/default/default.properties# 导入src/main/resource/service.yml配置文件- classpath:/service/service.yml
- optional: 导入系统目录下的配置文件
spring:config:import:# 导入系统目录/Users/yuqiyu/Downloads/system.properties配置文件- optional:/Users/yuqiyu/Downloads/system.properties
- 导入Nacos配置中心的配置文件
spring:config:import:# 导入nacos配置中心的配置文件- optional:nacos:spring-config-import-example.yaml
命令行启动参数配置
命令行运行
在用命令行方式启动 SpringBoot 应用时,连续的两个减号 – 就是对 application.properties 中的属性值进行赋值的标识,比如下列命令,将添加/替换掉application.properties中同名的属性。
java -jar xxx.jar --server.port=8888
springboot配置优先级,从高到低:
命令行–操作系统(System.getProperties())–系统环境变量(Enviorent)–application-profile.properties–application.properties
需要注意读取properties文件时先读jar外的同路径文件再读jar包中的同名文件。
设置Macos系统JDK
打开终端,编辑.bashrc或.bash_profile文件,一般是编辑.bash_profile文件,添加以下行:
export JAVA_HOME=/path/to/your/jdk
export PATH=$JAVA_HOME/bin:$PATH
然后执行
source ~/.bash_profile
指定JDK版本执行
这个命令在mac下好用,/java21是随意写的一个值,测试后,发现其是采用代码编译的版本,同时再查找本地是否有相应的JDK。
JAVA_HOME=/java21 java -jar your-application.jar
另一个官方命令如下,但在mac下不好用,不知道为啥
java -Djava.home=/opt/jdk1.8.0_231 -jar your-application.jar
核心注解
@Configuration
通过@Configuration注解的类将被作为配置类使用,使用@Configuration 注解的类本身也是一个Bean,同时@Configuration在实现上也会被@Component注解,所以不需要再有@Component标识。
import org.springframework.context.annotation.Configuration;
@Configuration("ctxConfig")
public class ApplicationContextConfig {
//定义Bean配置元数据
}
@ConfigurationProperties
一般要和类的@Component一起使用,目的是将使用了 @ConfigurationProperties 注解的配置类生效,将该类注入到 IOC 容器中统一管理。
@Component
@EnableConfigurationProperties(DemoConfig.class)
public class Test implements ApplicationRunner {@AutowiredDemoConfig demoConfig;@Overridepublic void run(ApplicationArguments args) throws Exception {System.out.println(demoConfig);}
}
@Bean
@Bean注解主要用于标识在方法上,同时方法所在的类也要用@Component标识。方法名默认就是Bean名,该方法返回值就是Bean对象。
@Bean
@Scope(value="prototype")
public Printer prototypePrinter() {return new Printer();}@Bean
@Scope(value="singleton")
public Printer singletonPrinter() {return new Printer();
}
@Import
这个注解主要用于类上,它可以把非bean变成一个bean类(即不在springboot扫描范围内的类)。此注解一般也被设计用于模块化配置类。
@Import({TestBean1.class,ImportBeanByConfig.class,ImportBeanByImportSelector.class,ImportBeanByImportBeanDefinitionRegistrar.class})
@Configuration
public class ImportTest {//这种用法是TestBean1不被spring扫描到,这样才有意义@Autowired(required = false)private TestBean1 testBean1;}
ImportSelector接口
另一种用ImportSelector接口实现的示例如下,注意这里引入的是实现类的名称,这种方法适合在外部组织引入的模块化。方法中annotationMetadata参数表示当前被@Import注解给标注的所有注解信息。
public class ImportBeanByImportSelector implements ImportSelector {@Overridepublic String[] selectImports(AnnotationMetadata annotationMetadata) {// 这里全类名return new String[]{"com.gzl.cn.springbootcache.config.TestBean3"};}
}//使用
@Import({ImportBeanByImportSelector.class})
ImportBeanDefinitionRegistrar接口
另一种更方便的实现方法如下,其用法同ImportSelector接口一样。
public class ImportBeanByImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(TestBean4.class); // 这里类名registry.registerBeanDefinition("TestBean4", rootBeanDefinition);}
}
@RequestMapping
这个注解表示通用的GetMapping和PostMapping,但使用起来有点实践上的区别,RequestMapping配合通配符使用,可以起到模块划分的作用。即在类上面注解会比较合适。
- 多个URL映射到同一地址:@RequestMapping(value={“/test1”, “/user/create”}):
- 过滤请求:@RequestMapping(params=“submitFlag=create”, method=RequestMethod.GET),表示只接受请求中有
“submitFlag=create”请求参数且请求方法为“GET”的URL地址; - 只对请求头为“Content-Type:application/json”的进行请求处理:@RequestMapping(value = “/request/ContentType”, method = RequestMethod.POST, headers = “Content-Type=application/json”)
//类似URL为/api/user/的请求全部转到这个模块,这样也方便异常处理
@Controller
@RequestMapping("/api/user/")
public class ReadingListController {}
@Profile
当应用程序需要部署到不同的运行环境时,一些配置细节通常会有所不同。比如,数据库连 接的细节在开发环境下和测试环境下就会不一样,在生产环境下又不一样。
@Profile("production")
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {}
使用@Profile注解要求运行时激活production Profile,这样才能应用该配置。可以启动时指定,也可以在配置文件中配置,比如:
$ java -jar readinglist-0.0.1-SNAPSHOT.jar --spring.profiles.active=production
SpringBootTest测试
有几个特殊的注解需要理解下:
- @RunWith(SpringJUnit4ClassRunner.class):引入 Spring对 JUnit4的支持,一般不太使用;
- @SpringApplicationConfiguration (classes= HelloApplication. class):指定SpringBoot的启动类;
- @WebAppConfiguration:开启 Web 应用的配置,用于模拟 ServletContexto MockMvc对象;
- Mockery.java:jMock核心类,用于创建Mock对象的,通过其mock方法来创建相应接口或类的Mock对象。
一个完整的例子如下:
public class GoodsHibernateDaoUnitTest {//Mock对象上下文,用于创建Mock对象, ClassImposteriser.INSTANCE表示可以支持Mock非接口类,默认只支持Mock接口private final Mockery context = new Mockery() {{setImposteriser(ClassImposteriser.INSTANCE);}};//Mock IGoodsCodeDao接口private IGoodsCodeDao goodsCodeDao = context.mock(IGoodsCodeDao.class);@Beforepublic void setUp() { }@Testpublic void testSave () {final GoodsModel expected = new GoodsModel();//8、定义预期行为,并在后边来验证预期行为是否正确(可选)context.checking(new org.jmock.Expectations() {goodsCodeDao.get(GoodsModel.class, 1) //调用一次goodsCodeDao.get()方法,且参数必须为1.one().will(returnValue(expected)); //返回值});GoodsModel actual = goodsDao.get(1); //验证第8步定义的预期行为是否正确执行了(可选)context.assertIsSatisfied(); //验证goodsDao.get(1)返回结果是否正确 , expected是一个事先定义好的变量Assert.assertEquals(goods, expected);}
切换日志组件
默认情况下,Spring Boot会用Logback(http://logback.qos.ch)来记录日志,并用INFO级别输 出到控制台。如果决定使 用Log4j或者Log4j2,那么你只需要修改依赖,引入对应该日志实现的起步依赖,同时排除掉 Logback。
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId><exclusions><exclusion> <groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-logging</artifactId></exclusion></exclusions>
</dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-log4j</artifactId>
</dependency>
开发自己的springboot-starter
有一个叫spring cloud starts是一个基础的starts版本。
Maven相关
Maven内置变量
maven 内置变量 Maven 提供了一些内置的变量,称为"内置属性",可以在项目的pom.xml文件中直接使用。使用示例如下:
<build><directory>${project.basedir}/target</directory><outputDirectory>${project.build.directory}/classes</outputDirectory><finalName>${project.artifactId}-${project.version}</finalName>
</build>
- 基于项目的变量:
{basedir}
: 项目根目录${version}
:项目版本${project.basedir}
:项目的根目录,即包含pom.xml
文件的目录。${project.baseUri}
:项目的文件地址(URI)。${project.groupId}
:项目的 groupId。${project.artifactId}
:项目的 artifactId。${project.version}
:项目的版本。${project.packaging}
:项目的打包方式,如jar
、war
等。${project.name}
:项目的名称。${project.description}
:项目的描述。${project.build.directory}
:构建生成的文件存放的目录,默认为${project.basedir}/target
。${project.build.outputDirectory}
:主代码编译后的输出目录,默认为${project.build.directory}/classes
。${project.build.sourceDirectory}
:主代码的源目录,默认为src/main/java
。${project.build.testSourceDirectory}
:测试代码的源目录,默认为src/test/java
。${project.build.testOutputDirectory}
:测试代码编译后的输出目录,默认为${project.build.directory}/test-classes
。${project.build.finalName}
产出物名称,缺省为 ${project.artifactId} - ${project.version}
- 设置和环境变量:
${settings.localRepository}
:本地仓库的路径。${env.JAVA_HOME}
:Java 安装目录的环境变量。${env.MAVEN_OPTS}
:传递给 Maven JVM 的参数。
- 时间和构建相关:
${maven.build.timestamp}
:构建开始的时间戳。${maven.build.timestamp.format}
:构建开始时间的格式化字符串,默认为yyyyMMdd-HHmm
。
- Maven 系统属性:
- 这些是 Java 系统属性,可以通过
System.getProperty()
访问,也可以在 Maven 中使用。例如${java.version}
、${os.name}
等。
- 这些是 Java 系统属性,可以通过
- 命令行属性:
- 你可以在 Maven 命令行中通过
-D
参数设置自定义属性。例如mvn install -DmyProp=myValue
,然后在 POM 或构建脚本中通过${myProp}
引用它。
- 你可以在 Maven 命令行中通过
SpringBoot-maven插件
主要功能包括: 打包你的应用为一个可执行的JAR或WAR,包含所有必要的类和资源,可以在生产环境中执行,内嵌了Tomcat, Jetty, Undertow。如果不提供这个插件,则用maven package指令打包后的应用不能正常运行,因为没有依赖包。
<plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId>
</plugin>mvn clean package
mvn spring-boot:run