spring的使用技巧一

一、spring事件机制的使用

spring中的事件是一种发布-订阅模式,允许组件监听和响应特定的事件,也可以当作观察者模式的一种。其可以用来异步操作,通知,日志记录等。其主要接口和类如下:

ApplicationEvent:事件对象。通过该类,可以自定义需要监听的事件。

ApplicationListener:事件监听类,持有事件对象。通过该类,可以自定义事件的处理

ApplicationEventPublisher:事件发布者类。通过该类,可以在需要的地方触发特定事件

ApplicationContext:事件发布类,发布到applicationContext,applicationContext传递个特定的事件监听类。

其异步记录日志demo如下:

typescript 复制代码
// 日志事件对象
public class LogEventDemo extends ApplicationEvent {

	private String logMessage;

	public LogEventDemo(Object source, String logMessage) {
		super(source);
		this.logMessage = logMessage;
	}

	public String getLogMessage() {
		return logMessage;
	}
}
// 日志事件监听类,需要spring进行管理,除此之外,也可以通过@EventListener注解(方法)来实现
public class LogEventListener implements ApplicationListener<LogEventDemo> {

	ThreadPoolExecutor pool = new ThreadPoolExecutor(2,4,2, TimeUnit.SECONDS,new ArrayBlockingQueue<>(3));

	@Override
	public void onApplicationEvent(LogEventDemo event) {
		// 线程池中活跃的线程数量
		System.out.println("activeCount:"+pool.getActiveCount());
		// 线程池中已经装载的最大数量
		System.out.println("largestPoolSize:"+pool.getLargestPoolSize());
		BlockingQueue<Runnable> queue = pool.getQueue();
		System.out.println("size:"+queue.size());
		// 线程池可容纳的最大数量=maximunPoolSize+阻塞队列的容量
		pool.execute(()->{
			try {
				TimeUnit.SECONDS.sleep(1);
				System.out.println(Thread.currentThread().getName()+"日志打印:"+event.getLogMessage());
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		});
	}
}
// 事件发布者类,需要spring管理
public class Logger {
	@Autowired
	private ApplicationEventPublisher publisher;

	public void log(String message) {
		// create and publisher event
		LogEventDemo logEvent = new LogEventDemo(this, message);
		publisher.publishEvent(logEvent);
	}
}
当需要打印日志时,通过调用logger.log()即可实现

二、文件资源获取:Resource

spring中对文件资源的操作都封装在了Resource类中,可根据此类,自定义文件资源的获取。常用的有一个工具类ResourceUtils,读取文件。其用法如下:

ini 复制代码
// 获取当前项目下的文件
File file = ResourceUtils.getFile("config/1.txt");
System.out.println(file.getAbsolutePath());
if(!file.exists()) {
		// 获取类路径下的文件
		file = ResourceUtils.getFile("classpath:1.txt");
	}

三、@Scheduled定时任务注解

如何动态传入cron表达式:在配置文件中定义,然后在注解当中使用 @scheduled(cron="${属性名:-}"),此表示如果表达式的值为空则不启用任务

四、ServletRequest的使用

前言: servletRequest对象在调用getInputStream()方法后,不能重复使用的问题。这和inputStream的流的读取有关,其内部通过指针实现,在读取完毕后指针并不会rest。

1、数据封装原理(实际使用都是经过springmvc封装好的):

css 复制代码
1.1:post请求,传参形式为 form-data:直接用实体类,或者在controller上用参数接收,手动可以通过request.getParameter()
 
 1.2:post请求,传参形式为 json时,其需要在实体类之前添加@RequestBody注解或者使用request.getInputStream()获取流数据

2、springmvc的封装

scss 复制代码
 2.1 对于get请求,不做处理,一般参数在url后面
 
 2.2 对于post/put请求,发送 form-data数据,会将其封装成一个StandardMultipartHttpServletReques对象,调用parseRequest(request)对象,对InputStream流进一步封装
 
 2.3 对于post/put请求,发送 application/x-www-form-urlencoded 格式的数据时,会在第一次调用getParameter()或getParameterNames()时,从Inpustream流中读取数据,将其封装到map中
 
 2.4 对于post/put请求,对于发送json格式数据,则会调用getInputStream()或者getReader()获取

3、解决方案

实现Filter接口,将流拷贝一份,即可实现重复使用,在spring项目中通过继承HttpServletRequestWrapper,重写getInputStream()和getReader()方法即可。

第一步:自定义request包装类,继承HttpServletRequestWrapper

第二步:将自定义的包装类,添加到filter链中,将其设为优先级最高的

具体实现代码为如下: 复制代码
自定义包装类:
public class CustomHttpRequestWrapper extends HttpServletRequestWrapper {

    byte[] b;

    public CustomHttpRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        b = StreamUtils.copyToByteArray(request.getInputStream());
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        ByteArrayInputStream bis = new ByteArrayInputStream(b);
        return new ServletInputStream() {
            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener readListener) {

            }

            @Override
            public int read() throws IOException {
                return bis.read();
            }
        };
    }
}
添加到拦截器链中:
@Component
@Order(-1)
public class CustomFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        HttpServletRequest request = new CustomHttpRequestWrapper(httpServletRequest);
        filterChain.doFilter(request,httpServletResponse);
    }
}

五、Graceful Response的使用

graceful Response会自动对响应结果进行封装

源码地址:github.com/feiniaojin/...

文档地址:doc.feiniaojin.com/graceful-re...

maven引入(以springboot2为例,如果是springboot3将版本改为3.2.0-boot3即可):

xml 复制代码
 <dependency>
            <groupId>com.feiniaojin</groupId>
            <artifactId>graceful-response</artifactId>
            <version>3.2.0-boot2</version>
 </dependency>

六、断言

对于一些常见的判断,可以通过断言来实现,而不用手动判断,断言类为org.springframework.util.Assert;

七、springboot应用监控 actuator

一、引入:

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
    <version>2.0.1.RELEASE</version>
</dependency>

二、在配置文件中配置相对应的信息

yaml 复制代码
management:
  endpoints:
    web:
      exposure:
        include: [beans,health,env,info,mappings,httptrace,metrics,scheduledtasks,loggers,] #开放的端点
    enabled-by-default: true # false关闭所有端点暴露
  endpoint:
    health:
      show-details: always #此开关不仅可以展示springboot应用,还可以展示第三方应用的健康信息
      
浏览器访问方式:ip:port/actuator/端点名,该种方式有可能造成信息的泄漏,强烈建议对该url的访问进行验证

八、获取springboot项目启动后的pid

springboot中使用自带的ApplicationPidFileWriter类,可将pid持久化到文件当中,本身不开启,需要手动添加

一、使用方式如下

ini 复制代码
SpringApplication application = new SpringApplication(StartMain.class);
//将项目启动后的进程pid持久化
application.addListeners(new ApplicationPidFileWriter());
application.run(args);

然后再配置文件中配置文件的位置和名称: spring.pid.file:文件位置和名称,
默认是application.pid,默认与jar包统一目录

二、根据pid文件,进行项目的启停脚本

bash 复制代码
启动脚本:
nohup java -jar jar包名 > log.out 2>&1 &

停止脚本:
kill -9 $(cat pid文件名称)
echo 'success'
# 删除pid文件
rm -rf pid文件名称

九、springboot打包pom常用配置

xml 复制代码
 <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                 <artifactId>spring-boot-maven-plugin</artifactId>
                 <executions>
                     <execution>
                         <!--重新打包,剔除其他依赖-->
                         <goals>
                             <goal>repackage</goal>
                         </goals>
                     </execution>
                 </executions>
                <configuration>
                    <!--打包后的路径,当前根路径下-->
                    <outputDirectory>${basedir}/${project.artifactId}</outputDirectory>
                    <!--打包后的包名-->
                    <finalName>springboot-demo2</finalName>
                    <!--打包类型,可忽略,类型为大写 ZIP JAR WAR-->
<!--                    <layout>JAR</layout>-->
                </configuration>
            </plugin>
             <!--apache打包资源的插件-->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-resources-plugin</artifactId>
<!--                <version>3.2.0</version>-->
                <executions>
                    <execution>
                        <!--拷贝资源文件-->
                        <id>copy-resources</id>
                        <phase>package</phase>
                        <goals>
                            <goal>copy-resources</goal>
                        </goals>
                        <configuration>
                            <!--输出文件到指定目录,比如我这里是config目录-->
                            <outputDirectory>${basedir}/${project.artifactId}/config/
                            </outputDirectory>
                            <resources>
                                <!--可包含多个resource,里面可以指定需要拷贝的资源目录,然后可以进行包含或者排除操作,最终放到上面指定的目录-->
                                <resource>
                                    <!--需要拷贝资源文件的目录-->
                                    <directory>src/main/resources/</directory>
                                    <!--包含文件,也可以使用排除excludes-->
                                    <includes>
                                        <include>**/application.properties</include>
                                        <include>**/application.yml</include>
                                        <include>**/logback.xml</include>
                                    </includes>
                                </resource>
                            </resources>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

        </plugins>
    </build>

十、对配置文件中的明文进行加密,解密

使用jasypt工具类可以进行配置文件中信息的加密,解密

使用方法如下:

yaml 复制代码
1、引入jasypt jar包
 <dependency>
            <groupId>com.github.ulisesbocchio</groupId>
            <artifactId>jasypt-spring-boot-starter</artifactId>
            <version>3.0.5</version>
 </dependency>
2、 在配置文件中配置相关信息:
 jasypt:
  encryptor:
    #秘钥
    password: 1qaz2wsx!@#
    property:
      #密文前缀
      prefix: ENC( #默认值
      #密文后缀
      suffix: ) #默认值
    # bean: jasyptStrEncryptor #指定加解密的类
 
 其中秘钥可以不放在配置文件中,而是在启动jar包是指定,其方式如下:
 java -jar xxx.jar --jasypt.encryptor.password=秘钥

如果jdk为1.8且启动时如果提示以下错误: Encryption raised an exception. A possible cause is you are using strong encryption algorithms and you have not installed the Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files in this Java Virtual Machine

解决方案: 这是由于jdk中的安全认证包不支持,可通过替换新的即可,其地址如下: www.oracle.com/technetwork...

需要替换的jar包位置为:jre\lib\security\

相关推荐
码上一元2 小时前
SpringBoot自动装配原理解析
java·spring boot·后端
枫叶_v4 小时前
【SpringBoot】22 Txt、Csv文件的读取和写入
java·spring boot·后端
杜杜的man5 小时前
【go从零单排】Closing Channels通道关闭、Range over Channels
开发语言·后端·golang
java小吕布6 小时前
Java中Properties的使用详解
java·开发语言·后端
2401_857610036 小时前
Spring Boot框架:电商系统的技术优势
java·spring boot·后端
杨哥带你写代码8 小时前
网上商城系统:Spring Boot框架的实现
java·spring boot·后端
camellias_8 小时前
SpringBoot(二十一)SpringBoot自定义CURL请求类
java·spring boot·后端
背水9 小时前
初识Spring
java·后端·spring
晴天飛 雪9 小时前
Spring Boot MySQL 分库分表
spring boot·后端·mysql
weixin_537590459 小时前
《Spring boot从入门到实战》第七章习题答案
数据库·spring boot·后端