ch02-依赖注入
服务端开发-依赖注入(Dependency Injection)
课程录屏
23 秋 服务端开发-02_哔哩哔哩_bilibili
Spring的两个核心技术
DI(Dependency Injection)
保留抽象接口,让组件(Component)依赖于抽象接口,当组件要与其他实际的对象发生依赖关 系时,由抽象接口来注入依赖的实际对象
AOP(Aspect Oriented Programming)
通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术
利用AOP可以对业务逻辑的各个部分进行隔离, 从而使得业务逻辑各部分之间的耦合度降低,提 高程序的可重用性,同时提高了开发效率
Spring的核心是提供了一个容器(container)
应用上下文
AnnotationConfigApplicationContext:基于注解的
AnnotationConfigWebApplicationContext
ClassPathXmlApplicationContext:基于xml配置文件生成上下文
FileSystemXmlApplicationContext
XmlWebApplicationContext
classPathXmlApplicationContext获取上下文
getBean获得一个bean
bean的生命周期
例子代码类图
虚线箭头:实现,具体的类实现接口
实线:依赖,player有接口的引用
自动化配置
组件扫描(component scanning)
自动装配(autowiring)
组件扫描
@Configuration
@ComponentScan
等价
基础包(basePackages={“…”,”…”})
类型不安全(not type-safe)
basePackageClasses={.class,.class}
Marker interface
自动装配
@Autowired
用在构造器
用在属性Setter方法
用在(私有)属性
required=fals
JavaConfig
自动化配置有时会行不通,如:第三方库
@Configuration
@Bean(name=“…”)
注入
调用方法()
通过方法参数自动装配(其它配置类、其它方式创建的Bean)
注意与业务逻辑和领域代码分开
代码演示
自动化配置
MediaPlayer.java 接口
1 2 3 4 5 6 7 package soundsystem;public interface MediaPlayer { void play () ; }
SgtPeppers.java 实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package soundsystem;import org.springframework.stereotype.Component;@Component public class SgtPeppers implements CompactDisc { private String title = "Sgt. Pepper's Lonely Hearts Club Band" ; private String artist = "The Beatles" ; public void play () { System.out.println("Playing " + title + " by " + artist); } }
CDPlayer.java
指定配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package soundsystem;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;@Component("soundsystem") public class CDPlayer implements MediaPlayer { @Autowired private CompactDisc cd; @Autowired public CDPlayer (CompactDisc cd) { this .cd = cd; } @Autowired public void setCd (CompactDisc cd) { this .cd = cd; } public void play () { cd.play(); } }
Object=Bean=Component
CDPlayerConfig.java
指定配置类 :构建上下文的入口类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package soundsystem;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;@Configuration @ComponentScan(basePackages = {"soundsystem", "abc"}) @ComponentScan(basePackageClasses = {CDPlayer.class, abc.class}) public class CDPlayerConfig { }
SgtPeppers.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package soundsystem;import org.springframework.stereotype.Component;@Component @Primary public class SgtPeppers implements CompactDisc { private String title = "Sgt. Pepper's Lonely Hearts Club Band" ; private String artist = "The Beatles" ; public void play () { System.out.println("Playing " + title + " by " + artist); } }
java程序的入口
MyAnnotationApp.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package app;import org.springframework.context.ApplicationContext;import org.springframework.context.annotation.AnnotationConfigApplicationContext;import soundsystem.CDPlayerConfig;import soundsystem.MediaPlayer;public class MyAnnotationApp { public static void main (String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext (CDPlayerConfig.class); MediaPlayer player = ctx.getBean(MediaPlayer.class); player.play(); } }
测试
cdplayertest.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 package soundsystem;import org.junit.Rule;import org.junit.Test;import org.junit.contrib.java.lang.system.StandardOutputStreamLog;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.test.context.ContextConfiguration;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;import static org.junit.Assert.assertEquals;import static org.junit.Assert.assertNotNull;@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = CDPlayerConfig.class) public class CDPlayerTest { @Rule public final StandardOutputStreamLog log = new StandardOutputStreamLog (); @Autowired private MediaPlayer player; @Autowired private CompactDisc cd; @Test public void cdShouldNotBeNull () { assertNotNull(cd); } @Test public void play () { player.play(); assertEquals( "Playing Sgt. Pepper's Lonely Hearts Club Band by The Beatles" + System.getProperty("line.separator" ), log.getLog()); } }
componentscan的替代方法:javaConfig
实例化第三方库,无法拿到原代码
CDPlayerConfig.java
配置类里方法调用的逻辑跟普通类有所区别,业务代码不适合放置在配置类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 package soundsystem;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configuration public class CDPlayerConfig { @Bean public CompactDisc compactDisc () { return new SgtPeppers (); } @Bean(name = "CDPlayer") public CDPlayer cdPlayer (CompactDisc cd) { return new CDPlayer (cd); } @Bean public CDPlayer cdPlayer2 (CompactDisc cd) { return new CDPlayer (cd); } }
cdplayer.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package soundsystem;import org.springframework.beans.BeansException;import org.springframework.beans.factory.BeanNameAware;import org.springframework.context.ApplicationContext;import org.springframework.context.ApplicationContextAware;public class CDPlayer implements MediaPlayer , BeanNameAware, ApplicationContextAware { private CompactDisc cd; public CDPlayer (CompactDisc cd) { this .cd = cd; } public void play () { cd.play(); } @Override public void setBeanName (String name) { System.out.println("====" + name); } @Override public void setApplicationContext (ApplicationContext applicationContext) throws BeansException { System.out.println(applicationContext.getApplicationName()); } }
xml配置
ConstructorArgReferenceTest-context.xml
1 2 3 4 5 6 7 8 9 10 11 <?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" > <bean id ="compactDisc" class ="soundsystem.SgtPeppers" /> <bean id ="cdPlayer" class ="soundsystem.CDPlayer" > <constructor-arg ref ="compactDisc" /> </bean > </beans >
测试
ConstructorArgReferenceTest.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package soundsystem;import static org.junit.Assert.*;import org.junit.Rule;import org.junit.Test;import org.junit.contrib.java.lang.system.StandardOutputStreamLog;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.test.context.ContextConfiguration;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations="ConstructorArgReferenceTest-context.xml") public class ConstructorArgReferenceTest { @Rule public final StandardOutputStreamLog log = new StandardOutputStreamLog (); @Autowired private MediaPlayer player; @Test public void play () { player.play(); assertEquals( "Playing Sgt. Pepper's Lonely Hearts Club Band by The Beatles\r\n" , log.getLog()); } }
可以通过xml传递参数(了解即可)
ConstructorArgValueTest-context.xml
value指定字面量
ref指定引用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <?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" > <bean id ="compactDisc" class ="soundsystem.BlankDisc" > <constructor-arg value ="Sgt. Pepper's Lonely Hearts Club Band" /> <constructor-arg value ="The Beatles" /> </bean > <bean id ="cdPlayer" class ="soundsystem.CDPlayer" > <constructor-arg ref ="compactDisc" /> </bean > </beans >
ConstructorArgValueTest.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package soundsystem;import static org.junit.Assert.*;import org.junit.Rule;import org.junit.Test;import org.junit.contrib.java.lang.system.StandardOutputStreamLog;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.test.context.ContextConfiguration;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration public class ConstructorArgValueTest { @Rule public final StandardOutputStreamLog log = new StandardOutputStreamLog (); @Autowired private MediaPlayer player; @Test public void play () { player.play(); assertEquals( "Playing Sgt. Pepper's Lonely Hearts Club Band by The Beatles\r\n" , log.getLog()); } }
如果参数是列表:list子元素
ConstructorArgCollectionTest-context.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 <?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" > <bean id ="compactDisc" class ="soundsystem.collections.BlankDisc" > <constructor-arg value ="Sgt. Pepper's Lonely Hearts Club Band" /> <constructor-arg value ="The Beatles" /> <constructor-arg > <list > <value > Sgt. Pepper's Lonely Hearts Club Band</value > <value > With a Little Help from My Friends</value > <value > Lucy in the Sky with Diamonds</value > <value > Getting Better</value > <value > Fixing a Hole</value > <value > She's Leaving Home</value > <value > Being for the Benefit of Mr. Kite!</value > <value > Within You Without You</value > <value > When I'm Sixty-Four</value > <value > Lovely Rita</value > <value > Good Morning Good Morning</value > <value > Sgt. Pepper's Lonely Hearts Club Band (Reprise)</value > <value > A Day in the Life</value > </list > </constructor-arg > </bean > <bean id ="cdPlayer" class ="soundsystem.CDPlayer" > <constructor-arg ref ="compactDisc" /> </bean > </beans >
test
ConstructorArgCollectionTest.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 package soundsystem;import static org.junit.Assert.*;import org.junit.Rule;import org.junit.Test;import org.junit.contrib.java.lang.system.StandardOutputStreamLog;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.test.context.ContextConfiguration;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration public class ConstructorArgCollectionTest { @Rule public final StandardOutputStreamLog log = new StandardOutputStreamLog (); @Autowired private MediaPlayer player; @Test public void play () { player.play(); assertEquals( "Playing Sgt. Pepper's Lonely Hearts Club Band by The Beatles\r\n" + "-Track: Sgt. Pepper's Lonely Hearts Club Band\r\n" + "-Track: With a Little Help from My Friends\r\n" + "-Track: Lucy in the Sky with Diamonds\r\n" + "-Track: Getting Better\r\n" + "-Track: Fixing a Hole\r\n" + "-Track: She's Leaving Home\r\n" + "-Track: Being for the Benefit of Mr. Kite!\r\n" + "-Track: Within You Without You\r\n" + "-Track: When I'm Sixty-Four\r\n" + "-Track: Lovely Rita\r\n" + "-Track: Good Morning Good Morning\r\n" + "-Track: Sgt. Pepper's Lonely Hearts Club Band (Reprise)\r\n" + "-Track: A Day in the Life\r\n" , log.getLog()); } }
命名空间
CNamespaceValueTest-context.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <?xml version="1.0" encoding="UTF-8" ?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:c ="http://www.springframework.org/schema/c" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" > <bean id ="compactDisc" class ="soundsystem.BlankDisc" c:_0 ="Sgt. Pepper's Lonely Hearts Club Band" c:_1 ="The Beatles" /> <bean id ="cdPlayer" class ="soundsystem.CDPlayer" c:_-ref ="compactDisc" /> </beans >
p命名空间
p属性
PropertyRefTest-context.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?xml version="1.0" encoding="UTF-8" ?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:p ="http://www.springframework.org/schema/p" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" > <bean id ="compactDisc" class ="soundsystem.BlankDisc" > <constructor-arg value ="Sgt. Pepper's Lonely Hearts Club Band" /> <constructor-arg value ="The Beatles" /> </bean > <bean id ="cdPlayer" class ="soundsystem.properties.CDPlayer" p:compactDisc-ref ="compactDisc" /> </beans >
Spring Web开发框架的分层
Faker
1 2 3 4 5 6 7 8 9 10 11 private Contact generateContact () { id++; Faker faker = new Faker (Locale.ENGLISH); Contact contact = new Contact (); contact.setId(id); contact.setFirstName(faker.hame().firstName()); contact.setLastName(faker.name().lastName()); contact.setPhoneNumber(faker.phoneNumber().cellPhone()); contact.setEmailAddress(faker,internet(),emailAddress()): return contact: }
模拟工具
DiUS/java-faker: Brings the popular ruby faker gem to Java (github.com)
profile
配置类
profile在不同的环境下选择不同的bean创建
@Profile
@Configuration
@Profile(“dev”) 类
@Bean
@Profile(“prov”)
方法
激活
缺省属性,如果不指定,默认第一个;如果指定,则为第二个
spring.profiles.default
spring.profiles.active
@ActiveProfiles(“dev”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 package com.myapp;import javax.sql.DataSource;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Profile;import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;import org.springframework.jndi.JndiObjectFactoryBean;@Configuration public class DataSourceConfig { @Bean(destroyMethod = "shutdown") @Profile("dev") public DataSource embeddedDataSource () { return new EmbeddedDatabaseBuilder () .setType(EmbeddedDatabaseType.H2) .addScript("classpath:schema.sql" ) .addScript("classpath:test-data.sql" ) .build(); } @Bean @Profile("prod") public DataSource jndiDataSource () { JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean (); jndiObjectFactoryBean.setJndiName("jdbc/myDS" ); jndiObjectFactoryBean.setResourceRef(true ); jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class); return (DataSource) jndiObjectFactoryBean.getObject(); } }
根配置
@Configeration
@ComponetScan
@Import(其它配置类…)
@ImportResource(其它xml文件)
Class BootConfig(){ }
@Conditional
@Bean 或@Component
@Conditional(**.class)
接口
Condition{ boolean matches(…) }
自动装配的歧义性
@Component 或 @Bean
@Primary
定义时
@Componet或@Bean
@Qualifier(“…”)自定义限定符
使用时
@Autowired
@Qualifier(“…”) bean名称或自定义限定符,默认Bean名是限定符
也可以自定义注解,这些注解本身也加了@Qualifier注解
@Cold
@Creamy
Bean的作用域
@Scope可以与@Component和@Bean一起使用,指定作用域
◼ Singleton,单例,在整个应用中,只创建bean的一个实例
◼ Prototype,原型,每次注入或者通过Spring应用上下文获取的时候,都会创建一个新bean实例
◼ Session,会话,在Web应用中,为每个会话创建一个bean实例
◼ Request,请求,在Web应用中,为每个请求创建一个bean实例
使用会话和请求作用域
@Component
@Scope(value=WebApplicationContext.SCOPE_SESSION, proxyMode=ScopedProxyMode.INTERFACES)
public ShoppingCart cart(){…}
通过代理注入给单例对象
运行时注入外部值
使用Environment检索属性
使用@PropertySource指定属性文件
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 )