Spring 基础知识 (一)
1.1、为什么要使用Spring
问题1:代码耦合高
先来看一段代码:
public class A {
AInterface a;
A()
{
a = new AInterfaceImp();
}
}
接口驱动编程有很多好处,它可以提供不同灵活的子类实现,增加代码稳定和健壮性等等,但是接口一定是需要实现的,也就是如下语句迟早要执行:IADAO ADAO = new ADAOImpl()
这样一来,耦合关系就产生了。
Class A与AInterfaceImp就是依赖关系,如果想使用AInterface的另外一个实现AInterfaceImpB就需要更改代码了。当然我们可以建立一个Factory来根据条件生成想要的AInterface的具体实现,即:
InterfaceImplFactory
{
AInterface create(Object condition)
{
if(condition == condA)
{
return new AInterfaceImpA();
}
else if(condition == condB)
{
return new AInterfaceImpB();
}
else
{
return new AInterfaceImp();
}
}
}
表面上是在一定程度上缓解了以上问题,但实质上这种代码耦合并没有改变。
问题2:控制事务的繁琐
对于事务操作,必须在所有需要控制事务的地方,手动用代码完成几乎完全相同的事务控制逻辑,开发效率低下,产生大量代码冗余,并且难以方便的处理事务嵌套需求。
如何降低业务逻辑部分之间耦合度,提高程序的可重用性,同时提高开发的效率,就成为了重点关注的内容。
AService{
public void save(...){
开启事务...
dao.save(...);
提交事务...
}
public void update(...){
开启事务...
dao.update(...);
提交事务...
}
}
1.2、Spring概述
- Spring是一个轻量级的DI/IoC和AOP容器的开源框架。
- Spring提倡以”最少侵入”的方式来管理应用中的代码,这意味着我们可以随时安装或卸载Spring。
- 非侵入式设计,从框架角度可以这样理解,无需继承框架提供的类,这种设计就可以看作是非侵入式设计,如果继承了这些框架类,就是侵入设计,如果以后想更换框架之前写过的代码几乎无法重用,如果非侵入式设计则之前写过的代码仍然可以继续使用。
IoC和DI
**IoC : **即“控制反转”,它是一种设计思想,不是一种技术。在开发中,这意味着将你设计好的对象交给一个容器控制,而不是传统的在你的对象内部直接控制。
在传统的程序设计中,我们是直接在对象内部通过new关键字来创建对象,是程序主动去创建所依赖的对象;而现在,可以有一个专门的容器来创建这些对象,即由Ioc容器来控制对象的创建;由容器帮我们查找及注入所依赖的对象,你的对象只需要接受依赖的对象,并不需要再自己控制。
**DI : **即“依赖注入”,和字面意思一样,就是由容器动态的将某个依赖关系注入到代码之中,通过依赖注入机制,可以提升组件重用的频率,只需要通过简单的配置,就可指定目标需要的资源,不需要关心具体的资源来自何处,由谁实现。这里需要注意一点,注入是指注入某个对象所需要的外部资源,包括对象、资源、常量数据等,而不仅仅局限于对象。
DI是IoC的表现,IoC是DI的基础,其实就是同一个概念的不同描述。
举个简单的例子来理解,这就像是在生活中的租房一样,常见的情况是,我们会到处去找要出租的房子,然后去看房子的价格、地段等等信息是不是符合我们的需求,需要自己处理其中的每个环节。
现在我们有了中介所,就相当于IoC,我们现在要做的就是告诉中介,我需要一个什么样的房子,它需要满足什么条件,这在程序中就表现为接口,我们只需要等中介给我们选房子就可以了,整个选房子的过程不再由我自己控制,而是由中介这样一个类似容器的机构来控制。
在理解了DI/IoC后,再来看看问题一,现在尝试着将问题一的代码改造一下:
BeanFactory类
//对象工厂
public enum BeanFactory {
INSTANCE;
private static Map<String, String> beanMap = new HashMap<>();
// Map中的key就是对象的名字,value就是一个class对象.
private Map<String, Object> cache = new HashMap<>();
static {
try {
InputStream in = BeanFactory.class.getClassLoader()
.getResourceAsStream("beans.xml");
// 把beans.xml中的数据存放于Map中
initBeanMap(in);
} catch (Exception e) {
e.printStackTrace();
}
}
// 约定优于配置:传递的name参数必须等于beanMap中的key(beans.xml中bean元素的id值)
public <T> T getBean(String name, Class<T> requiredType) {
if (!beanMap.containsKey(name)) {
throw new RuntimeException("在beans.xml文件中找不到指定的" + name);
}
String className = beanMap.get(name);
try {
Object obj = cache.get(name);
if (obj == null) {
obj = Class.forName(className).newInstance();
if (!requiredType.isInstance(obj)) {
throw new RuntimeException("bean类型不匹配");
}
cache.put(name, obj);
}
return (T) obj;
} catch (Exception e) {
throw new RuntimeException("创建对象bean失败", e);
}
}
private static void initBeanMap(InputStream in) {
try {
SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
parser.parse(in, new DefaultHandler() {
private String tagName = null;
public void startElement(String uri, String localName, String qName, Attributes attr) throws SAXException {
tagName = qName;
if ("bean".equals(tagName)) {
String id = attr.getValue("id");
String className = attr.getValue("class");
beanMap.put(id, className);
}
}
public void endElement(String uri, String localName, String qName) throws SAXException {
tagName = null;
}
});
} catch (Exception e) {
throw new RuntimeException("操作失败", e);
}
}
}
A类
public class A {
public AInterface a;
A()
{
a = BeanFactory.INSTANCE.getBean("AInterfaceImp", AInterface.class);
}
}
beans.xml
<beans>
<bean id="AInterfaceImp" class="com.demo.start.AInterfaceImp" />
</beans>
通过IoC模式可以彻底解决问题一中的代码耦合,它把耦合从代码中移出去,放到统一的XML 文件中,通过一个容器在需要的时候把这个依赖关系形成,即把需要的接口实现注入到需要它的类中。
采用依赖注入技术之后,不需要直接new来获得这个对象,而是通过相关的容器控制程序来将对象在外部new出来并注入到主类里的引用中。而具体获取的方法、对象被获取时的状态由配置文件(如XML)来指定。
面向切面编程(AOP)
关于AOP的相关内容将会在后面的文章中详细介绍,这里就不再赘述;
1.3、Spring使用
Spring需要的jar包
<properties>
<spring-version>5.2.1.RELEASE</spring-version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring-version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring-version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jcl</artifactId>
<version>${spring-version}</version>
<scope>compile</scope>
</dependency>
<!--Spring测试包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring-version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring-version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring-version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${spring-version}</version>
<scope>compile</scope>
</dependency>
</dependencies>
Spring示例程序
创建一个applicationContext.xml文件,在applicationContext.xml中完成bean的配置。
<?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="helloword" class="com.SpringDemo.HelloWorld" >
<property name="name" value="zhangsan"/>
</bean>
</beans>
启动容器,从容器中得到bean,调用bean响应的方法。
public class HelloWorld {
private String name;
public void setName(String name) {
this.name = name;
}
public void hello(){
System.out.println("你好:"+name);
}
public static void main(String []args) throws Exception{
HelloWorld helloWord = null;
Resource resource = new ClassPathResource("applicationContext.xml");
BeanFactory beanFactory = new XmlBeanFactory(resource);
helloWord = (HelloWorld) beanFactory.getBean("helloword");
helloWord.hello();
}
}
Spring中获取bean的三种方式
1.按照类型拿bean: 要求在Spring中只配置一个这种类型的实例
helloWord = (HelloWorld) beanFactory.getBean(HelloWorld.class);
2.按照bean的名字拿bean: 按照名字拿bean不太安全
helloWord = (HelloWorld) beanFactory.getBean("helloword");
3.按照名字和类型: 推荐使用这种方式
helloWord = (HelloWorld) beanFactory.getBean("helloword",HelloWorld.class);