1) IOC/DI (IOC容器)
2) AOP面向切面编程
3) 声明式事务
io表示开发者平台


Spring 基础框架,可以视为 Spring 基础设施,基本上任何其他 Spring 项目都是以 Spring Framework 为基础的。
Spring5,基于jdk1.8 。
1 非侵入式:使用 Spring Framework 开发应用程序时,Spring 对应用程序本身的结构影响非常小。对领域模型(Java Bean)可以做到零污染;对功能性组件也只需要使用几个简单的注解进行标记,完全不会破坏原有结构,反而能将组件结构进一步简化。这就使得基于 Spring Framework 开发应用程序时结构清晰、简洁优雅。
2 控制反转:IOC——Inversion of Control,翻转资源获取方向。把自己创建资源、向环境索取资源变成环境将资源准备好,我们享受资源注入。
3 面向切面编程:AOP——Aspect Oriented Programming,在不修改源代码的基础上增强代码功能。
4 容器:Spring IOC 是一个容器,因为它包含并且管理组件对象的生命周期。组件享受到了容器化的管理,替程序员屏蔽了组件创建过程中的大量细节,极大的降低了使用门槛,大幅度提高了开发效率。
5 组件化:Spring 实现了使用简单的组件配置组合成一个复杂的应用。在 Spring 中可以使用 XML 和 Java 注解组合这些对象。这使得我们可以基于一个个功能明确、边界清晰的组件有条不紊的搭建超大型复杂应用系统。
6 声明式:很多以前需要编写代码才能实现的功能,现在只需要声明需求即可由框架代为实现。
7 一站式:在 IOC 和 AOP 的基础上可以整合各种企业应用的开源框架和优秀的第三方类库。而且 Spring 旗下的项目已经覆盖了广泛领域,很多方面的功能性需求可以在 Spring Framework 的基础上全部使用 Spring 来实现。


普通容器只能用来存储,没有更多功能。
变量,数组,集合
普通容器只能用来存储,没有更多功能。

政府管理我们的一生,生老病死都和政府有关。
Spring 的 IOC 容器就是 IOC 思想的一个落地的产品实现。IOC 容器中管理的组件也叫做 bean。在创建 bean 之前,首先需要创建 IOC 容器。Spring 提供了 IOC 容器的两种实现方式:
①BeanFactory
这是 IOC 容器的基本实现,是 Spring 内部使用的接口。面向 Spring 本身,不提供给开发人员使用。
②ApplicationContex -- 我们自己使用
BeanFactory 的子接口,提供了更多高级特性。面向 Spring 的使用者,几乎所有场合都使用 ApplicationContext 而不是底层的 BeanFactory。
所以,以后在 Spring 环境下看到一个类或接口的名称中包含 ApplicationContext,那基本就可以断定,这个类或接口与 IOC 容器有关。
③ApplicationContext的主要实现类


由 Spring 的 IOC 容器创建类的对象,并根据bean的id属性值来获取bean对象。

自定义一个类IEmpService => EmpServiceImpl,这个类在Spring的配置文件中进行配置,然后用ClassPathXMLApplicationContext这个类去读取配置文件,然后根据配置文件中配置的东西,将这个对象建立出来。
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.hy</groupId> <artifactId>mybatis001</artifactId> <version>0.0.1</version> <packaging>war</packaging>
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <maven.compiler.encoding>UTF-8</maven.compiler.encoding> </properties>
<dependencies> <!-- Spring core--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.20.RELEASE</version> </dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.20</version> </dependency>
<!-- Spring-Test --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.2.20.RELEASE</version> </dependency> <!--junit的核心jar包 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> </dependencies>
<build> <plugins> <!-- 指定jdk,防止update project --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.8</source> <target>1.8</target> <!-- 项目编码 --> <encoding>UTF-8</encoding> </configuration> </plugin> </plugins> </build> </project> |
导入一个依赖,传递过来剩下的jar文件



|
package com.hy.service.impl;
import com.hy.service.EmpService;
public class EmpServiceImpl implements EmpService { @Override public void reg() { System.out.println("用户注册"); } }
|
1) 先导入spring的相关环境,其实就是导入pom文件jar包的坐标gav。
2) 创建spring的配置文件applicationContext.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">
<!-- 实验01 [重要] 创建bean --> <!-- bean标签,通过配置bean标签告诉IOC容器需要创建是哪个类的对象 id属性:当前bean的唯一标识,设定了这个唯一标识后,在其他地方就可以用来引用这个bean对象 class属性:指定这个bean要配置的类的全类名 --> <bean id="empService" class="com.hy.service.impl.EmpServiceImpl" /> </beans> |
这个bean没有属性,所以可以写成单标签
|
public class TestService { // ApplicationContext就是我们的IOC容器 private ApplicationContext iocContainer = new ClassPathXmlApplicationContext("applicationContext.xml");
//实验01 [重要] 创建bean //从IOC容器中获取已配置的bean @Test public void testEmpService() { //通过bean的id属性的值得,获取这个bean对象 EmpService empService = (EmpService)iocContainer.getBean("empService"); empService.reg(); } } |
注意:Spring底层默认通过反射技术调用类的无参构造方法来创建类的对象。
|
package com.hy.service.impl;
import com.hy.service.EmpService;
public class EmpServiceImpl implements EmpService {
public EmpServiceImpl(int age) { }
@Override public void reg() { System.out.println("用户注册"); } } |
报错: 如果没有无参构造方法,则会抛出下面的异常:


所以对一个JavaBean来说,无参构造器和属性的getXxx()、setXxx()方法是必须存在的,特别是在框架中。


被放入到容器当中,相当于加入了会员俱乐部
在Spring环境下能够享受到的所有福利,都必须通过 IOC 容器附加到组件类上,所以随着我们在 Spring 中学习的功能越来越多,IOC 容器创建的组件类的对象就会比自己 new 的对象 其功能强大的越来越多。

|
package com.hy.test;
import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.hy.service.EmpService; import com.hy.service.impl.EmpServiceImpl;
public class TestService2 { // ApplicationContext就是我们的IOC容器 private ApplicationContext iocContainer = new ClassPathXmlApplicationContext("applicationContext.xml");
//实验02 [重要] 创建bean,并且根据类型获取bean对象 //从IOC容器中根据类型获取已配置的bean对象 @Test public void testEmpService() { // EmpService empService = iocContainer.getBean(EmpService.class); //正确 EmpService empService = iocContainer.getBean(EmpServiceImpl.class); //正确 empService.reg(); //用户注册 } }
|

|
<?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">
<!-- 实验01 [重要] 创建bean --> <!-- bean标签,通过配置bean标签告诉IOC容器需要创建是哪个类的对象 id属性:当前bean的唯一标识,设定了这个唯一标识后,在其他地方就可以用来引用这个bean对象 class属性:指定这个bean要配置的类的全类名 --> <bean id="empService" class="com.hy.service.impl.EmpServiceImpl" />
<bean id="empService2" class="com.hy.service.impl.EmpServiceImpl" /> </beans>
|
|
package com.hy.test;
import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.hy.service.EmpService; import com.hy.service.impl.EmpServiceImpl;
public class TestService2 { // ApplicationContext就是我们的IOC容器 private ApplicationContext iocContainer = new ClassPathXmlApplicationContext("applicationContext.xml");
//实验02 [重要] 创建bean,并且根据类型获取bean对象 //如果容器中配置了同样类型的两个bean对象, //从IOC容器中根据类型获取已配置的bean对象时,则报错。 @Test public void testEmpService() { // EmpService empService = iocContainer.getBean(EmpService.class); //失败 EmpService empService = iocContainer.getBean(EmpServiceImpl.class); //失败 } }
|


如果一个类实现了某个接口,根据接口类型可以获取 bean 吗?
可以,前提是bean唯一,如下的配置就不行。
|
<bean id="empService" class="com.hy.service.impl.EmpServiceImpl" />
<bean id="empService2" class="com.hy.service.impl.EmpServiceImpl" /> |
如果一个接口有多个实现类,这些实现类都配置了 bean标签,根据接口类型可以获取 bean 吗?
不行,因为bean不唯一
根据类型来获取bean时,在满足bean唯一性的前提下,其实只是看:『对象 instanceof 指定的类型』的返回结果,只要返回的是true就可以认定为和类型匹配,能够获取到。
|
package com.hy.service.impl;
import com.hy.service.EmpService;
public class EmpServiceImpl implements EmpService { private String empStatus;
public String getEmpStatus() { return empStatus; }
public void setEmpStatus(String empStatus) { this.empStatus = empStatus; } @Override public void reg() { System.out.println("用户注册"); } } |

|
<!-- 实验03 [重要] 创建bean,同时给bean的属性赋值:通过setXxx方法注入 --> <bean id="empService3" class="com.hy.service.impl.EmpServiceImpl"> <!-- property标签:通过类的setXxx()方法给该类的对象设置属性值 name属性:指定属性名(其实这个名字是setXxx和getXx方法定义的,和属性名无关) value属性:指定属性值 --> <property name="empStatus" value="积极"/> </bean> |
|
package com.hy.test;
import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.hy.service.impl.EmpServiceImpl;
public class TestService3 { private ApplicationContext iocContainer = new ClassPathXmlApplicationContext("applicationContext.xml");
//实验03 [重要] @Test public void testEmpService() { //通过bean的id属性的值得,获取这个bean对象 EmpServiceImpl empService3 = (EmpServiceImpl)iocContainer.getBean("empService3"); System.out.println(empService3.getEmpStatus()); //积极 } } |
|
<!-- 实验四 [重要]给bean的属性赋值:引用外部已声明的bean --> <bean id="empMapper" class="com.hy.mapper.impl.EmpMapperImpl "> <property name="name" value="范冰冰"/> </bean>
<bean id="empService" class="com.hy.service.impl.EmpServiceImpl "> <!-- 使用外部已经配置好的empMapper这个bean来给当前组件的happyMachine属性赋值 --> <!-- 引用另外一个bean不能再使用value属性,而要使用ref属性指定bean的id --> <!-- 如果错把ref属性写成了value属性,会抛出异常: Caused by: java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'com.' for property ' empMapper ': no matching editors or conversion strategy found 意思是不能把String类型转换成我们要的EmpMapper类型 说明我们使用value属性时,Spring只把这个属性看做一个普通的字符串,不会认为这是一个bean的id,更不会根据它去找到bean来赋值 --> <property name="empMapper" ref=" empMapper "/> </bean> |
|
<!-- 内部bean PersonServiceImpl里有一个Dept属性 --> <bean id="personService" class="com.hy.service.impl.PersonServiceImpl"> <property name="dept" > <!-- 在property标签内部再配置一个bean,这就是内部bean 内部bean可以直接用来给property指定的属性赋值 - -> <!-- 内部bean,仅仅被外部bean使用,不能让其他的bean使用,所以内部bean不需要id --> <bean class="com.hy.bean.Dept"/> </property> </bean> |
<!-- 实验六 [重要]给bean的属性赋值:引入外部属性文件 -->
<!-- 使用context名称空间下的property-placeholder标签引入外部属性文件(本质上就是记录外部属性文件的位置) -->
<!-- location属性:指定外部属性文件的路径。classpath:表示这个路径从类路径根目录开始 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<!-- 通过 ${key}的形式引用外部属性文件中的数据 -->
<property name="url" value="${jdbc.url}"/>
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="username" value="${jdbc.user}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
使用内部bean,给级联属性赋值。
<!-- 实验七 给bean的属性赋值:级联属性赋值 -->
<bean id="happyComponent7" class="com.hy.ioc.component.HappyComponent">
<!-- 使用内部bean的形式将happyMachine属性初始化 -->
<property name="happyMachine">
<!-- 相当于创建了HappyMachine的空对象赋值给happyMachine属性 -->
<bean class="com.hy.ioc.component.HappyMachine"/>
</property>
<!--
如果happyMachine属性没有被初始化,那么直接访问级联属性会抛出异常:
Caused by: org.springframework.beans.NullValueInNestedPathException: Invalid property 'happyMachine' of bean class [com.atguigu.ioc.component.HappyComponent]: Value of nested property 'happyMachine' is null
-->
<property name="happyMachine.machineName" value="aaa"/>
</bean>
<!-- 实验八 给bean的属性赋值:构造器注入 -->
<bean id="happyTeam" class="com.hy.ioc.component.HappyTeam">
<!-- 调用类的构造器创建对象并同时传入参数值 -->
<!-- constructor-arg标签:给构造器对应位置传入参数 -->
<!-- index属性:指定当前参数在参数列表中的索引位置 -->
<!-- name属性:指定当前参数的参数名 -->
<!-- value属性:指定参数值 -->
<constructor-arg index="0" name="teamName" value="gaysTeam"/>
<constructor-arg index="1" name="memberCount" value="10" />
<constructor-arg index="2" name="memberSalary" value="555.55"/>
</bean>
private List<String> nameList; //标签不需要完全一致,一样可以赋值成功
private Map<String,String> nameMap;
<!-- 实验十一 给bean的属性赋值:集合属性 -->
<bean id="classes" class="com.hy.ioc.component.Classes">
<property name="nameList">
<!-- list标签:准备一组集合类型的数据,给集合属性赋值 -->
<!--<list>
<value>member01</value>
<value>member02</value>
<value>member03</value>
</list>-->
<!-- 使用set标签也能实现相同效果,只是附带了去重功能 -->
<!--<set>
<value>member01</value>
<value>member02</value>
<value>member02</value>
</set>-->
<!-- array也同样兼容 -->
<array>
<value>member01</value>
<value>member02</value>
<value>member02</value>
</array>
</property>
<property name="nameMap">
<!-- 给Map类型的属性赋值 -->
<!--<map>
<entry key="财务部" value="张三"/>
<entry key="行政部" value="李四"/>
<entry key="销售部" value="王五"/>
</map>-->
<!-- 也可以使用props标签 -->
<props>
<prop key="财务部">张三2</prop>
<prop key="行政部">李四2</prop>
<prop key="销售部">王五2</prop>
</props>
</property>
</bean>
所谓自动装配就是一个组件需要其他组件时,由 IOC 容器负责找到那个需要的组件,并装配进去。
|
<!-- 实验十二 自动装配 --> <bean id="happyService3" class="com.hy.ioc.component.HappyService"/> <bean id="happyService2" class="com.hy.ioc.component.HappyService"/>
<!-- 使用bean标签的autowire属性设置自动装配效果 --> <!-- byType表示根据类型进行装配,此时如果类型匹配的bean不止一个,那么会抛NoUniqueBeanDefinitionException --> <!-- byName表示根据bean的id进行匹配。而bean的id是根据需要装配组件的属性的属性名来确定的 --> <bean id="happyController" class="com.hy.ioc.component.HappyController" autowire="byName"> <!-- 手动装配:在property标签中使用ref属性明确指定要装配的bean --> <!--<property name="happyService" ref="happyService"/>--> </bean> |
@Test
public void testExperiment12() {
HappyController happyController = iocContainer.getBean(HappyController.class);
HappyService happyService = happyController.getHappyService();
System.out.println("happyService = " + happyService);
}
5.11 bean的声明周期以及初始化
在无参构造方法打一个断点。
bean标签scope配置singleton时,bean的创建是在IOC容器初始化的时候,创建的,而不是在getBean的时候创建的。
bean标签scope配置prototype时,bean的创建实在调用getBean方法的时候创建的。

IOC容器关闭之前销毁bean对象。


这个接口总没有close方法在子接口中。


和 XML 配置文件一样,注解本身并不能执行,注解本身仅仅只是做一个标记,具体的功能是框架检测到注解标记的位置,然后针对这个位置按照注解标记的功能来执行具体操作。
本质上:所有一切的操作都是Java代码来完成的,XML和注解只是告诉框架中的Java代码如何执行。


1) 首先根据所需要的组件类型到IOC容器中查找
能够找到唯一的bean:直接执行装配
如果完全找不到匹配这个类型的bean:装配失败
和所需类型匹配的bean不止一个
没有@Qualifier注解:根据@Autowired标记位置成员变量的变量名作为bean的id进行匹配
能够找到:执行装配
找不到:装配失败
使用@Qualifier注解:根据@Qualifier注解中指定的名称作为bean的id进行匹配
能够找到:执行装配
找不到:装配失败
很好奇,就使用rubyonrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提
我想为Heroku构建一个Rails3应用程序。他们使用Postgres作为他们的数据库,所以我通过MacPorts安装了postgres9.0。现在我需要一个postgresgem并且共识是出于性能原因你想要pggem。但是我对我得到的错误感到非常困惑当我尝试在rvm下通过geminstall安装pg时。我已经非常明确地指定了所有postgres目录的位置可以找到但仍然无法完成安装:$envARCHFLAGS='-archx86_64'geminstallpg--\--with-pg-config=/opt/local/var/db/postgresql90/defaultdb/po
我打算为ruby脚本创建一个安装程序,但我希望能够确保机器安装了RVM。有没有一种方法可以完全离线安装RVM并且不引人注目(通过不引人注目,就像创建一个可以做所有事情的脚本而不是要求用户向他们的bash_profile或bashrc添加一些东西)我不是要脚本本身,只是一个关于如何走这条路的快速指针(如果可能的话)。我们还研究了这个很有帮助的问题:RVM-isthereawayforsimpleofflineinstall?但有点误导,因为答案只向我们展示了如何离线在RVM中安装ruby。我们需要能够离线安装RVM本身,并查看脚本https://raw.github.com/wayn
我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当
我有一个奇怪的问题:我在rvm上安装了rubyonrails。一切正常,我可以创建项目。但是在我输入“railsnew”时重新启动后,我有“程序'rails'当前未安装。”。SystemUbuntu12.04ruby-v"1.9.3p194"gemlistactionmailer(3.2.5)actionpack(3.2.5)activemodel(3.2.5)activerecord(3.2.5)activeresource(3.2.5)activesupport(3.2.5)arel(3.0.2)builder(3.0.0)bundler(1.1.4)coffee-rails(
我刚刚为fedora安装了emacs。我想用emacs编写ruby。为ruby提供代码提示、代码完成类型功能所需的工具、扩展是什么? 最佳答案 ruby-mode已经包含在Emacs23之后的版本中。不过,它也可以通过ELPA获得。您可能感兴趣的其他一些事情是集成RVM、feature-mode(Cucumber)、rspec-mode、ruby-electric、inf-ruby、rinari(用于Rails)等。这是我当前用于Ruby开发的Emacs配置:https://github.com/citizen428/emacs
我有一个围绕一些对象的包装类,我想将这些对象用作散列中的键。包装对象和解包装对象应映射到相同的键。一个简单的例子是这样的:classAattr_reader:xdefinitialize(inner)@inner=innerenddefx;@inner.x;enddef==(other)@inner.x==other.xendenda=A.new(o)#oisjustanyobjectthatallowso.xb=A.new(o)h={a=>5}ph[a]#5ph[b]#nil,shouldbe5ph[o]#nil,shouldbe5我试过==、===、eq?并散列所有无济于事。
我有一些Ruby代码,如下所示:Something.createdo|x|x.foo=barend我想编写一个测试,它使用double代替block参数x,这样我就可以调用:x_double.should_receive(:foo).with("whatever").这可能吗? 最佳答案 specify'something'dox=doublex.should_receive(:foo=).with("whatever")Something.should_receive(:create).and_yield(x)#callthere
我正在尝试在我的centos服务器上安装therubyracer,但遇到了麻烦。$geminstalltherubyracerBuildingnativeextensions.Thiscouldtakeawhile...ERROR:Errorinstallingtherubyracer:ERROR:Failedtobuildgemnativeextension./usr/local/rvm/rubies/ruby-1.9.3-p125/bin/rubyextconf.rbcheckingformain()in-lpthread...yescheckingforv8.h...no***e
我的最终目标是安装当前版本的RubyonRails。我在OSXMountainLion上运行。到目前为止,这是我的过程:已安装的RVM$\curl-Lhttps://get.rvm.io|bash-sstable检查已知(我假设已批准)安装$rvmlistknown我看到当前的稳定版本可用[ruby-]2.0.0[-p247]输入命令安装$rvminstall2.0.0-p247注意:我也试过这些安装命令$rvminstallruby-2.0.0-p247$rvminstallruby=2.0.0-p247我很快就无处可去了。结果:$rvminstall2.0.0-p247Search