- 从大小、开销两方面而言,Spring 都是轻量级的;
- 通过控制反转技术(IOC)达到松耦合;
- 通过面向切面编程(AOP)实现业务逻辑和系统级服务的分离;
- 作为容器包含和管理应用对象的配置和生命周期;
- 将简单的组件配置组合成复杂的应用。
官网
框架
- 半成品,定制规范;
- 封装了特定的处理流程和控制逻辑,是高内聚的(而类库是松散的工具组合);
- 提高代码重用度、开发效率和质量,易于上手、快速解决问题。
控制反转(IOC)
- 控制反转即获得依赖对象的过程被反转;控制权的转移,应用程序本身不负责对象的创建和维护,而是由外部容器负责创建和维护;
- 依赖注入:IOC 的一种实现,创建对象并组装对象之间的关系,由 IOC 容器(中介)在运行期间动态地将某种依赖关系注入到对象中并返回。
面向接口编程
- 接口:用于沟通的中介物的抽象化(提供功能方法的声明);
- 面向接口编程
- 系统设计中,分清层次和调用关系,每层只向上层提供功能接口,层间仅依赖接口而非实现类;
- 接口实现的变动不影响各层间的调用(在公共服务中尤其重要)。
接口与实现类的使用
public interface OneInterface {
public void say(String arg);
}
public class OneInterfaceImpl implements OneInterface {
public void say(String arg) {
System.out.println("ServiceImpl say: " + arg);
}
}
依赖注入的实现
- 基于注解
- 基于 XML 配置文件
配置文件 spring-ioc.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="oneInterface" class="com.imooc.ioc.interfaces.OneInterfaceImpl"></bean>
</beans>
测试基类:用于对 Spring 配置文件的加载、销毁
public class UnitTestBase {
private ClassPathXmlApplicationContext context;
private String springXmlpath;
public UnitTestBase() {}
public UnitTestBase(String springXmlpath) {
this.springXmlpath = springXmlpath;
}
@Before
public void before() {
if (StringUtils.isEmpty(springXmlpath)) {
springXmlpath = "classpath*:spring-*.xml";
}
try {
context = new ClassPathXmlApplicationContext(springXmlpath.split("[,\\s]+"));
context.start();
} catch (BeansException e) {
e.printStackTrace();
}
}
@After
public void after() {
context.destroy();
}
@SuppressWarnings("unchecked")
protected <T extends Object> T getBean(String beanId) {
try {
return (T) context.getBean(beanId);
} catch (BeansException e) {
e.printStackTrace();
return null;
}
}
protected <T extends Object> T getBean(Class<T> clazz) {
try {
return context.getBean(clazz);
} catch (BeansException e) {
e.printStackTrace();
return null;
}
}
}
注入测试
@RunWith(BlockJUnit4ClassRunner.class)
public class TestOneInterface extends UnitTestBase {
public TestOneInterface() {
super("classpath*:spring-ioc.xml");
}
@Test
public void testSay() {
OneInterface oneInterface = super.getBean("oneInterface");
oneInterface.say("This is a test.");
}
}
Spring 注入方式
- 启动 Spring 容器加载 Bean 配置的时候,完成对变量的赋值行为;
- 常用的注入方式:设值注入(自动调用 Setter 方法对属性赋值)、构造注入(通过构造器传入 bean)。
<!-- Service 层设值注入 -->
<bean id="injectionService" class="com.imooc.ioc.injection.service.InjectionServiceImpl">
<property name="injectionDAO" ref="injectionDAO"></property>
</bean>
<!-- Service 层构造注入 -->
<bean id="injectionService" class="com.imooc.ioc.injection.service.InjectionServiceImpl">
<constructor-arg name="injectionDAO" ref="injectionDAO"></constructor-arg> <!-- 在构造方法中传入 injectionDAO,注意参数名称必须一致 -->
</bean>
<!-- DAO 层 -->
<bean id="injectionDAO" class="com.imooc.ioc.injection.dao.InjectionDAOImpl"></bean> <!-- 作为 injectionService 对象的成员变量 -->
Bean 装配(基于 XML 配置文件)
配置项
Spring IOC Bean 容器中常用到以下配置项,其中 class 是必须的:
- id
- class
- scope
- constructor arguments
- properties
- autowiring mode
- lazy-initialization mode
- initializa/destruction method
作用域
- singleton:Bean 容器中(
context = new ClassPathXmlApplicationContext(xxx)
)唯一(注意对一个类进行单元测试时每个方法都是不同的 IOC 容器); - prototype:每次请求(使用)创建新的实例,由 destroy 销毁;
- request:每次 http 请求创建要给实例且仅在当前 request 中生效;
- session:每次 http 请求创建,当前 Session 内有效;
- global session:常用于多系统集成,基于 portlet 的 web 中有效(portlet 定义了 global session),如果是在 Web 中则与 session 相同。
生命周期
- 定义:在 XML 配置文件中定义(见前面)
- 初始化 :实现
org.springframework.beans.factory.InitializingBean
接口,并覆盖afterPropertiesSet
方法;或配置init-method
; - 使用 ;
- 销毁:与初始化类似,实现
org.springframework.beans.factory.DisposableBean
接口,并覆盖destroy
方法,用于释放连接池等清理操作。
配置全局默认初始化、销毁方法(自己配置的 init-method 和 destroy-method 会覆盖)
<?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"
default-init-method="defautInit" default-destroy-method="defaultDestroy">
<bean id="beanLifeCycle" class="com.imooc.lifecycle.BeanLifeCycle" init-method="start" destroy-method="stop"></bean>
</beans>
public class BeanLifeCycle implements InitializingBean, DisposableBean {
public void defautInit() {
System.out.println("Bean defautInit.");
}
public void defaultDestroy() {
System.out.println("Bean defaultDestroy.");
}
@Override
public void destroy() throws Exception {
System.out.println("Bean destroy.");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("Bean afterPropertiesSet.");
}
public void start() {
System.out.println("Bean start .");
}
public void stop() {
System.out.println("Bean stop.");
}
}
Aware
- Spring 中提供以 Aware 结尾的接口,可用于获取相应的资源、执行一定操作;
- 为对 Spring 进行简单的扩展提供了方便的入口。
常用接口:
- MessageSourceAware
- NotificationPublisherAware
- PortletConfitAware
- PortletContextAware
- ResourceLoaderAware
- ServletConfigAware
- ServletContextAware
实现 BeanNameAware
接口
public class MoocBeanName implements BeanNameAware, ApplicationContextAware {
private String beanName;
@Override
public void setBeanName(String name) {
this.beanName = name;
System.out.println("MoocBeanName : " + name);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("setApplicationContext : " + applicationContext.getBean(this.beanName).hashCode());
}
}
测试
@Test
public void textMoocBeanName() {
System.out.println("textMoocBeanName : " + super.getBean("moocBeanName").hashCode());
}
自动装配
- 前面讲到 Spring 的注入时设置注入和构造注入都需要在 XML 配置文件、
<bean>
配置项中声明; -
使用自动装配,可以根据 Bean 的名称、类型、构造器等自动解决依赖关系,有以下实现方式:
spring-autowiring.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"
default-autowire="constructor">
<bean id="autoWiringService" class="com.imooc.autowiring.AutoWiringService" ></bean>
<bean class="com.imooc.autowiring.AutoWiringDAO" ></bean>
</beans>
AutoWiringDAO.java
public class AutoWiringDAO {
public void say(String word) {
System.out.println("AutoWiringDAO : " + word);
}
}
AutoWiringService.java
public class AutoWiringService {
private AutoWiringDAO autoWiringDAO;
public AutoWiringService(AutoWiringDAO autoWiringDAO) {
System.out.println("AutoWiringService");
this.autoWiringDAO = autoWiringDAO;
}
public void setAutoWiringDAO(AutoWiringDAO autoWiringDAO) {
System.out.println("setAutoWiringDAO");
this.autoWiringDAO = autoWiringDAO;
}
public void say(String word) {
this.autoWiringDAO.say(word);
}
}
TestAutoWiring.java
@RunWith(BlockJUnit4ClassRunner.class)
public class TestAutoWiring extends UnitTestBase {
public TestAutoWiring() {
super("classpath:spring-autowiring.xml");
}
@Test
public void testSay() {
AutoWiringService service = super.getBean("autoWiringService");
service.say(" this is a test");
}
}
Resources
针对于资源文件的统一接口
public class MoocResource implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.applicationContext = applicationContext;
}
public void resource() throws IOException {
Resource resource = applicationContext.getResource("config.txt");
System.out.println(resource.getFilename());
System.out.println(resource.contentLength());
}
}
@RunWith(BlockJUnit4ClassRunner.class)
public class TestResource extends UnitTestBase {
public TestResource() {
super("classpath:spring-resource.xml");
}
@Test
public void testResource() {
MoocResource resource = super.getBean("moocResource");
try {
resource.resource();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Bean 装配(基于注解)
spring-beanannotation.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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd" >
<context:component-scan base-package="com.imooc.beanannotation"></context:component-scan>
</beans>
BeanAnnotation.java
@Scope
@Component
public class BeanAnnotation {
public void say(String arg) {
System.out.println("BeanAnnotation : " + arg);
}
public void myHashCode() {
System.out.println("BeanAnnotation : " + this.hashCode());
}
}
测试
@RunWith(BlockJUnit4ClassRunner.class)
public class TestBeanAnnotation extends UnitTestBase {
public TestBeanAnnotation() {
super("classpath*:spring-beanannotation.xml");
}
@Test
public void testSay() {
BeanAnnotation bean = super.getBean("beanAnnotation");
bean.say("This is test.");
bean = super.getBean("bean");
bean.say("This is test.");
}
@Test
public void testScpoe() {
BeanAnnotation bean = super.getBean("beanAnnotation");
bean.myHashCode();
bean = super.getBean("beanAnnotation");
bean.myHashCode();
}
}
Bean 的定义
其中注解中的参数表示 Bean 在容器中的 Id,默认为类名(首字母改为小写)。
Classpath 扫描与组件管理:@Component
、@Repository
、@Service
、@Controller
元注解:@Target
、@Retention
、@Documented
类的自动检测及 Bean 的注册
使用 component-scan 可以扫描加上了
@Component
注解(及其子注解)的类,在 base-package 上配置需要扫描的包,包括了 annotation-config 的功能。
使用过滤器进行自定义扫描
作用域:@Scope
代理方式
常用注解说明
@Required
@Autowired
public interface InjectionService {
public void save(String arg);
}
@Service
public class InjectionServiceImpl implements InjectionService {
// @Autowired
private InjectionDAO injectionDAO;
@Autowired
public InjectionServiceImpl(InjectionDAO injectionDAO) {
this.injectionDAO = injectionDAO;
}
// @Autowired
public void setInjectionDAO(InjectionDAO injectionDAO) {
this.injectionDAO = injectionDAO;
}
public void save(String arg) {
System.out.println("Service接收参数:" + arg);
arg = arg + ":" + this.hashCode();
injectionDAO.save(arg);
}
}
@RunWith(BlockJUnit4ClassRunner.class)
public class TestInjection extends UnitTestBase {
public TestInjection() {
super("classpath:spring-injection.xml");
}
@Test
public void testSetter() {
InjectionService service = super.getBean("injectionService");
service.save("这是要保存的数据");
}
@Test
public void testCons() {
InjectionService service = super.getBean("injectionService");
service.save("这是要保存的数据");
}
}
public interface BeanInterface {}
@Order(2)
@Component
public class BeanImplOne implements BeanInterface {}
@Order(1)
@Component
public class BeanImplTwo implements BeanInterface {}
@Component
public class BeanInvoker {
@Autowired
private List<BeanInterface> list;
@Autowired
private Map<String, BeanInterface> map;
@Autowired
@Qualifier("beanImplTwo")
private BeanInterface beanInterface;
public void say() {
if (null != list && 0 != list.size()) {
System.out.println("list...");
for (BeanInterface bean : list) {
System.out.println(bean.getClass().getName());
}
} else {
System.out.println("List<BeanInterface> list is null !!!!!!!!!!");
}
System.out.println();
if (null != map && 0 != map.size()) {
System.out.println("map...");
for (Map.Entry<String, BeanInterface> entry : map.entrySet()) {
System.out.println(entry.getKey() + " " + entry.getValue().getClass().getName());
}
} else {
System.out.println("Map<String, BeanInterface> map is null !!!!!!!!!!");
}
System.out.println();
if (null != beanInterface) {
System.out.println(beanInterface.getClass().getName());
} else {
System.out.println("beanInterface is null...");
}
}
}
@Qualifier
@Bean - 基于 Java 容器的注解
使用 @Bean
注解,Bean 的名称默认是方法的名称。
@Configuration
@ImportResource("classpath:config.xml")
public class StoreConfig {
// @Value("${url}")
// private String url;
//
// @Value("${jdbc.username}")
// private String username;
//
// @Value("${password}")
// private String password;
//
// @Bean
// public MyDriverManager myDriverManager() {
// return new MyDriverManager(url, username, password);
// }
// @Bean(name = "stringStore", initMethod="init", destroyMethod="destroy")
// public Store stringStore() {
// return new StringStore();
// }
// @Bean(name = "stringStore")
// @Scope(value="prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
// public Store stringStore() {
// return new StringStore();
// }
@Autowired
private Store<String> s1;
@Autowired
private Store<Integer> s2;
@Bean
public StringStore stringStore() {
return new StringStore();
}
@Bean
public IntegerStore integerStore() {
return new IntegerStore();
}
// @Bean(name = "stringStoreTest")
// public Store stringStoreTest() {
// System.out.println("s1 : " + s1.getClass().getName());
// System.out.println("s2 : " + s2.getClass().getName());
// return new StringStore();
// }
}
public class StringStore implements Store<String> {
public void init() {
System.out.println("This is init.");
}
public void destroy() {
System.out.println("This is destroy.");
}
}
@RunWith(BlockJUnit4ClassRunner.class)
public class TestJavabased extends UnitTestBase {
public TestJavabased() {
super("classpath*:spring-beanannotation.xml");
}
@Test
public void test() {
Store store = super.getBean("stringStore");
System.out.println(store.getClass().getName());
}
@Test
public void testMyDriverManager() {
MyDriverManager manager = super.getBean("myDriverManager");
System.out.println(manager.getClass().getName());
}
@Test
public void testScope() {
Store store = super.getBean("stringStore");
System.out.println(store.hashCode());
store = super.getBean("stringStore");
System.out.println(store.hashCode());
}
@Test
public void testG() {
StringStore store = super.getBean("stringStoreTest");
}
}
面向切面编程
面向切面编程(AOP)是分离应用的业务逻辑和系统级服务进行内聚性的开发。