跳至主要內容

spring搭建

wangdx大约 13 分钟

对象实例化的本质操作

完整的分层设计与类结构定义

Java 面向对象编程之中是以类和对象为基础展开的,而后在 Java 所提供的众多的语法之中都是在围绕着类结构代码的可重用设计展开的,为了可以更好的表达出不同层次之的设计,在开发中往往要引入接口与抽象类,所以一个完整的系统开发中往往会出现如图所示的类结构

项目中的实例化对象

在传统的项目开发中,每一个用户请求的线程都有可能会去创建多个与业务处理相关的实例化对象,所以当线程数量逐步增加时,最终也会导致对象数量的暴增,从而影响到也需要进行程序的运行性能,如图所示。所以一个项目中除了设计良好的结构类之外,对象的有效管理,例如:如何有效的实现对象实例化的操作,或者是如何可以避免产生过多的对象,从而造成 GC 操作的频繁触发而影响最终程序的执行性能。

关键字 new 实例化对象

在 Java 语言的设计开发之中,最为常见的对象实例化的处理形式使用的就是关键字 new 即:通过关键字 new 调用类中的构造方法,随后在堆栈内存中进行内存分配后,即可使用该对象进行类结构的处理操作,操作结构如图所示。虽然这样的对象创建形式简单,但是如果直接使用也会造成代码的耦合度增加,为便于读者分析出当前程序所存在的问下面通过具体的程序代码进行说明。

package com.yootk.service;

public interface IMessageService { // 传统的业务接口
    /**
     * 实现消息服务处理
     * @param msg 当前要处理的消息内容
     * @return 消息处理后的结果
     */
    public String echo(String msg);
}
package com.yootk.service.impl;

import com.yootk.service.IMessageService;

public class MessageServiceImpl implements IMessageService { // 定义接口的实现子类

    @Override
    public String echo(String msg) { // 方法覆写
        return "【ECHO】" + msg;
    }
}
package com.yootk.service;

import com.yootk.service.impl.MessageServiceImpl;

public class YootkDemo {
    public static void main(String[] args) {
        IMessageService messageService = new MessageServiceImpl(); // 通过接口子类创建接口实力
        System.out.printf(messageService.echo("沐言科技:www.yootk.com")); // 业务接口调用
    }
}

工厂设计模式与对象实例化

基于反射实现工厂设计模式

  • 如果现在需要对外部隐藏接口的实现子类细节,那么最佳的做法就是要引入一个工厂设计模式,利用工厂类来封装接口子类实例化过程,这样开发者就可以通过工厂类来获取接口实例,从而避免了使用类和接口实现子类之间的耦合问题,但是在进行工厂类设计时,考虑到子类动态配置的需要,最佳的做法是基于反射机制实现
1package com.yootk.util;

public class ObjectFactory { // 定义一个对象工厂类
    private ObjectFactory() {} // 构造方法私有化

    /**
     * 指定的类型的类,可以通过此方法获取该类的实例化对象
     * @param className 类名称
     * @param returnType 类型,只是作为一个标记操作使用
     * @return 实例化对象,如果产生异常则返回null
     * @param <T> 返回的对象类型标记
     */
    public static <T> T getInstance(String className, Class<T> returnType) {
        Object instance = null; // 保存最终的实例化对象
        try {
            Class<?> clazz = Class.forName(className); // 获取Class对象实例
            instance = clazz.getConstructor().newInstance();
        } catch (Exception e) {
            e.printStackTrace(); // 简单一点,直接把异常进行打印输出
        }
        return (T) instance;
    }
}

2package com.yootk.service;
import com.yootk.util.ObjectFactory;
public class YootkDemo {
    public static void main(String[] args) {
        String className = "com.yootk.service.impl.MessageServiceImpl"; // 定义类名称
        // 利用工厂类进行对象实例化,隐藏了对象实例化的处理细节,同时对外只暴露接口,隐藏了子类的存在
        IMessageService messageService = ObjectFactory.getInstance(className, IMessageService.class);
        System.out.printf(messageService.echo("沐言科技:www.yootk.com")); // 业务接口调用
    }
}

基于配置文件管理使用类

Bean 管理模式

  • 通过反射机制虽然可以解决调用类与接口子类之间的耦合性问题,但是直接将使用类的名称定义在源代码之中,一定会对代码的可维护性造成影响,所以最佳的做法是基于配置文件的方式来进行管理
1、
messageService=com.yootk.service.impl.MessageServiceImpl

2package com.yootk.util;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

public class ObjectFactory { // 定义一个对象工厂类
    // 如果此时你要处于并发访问环境下的更新或修改,则更换为ConcurrentHashMap
    private static final Map<String, Object> INSTANCE_POOL_MAP = new HashMap<>(); // 保存业务接口实力
    private static final String CONFIG_INSTANCE = "Beans"; // 配置文件的名称
    static {
        String configPath = Thread.currentThread().getContextClassLoader()
                .getResource("").getPath() + "com" + File.separator + "yootk" +
                File.separator + "config" + File.separator + CONFIG_INSTANCE + ".properties";
        System.out.println("【配置文件路径】" + configPath);
        Properties properties = new Properties(); // 实例化属性操作类
        try {
            properties.load(new FileInputStream(configPath)); // 文件数据读取
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        System.out.println("【配置属性】" + properties);
        for (Map.Entry<Object, Object> entry : properties.entrySet()) { // 迭代Properties属性集合
            String beanName = entry.getKey().toString(); // 获取注册的Bean名称
            try {
                Class<?> clazz = Class.forName(entry.getValue().toString()); // 获取Class对象实例
                Object instance = clazz.getConstructor().newInstance();
                INSTANCE_POOL_MAP.put(beanName, instance); // 保存在Map集合里面
            } catch (Exception e) {
                e.printStackTrace(); // 简单一点,直接把异常进行打印输出
            }
        }
    }
    private ObjectFactory() {} // 构造方法私有化

    /**
     * 指定的类型的类,可以通过此方法获取该类的实例化对象
     * @param beanName 类名称
     * @param returnType 类型,只是作为一个标记操作使用
     * @return 实例化对象,如果产生异常则返回null
     * @param <T> 返回的对象类型标记
     */
    public static <T> T getInstance(String beanName, Class<T> returnType) {
        return (T) INSTANCE_POOL_MAP.get(beanName);
    }
}
3package com.yootk.service;
import com.yootk.util.ObjectFactory;
public class YootkDemo {
    public static void main(String[] args) {
        // 利用工厂类进行对象实例化,隐藏了对象实例化的处理细节,同时对外只暴露接口,隐藏了子类的存在
        IMessageService messageService = ObjectFactory.getInstance("messageService", IMessageService.class);
        System.out.printf(messageService.echo("沐言科技:www.yootk.com")); // 业务接口调用
    }
}

搭建 Spring 项目

IDEA 项目结构管理

-【IDEA 工具】建立一个新的空项目,随后在该项目中进行项目模块的创建而子项目模块的创建需要在项目结构的管理中进行定义,此时需要打开“项目设置”选项中的“项目结构”子项

  • 创建 Gradle 模块

    -【IDEA 工具】在项目结构管理中创建一个新的项目模块

  • 配置 Gradle 项目信息

    • 【IDEA 工具】在进行 Gradle 项目时还需要配置相关的信息,本次的配置信息为:组织名称(com.yootk)、模块名称(yootk-spring)、版本编号(1.0.0)
  • Gradle 项目创建完成

    • Gradle 项目创建成功之后,可以在 IDEA 主界面见到名称为“yootk-spring”的项目信息,同时在右侧边栏也会列出该项目有关的 Gradle 任务项供开发者使用。
  • gradle.properties
project_group=com.yootk
project_version=1.0.0
project_jdk=17
  • build.gradle
group project_group					// 组织名称
version project_version 					// 项目版本
apply from: 'dependencies.gradle' // 导入依赖配置
def env = System.getProperty("env") ?: 'dev' 		// 获取env环境属性
subprojects { 						// 配置子项目
    apply plugin: 'java' 					// 子模块插件
    sourceCompatibility = project_jdk			// 源代码版本
    targetCompatibility = project_jdk 			// 生成类版本
    repositories { 					// 配置Gradle仓库
        mavenLocal()					// Maven本地仓库
        maven{ 						// 阿里云仓库
            allowInsecureProtocol = true
            url 'http://maven.aliyun.com/nexus/content/groups/public/'}
        maven { // spring官方仓库
            allowInsecureProtocol = true
            url 'https://repo.spring.io/libs-milestone'
        }
        mavenCentral()					// Maven远程仓库
    }
    dependencies {					// 公共依赖库管理

    }
    sourceSets {						// 源代码目录配置
        main {						// main及相关子目录配置
            java { srcDirs = ['src/main/java'] }
            resources { srcDirs = ['src/main/resources', "src/main/profiles/$env"] }
        }
        test { 						// test及相关子目录配置
            java { srcDirs = ['src/test/java'] }
            resources { srcDirs = ['src/test/resources'] }
        }
    }
    test { 						// 配置测试任务
        useJUnitPlatform()					// 使用JUnit测试平台
    }
    task sourceJar(type: Jar, dependsOn: classes) {		// 源代码的打包任务
        archiveClassifier = 'sources'			// 设置文件的后缀
        from sourceSets.main.allSource			// 所有源代码的读取路径
    }
    task javadocTask(type: Javadoc) { 			// JavaDoc文档打包任务
        options.encoding = 'UTF-8'				// 设置文件编码
        source = sourceSets.main.allJava 			// 定义所有的Java源代码
    }
    task javadocJar(type: Jar, dependsOn: javadocTask) {	// 先生成JavaDoc再打包
        archiveClassifier = 'javadoc'			// 文件标记类型
        from javadocTask.destinationDir 			// 通过Javadoc任务找到目标路径
    }
    tasks.withType(Javadoc) {            			// 文档编码配置
        options.encoding = 'UTF-8'            		// 定义编码
    }
    tasks.withType(JavaCompile) {        			// 编译编码配置
        options.encoding = 'UTF-8'				// 定义编码
    }
    artifacts {  						// 最终打包的操作任务
        archives sourceJar 				// 源代码打包
        archives javadocJar				// javadoc打包
    }
    gradle.taskGraph.whenReady { 				// 在所有的操作准备好后触发
        tasks.each { task -> 				// 找出所有的任务
            if (task.name.contains('test')) { 		// 如果发现有test任务
                task.enabled = true 				// 执行测试任务
            }
        }
    }
    [compileJava, compileTestJava, javadoc]*.options*.encoding = 'UTF-8'// 编码配置
}
  • dependencies.gradle
ext.versions = [                // 定义全部的依赖库版本号
    spring           : '6.0.0-M3'      // Spring版本号
]
ext.libraries = [            // 依赖库引入配置
     'spring-context'        :
             "org.springframework:spring-context:${versions.spring}"
]
  • base
rootProject.name = 'yix-spring'
include 'base'

编写第一个 Spring 应用

Spring 项目结构

  • Spring 在运行的过程中提供了一个完整的容器实例,在该实例内部可以有效的进行对象的统一管理,如果要想在项目的开发中去使用这个 Spring 容器,那么就可以通过 Gradle 来引入所需要的依赖库,并且依据 Spring 给定的方式运行项目
  • dependencies.gradle

mvn 仓库open in new window

// 定义全部的依赖库版本号
ext.versions = [
        spring: '6.1.4'      // Spring版本号
]
// 依赖库引入配置
ext.libraries = [
        // 以下的配置为Spring基础以来支持包
        'spring-context'        : "org.springframework:spring-context:${versions.spring}",
        'spring-core'           : "org.springframework:spring-core:${versions.spring}",
        'spring-context-support': "org.springframework:spring-context-support:${versions.spring}",
        'spring-beans'          : "org.springframework:spring-beans:${versions.spring}"
]
  • build.gradle
....
project(":base") {
    dependencies {
        implementation(libraries.'spring-context')
        implementation(libraries.'spring-core')
        implementation(libraries.'spring-context-support')
        implementation(libraries.'spring-beans')
    }
}

  • spring/spring-base.xml
<?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">
    <!--  Spring是一个完整的容器,此时的配置是表示将MessageServiceImpl这个类的对象实例交由容器管理  -->
    <!--  此时的代码里面出现有完整的类名称,不需要任何的思考,直接使用的就是反射机制  -->
    <bean id="messageService" class="com.yootk.service.impl.MessageServiceImpl"/>
</beans>
package com.yootk.main;

import com.yootk.service.IMessageService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class StartYootkSpringApplication { // Spring容器的启动类
    public static void main(String[] args) {
        // 容器的启动需要使用到特定的接口和类,此时的配置文件保存在CLASSPATH路径下,所以直接使用此类实例化
        ApplicationContext context = new ClassPathXmlApplicationContext("spring/spring-base.xml");
        // 所有配置的Bean都存在有一个id属性,那么这个id的属性内容就是查询的名称
//        IMessageService messageService = (IMessageService) context.getBean("messageService"); // 获取指定的Bean
        // 通过当前Spring上下文获取已经注册的指定ID名称的Bean实例
        IMessageService messageService = context.getBean("messageService", IMessageService.class);
        System.out.printf(messageService.echo("沐言科技:www.yootk.com"));
    }
}

SpringTest 运行测试

应用测试

  • Spring 除了提供了丰富的管理容器之外,实际上还考虑到了代码测试的支持,一个完整的服务代码除了要保证实现了最基础的功能之外,也需要保证功能的稳定性。在现实的开发中,会在项目开发完毕后交由测试人员进行用例测试,以保证程序的业务功能处理的正确性
1、
testImplementation(enforcedPlatform("org.junit:junit-bom:5.8.2"))
testImplementation('org.junit.jupiter:junit-jupiter-api:5.8.2')
testImplementation('org.junit.vintage:junit-vintage-engine:5.8.2')
testImplementation('org.junit.jupiter:junit-jupiter-engine:5.8.2')
testImplementation('org.junit.platform:junit-platform-launcher:1.8.2')
testImplementation('org.springframework:spring-test:6.0.0-M3')


// https://mvnrepository.com/artifact/org.junit/junit-bom
testImplementation group: 'org.junit', name: 'junit-bom', version: '5.10.1', ext: 'pom'
// https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api
testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.10.1'
// https://mvnrepository.com/artifact/org.junit.vintage/junit-vintage-engine
testImplementation group: 'org.junit.vintage', name: 'junit-vintage-engine', version: '5.10.1'
// https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-engine
testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.10.1'
// https://mvnrepository.com/artifact/org.junit.platform/junit-platform-launcher
testImplementation group: 'org.junit.platform', name: 'junit-platform-launcher', version: '1.10.2'




2、
ext.versions = [                // 定义全部的依赖库版本号
    spring                          : '6.0.0-M3',      // Spring版本号
    junit                           : '5.8.2',          // Junit版本编号
    junitPlatform                   : '1.8.2'          // Junit版本编号
]
ext.libraries = [            // 依赖库引入配置
    // 以下的配置为Spring基础以来支持包
    'spring-context'                :  "org.springframework:spring-context:${versions.spring}",
    'spring-core'                   :  "org.springframework:spring-core:${versions.spring}",
    'spring-context-support'        :  "org.springframework:spring-context-support:${versions.spring}",
    'spring-beans'                  :  "org.springframework:spring-beans:${versions.spring}",
    // 以下的配置为JUnit5相关的测试环境依赖
    'junit-bom'                     : "org.junit:junit-bom:${versions.junit}",
    'junit-jupiter-api'             : "org.junit.jupiter:junit-jupiter-api:${versions.junit}",
    'junit-vintage-engine'          : "org.junit.vintage:junit-vintage-engine:${versions.junit}",
    'junit-jupiter-engine'          : "org.junit.jupiter:junit-jupiter-engine:${versions.junit}",
    'junit-platform-launcher'       : "org.junit.platform:junit-platform-launcher:${versions.junitPlatform}",
    'spring-test'                   : "org.springframework:spring-test:${versions.spring}"
]

3、
    dependencies {					// 公共依赖库管理
        testImplementation(enforcedPlatform(libraries.'junit-bom'))
        testImplementation(libraries.'junit-jupiter-api')
        testImplementation(libraries.'junit-vintage-engine')
        testImplementation(libraries.'junit-jupiter-engine')
        testImplementation(libraries.'junit-platform-launcher')
        testImplementation(libraries.'spring-test')
    }

4、
package com.yootk.test;

import com.yootk.service.IMessageService;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;

// 表示要进行Spring配置文件的加载,后续也可能是进行配置类的加载
@ContextConfiguration(locations = {"classpath:spring/spring-base.xml"}) // 定义XML配置文件
@ExtendWith(SpringExtension.class) // 表示此时使用外部的测试工具(JUnit5)
public class TestMessageService {
    @Autowired // 自动注入接口实例
    private IMessageService messageService; // 要使用的业务接口
    @Test
    public void testEcho() {
        System.out.println(this.messageService.echo("沐言科技:www.yootk.com"));
    }
}

Spring 整合 Logback 日志组件

项目应用与日志记录

  • 每一个完善的项目应用之中都需要进行大量的日志记录,这样一旦应用出现了问题之后开发人员就可以根据日志中记录的内容排查问题
ext.versions = [                // 定义全部的依赖库版本号
    spring                          : '6.0.0-M3',      // Spring版本号
    junit                           : '5.8.2',          // Junit版本编号
    junitPlatform                   : '1.8.2',          // Junit版本编号
    slf4j                           : '1.7.36',         // SLF4J日志标准的版本号
    logback                         : '1.2.11'         // 日志实现标准的版本号
]
ext.libraries = [            // 依赖库引入配置
    // 以下的配置为Spring基础以来支持包
    'spring-context'                :  "org.springframework:spring-context:${versions.spring}",
    'spring-core'                   :  "org.springframework:spring-core:${versions.spring}",
    'spring-context-support'        :  "org.springframework:spring-context-support:${versions.spring}",
    'spring-beans'                  :  "org.springframework:spring-beans:${versions.spring}",
    // 以下的配置为JUnit5相关的测试环境依赖
    'junit-bom'                     : "org.junit:junit-bom:${versions.junit}",
    'junit-jupiter-api'             : "org.junit.jupiter:junit-jupiter-api:${versions.junit}",
    'junit-vintage-engine'          : "org.junit.vintage:junit-vintage-engine:${versions.junit}",
    'junit-jupiter-engine'          : "org.junit.jupiter:junit-jupiter-engine:${versions.junit}",
    'junit-platform-launcher'       : "org.junit.platform:junit-platform-launcher:${versions.junitPlatform}",
    'spring-test'                   : "org.springframework:spring-test:${versions.spring}",
    // 以下的配置为Logback日志组件所需要的依赖库
    'slf4j-api'                     : "org.slf4j:slf4j-api:${versions.slf4j}",
    'logback-classic'               : "ch.qos.logback:logback-classic:${versions.logback}"
]
  • build.gradle
  // 以下的配置为日志组件的依赖库
  implementation(libraries.'slf4j-api')
  implementation(libraries.'logback-classic')
  • logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
    <property name="LOG_HOME" value="d:/muyan-logs" />	<!-- 日志目录 -->
    <!-- 日志记录时需要有一个明确的日志记录格式,本次将日志数据的格式定义为一个配置属性 -->
<!--  %d{yyyy-MM-dd HH:mm:ss.SSS}   -->
    <property name="logging.pattern"
              value="沐言科技(www.yootk.com) [%thread] %-5level %logger{50} - %msg%n"/>
    <!-- 为便于代码调试,在每次应用程序启动时,可以将日志信息显示在控制台中 -->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>${logging.pattern}</pattern> 		<!-- 格式引用 -->
        </layout>
    </appender>
    <!-- 将每天的日志保存在一个文件之中 -->
    <appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <Prudent>true</Prudent>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <FileNamePattern>
                ${LOG_HOME}/%d{yyyy-MM}/yootk_%d{yyyy-MM-dd}.log
            </FileNamePattern>
            <MaxHistory>365</MaxHistory>			<!-- 删除超过365天的日志 -->
        </rollingPolicy>
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>ERROR</level> 				<!-- ERROR及以上级别日志 -->
        </filter>
        <encoder>
            <Pattern>${logging.pattern}</Pattern>  		<!-- 格式引用 -->
        </encoder>
    </appender>
    <root level="DEBUG"> 					<!-- 全局日志级别 -->
        <appender-ref ref="console"/> 			<!-- 控制台日志 -->
        <appender-ref ref="file"/> 				<!-- 文件日志 -->
    </root>
</configuration>
package com.yootk.test;

import com.yootk.service.IMessageService;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;

// 表示要进行Spring配置文件的加载,后续也可能是进行配置类的加载
@ContextConfiguration(locations = {"classpath:spring/spring-base.xml"}) // 定义XML配置文件
@ExtendWith(SpringExtension.class) // 表示此时使用外部的测试工具(JUnit5)
public class TestMessageService {
    private static final Logger LOGGER =
            LoggerFactory.getLogger(TestMessageService.class) ;// 获取日志实例
    @Autowired // 自动注入接口实例
    private IMessageService messageService; // 要使用的业务接口
    @Test
    public void testEcho() {
        LOGGER.info("【INFO等级】调用测试:{}",
                this.messageService.echo("沐言科技:www.yootk.com"));
        LOGGER.error("【ERROR等级】调用测试:{}",
                this.messageService.echo("沐言科技:www.yootk.com"));
        LOGGER.debug("【DEBUG等级】调用测试:{}",
                this.messageService.echo("沐言科技:www.yootk.com"));
    }
}

demo


上次编辑于: