《Spring实战》读书笔记-第3章 高级装配,最新Java大厂高频面试题
<jee:jndi-lookup id=“dataSource”
lazy-init=“true”
jndi-name=“jdbc/myDatabase”
resource-ref=“true”
proxy-interface=“javax.sql.DataSource” />
下一步就是激活某个profile
Spring在确定哪个profile处于激活状态时,需要依赖两个独立的属性:spring.profiles.active和spring.profiles.default。如果设置了spring.profiles.active属性的话,那么它的值就会用来确定哪个profile是激活的,但如果没有设置spring.profiles.active属性的话,那Spring将会查找spring.profiles.default的值。如果spring.profiles.active和spring.profiles.default均没有设置的话,那就没有激活的profile,因此只会创建那些没有定义在profile中的bean。
有多种方式来设置这两个属性:
作为DispatcherServlet的初始化参数;
作为Web应用的上下文参数;
作为JNDI条目;
作为环境变量;
作为JVM的系统属性;
在集成测试类上,使用@ActiveProfiles注解设置。
例如,在web应用中,设置spring.profiles.default的web.xml文件会如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version=“2.5”
xmlns=“http://java.sun.com/xml/ns/javaee”
xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance”
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
contextConfigLocation
/WEB-INF/spring/root-context.xml
spring.profiles.default
dev
org.springframework.web.context.ContextLoaderListener
appServlet
org.springframework.web.servlet.DispatcherServlet
spring.profiles.default
dev
1
appServlet
/
3.2 条件化的bean
Spring4实现了条件化配置,需要引入@Conditional(可以用到带有@bean注解的方法上)注解。如果给定条件为true,则创建这个bean,反之,不创建。
例如:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MagicConfig {
@Bean
@Conditional(MagicExistsCondition.class) // 条件化创建bean
public MagicBean magicBean() {
return new MagicBean();
}
}
@Conditional中给定了一个Class,它指明了条件——本例中是MagicExistsCondition。@Conditional将会通过Condition接口进行条件对比:
public interface Condition {
boolean matches(ConditionContext ctxt AnnotatedTypeMetadata metadata);
}
接下来是MagicExistsCondition的实现类:
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;
public class MagicExistsCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment env = context.getEnvironment();
// 根据环境中是否存在magic属性来决策是否创建MagicBean
return env.containsProperty(“magic”);
}
}
ConditionContext是一个接口,大致如下所示:
public interface ConditionContext {
BeanDefinitionRegistry getRegistry();
ConfigurableListableBeanFactory getBeanFactory();
Environment getEnvironment();
ResourceLoader getResourceLoader();
ClassLoader getClassLoader();
}
ConditionContext实现的考量因素可能会更多,通过ConditionContext,我们可以做到如下几点:
借助getRegistry() 返回的BeanDefinitionRegistry检查bean定义;
借助getBeanFactory() 返回的ConfigurableListableBeanFactory检查bean是否存在,甚至探查bean的属性;
借助getEnvironment() 返回的Environment检查环境变量是否存在以及它的值是什么;
读取并探查getResourceLoader() 返回的ResourceLoader所加载的资源。
借助getClassLoader() 返回的ClassLoader加载并检查类是否存在。
AnnotatedTypeMetadata则能够让我们检查带有@Bean注解的方法上还有什么其他的注解,它也是一个接口,如下所示:
public interface AnnotatedTypeMeta {
boolean isAnnotated(String annotationType);
Map<String, Object> getAnnotationAttributes(String annotationType);
Map<String, Object> getAnnotationAttributes(String annotationType, boolean classValuesAsString);
MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationType);
MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationType, boolean classValuesAsString);
}
借助isAnnotated()方法,能够判断带有@Bean注解的方法是不是还有其他特定的注解。
3.3 处理自动装配的歧义性
当自动装配bean时,遇到多个实现类的情况下,就出现了歧义,例如:
@Autowired
public void setDessert(Dessert dessert) {
this.dessert = dessert;
}
Dessert是一个接口,并且有三个类实现了这个接口,如下所示:
@Component
public class Cake implements Dessert { … }
@Component
public class Cookies implements Dessert { … }
@Component
public class IceCream implements Dessert { … }
三个实现均使用了@Component,在组件扫描时,能够创建它们的bean。但Spring试图自动装配setDessert()中的Dessert参数是,它并没有唯一、无歧义的可选值,Spring无法做出选择,则会抛出NoUniqueBeanDefinitionException的异常。
两种解决办法:
第一种方法:标示首选的bean
如下所示:
@Component
@Primary
public class IceCream implements Dessert { … }
或者,如果通过JavaConfig配置,如下:
@Bean
@Primary
public Dessert iceCream() {
return new IceCream();
}
或者,使用XML配置bean的话,如下:
需要注意的是:不能标示两个或更多的首选bean,这样会引来新的歧义。
第二种方法:限定自动装配的bean
如下所示:
@Autowired
@Qualifier(“iceCream”)
public void setDessert(Dessert dessert) {
this.dessert = dessert;
}
如果不想用默认的bean的名称,也可以创建自定义的限定符
@Component
@Qualifier(“cold”)
public class IceCream implements Dessert { … }
@Autowired
@Qualifier(“cold”)
public void setDessert(Dessert dessert) {
this.dessert = dessert;
}
或者使用JavaConfig配置
@Bean
@Qualifier(“cold”)
public Dessert iceCream() {
return new IceCream();
}
如果出现多个Qualifier,尝试为bean也标示多个不同的Qualifier来表明要注入的bean。
@Component
@Qualifier(“cold”)
@Qualifier(“creamy”)
public class IceCream implements Dessert { … }
@Component
@Qualifier(“cold”)
@Qualifier(“fruity”)
public class Popsicle implements Dessert { … }
@Autowired
@Qualifier(“cold”)
@Qualifier(“creamy”)
public void setDessert(Dessert dessert) {
this.dessert = dessert;
}
但有个问题,Java不允许在同一个条目上重复出现相同类型的注解,编译器会提示错误。
解决办法是我们可以自定义注解:
@Targe({ElementType.CONSTRUCTOR, ElementType.FIELD,
ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Cold { }
@Targe({ElementType.CONSTRUCTOR, ElementType.FIELD,
ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Creamy { }
@Targe({ElementType.CONSTRUCTOR, ElementType.FIELD,
ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Fruity { }
重新标注IceCream
@Component
@Cold
@Creamy
public class IceCream implements Dessert { … }
@Component
@Cold
@Fruity
public class Popsicle implements Dessert { … }
注入setDessert() 方法
@Autowired
@Cold
@Creamy
public void setDessert(Dessert dessert) {
this.dessert = dessert;
}
3.4 Bean的作用域
默认情况下,Spring应用上下文所有bean都是作为以单例的形式创建的。
Spring定义了多种作用域,可以基于这些作用域创建bean,包括:
单例(Singleton):在整个应用中,只创建bean的一个实例。
原型(Prototype):每次注入或者通过Spring应用上下文获取的时候,都会创建一个新的bean实例。
会话(Session):在Web应用中,为每个会话创建一个bean实例。
请求(Request):在Web应用中,为每个请求创建一个bean实例。
例如,如果你使用组件扫描,可以在bean的类上使用@Scope注解,将其声明为原型bean:
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class Notepad { … }
或者在JavaConfig上声明:
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Notepad notepad {
return new Notepad();
}
或者在XML上声明:
在web应用中,如果能够实例化在会话和请求范围内共享bean,那将很有价值。例如:电子商务的购物车,会话作用域最为适合。
@Component
@Scope(value=WebApplicationContext.SCOPE_SESSION,
proxyMode=ScopedProxyMode.TARGET_CLASS)
public class ShoppingCart { … }
注入一个服务类
@Component
public class StoreService {
@Autowired
public void setShoppingCart (ShoppingCart shoppingCart) {
this.shoppingCart = shoppingCart;
}
}
因为StoreService是一个单例的bean,会在Spring应用上下文加载的时候创建。当它创建的时候,Spring会试图将ShoppingCart bean注入到setShoppingCart() 方法中。但是ShoppingCart bean是会话作用域的,此时不存在。直到某个用户进入系统,创建了会话之后,才会出现ShoppingCart实例。
另外,系统中将会有多个ShoppingCart实例:每个用户一个。我们并不想让Spring注入某个固定的ShoppingCart实例到StoreService中。我们希望的是当StoreService处理购物车功能时,它所用的ShoppingCart实例恰好是当前会话所对应的那一个。
Spring并不会将实际的ShoppingCart bean注入到StoreService中,Spring会注入一个到ShoppingCart bean的代理,如下图。这个代理会暴露与ShoppingCart相同的方法,所以StoreService会认为它就是一个购物车。但是,当StoreService调用ShoppingCart的方法时,代理会对其进行懒解析并将调用委托给会话作用域内真正的ShoppingCart bean。
如果ShoppingCart是接口而不是类的话,就用ScopedProxyMode.TARGET_INTERFACES(用JDK的代理)。如果是类而不是接口,就必须使用CGLib来生成基于类的代理,所以要用ScopedProxyMode.TARGET_CLASS。
请求的作用域原理与会话作用域原理一样。
作用域代理能够延迟注入请求和会话作用域的bean
也可用XML配置
<aop:scoped-proxy />
<aop:scoped-proxy />
是与@Scope注解的proxy属性功能相同的SpringXML配置元素。它会告诉Spring为bean创建一个作用域代理。默认情况下,它会使用CGLib创建目标类的代理。我们也可以将proxy-targe-class属性设置为false,进而要求生成基于接口的代理:
<bean id=“cart”
class=“com.myapp.ShoppingCart”
scope=“session” >
<aop:scoped-proxy proxy-targe-class=“false”/>
3.5 运行时植注入
我们之前在javaConfig配置中,配置了BlankDisc:
@Bean
public CompactDisc sgtPeppers() {
return new BlankDisc (
“Sgt. Pepper’s Lonely Hearts Club Band”,
“The Beatles”
);
}
这种硬编码实现了要求,但有时我们希望避免,而是想让这些值在运行时再确定。为了实现这些功能,Spring提供了两种在运行时求值的方式:
属性占位符 (Property placeholder)。
Spring表达式语言(SpEL)。
在Spring中,最简单的方式就是声明属性源并通过Spring的Environment来检索属性。
package com.springinaction;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
@Configuration
@ComponentScan(“com.springinaction”)
@PropertySource(“app.properties”)
public class AppConfig {
@Autowired
Environment environment;
@Bean
public BlankDisc disc(){
return new BlankDisc(
environment.getProperty(“disc.title”),
environment.getProperty(“disc.artist”));
}
}
在本例中,@PropertySource引用了类路径中一个名为app.properties的文件,如下所示:
disc.title=Sgt. Peppers Lonely Hearts Club Band
disc.artist=The Beatles
Environment中getProperty有四个重载方法:
String getProperty(String key);
String getProperty(String key, String defaultValue);
T getProperty(String key, Class type);
T getProperty(String key, Class type, T defaultValue);
第二个方法与第一个的差别就是有了默认值。
第三、四个方法不会将所有值视为String,可以转换为别的类型,如
int connectionCount = env.getProperty(“db.connection.count”, Integer.class, 30);
其他方法还有
// 如果key没有,则抛出IllegalStateException异常
String getRequiredProperty(String key);
// 检查key的value是否存在
boolean containsProperty(String key)
// 将属性解析为类
Class getPropertyAsClass(String key, Class type);
// 返回激活profile名称的数组
String[] getActiveProfiles();
// 返回默认profile名称的数组
String[] getDefaultProfiles()
// 如果environment支持给定profile的话,就返回true
boolean acceptsProfiles(String… profiles)
我们还可以用属性占位符来注入,占位符的形式为使用“${ … }”包装的属性名称。
<bean id=“sgtPeppers” class=“soundsystem.BlankDisc”
c:_title="${disc.title}"
c:_artist="${disc.artist}" />
ey);
String getProperty(String key, String defaultValue);
T getProperty(String key, Class type);
T getProperty(String key, Class type, T defaultValue);
第二个方法与第一个的差别就是有了默认值。
第三、四个方法不会将所有值视为String,可以转换为别的类型,如
int connectionCount = env.getProperty(“db.connection.count”, Integer.class, 30);
其他方法还有
// 如果key没有,则抛出IllegalStateException异常
String getRequiredProperty(String key);
// 检查key的value是否存在
boolean containsProperty(String key)
// 将属性解析为类
Class getPropertyAsClass(String key, Class type);
// 返回激活profile名称的数组
String[] getActiveProfiles();
// 返回默认profile名称的数组
String[] getDefaultProfiles()
// 如果environment支持给定profile的话,就返回true
boolean acceptsProfiles(String… profiles)
我们还可以用属性占位符来注入,占位符的形式为使用“${ … }”包装的属性名称。
<bean id=“sgtPeppers” class=“soundsystem.BlankDisc”
c:_title="${disc.title}"
c:_artist="${disc.artist}" />
[外链图片转存中…(img-Rn8ROGwo-1635180696930)]
《Spring实战》读书笔记-第3章 高级装配,最新Java大厂高频面试题相关推荐
- 《Spring实战》读书笔记-第3章 高级装配
<Spring实战>是学习Spring框架的一本非常经典的书籍,之前阅读了这本书,只是在书本上写写画画,最近整理了一下<Spring实战>的读书笔记,通过博客的方式进行记录分享 ...
- 《Spring实战》读书笔记-第3章 高级装配,zookeeper原理图
文章目录 3.1 环境与profile 3.2 条件化的bean 3.3 处理自动装配的歧义性 3.4 Bean的作用域 3.5 运行时植注入 3.6 小结 本章内容: Spring profile ...
- 《Spring实战》读书笔记-第3章 高级装配,字节跳动四面技术面
当自动装配bean时,遇到多个实现类的情况下,就出现了歧义,例如: @Autowired public void setDessert(Dessert dessert) { this.dessert ...
- spring boot 503_Spring实战读书笔记第4章 面向切面的Spring
本章内容: 面向切面编程的基本原理 通过POJO创建切面 使用@AspectJ注解 为AspectJ切面注入依赖 在软件开发中,散布于应用中多的功能被称为横切关注点(cross-cutting con ...
- Spring实战读书笔记 高级装配(1)
一.条件化装配 1. 当你希望你的bean在特殊条件下才能装配时,比如在声明了特定的bean时,或者配置了特定的环境变量的时候.那么就可以使用 @Conditional注解,可以用在 @Bean注解下 ...
- APUE读书笔记-第14章-高级I/O
14.1 引言 *高级I/O包括非阻塞I/O.记录锁.系统V流机制.I/O多路转换(select和poll函数).readv和writev函数以及存储映射I/O(mmap) 14.2 非阻塞I/O * ...
- hive实战读书笔记(第9章)Hive性能优化
hive用户面临的一个比较大的问题是,用户需要等待较长的响应时间,与传统关系数据库查询的性能相比,hive响应速度慢的令人发指 本章介绍一套诊断改进hive查询性能的系统方法,通过这个过程,将单个hi ...
- jQuery实战读书笔记(第一章至第四章)
2019独角兽企业重金招聘Python工程师标准>>> 第一章 jQuery 基础 1. 包装器 jQuery对包装器(Wrapper)或包装集(wrapped set)进行操作,即 ...
- maven实战--读书笔记之第一章和第二章
第一章:Maven简介 1.本书为国内社区公认的专家徐晓斌所写,本书基于maven3.0所编写,maven是非常优秀的建模工具,maven最大化的消除了构建的重复,抽象了构建生命,他还有一个优点,帮助 ...
最新文章
- phpstorm安装_PHPstorm设置浏览器打开代码
- js callback回调的一种写法
- Oracle VM VirtualBox启动新建虚拟机弹错--不能为虚拟机xxxx电脑 打开一个新任务 解决方法;
- snmpwalk用法
- Java学习笔记十(注解)
- Python入门100题 | 第024题
- instance 怎么获得自己的 Metadata - 每天5分钟玩转 OpenStack(169)
- 清华2020计算机系张晨,2020清华特奖入围名单公布:电子系学霸两篇顶会一作
- css不继承上级样式_【FrontEnd】CSS
- Android Support Annotation Library使用详解
- windows找不到文件gpedit.msc_极简技术|电脑文件全搜索,没有找不到的东西
- linux编辑音频文件,Linux 上的最佳音频编辑工具推荐
- Tomcat 7 下载地址
- 如何使用QXDM 的1477项 转化utc时间
- 【软考】专栏导读(软考全面介绍、资格报考建议)
- 【高数】高数第八章节——向量代数与空间解析几何空间直角坐标系
- 报价单与贸易术语关系
- ZROI 2018 ZYB和售货机(goods)
- 乒乓球【击球】基础知识
- 网上出售企业支付宝骗局,不看后悔