这学期的课程安排,我们可以系统的学习spring框架了。 相比较自己看博客笔记,系统的在课堂上学习还是很有必要。
spring是一个开源框架,是为了解决企业应用程序开发复杂性而创建的。 框架的主要优势之一就是利用其分层架构。 分层架构允许选择使用哪一个组件,同时为了J2EE 应用程序开发提供集成的框架。
spring 的核心是 IOC, Inverse of control, 控制反转, 和 AOP , Aspect oriented programming,围绕切面的编程。
第一步:
导入maven包:
spring含有7个逻辑组件,我们有必要对每个组件的作用进行相应的了解,因为只有了解了每个组件的作用以及它相关的依赖关系,我们在使用时才能左到心里有数,才算得上“学习”吧。
我在早些时间对spring的4个包进行了源码的遍历,虽然意义不大,但是纠正了我的一些误区。 比如 : spring-beans,提供了很多的功能,但是它并不是spring提出的逻辑组件。 就像默默无闻的科技工作者一下,虽不被人所知,但是发挥着它强有力的作用! 此外,spring-beans 位于spring-aop逻辑组件中。 但是spring-aop即作为了一个逻辑组件的名称,同时也是一个包。 可谓双赢。 就像 李彦宏,马克扎克伯格之类。 同时spring-context也是被自己遍历过,所以它里面的每个类我都应该不是第一次照面。
先看书本的理论: spring-context即spring上下文是一个配置文件,向spring框架提供上下文信息。 spring上下文包括企业服务,如: JNDI,EJB,电子邮件,国际化,检验和调度。
理解这个概念的关键在于一个词语: 上下文! 何谓上下文?
有道释义:context: 上下文,语境。
理解上下文的含义对于初次接触的人来说并不轻松。 先说自己所接触过的上下文吧: 在学习javaweb的时候,首次听说了上下文的概念----> 之后接触到了spring框架,再次接触到了 上下文的说法 ------> 直到最后学习了安卓,又接触到了上下文,并且在里面摸爬滚打了好一阵。 总算是对上下文有一些感性上的认识。
就我的理解来看,我们直到一个程序要被执行,它需要经过这样一些流程: 源代码(二进制串,被保存在硬盘上)--->被加载进内存--> 运行。 当然它实际上还经历了很多复杂的过程如链接资源文件,内存重定位,权限验证等等。广范一点来说,上下文就是整个程序二进制串在内存中的长度所代表的所有内容。 狭义一点的说,它是程序使用者指定的当前程序的某一段内存长度。(不一定是连贯的,但是它们在逻辑一般具有连贯关系)。 上下文就是这样的一种存在,能够在你当前的环境(进程中)得到你想要得到的。
对上下文最简单的一种实现: public static final Map context = new HashMap(); 只要它能满足: 单例,生命周期跟进程一样长。 那么他就是当前程序的上下文了。上下文的本质是内存。
继续回到练习:当我们导入改包了之后,它提供给我们的包结构有:
其中属于core逻辑组件的是: core,logging; 属于aop组件的是:core,beans,aop; 属于expression组件的是:core,beans,aop,expression,context。 总之,就是如果我们用的产品,都要依赖逻辑组件: spring-core,spring-aop,spring-context。 也就是要包含这里的依赖。 当然,如果我们是要基于spring来开发产品,那么依赖可能就会另算。
ok,那还有个问题,就是我们为什么必需得用到上下文的时候才能IOC呢?
通过学习的理论知识我们知道,spring提供容器的接口是: BeanFactory,它位于核心组件: spring-aop下,(spring-beans包下)但是我们都知道,接口不能被我们实例化。 或者说只能以全实现的方式实现一个接口,那样的话接口存在的意义就不大了。 我对接口的理解就是,它具有发散性,抽闲类具有内聚性,当我们设计的时候,判断某个逻辑块将来具有发散的趋势就用接口,具有内聚的趋势就用抽象类。 因此,这个BeanFactory不能被我们直接使用,或者不能被我们方便的使用。因此我们需要寻找它的实现类。
在spring-context中就提供了两个它的实现:
但是它实际应该是有三个实现的,还有一个是XmlWebApplicationContext。 它是与web项目紧密结合的,因此它的实现位于:
这里顺便提一下,web项目本身有一个上下文。 是由服务器容器提供的,spring-web核心组件提供了相关的api使得这两个容器在某种程度上能够共享。 应该是共享而不是拷贝,因为无论从性能方面还是设计方面拷贝都不占什么优势,只是在多线程并发上面可能有一点好处。
嗯嗯 ,它的三个实现类我知道的都写在上面了。 但是去看Beanfactory的实现结构的时候,发现还是有一些非抽象类也实现了它,或许也可以用吧。 等以后去探究探究。
----------------------
到目前为止,我们已经实现了环境的准备。下面就开始使用吧。
先看ClassPathXmlApplicationContext的用法:
它可以通过指定一个父类上下文(相当于父类上下文的一个子类,类似于拷贝吧),单个配置文件,多个配置文件,以及单个配置文件的路径或者多个配置文件的路径。 嗯嗯算是所有的使用方式了呢!
再看看FileSystemXmlApplicationContext的用法:
它能够通过父类上下文,单个配置文件,多个配置文件来进行配置。 它们的使用还是大同小异的。
我本次练习使用的是ClassPath的实现。
先定义好一个实体类:
package com.automannn.practice.entity;
/**
* @author automannn@163.com
* @time 2018/9/19 9:44
*/
public class User {
private Long id;
private String username;
public User() {
}
public User(Long id, String username) {
this.id = id;
this.username = username;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
'}';
}
}
然后建一个配置文件:配置文件的名称随意。 但是必须引入shema的xml约束。 写法再spirng.io可以查到。 但是其实不写这个shema应该也是可以,只是sping官方强制要我们写,不然报错。 因为这样可以提高命中率,不会由于你的xml结果的问题而导致程序异常退出。 因为游戏规则是别人制定的,并且遵守规则对大家都有好处,因此我们按照它的shema来就是。
<?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="user" class="com.automannn.practice.entity.User" />
</beans>
最后就是使用了,最简单的就是新建一个还有main入口方法的函数:
package com.automannn.practice;
import com.automannn.practice.entity.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author automannn@163.com
* @time 2018/9/19 9:48
*/
public class Main {
public static void main(String[] args) {
ApplicationContext context=null;
//推荐使用
context= new ClassPathXmlApplicationContext("beans.xml");
//String[] configs = {"beans1.xml","beans2.xml","beans3.xml"};
//context = new ClassPathXmlApplicationContext(configs);
//推荐用于 使用spring 框架的独立的应用程序种
//context = new FileSystemXmlApplicationContext("配置文件的全路径");
//String[] configss = {"path1","path2","path3"};
//context = new FileSystemXmlApplicationContext(configss);
//spring-web 提供的一个用于 web工程量身定制的一个实现类
//该静态方法可支持 在jsp 和servlet 种 根据 ServletContext 取得 IOC容器的引用
//context = WebApplicationContextUtils.getRequiredWebApplicationContext(null);
User u = (User) context.getBean("user");
System.out.println(u.toString());
}
}
最后一步,就是将程序启动起来了:
通过截图,我们发现虽然打印了出来我们要的信息,但是这几乎不是我们要的结果。 因为我们用的时候都是: new User("属性1","属性2"); 或者:User u= new User(); u.setShuxing1("属性1"); u.setShuxing2("属性2"); 因此我们有必要了解bean 容器配置文件的相关用法。
一,bean工厂获得实例的方法:
1, 使用构造器的方法:
2,使用静态工厂实例化的方法:
package com.automannn.practice.entity;
/**
* @author automannn@163.com
* @time 2018/9/19 10:19
*/
public class UserFactory {
private static final User user =new User(1L,"小二郎");
private UserFactory(){}
public static User getInstance(){
return user;
}
}
3,使用实例工厂实例化的方法:
package com.automannn.practice.entity;
/**
* @author automannn@163.com
* @time 2018/9/19 10:19
*/
public class UserFactory {
private static final User user =new User(1L,"小二郎");
private UserFactory(){}
public static User getInstance(){
return user;
}
public User getUser(){
return new User(2L,"李四");
}
}
二,依赖注入的配置方法:
1,property的方式:
2,有参构造方法的方式:
当参数类型冲突的时候,需将二者结合使用。
3.autowire,自动装配的方式:
package com.automannn.practice.entity;
/**
* @author automannn@163.com
* @time 2018/10/9 12:42
*/
public class UserComponent {
User user;
public User getUser() {
return user;
}
@Override
public String toString() {
return user.toString();
}
}
默认的自动注入的方式是按类型。 因此当我们有多个接口的实现类并且以接口的方式注入的话,就不能用@Autowire,而用@Resource(name="").
当然这里说到了注解,那么注解又是spring框架提供给我们的另一个强大功能!也就是基于注解的spring容器。
基于注解的spring容器的学习:
在学习之前我们有必要回顾一些历史:
java的注解与1.5引入,时间在2004年前后,同时,java1.5之后,Java就成了一门成熟的开发语言,算是正式在语言市场确定自己江湖霸主的地位。虽不能算上武林盟主,但怎么也能算的是华山派啊,少林派啊这等有头有脸的大门派了。
也就是04年左右,spring框架势如破竹,在江湖上展露头角,并且颇受大宗族的喜爱。一路势如破竹,遂成为现今武林世界不容小觑的一股力量! 当然14年左右springboot也展露了头角,它的未来必定又是一方雄霸。
因此,spring也好,springboot也好,它们的发展都是与java的脉络发展息息相关的。 同时我们有必要知道,注解并不是spring所带来的功能,而是Java自身的一个功能。 理解这点对我们学习很有必要!
下面就正式进入注解的学习:
为什么使用注解? 因为它很好的满足了我们程序开发的两个要求:高内聚,低耦合。就是说,我们希望开发的时候,与一个逻辑相关的所有代码都尽量的放在一起,越紧密越方便自己找到,就能更轻松的实现业务逻辑;但是对于逻辑联系不强烈,或者说将来有很大可能性要修改的逻辑块,我希望它们就最好一点关系都没有,将来修改的时候不会导致牵一发而动全身!
注解本身不给程序改变,它只是提供了一种新型的方式编程。 就spring的ioc注解而言,它的逻辑任然是一样的,它主要就是以注解去代替了xml配置文件! 注意是代替,所以它们的逻辑功能是一毛一样的。 因为当配置文件大起来,多起来的时候,配合复杂的业务逻辑,足够让我们感到烦躁了!
bean容器的注解,实际上就一个: @Component
但是它派生了一些:@Controller,@Service,@Repository
自动装配的注解: @Autowire; 此外要注意,自动装配的方式是通过setter访问器写入的,而非使用直接使用反射的方式去设置属性。 大概是因为开销的原因吧。 因此,当我们用@Resource(name="")的时候,即可以写在属性上面(前提是必须是非静态属性,并且有标准的setter访问器),也可以写在setter访问器上的原因。
使用注解的步骤:
spring1.x,需要我们手动的打开注解的功能:
<context:annotation-config/>
配置扫描的包路径:
<context:component-scan base-package="com.automannn.practice"/>
如果是spirng2.x及以后的版本,则只需要指定扫描的包路径就可以了。
----最后总结一下,spring容器的使用:
一,基于配置文件
1,引入spirng-context的依赖
2,定义一个实体
3,新建一个配置文件
4,使用spring容器
二,基于注解:
1,开启注解扫描的功能
2,指定指定扫描的路径
3,使用