Skip to content

springBoot使用

springBoot
This page demonstrates some of the built-in markdown extensions provided by VitePress.

pom.xml配置

引入父项目

md
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.3.12.RELEASE</version>
</parent>

build配置

md
<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <mainClass>mainClass</mainClass>
                <includeSystemScope>true</includeSystemScope>
            </configuration>
            <executions>
                <execution>
                    <goals>
                        <goal>repackage</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

启动类

md
```java 
@EnableScheduling
@EnableAsync
@MapperScan(value = {"com.lzhuai.store.dao"})
@SpringBootApplication
public class App {

    public static void main( String[] args) {
        IdGeneratorOptions options = new IdGeneratorOptions();
        options.WorkerId = 4;//机器编码
        YitIdHelper.setIdGenerator(options);
        JwtUtil.initRSA();
        SpringApplication.run(App.class,args);
    }

}
```

logback.xml

md
```xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
     <springProfile name="prod">
	   <property name="LOG_PATH" value="/usr/local/projects/logs/gateway"></property>
	</springProfile>
    <springProfile name="dev">
	    <property name="LOG_PATH" value="/usr/local/projects/logs/gateway"></property>
	</springProfile>
    <springProfile name="local">
        <property name="LOG_PATH" value="/usr/local/projects/logs/gateway"></property>
    </springProfile>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <Pattern>[%date{yyyy-MM-dd HH:mm:ss}] [%-5level] [%t] --%mdc{client}%msg%n</Pattern>
        </encoder>
    </appender>
    <appender name="TRACE_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH}/trace.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <FileNamePattern>${LOG_PATH}/trace.%d{yyyy-MM-dd}.zip</FileNamePattern>
            <MaxHistory>30</MaxHistory>
        </rollingPolicy>
        <layout>
            <pattern>[%date{yyyy-MM-dd HH:mm:ss}] [%-5level] [%t] [%logger:%line]--%mdc{client} %msg%n</pattern>
        </layout>
    </appender>
    <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH}/info.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <FileNamePattern>${LOG_PATH}/info.%d{yyyy-MM-dd}.zip</FileNamePattern>
            <MaxHistory>30</MaxHistory>
        </rollingPolicy>
        <layout>
            <pattern>[%date{yyyy-MM-dd HH:mm:ss}] [%-5level] [%t] [%logger:%line]--%mdc{client} %msg%n</pattern>
        </layout>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>INFO</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>
    <appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH}/debug.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <FileNamePattern>${LOG_PATH}/debug.%d{yyyy-MM-dd}.zip</FileNamePattern>
            <MaxHistory>30</MaxHistory>
        </rollingPolicy>
        <layout>
            <pattern>[%date{yyyy-MM-dd HH:mm:ss}] [%-5level] [%t] [%logger:%line]--%mdc{client} %msg%n</pattern>
        </layout>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>DEBUG</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>
    <appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH}/warn.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <FileNamePattern>${LOG_PATH}/warn.%d{yyyy-MM-dd}.zip</FileNamePattern>
            <MaxHistory>30</MaxHistory>
        </rollingPolicy>
        <layout>
            <pattern>[%date{yyyy-MM-dd HH:mm:ss}] [%-5level] [%t] [%logger:%line]--%mdc{client} %msg%n</pattern>
        </layout>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>WARN</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>
    <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH}/error.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <FileNamePattern>${LOG_PATH}/error.%d{yyyy-MM-dd}.zip</FileNamePattern>
            <MaxHistory>30</MaxHistory>
        </rollingPolicy>
        <layout>
            <pattern>[%date{yyyy-MM-dd HH:mm:ss}] [%-5level] [%t] [%logger:%line]--%mdc{client} %msg%n</pattern>
        </layout>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>
    <root level="INFO">
        <appender-ref ref="INFO_FILE" />
        <appender-ref ref="WARN_FILE"/>
        <appender-ref ref="ERROR_FILE"/>
    </root>
    <root level="INFO">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>  
```

工具类

md
```java
@Slf4j
public class JwtUtil {

    //有效期为
    public static final Long JWT_TTL = 30L * 24 * 60 * 60 * 1000;// 两天
    private static final String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDRVoLElcdVvHjkYMDQ7IlcWzBrFHrhyWtefTdblNynWFMQl330Aov98sZvPjOF188iH41r/SqKD1WcsayZmwDaAVjFK/vpFZZjkVlbv4I/ob03SzYpxEgAGfRcQcbyOxJH2Opma5GA2Pz5R3IrpQHX1t70XXI1u+TIDufm2VCnXQIDAQAB";
    private static final String privateKey = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBANFWgsSVx1W8eORgwNDsiVxbMGsUeuHJa159N1uU3KdYUxCXffQCi/3yxm8+M4XXzyIfjWv9KooPVZyxrJmbANoBWMUr++kVlmORWVu/gj+hvTdLNinESAAZ9FxBxvI7EkfY6mZrkYDY/PlHciulAdfW3vRdcjW75MgO5+bZUKddAgMBAAECgYAV9V+pNFbhAPCjcBy5rZ4pAG2/RbowFfs7waG9awuMSEEKVAHDx0KdBErZX2pzJzSUjT+97KP6MGqYEbEbub0ocGh3UpWL0HKjxa+4ILnneTw9ZeWOOS2Wyv3MDvHCOg0NoAtuezAHE1qtaxupsBXzAltoOVtIjxq7Xx9rrgwFNwJBAPTA98iNnw47ivS+hCLDMH2GzCKmfp+0Hpagn7LGFgwt3agSJNftg3FSYr3SLL8HeIs744G/5MgMsGdYVLrk7IMCQQDa9PJ+ya151FlaiwXFq75FiJmqOrjDgJxbZ0kD8NwKkt+QkM/hozKyzi2uLA836uCT/uAyDb3E7kAjiZUnr5afAkEAq/DEIYgcLQt3WuhcO2+UKRsBXXZJjjgjJP5CMn0mD/hIt0HH0ElzBbEy3NdByBGfhXDVKtl8B22in3dWTtVzsQJAVR8A3vV8sW4yRpbhaZV45QNT6e/mQGrRXl9q7VNH1/x9WXBSwL93SDgLXjjWucBgCmj9s7fP3K1a1fX4vHLwtQJBANu5cpFogvEb/AL0TrLjgOV4RdG3M3M2JMADqjgnGcsFHXxKwtx5ZVghw+qxZzABejWQoQF8x99gfwl5pxqNf7g=";
    private static RSA rsa = new RSA(privateKey, publicKey);

    public static String getUUID(){
        String token = UUID.randomUUID().toString().replaceAll("-", "");
        return token;
    }

    /**
     * 生成jtw
     * @param subject token中要存放的数据(json格式)
     * @return
     */
    public static String createJWT(String subject) {
        byte[] encrypt = rsa.encrypt(StrUtil.bytes(subject, CharsetUtil.CHARSET_UTF_8), KeyType.PublicKey);
        return bytesToBase64(encrypt);
    }

    public static Long userId(){
        return getSysVO().getId();
    }

    public static String phone(){
        return getSysVO().getPhone();
    }

    public static String userName(){
        return getSysVO().getName();
    }

    private static SysUserVO getSysVO(){
        ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        String token = request.getHeader(SecurityConfig.TOKEN);
        Assert.notBlank(token,"token不存在");
        byte[] decrypt = rsa.decrypt(token, KeyType.PrivateKey);
        String userJsonStr = new String(decrypt,StandardCharsets.UTF_8);
        SysUserVO sysUserVO = JSONUtil.toBean(userJsonStr, SysUserVO.class);
        return sysUserVO;
    }


    public static Long supplierId() {
        return getSysVO().getSupplierId();
    }


    /**
     * 字节数组转Base64编码
     *
     * @param bytes 字节数组
     * @return Base64编码
     */
    private static String bytesToBase64(byte[] bytes) {
        byte[] encodedBytes = Base64.getEncoder().encode(bytes);
        return new String(encodedBytes, StandardCharsets.UTF_8);
    }

    /**
     * Base64编码转字节数组
     *
     * @param base64Str Base64编码
     * @return 字节数组
     */
    private static byte[] base64ToBytes(String base64Str) {
        byte[] bytes = base64Str.getBytes(StandardCharsets.UTF_8);
        return Base64.getDecoder().decode(bytes);
    }

    /**
     * demo
     * @param args
     */
    public static void main(String[] args) {
        String text = "人最宝贵的是生命.生命对每个人只有一次.人的一生应当这样度过:当他回首往事的时候,不会因为虚度年华而悔恨,也不会因为碌碌无为而羞耻.这样,在临死的时候,他能够说:“我已把自己的整个的生命和全部的精力献给了世界上最壮丽的事业---------为人类的解放而斗争.”";
        System.out.println("原文:" + text);
        //生成公私钥对
        KeyPair pair = SecureUtil.generateKeyPair("RSA");
        PrivateKey privateKey = pair.getPrivate();
        PublicKey publicKey = pair.getPublic();
        //获得私钥
        String privateKeyStr = bytesToBase64(privateKey.getEncoded());
        System.out.println("私钥:" + privateKeyStr);
        //获得公钥
        String publicKeyStr = bytesToBase64(publicKey.getEncoded());
        System.out.println("公钥:" + publicKeyStr);
        RSA rsa = new RSA(privateKeyStr, publicKeyStr);
        System.out.println(rsa);
        //公钥加密,私钥解密
        byte[] encrypt = rsa.encrypt(StrUtil.bytes(text, CharsetUtil.CHARSET_UTF_8), KeyType.PublicKey);
        System.out.println("公钥加密:" + bytesToBase64(encrypt));
        byte[] decrypt = rsa.decrypt(encrypt, KeyType.PrivateKey);
        System.out.println("私钥解密:" + new String(decrypt,StandardCharsets.UTF_8));
        Sign sign = SecureUtil.sign(SignAlgorithm.SHA256withRSA, privateKeyStr, publicKeyStr);
        //签名
        byte[] data = text.getBytes(StandardCharsets.UTF_8);
        byte[] signed = sign.sign(data);
        String signedStr = bytesToBase64(signed);
        System.out.println("签名:" + signedStr);
        //验证签名
        boolean verify = sign.verify(data, base64ToBytes(signedStr));
        System.out.println("验签:" + verify);

    }

}
```

常用工具

controller返回对象R

md
```java 
@Data
public class R {

    private Boolean success;
    private int code = 200;
    private String msg;
    private Object data;
    private String threadName;

    public R() {
        this.threadName = Thread.currentThread().getName();
    }

    public static R ok() {
        R r = new R();
        r.setSuccess(true);
        r.setMsg("操作成功");
        return r;
    }

    public static R fail() {
        R r = new R();
        r.setSuccess(false);
        r.setMsg("操作失败");
        return r;
    }

    public static R setResult(Boolean success, String msg, Object data) {
        return ok().success(success).msg(msg).data(data);
    }

    public R success(Boolean success) {
        this.setSuccess(success);
        return this;
    }

    public R code(Integer code) {
        this.setCode(code);
        return this;
    }

    public R msg(String msg) {
        this.setMsg(msg);
        return this;
    }

    public R data(Object data) {
        this.setData(data);
        return this;
    }

    public static R ok(String msg, Object data) {
        R r = new R();
        r.setSuccess(true);
        r.setMsg(msg);
        r.setData(data);
        return r;
    }

    public static R ok(Object data) {
        R r = new R();
        r.setSuccess(true);
        r.setMsg("操作成功");
        r.setData(data);
        return r;
    }

    public static R fail(String msg) {
        R r = new R();
        r.setSuccess(false);
        r.setMsg(msg);
        return r;
    }
}

```

ApplicationContextUtil

md
```java
@Component
public class ApplicationContextUtil implements ApplicationContextAware {

	private static ApplicationContext context ;

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		context = applicationContext;
	}
	
	public static ApplicationContext getContext() {
		return context;
	}
	
    public static Object getBean(String name){
        return getContext().getBean(name);
    }
    
    public static <T> T getBean(Class<T> clazz){
        return getContext().getBean(clazz);
    }    

    public static <T> T getBean(String name,Class<T> clazz){
        return getContext().getBean(name, clazz);
    }    
}
```

Md5Util

md
```java
public class Md5Util {

    public static String getMD5(String str,int i) {
        try {
            for (int j = 0; j < i; j++) {
                // 生成一个MD5加密计算摘要
                MessageDigest md = MessageDigest.getInstance("MD5");
                // 计算md5函数
                md.update(str.getBytes());
                // digest()最后确定返回md5 hash值,返回值为8为字符串。因为md5 hash值是16位的hex值,实际上就是8位的字符
                // BigInteger函数则将8位的字符串转换成16位hex值,用字符串来表示;得到字符串形式的hash值
                String md5=new BigInteger(1, md.digest()).toString(16);
                //BigInteger会把0省略掉,需补全至32位
                str = fillMD5(md5);
            }
            return str;
        } catch (Exception e) {
            throw new RuntimeException("MD5加密错误:"+e.getMessage(),e);
        }
    }

    public static String getPwd(String str){
        int i = RandomUtil.randomInt(5);
        String pwd = Md5Util.getMD5(str, i);
        pwd = pwd + i;
        return pwd;
    }

    public static String fillMD5(String md5){
        return md5.length()==32?md5:fillMD5("0"+md5);
    }

    public static void main(String[] args) {
        System.out.println(Md5Util.getMD5("JG509811967553400",1));
    }
}
```

redis

pom

md
```xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
```

配置

md
```java
@Configuration
public class RedisConfig {

    @Bean
    public CacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory){
        RedisCacheManager redisCacheManager = new RedisCacheManager(
                RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory),
                this.getRedisCacheConfigurationWithTtl(3600),
                this.getRedisCacheConfigurationMap()
        );
        return redisCacheManager;
    }


    private Map<String, RedisCacheConfiguration> getRedisCacheConfigurationMap(){
        HashMap<String, RedisCacheConfiguration> configurationHashMap = new HashMap<>();
        //configurationHashMap.put(DEMO_CACHE,this.getRedisCacheConfigurationWithTtl(600));
        return configurationHashMap;
    }

    private RedisCacheConfiguration getRedisCacheConfigurationWithTtl(int second){
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);

        return RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofSeconds(second))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(stringRedisSerializer));
    }

    @Bean
    public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        // 使用Jackson2JsonRedisSerialize 替换默认序列化
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
        // 初始化string的序列化方式
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        // key采用String的序列化方式
        redisTemplate.setKeySerializer(stringRedisSerializer);
        // hash的key也采用String的序列化方式
        redisTemplate.setHashKeySerializer(stringRedisSerializer);
        // value序列化方式采用jackson
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        // hash的value序列化方式采用jackson
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }
}
```

redisson

md
```xml
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
</dependency>
```

配置

md
```java
    @Bean
public org.redisson.api.RedissonClient redisClient(){
    Config config = new Config();
    SingleServerConfig singleServerConfig = config.useSingleServer();
    String url = "redis://" + redisHost + ":" + redisPort;
    log.info("RedissonClient.redisClient - 初始化redisson[参数]{}", url);
    singleServerConfig.setAddress(url)
            .setPassword(password)
            .setDatabase(dataBase)
            .setConnectionMinimumIdleSize(1);;
    return org.redisson.Redisson.create(config);
}
```

xxl-job

md
```xml
<dependency>
    <groupId>com.xuxueli</groupId>
    <artifactId>xxl-job-core</artifactId>
    <version>2.3.0</version>
</dependency>
```

配置

md
```java
    @Bean
    public XxlJobSpringExecutor xxlJobExecutor() {
        if (!enable){
            return null;
        }
        log.info("init xxl-job..............");
        XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
        xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
        xxlJobSpringExecutor.setAppname(appName);
        xxlJobSpringExecutor.setPort(port);
        xxlJobSpringExecutor.setAccessToken(accessToken);
        xxlJobSpringExecutor.setLogPath(logPath);
        xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays);
        return xxlJobSpringExecutor;
    }
```

rabbitmq

md
```xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
```

dataSource

md
```xml
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
</dependency>
```

mail

md
```xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-mail</artifactId>
</dependency>
```

yml

md
```yml
spring:
    cloud:
        nacos:
            username: nacos
            password: nacos
            discovery:
                server-addr: ${concare.nacos-addr}
                service: ${spring.application.name}
    datasource:
        dynamic:
            p6spy: false
            lazy: true
            primary: master
            druid:
                initial-size: 2
                max-active: 8
                min-idle: 1
                max-wait: 1000
                validation-query: 'select 0'
                query-timeout: 30000
            datasource:
                master:
                    driver-class-name: com.mysql.cj.jdbc.Driver
                    url: jdbc:mysql://127.0.0.1:6306/cloud_cts?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
                    username: root
                    password: 
                slave_1:
                    driver-class-name: com.mysql.cj.jdbc.Driver
                    url: jdbc:mysql://127.0.0.1:6306/cloud_cts?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
                    username: root
                    password: 
    rabbitmq:
        username: user
        password: pwd
        host: 127.0.0.1
        port: 5672
        listener:
            simple:
                default-requeue-rejected: false
                acknowledge-mode: auto

    redis:
        host: 127.0.0.1
        password: 
        port: 6379
        database: 2
    servlet:
        multipart:
            max-file-size: 10485760
    mail:
        host: smtp.163.com
        username: 123456@163.com
        password: 24324324
        default-encoding: UTF-8
        properties:
          mail:
            smtp:
              auth: true
              starttls:
                enable: true
                required: true
              socketFactory:
                class: javax.net.ssl.SSLSocketFactory
        protocol: smtp
xxl-job:
  enable: false
  accessToken: 
  executor:
    appname: concare-tms #执行器名臣
    logpath: /data/applogs/tms/jobhandler
    logretentiondays: 10
    port: 9998 #当前应用绑定的xxl端口号
  admin:
    addresses: http://127.0.0.1:9000/xxl-job-admin #xxl-job服务器登录首页
logging.level.com.concare.cts.mapper: debug
```

邮箱发送例子

md
```java 
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage,true,"UTF-8");
helper.setFrom(sendName + "<" + sendEmail + ">");
helper.setSubject("平台账号开通成功");
String content = "<p style=\"line-height: 2;\">亲爱的老师,您好!<br> &nbsp; &nbsp; &nbsp;</p>";
content = content + "<p style=\"line-height: 2;\">扫描二维码跳转到小程序</p>";
content = content + "<br/><img src='https://target.png' style='length:400px;width:450px'/>";
content = content + "<br/><br/><img src='https://target.png' style='length:400px;width:450px'/>";
helper.setText(content,true);
// 读取LMS操作文件压缩包
String pdfPathSource= ResourceUtils.getFile(logoPath).getPath()+"/operate.pdf";
DataHandler operation = new DataHandler(new FileDataSource(pdfPathSource));
InputStream inputStream = operation.getInputStream();
ByteArrayResource resource = new ByteArrayResource(IOUtils.toByteArray(inputStream));
helper.addAttachment("操作手册.pdf", resource);
helper.setTo(targetEmail);
javaMailSender.send(mimeMessage);
```

阿里云OSS例子

引入依赖

md
<dependency>
    <groupId>com.aliyun.oss</groupId>
    <artifactId>aliyun-sdk-oss</artifactId>
    <version>3.8.0</version>
</dependency>

java列子

md
    public static void main(String[] args) throws Exception {
        System.setProperty(AuthUtils.ACCESS_KEY_SYSTEM_PROPERTY,"");
        System.setProperty(AuthUtils.SECRET_KEY_SYSTEM_PROPERTY,"");
        // Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。
        String endpoint = "https://oss-cn-shenzhen.aliyuncs.com";
        // 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
        SystemPropertiesCredentialsProvider credentialsProvider = CredentialsProviderFactory.newSystemPropertiesCredentialsProvider();
        // 填写Bucket名称,例如examplebucket。
        String bucketName = "lzh-files";
        // 填写Object完整路径,完整路径中不能包含Bucket名称,例如exampledir/exampleobject.txt。
        String objectName = "images/avatar.jpg";
        // 填写本地文件的完整路径,例如D:\\localpath\\examplefile.txt。
        // 如果未指定本地路径,则默认从示例程序所属项目对应本地路径中上传文件流。
        String filePath= "C:\\Users\\ThinkPad\\Desktop\\oss\\avatar.jpg";

        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, credentialsProvider);

        try {
            InputStream inputStream = new FileInputStream(filePath);
            // 创建PutObjectRequest对象。
            PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, objectName, inputStream);
            // 创建PutObject请求。
            PutObjectResult result = ossClient.putObject(putObjectRequest);
            ResponseMessage response = result.getResponse();
            System.out.println(response.getUri());
        } catch (OSSException oe) {
            System.out.println("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            System.out.println("Error Message:" + oe.getErrorMessage());
            System.out.println("Error Code:" + oe.getErrorCode());
            System.out.println("Request ID:" + oe.getRequestId());
            System.out.println("Host ID:" + oe.getHostId());
        } catch (ClientException ce) {
            System.out.println("Caught an ClientException, which means the client encountered "
                    + "a serious internal problem while trying to communicate with OSS, "
                    + "such as not being able to access the network.");
            System.out.println("Error Message:" + ce.getMessage());
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }

More

Check out the documentation for the full list of markdown extensions.