1 依赖注入
通过依赖注入(Dependency Injection, DI),对象的依赖关系将由系统中负责协调各对象的第三方组件在创建对象的时候进行设定。
1.1 实现
对于如下类
package com.junzerg.spring_knight;
public class BraveKnight implements Knight {
private Quest quest;
public BraveKnight(Quest quest) {
this.quest = quest;
}
public void embarkOnQuest() {
quest.embark();
}
}
- BraveKnight没有自己创建探险任务,而是在构造的时候把探险任务作为构造器参数传入。这是依赖注入的一种方式,构造器注入(constructor injection)。
- 传入的探险类型Quest,是所有的探险任务都必须实现的一个接口。
- BraveKnight没有与特定的Quest实现发生耦合,只要求探险任务实现了Quest接口。
可以看出:
DI带来的最大的好处就是松耦合。如果一个对象只通过接口(而不是具体实现或者初始化过程)来表明依赖关系,那么这种依赖就能在对象本身毫不知情的情况下,用不同的具体实现进行替换。
1.2 注入
对于#1中的BraveKnight类。可以接受任意一种Quest的实现,例如如下类:
package com.junzerg.spring_knight;
import java.io.PrintStream;
public class SlayDragonQuest implements Quest {
private PrintStream stream;
public SlayDragonQuest(PrintStream stream) {
this.stream = stream;
}
public void embark() {
stream.println("Embarking on quest to slay the dragon!");
}
}
创建了这个类之后,剩下的就是将这个类交给BraveKnight。这就是创建应用组件之间的写作的行为,被称为装配(wiring)。
1.3 装配
Spring支持两种装配的方式。
1.3.1 xml配置
<?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="knight" class="com.junzerg.spring_knight.BraveKnight">
<constructor-arg ref="quest" />
</bean>
<bean id="quest" class="com.junzerg.spring_knight.SlayDragonQuest">
<constructor-arg value="#{T(System).out}" />
</bean>
</beans>
以上配置功能为:
- 将BraveKnight和SlayDragonQuest声明为Spring中的bean。
- 在构造BraveKnight bean的时候传入SlayDragonQuest bean的应用,作为构造器的参数。
- 在构造SlayDragonQuest bean的时候将System.out传入到构造器中。
1.3.2 Java描述配置
package com.junzerg.spring_knight.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.junzerg.spring_knight.BraveKnight;
import com.junzerg.spring_knight.Knight;
import com.junzerg.spring_knight.Quest;
import com.junzerg.spring_knight.SlayDragonQuest;
@Configuration
public class KnightConfig {
@Bean
public Knight knight() {
return new BraveKnight(quest());
}
@Bean
public Quest quest() {
return new SlayDragonQuest(System.out);
}
}
作用和用 xml配置的一样。
1.4 实现
1.4.1 xml配置的实现
对于xml配置的情况,使用ClassPathXmlApplicationContext(加载位于应用程序类路径下的一个或者多个XML配置文件)。
package com.junzerg.spring_knight;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class KnightMain {
@Autowired
Knight knight;
public static void main(String[] args) throws Exception {
ClassPathXmlApplicationContext context = new
ClassPathXmlApplicationContext("META-INF/spring/knight.xml");
Knight knight = context.getBean(Knight.class);
knight.embarkOnQuest();
context.close();
}
}
1.4.2 Java配置的实现
对于Java配置的情况,使用AnnotationConfigApplicationContext。
package com.junzerg.spring_knight;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.junzerg.spring_knight.config.KnightConfig;
public class KnightMain {
@Autowired
Knight knight;
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext content = new AnnotationConfigApplicationContext(KnightConfig.class);
Knight knight = content.getBean(Knight.class);
knight.embarkOnQuest();
}
}
1.5 上下文中其他类和结果
1.5.1 上下文中其他类
- Knight
package com.junzerg.spring_knight;
public interface Knight {
void embarkOnQuest();
}
- Quest
package com.junzerg.spring_knight;
public interface Quest {
void embark();
}
1.5.2 运行结果:
Embarking on quest to slay the dragon!
1.5.3 Maven
我是在Maven中添加Spring依赖的,这一节只需要用到Spring的上下文(context)依赖,如下:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>
2 面向切面编程(AOP)
通过面向切面编程(aspect-oriented programming, AOP),把遍布应用各处的功能分离出来形成可重用的组件。
AOP将横切关注点(跨越系统的多个组件,例如日子,事务管理,安全等)模块化,并以声明的方式将它们应用到需要影响的组件中去,这样能够使这些组件有更高的内聚性并且会更加关注自身的业务,不需要了解涉及系统服务带来的复杂性,从而保证了POJO的简单性。
2.1 AOP简单应用
对于#1中的骑士的事迹,用一个吟游诗人来记载。
package com.junzerg.spring_knight;
import java.io.PrintStream;
public class Minstrel {
private PrintStream stream;
public Minstrel(PrintStream stream) {
this.stream = stream;
}
public void singBeforeQuest() {
stream.println("Fa la la, the knight is so brave!");
}
public void singAfterQuest() {
stream.println("Tee hee hee, the brave knight " + "did embark on a quest!");
}
}
Minstrel类,在骑士执行每一个探险任务之前,singBeforeQuest被调用;在骑士完成探险任务之后,singAfterQuest()方法会被调用;这两种情况都通过PrintStream类来歌颂事迹,这个类是通过构造器来引入的。
**那么,在普通情况下,编写这个类之后,会在BraceKnight中使用这个类。但是利用AOP,可以在骑士类不访问吟游诗人类的情况下,让吟游诗人歌颂骑士的事迹。
这种技术是通过在Spring配置文件中将Minstrel声明为一个切面实现的。
minstrel.xml
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="knight" class="sia.knights.BraveKnight">
<constructor-arg ref="quest" />
</bean>
<bean id="quest" class="sia.knights.SlayDragonQuest">
<constructor-arg value="#{T(System).out}" />
</bean>
<bean id="minstrel" class="sia.knights.Minstrel">
<constructor-arg value="#{T(System).out}" />
</bean>
<aop:config>
<aop:aspect ref="minstrel">
<aop:pointcut id="embark" expression="execution(* *.embarkOnQuest(..))" />
<aop:before pointcut-ref="embark" method="singBeforeQuest" />
<aop:after pointcut-ref="embark" method="singAfterQuest" />
</aop:aspect>
</aop:config>
</beans>
2.3 结果和Maven
2.3.1 结果
Fa la la, the knight is so brave!
Embarking on quest to slay the dragon!
Tee hee hee, the brave knight did embark on a quest!
2.3.2 Maven
本节中利用到了AspectJ切点表达式,需要在Maven中添加如下依赖:
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.10</version>
</dependency>
<dependency>
<groupId>aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.5.4</version>
</dependency>