Spring的常见用法
1、spring常见实现加载xml配置的入口
Spring提供了ApplicationContext
接口的几种实现方式。在独立应用程序中,通常创建ClassPathXmlApplicationContext
或FileSystemXmlApplicationContext
的实例。
2、注入bean的方式
基于构造函数的依赖注入、基于 Setter
的依赖注入
- 1、xml中配置(构造函数实例化、静态工厂实例化、实例工厂方法实例化)
- 2、自动注入
- 3、方法注入(实现
ApplicationContextAware
接口)
3、自动装配的方式
自动装配的模式:
- no:xml配置
- byName:根据属性名称 (
@Resource
) - byType:根据属性类型(
@Autowired
) - constructor:根据构造函数匹配
4、bean的作用域
singleton
:只创建一个bean的实例prototype
:每个引用都创建一个bean的实例(例如:A类注入C类,B类也注入C类,C类一共创建了2个bean的实例)request
,session
,globalSession
,application
和websocket
范围仅在使用网络感知的 SpringApplicationContext
实现(例如XmlWebApplicationContext
)时可用。
5、bean初始化和销毁方法
5.1 初始化方法:
- 自定义初始化方法添加注解(
@PostConstruct
) - 实现
InitializingBean
回调接口定义的afterPropertiesSet()
- xml配置中自定义配置的
init()
方法(init-method="init"
)
5.2销毁方法:
- 自定义方法添加注解(
@PreDestroy
) - 实现
DisposableBean
回调接口定义的destroy()
- xml配置中自定义配置的
destroy()
方法(destroy-method="cleanup"
)
6、@Autowired
的用法
- 1、应用于构造函数
- 2、应用于具有任意名称和/或多个参数的方法
- 3、应用于字段,甚至将其与构造函数混合使用
- 4、应用于字段
- 5、添加到需要该类型数组的字段或方法中(如果希望数组或列表中的项按特定顺序则目标bean可实现
org.springframework.core.Ordered
或者接口或使用@Order
或标准@Priority
注解)
7、使用@Primary 微调基于 Comments 的自动装配
由于按类型自动布线可能会导致多个候选对象,因此通常有必要对选择过程进行更多控制。实现此目的的一种方法是使用 Spring 的@Primary
Comments。 @Primary
表示当多个 bean 可以自动连接到单值依赖项的候选对象时,应优先考虑特定的 bean。如果候选对象中仅存在一个“主” bean,它将是自动装配的值。
8、用限定符(@Qualifier
)微调基于 Comments 的自动装配
当可以确定一个主要候选对象时,@Primary
是在几种情况下按类型使用自动装配的有效方法。当需要对选择过程进行更多控制时,可以使用 Spring 的@Qualifier
Comments。您可以将限定符值与特定的参数相关联,从而缩小类型匹配的范围,以便为每个参数选择特定的 bean.
- 1、字段上指定
@Autowired
@Qualifier("main")
private MovieCatalog movieCatalog;
- 2、各个构造函数参数或方法参数上指定
private MovieCatalog movieCatalog;
private CustomerPreferenceDao customerPreferenceDao;
@Autowired
public void prepare(@Qualifier("main")MovieCatalog movieCatalog,
CustomerPreferenceDao customerPreferenceDao) {
this.movieCatalog = movieCatalog;
this.customerPreferenceDao = customerPreferenceDao;
}
- 3、自定义
自定义限定符 Comments。只需定义一个 Comments 并在定义中提供@Qualifier
Comments:
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Genre {
String value();
}
@Autowired
@Genre("Action")
private MovieCatalog actionCatalog;
private MovieCatalog comedyCatalog;
@Autowired
public void setComedyCatalog(@Genre("Comedy") MovieCatalog comedyCatalog) {
this.comedyCatalog = comedyCatalog;
}
9、@Resource
@Resource
具有名称属性,默认情况下,Spring 将该值解释为要注入的 Bean 名称。换句话说,它遵循* by-name *语义.
在未指定@Resource
且未指定显式名称且与@Autowired
类似的特殊情况下,@Resource
查找主类型匹配而不是特定的命名 Bean,并解析众所周知的可解决依赖项:BeanFactory
,ApplicationContext
,ResourceLoader
,ApplicationEventPublisher
和MessageSource
接口。
-
应用于字段
@Resource private MovieFinder movieFinder;
-
setter 方法中注入名称为“ movieFinder”的 bean
private MovieFinder movieFinder;
@Resource
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
10、@PostConstruct 和@PreDestroy
缓存将在初始化时预先填充,并在销毁时清除(可用于项目启动完成加载缓存数据,和项目关闭是使用JVM关闭钩子关闭资源,如:zk连接等)
@PostConstruct
public void populateMovieCache() {
// populates the movie cache upon initialization...
}
@PreDestroy
public void clearMovieCache() {
// clears the movie cache upon destruction...
}
11、@Component相关注解
@Repository
,@Service
和@Controller
:分别在持久层,服务层和表示层中。
12、使用 JSR 330 标准 Comments
maven依赖
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
12.1 使用@Inject 和@Named 进行依赖注入
代替@Autowired
,可以使用@javax.inject.Inject
如下:
import javax.inject.Inject;
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Inject
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
public void listMovies() {
this.movieFinder.findMovies(...);
...
}
}
与@Autowired
一样,可以在字段级别,方法级别和构造函数参数级别使用@Inject
。此外,您可以将注入点声明为Provider
,以允许按需访问范围更短的 bean,或者通过Provider.get()
调用来懒惰地访问其他 bean。作为上述示例的变体:
import javax.inject.Inject;
import javax.inject.Provider;
public class SimpleMovieLister {
private Provider<MovieFinder> movieFinder;
@Inject
public void setMovieFinder(Provider<MovieFinder> movieFinder) {
this.movieFinder = movieFinder;
}
public void listMovies() {
this.movieFinder.get().findMovies(...);
...
}
}
如果要为应该注入的依赖项使用限定名称,则应使用@Named
注解,如下所示:
import javax.inject.Inject;
import javax.inject.Named;
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Inject
public void setMovieFinder(@Named("main") MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}
像@Autowired
一样,@Inject
也可以与java.util.Optional
或@Nullable
一起使用。这在这里更加适用,因为@Inject
没有required
属性。
public class SimpleMovieLister {
@Inject
public void setMovieFinder(Optional<MovieFinder> movieFinder) {
...
}
}
public class SimpleMovieLister {
@Inject
public void setMovieFinder(@Nullable MovieFinder movieFinder) {
...
}
}
12.2 @Named 和@ManagedBean:@ComponentComments 的标准等效项
代替@Component
,可以按以下方式使用@javax.inject.Named
或javax.annotation.ManagedBean
:
import javax.inject.Inject;
import javax.inject.Named;
@Named("movieListener") // @ManagedBean("movieListener") could be used as well
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Inject
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}
在不指定组件名称的情况下使用@Component
是很常见的。 @Named
可以类似的方式使用:
import javax.inject.Inject;
import javax.inject.Named;
@Named
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Inject
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}
使用@Named
或@ManagedBean
时,可以使用与使用 SpringComments 完全相同的方式来使用组件扫描:
@Configuration
@ComponentScan(basePackages = "org.example")
public class AppConfig {
...
}
与
@Component
相反,JSR-330@Named
和 JSR-250ManagedBean
Comments 是不可组合的。请使用 Spring 的构造型模型来构建自定义组件 Comments。
12.3 JSR-330 标准 Comments 的局限性
使用标准 Comments 时,重要的是要知道某些重要功能不可用,如下表所示:
表 7.6. Spring 组件模型元素与 JSR-330 变体
Spring | javax.inject.* | javax.inject 限制/Comments |
---|---|---|
@Autowired | @Inject | @Inject 没有“必需”属性;可以与 Java 8 的Optional 一起使用。 |
@Component | @Named/@ManagedBean | JSR-330 没有提供可组合的模型,只是一种识别命名组件的方法。 |
@Scope(“singleton”) | @Singleton | JSR-330 的默认范围类似于 Spring 的prototype 。但是,为了使其与 Spring 的常规默认设置保持一致,默认情况下,在 Spring 容器中声明的 JSR-330 bean 为singleton 。为了使用singleton 以外的范围,您应该使用 Spring 的@Scope 注解。 javax.inject 还提供@ScopeComments。但是,此仅用于创建自己的 Comments。 |
@Qualifier | @ Qualifier/@ Named | javax.inject.Qualifier 只是用于构建自定义限定符的元 Comments。可以通过javax.inject.Named 来关联具体的字符串限定符(例如带有值的 Spring 的@Qualifier )。 |
@Value | - | no equivalent |
@Required | - | no equivalent |
@Lazy | - | no equivalent |
ObjectFactory | Provider | javax.inject.Provider 是 Spring 的ObjectFactory 的直接替代方法,只是方法名get() 较短。它也可以与 Spring 的@Autowired 或未 Comments 的构造函数和 setter 方法结合使用。 |
13、Spring 类型转换
Spring 3 引入了core.convert
软件包,该软件包提供了常规的类型转换系统。系统定义了一个用于实现类型转换逻辑的 SPI,以及一个用于在运行时执行类型转换的 API。在 Spring 容器中,此系统可以用作 PropertyEditor 的替代方案,以将外部化的 bean 属性值字符串转换为所需的属性类型。公共 API 也可以在应用程序中需要类型转换的任何地方使用。
13.1 转换器 SPI
实现类型转换逻辑的 SPI 很简单且类型很严格:
package org.springframework.core.convert.converter;
public interface Converter<S, T> {
T convert(S source);
}
要创建自己的转换器,只需实现上面的接口。将S
设置为要转换的类型,并将T
设置为要转换的类型。如果需要将S
的集合或数组转换为T
的数组或集合,则也可以透明地应用此类转换器,前提是还已委派了委派的数组/集合转换器(默认情况下DefaultConversionService
这样做)。
对于每次对convert(S)
的调用,保证源参数不为 null。如果转换失败,您的 Converter 可能会抛出任何未经检查的异常;具体来说,应抛出IllegalArgumentException
以报告无效的源值。注意确保Converter
实现是线程安全的。
为方便起见,在core.convert.support
包中提供了几种转换器实现。这些包括从字符串到数字和其他常见类型的转换器。以StringToInteger
为例,说明典型的Converter
实现:
package org.springframework.core.convert.support;
final class StringToInteger implements Converter<String, Integer> {
public Integer convert(String source) {
return Integer.valueOf(source);
}
13.2 ConverterFactory
当您需要集中整个类层次结构的转换逻辑时,例如,当从 String 转换为 java.lang.Enum 对象时,请实现ConverterFactory
:
package org.springframework.core.convert.converter;
public interface ConverterFactory<S, R> {
<T extends R> Converter<S, T> getConverter(Class<T> targetType);
}
参数化 S 为您要转换的类型,参数化 R 为定义可以转换为的类的“范围”的基本类型。然后实现 getConverter(Class),其中 T 是 R 的子类。
以StringToEnum
ConverterFactory 为例:
package org.springframework.core.convert.support;
final class StringToEnumConverterFactory implements ConverterFactory<String, Enum> {
public <T extends Enum> Converter<String, T> getConverter(Class<T> targetType) {
return new StringToEnumConverter(targetType);
}
private final class StringToEnumConverter<T extends Enum> implements Converter<String, T> {
private Class<T> enumType;
public StringToEnumConverter(Class<T> enumType) {
this.enumType = enumType;
}
public T convert(String source) {
return (T) Enum.valueOf(this.enumType, source.trim());
}
}
}
14、配置全局日期和时间格式
默认情况下,未使用@DateTimeFormat
Comments 的日期和时间字段是使用DateFormat.SHORT
样式从字符串转换的。如果愿意,可以通过定义自己的全局格式来更改此设置。
您将需要确保 Spring 不注册默认格式器,而应该手动注册所有格式器。根据使用的是 Joda-Time 库,使用org.springframework.format.datetime.joda.JodaTimeFormatterRegistrar
或org.springframework.format.datetime.DateFormatterRegistrar
类。
例如,以下 Java 配置将注册全局’`yyyyMMdd’格式。此示例不依赖于 Joda-Time 库:
@Configuration
public class AppConfig {
@Bean
public FormattingConversionService conversionService() {
// Use the DefaultFormattingConversionService but do not register defaults
DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService(false);
// Ensure @NumberFormat is still supported
conversionService.addFormatterForFieldAnnotation(new NumberFormatAnnotationFormatterFactory());
// Register date conversion with a specific global format
DateFormatterRegistrar registrar = new DateFormatterRegistrar();
registrar.setFormatter(new DateFormatter("yyyyMMdd"));
registrar.registerFormatters(conversionService);
return conversionService;
}
}
15、应用程序属性文件
SpringApplication
将从以下位置的application.properties
文件中加载属性,并将它们添加到 Spring Environment
中:
- 当前目录的
/config
子目录。 - 当前目录
- Classpath
/config
包 - Classpath 根
该列表按优先级排序(在列表较高位置定义的属性会覆盖在较低位置定义的属性)。
16、CORS 支持(跨域请求)
解决方案:
- 可以通过使用自定义的
addCorsMappings(CorsRegistry)
方法注册WebMvcConfigurer
bean 来定义全局 CORS 配置:
@Configuration
public class MyConfiguration {
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://localhost:8081")
.allowedMethods("*")
.allowedHeaders("*");
}
};
}
}
/** 表示本应用的所有方法都会去处理跨域请求, allowedMethods 表示允许通过的请求数,allowedHeaders 则表示允许的请求头。经过这样的配置之后,就不必在每个方法上单独配置跨域了。
存在的问题
了解了整个 CORS 的工作过程之后,我们通过 Ajax 发送跨域请求,虽然用户体验提高了,但是也有潜在的威胁存在,常见的就是 CSRF(Cross-site request forgery)跨站请求伪造。跨站请求伪造也被称为 one-click attack 或者 session riding,通常缩写为 CSRF 或者 XSRF ,是一种挟制用户在当前已登录的 Web 应用程序上执行非本意的操作的攻击方法
于此,浏览器在实际操作中,会对请求进行分类,分为简单请求,预先请求,带凭证的请求等,预先请求会首先发送一个 options 探测请求,和浏览器进行协商是否接受请求。默认情况下跨域请求是不需要凭证的,但是服务端可以配置要求客户端提供凭证,这样就可以有效避免 csrf 攻击。
- 添加 Filter 的方式,配置 CORS 规则,并手动指定对哪些接口有效
/**
* 配置跨域访问的过滤器
* @return
*/
@Bean
public FilterRegistrationBean registerFilter(){
FilterRegistrationBean bean = new FilterRegistrationBean();
bean.addUrlPatterns("/*");
bean.setFilter(new CrosFilter());
return bean;
}
import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class CrosFilter implements javax.servlet.Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletResponse res = (HttpServletResponse) servletResponse;
//*号表示对所有请求都允许跨域访问
res.addHeader("Access-Control-Allow-Origin", "*");
res.addHeader("Access-Control-Allow-Methods", "*");
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
}
}
- 使用
@CrossOrigin
注解实现
//类或方法上添加注解
@CrossOrigin(origins = {"http://localhost:9000", "null"})
- 通过Nginx配置转发解决跨域
server {
listen 80;
server_name xxx.com;
#charset koi8-r;
#access_log logs/host.access.log main;
location /client { #访问客户端路径
proxy_pass http://localhost:81;
proxy_redirect default;
}
location /apis { #访问服务器路径
rewrite ^/apis/(.*)$ /$1 break;
proxy_pass http://localhost:82;
}
}
17、配置 SSL
以通过设置各种server.ssl.*
属性(通常在application.properties
或application.yml
中)来声明性地配置 SSL。例如:
server.port=8443
server.ssl.key-store=classpath:keystore.jks
server.ssl.key-store-password=secret
server.ssl.key-password=another-secret
18、使用 Jetty 代替 Tomcat
Maven 中的示例:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
19、启用 HTTP 响应压缩
Jetty,Tomcat 和 Undertow 支持 HTTP 响应压缩。可以通过application.properties
启用它:
server.compression.enabled=true
默认情况下,响应的长度必须至少为 2048 个字节才能执行压缩。可以使用server.compression.min-response-size
属性进行配置。
默认情况下,仅当响应的 Content Type 为以下之一时,才会压缩它们:
text/html
text/xml
text/plain
text/css
可以使用server.compression.mime-types
属性进行配置。
20、项目启动时初始化资源
- 实现CommandLineRunner接口(多个使用@Order注解来实现资源加载的先后顺序,值越小,优先级越高)
- 实现ApplicationRunner接口(多个使用@Order注解来实现资源加载的先后顺序,值越小,优先级越高)
- 使用注解@PostConstruct
@PostConstruct使用特点如下:
- 只有一个非静态方法能使用此注解
- 被注解的方法不得有任何参数
- 被注解的方法返回值必须为void
- 被注解方法不得抛出已检查异常
- 此方法只会被执行一次
21、session 共享
项目中添加maven依赖:Spring Session 以及 Redis,如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
配置文件中配置redis即可