spring
Sping
作用于系统的服务层,Spring是面向 Bean(即 Java 对象) 的编程,spring 把相互协作的关系称作是依赖关系
spring框架架构
Spring 总共大约有20个模块,由1300 多个文件构成,这些组件被分别整合在 核心容器(Core Container)、AOP(Aspect Oriented Programming)和设备支持(Instrmentation)、数据访问及集成(Data Access、Integeration)、Web、报文发送(Messaging)、测试6个模块集合中。
-
核心容器:Spring-beans 和 Spring-core 模块是Spring 框架的核心模块,包含控制反转(Inversion of Control IoC)和依赖注入(Dependency Injection DI),核心容器提供 Spring 框架的基本功能。核心容器的主要组件是 BeanFactory,工厂模式的实现。BeanFactory 使用控制反转(IoC)思想将应用程序的配置和依赖性规范与实际的应用程序代码分开
Spring 上下文 Spring Context:Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能
Spring-Expression 模块是统一表达式语言(unified EL)的扩展模块,可以查询、管理运行中的对象,同时也方便的可以调用对象方法,如操作数组、集合等。他的语法类似与传统的 EL,但提供了额外的功能,最出色的就是函数调用和简单字符串的模板函数
-
Spring-AOP:Spring-AOP 是 Spring 的另外一个核心模块,在Spring中,他是以 JVM 的动态代理技术为基础,然后设计出了一系列的 AOP 横切实现,比如前置通知、返回通知、异常通知。通过其配置管理特性,Spring AOP模块直接将切面的编程功能集成到了 Spring 框架中。所以,可以很容易的使 Spring 框架的任何对象执行 AOP
-
Spring Data Access(数据访问):由Spring-idbc、spring-tx、spring-orm、spring-jms 和 spring-oxm 5个模块组成 。spring-jdbc 模块是 Spring 提供的 JDBC 抽象框架的主要实现模块。用于简化 Spring JDBC
Spring-tx 模块是spring JDBC 事务控制实现模块,使用 spring框架,他对事务做了很好的封装,通过他的 AOP 配置,可以灵活的配置在任何一层
spring-Orm 模块是 ORM框架的支持模块,主要集成 hibernate、Java Persistence API(JPA) 和 Java Data Objects(JDO)用于资源管理、数据访问对象(DAO)的实现和事务策略
Spring-Jms 模块 (Java Messaging Service) 能够发送和接受信息
Spring-Oxm 模块主要提供一个抽象层以支撑 OXM (OXM 是 Object-to-XML-Mapping 的缩写,他是一个 O/M-mapper,将Java对象映射成 XML 数据,或者将 XML 数据映射成Java对象),例如:JAXB、Castor、XMLBeans、JiBX和 XStream 等
-
Web 模块:由Spring-web、Spring-webmvc、Spring-webscoket 和 Spring-webmvc-portlet 4个模块构成,Web上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作
-
报文发送:即 Spring-messaging 模块
Spring-messaging 是 spring4 新加入的一个模块,主要职责是为 Spring 框架集成一些基础的保温传送应用
-
单元测试:即 Spring-test 模块,Spring-test 模块主要是为测试提供支持
Spring 环境搭建
需要 maven 项目进行环境搭建
Spring 依赖添加
Maven 仓库:https://mvnrepository.com/
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.4.RELEASE</version>
</dependency>
编写 Bean 对象
public class UserService{
public void test(){
System.out.println("Hello");
}
}
添加 spring 配置文件
-
创建 resource 文件夹,与 java 文件夹同级,并设置其为资源目录
-
新建 xml 文件(spring.xml)
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- xmlns 即 xml namespace xml 使用的命名空间 xmlns:xsi 即 xml schema instance xml 遵守的具体规范 xsi:schemaLocation 本文档 xml 遵守的规范(官方规定) --> <!-- xml 中配置 bean 对象 id:bean对象的唯一标识,一般是 对象的名称首字母小写 class:bean 对象的路径 --> <bean id="userService" class="com.xxx.service.UserService"></bean> </bean>
加载配置文件,获取实例化对象
public class App{
public static void main(String[] args){
// 获取 Spring 上下文环境
ApplicationContext context = new ClassPathXmlApplicationContext("apring.xml");
// 通过 getBean 方法得到 spring 容器中实例化好的 Bean 对象
// userService 代表的是配置文件中 bean 标签的 id 值
UserService service = (UserService) service.getBean("userService");
// 调用方法
service.test();
}
}
Spring IoC 配置文件加载
配置文件加载
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.3c.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userService" class="com.package.service.UserService"></bean>
</beans>
相对路径加载
Application ac = new ClassPathXmlApplicationContext("spring.xml");
多个配置文件加载
相对路径加载(创建多个配置文件,分别加载)
Application ac = new ClassPathXmlApplicationContext("service.xml", "dao.xml");
总配置文件加载引入其他配置文件,再通过相对路径加载
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.3c.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<import resource="dao.xml"/>
<import resource="service.xml"/>
</beans>
两大技术
spring 的核心概念就是 IoC 和 AOP
控制反转 / 依赖注入(IoC / DI)
不管是控制反转还是依赖注入,他们都可以这样理解:当某个Java实例(调用者)需要另一个Java实例(被调用者)时,在传统的程序设计过程中,通常有调用者来创建被调用者的实例。但是在依赖注入/控制反转模式下,创建被调用者的工作不再是有调用者来完成,而是由Spring容器来完成,然后注入调用者。
要实现 控制反转与依赖注入需要 Bean对象满足 JavaBean 的创建规范
Spring IOC 容器 Bean对象实例化模拟
- 定义 Bean 工厂接口,提供获取 Bean 方法
- 定义 Bean 工厂接口实现类,解析配置文件,实例化 Bean 对象
- 实现获取 Bean 方法
定义 Bean 属性对象
引入 dom4j 、XPath 依赖包
<!-- https://mvnrepository.com/artifact/org.dom4j/dom4j -->
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>1.1.6</version>
</dependency>
创建配置文件
如果没有 resources 资源目录需要手动创建
<?xml version="1.0" encoding="UTF-8" ?>
<beans>
<!-- 设置 javaBean 对应的标签 -->
<bean></bean>
<bean></bean>
</beans>
控制反转
即控制权的转移,将创建对象的方式反转 ,使用 spring 完成创建以及注入就是我们将控制权反转给程序
原始写法 UserDao dao = new UserImpl()
-> spring 控制 UserDao dao = ctx.getBean('dao', userDao.class)
依赖注入
在依赖注入模式下,创建被调用者的工作不由调用者来完成,因此称作依赖注入
依赖注入通常有三种方式:
1. 设置注入:IoC 容器使用属性的 setter 方法来注入被依赖的实例
2. 构造注入:IoC 容器使用构造器来注入被依赖的实例
3. 注解注入
// 设置注入 <property>标签
<bean id="hello" class="demo.Hello">
<!-- 写法1 -->
<property name="name" value="world"></property>
<!-- 写法2 -->
<property name="name">
<value>world</value>
<!-- 若value的值是特殊字符串,避免被转移可以使用如下格式 -->
<value><![CDATA[ string ]]></value>
</property>
</bean>
<!-- 写法3,直接p:带属性名赋值 -->
<bean id="person5" class="spingtest.collection.Person"
p:age="30" p:name="Queen" p:car-ref="car"></bean>
// 构造注入 <constructor-arg>标签;可以给属性赋值为 null
<bean id="car" class="spingtest.collection.Car">
// index 指定构造器属性顺序
<constructor-arg value="Audi" index="0"></constructor-arg>
<constructor-arg value="Shanghai" index="1"></constructor-arg>
// 如果有多个构造方法,相同位置的属性不同,可以使用 type 来区分对应的构造方法
<constructor-arg value="300000" type="double"></constructor-arg>
</bean>
Bean 对象的引用
// 内部引用,内部 Bean 只能被内部使用,
<bean id="car" class="com.panlei.demo.Car">
<property name="name" value="奥迪"></property>
<property name="price" value="400000"></property>
<property name="tires" >
<!-- 可以在里面用对应的数据类型装配数据 -->
<!-- 如果属性是map,需要用 <entry>标签存数据 -->
<class="com.panlei.demo.Tire">
<property name="name" value="朝阳"></property>
<property name="price" value="3000"></property>
</bean>
</property>
</bean>
// 外部引用,可以被多个 Bean 调用;使用 ref 标签引用
<bean id="tire" class="com.panlei.demo.Tire">
<property name="name" value="朝阳"></property>
<property name="price" value="3000"></property>
</bean>
<bean id="car" class="com.panlei.demo.Car">
<property name="name" value="奥迪"></property>
<property name="price" value="400000"></property>
<property name="tires" ref="tire"></property>
// 外部引用并赋值,需要先初始化对应的对象并且该对象具有这个属性才能进行其他属性的赋值
<property name="tire.number" value="4"></property>
</bean>
Bean 注解注入
声明 Bean 注解
@Component // 组件 没有明确规定其角色,作用在类级别上声明当前类作为一个业务租还,被 Spring IoC 容器维护
@Service // 在业务逻辑层(service)对类进行声明
@Repository // 在数据访问层(dao)对类进行声明
@Controller // 在展现层(MVC)使用,标注当前类为一个控制器
注入Bean注解
@Autowired // Spring 官方提供注解
@Inject
@Resource
使用注解注入就不需要在 xml 文件配置 bean 文件,需要添加 <context:component-scan base-package="annspring"/>
标签,base-package
里面就是需要扫描注解的包文件
@Test
加在实现类上面,可以不用写main函数,直接执行该方法
@Component("name")
在不明确 Bean 属于三层架构的那一层时就使用此注解;带来名字可以直接通过名字获取 Bean,若不带名字就使用类名获取 Bean;还可以写成 @Component(value = "name")
@Repository("name")
位于持久层,用于标注数据访问组件,即 Dao 层组件;可以带参数,代表 Bean 的名称。若不带参数 Bean 的名称就是类名 (首字母小写) ,服务层和控制层注解也能不带参数
@Service("name")
服务层的注解
@Controller("name")
控制层注解
@Autowired + @Qualifier
自动装配
// 注解的参数注入
@Autowired // 按照属性类型去找要注入的bean,可以单独使用,不需要setter方法
@Qualifier(value = "name") // 按照 id 查找,不能单独使用
// 写法1,在方法上进行参数构造
@Autowired
@Qualifier("serviceImpl")
public void service(ServiceImpl service){
this.service = service;
}
// 写法2,在构造器上注入参数
@Autowired
public void setter(@Qualifier("ServliceImpl")ServiceImpl service){
this.service = service;
}
spring 创建对象
@Scope("singleton") 单例模式,只创建一个对象在 IoC 容器中,默认这种方式
@Scope("prototype") 多例模式,使用一次 spring就会创建一个对象
@Resource(name = "name")
注解参数
@Resource 按照名称去查找参数,@Autowired 按照参数类型查找参数。前者精确度更高
@Lazy(true/false)
懒加载
当值为true时,spring初始化时不会创建这个对象,只有使用时才会创建
@PostConstruct
构造方法被执行完后,开始执行这个注解方法,加载初始化资源
@PreDestroy
在容器被销毁时执行这个方法,主要是为了正确释放资源
@Configuration
用来定义配置类,可以替换配置文件
被此注解注解的类中应该有一个或多个被 @Bean 注解的方法
三层架构注解之间的关系

Spring 工厂类
ApplicationContext :新版本的工厂类,加载配置文件的时候,就会将Spring管理的类都实例化。
ApplicationContext有两个实现类
ClassPathXmlApplicationContext :加载类路径下的配置文件
FileSystemXmlApplicationContext :加载文件系统下的配置文件
spring 框架部署
-
ClassPathXmlApplicationContext("applicationContext.xml") spring框架读取配置文件,创建一个容器(hashMap)
-
往容器里装东西(Java 对象)
- 明确需要装载 Java 对象
- 创建对应的 Java 对象 => 内部使用反射创建对象
- spring 先检查 bean 之间的依赖关系,给对应的属性进行赋值
- 成功创建出对象,将对象放在容器里面去
hashMap.put("Class_name", "created_Bean")
-
开发者使用容器里已经被创建好的Java对象(Bean)
-
配置 Java Bean,生命周期和 IoC容器生命周期一样长;默认情况(即单例模式)
-
如果说 JavaBean 在被多线程共享时,产生线程安全问题,那么需要使用多例模式。我们使用一次JavaBean,spring框架就为我们创建一个新的对象,这个对象不会在IoC容器中存放,在spring启动的时候,也不会创建这个JavaBean对象,调用者自己管理这个JavaBean的生命周期
-
单例模式(单例模式可以不用加 scope 标签)
多线程共享这个对象,在IoC容器中只有一份
-
懒加载(单例模式下使用)
lazy-init="true"
直接加在 JavaBean 标签上;局部懒加载标签优先default-laze-init="true"
全局懒加载,加在最外层的 Bean 标签上;在sping启动的时候,不会马上创建对象,第一次使用的时候再去创建对象,创建好的对象同样放在IoC容器,供后面的使用者共享,加快系统启动
注解注入 spring运行流程
- spring 启动的时候,创建 IoC 容器
- 扫描带注解的类 (component、controller、service、repository);IoC看到这四个注解就会创建 JavaBean,放在IoC容器里面,提供给调用者使用
- @Autowaird 这种类似的注解,加在属性上面,IoC 在启动时会扫描这种类似的注解,完成值的注入
- 静态属性的不能直接在属性上面注入,必须使用方法。静态的属性不属于 IoC 容器
- 单例懒加载注入
@Lazy(true)
;多例只需要再加上注解@Scope("prototype")
面向切面编程 (AOP)
非核心业务就叫切面
Aop是一种编程思想,是面向对象编程(OOP) 的一种补充,面向对象将程序抽象成各个层次的对象,而面向切片编程是将程序抽象成各个切面
AOP要达到的效果是,保证开发者不修改代码的前提下,去为系统中的业务添加某种通用功能。AOP 的本质是由AOP框架修改业务组件的多个方法的源代码。AOP 其实就是代理模式的典型应用(代理模式(Proxy):为其他对象提供一种代理以控制对这个对象的访问)
AOP是另一种编程思想
按照AOP框架修改源代码的时机可以将其分为两类:
静态 AOP 实现:
- AOP 框架在编译阶段对程序源代码进行修改,生成了静态的 AOP 代理类
生成的 *.class 文件已经被改掉了,需要使用特定的编译器,比如 AspectJ - 以数据库操作层dao为场景,将dao层中事务的开启和提交进行分离
- 存在的问题:
- 静态代理模式并没有做到事务的重用
- 假设dao层有100个类,100个proxy,接口中有多少方法,在proxy层就得实现多少方法,有多少方法就要开启和提交多少事务
- 如果一个proxy实现了多个接口,如果其中的一个接口发生变化(添加了一个方法),那么proxy也要做相应的改变
动态 AOP 实现:
-
没有写代理类的源代码,代理类出自字节码层
-
AOP 框架在运行阶段对动态代理生成的代理对象
在内存中以 JDK 动态代理,或 cGlib 动态地生成 AOP 代理类,比如 SpringAOP -
动态代理模式就是让jvm动态的生成的代理类;动态代理需要设置拦截器
-
目标类导入进来 personDao实现类
-
事务导入进来 : Transaction类
-
invoke完成 :执行
1、开启事务
2、调用目标对象的方法
3、事务的提交
-
常用AOP之间的比较关系
拦截器
1、拦截器的invoke方法是在什么时候执行的?
当在客户端,代理对象调用方法的时候,进入到了拦截器的invoke方法
2、代理对象的方法体的内容是什么?
拦截器的invoke方法的内容就是代理对象的方法的内容
3、拦截器中的invoke方法中的参数method是谁在什么时候传递过来的?
代理对象调用方法的时候,进入了拦截器中的invoke方法,所以invoke方法中的参数method就是代理对象调用的方法
存在问题
1、在拦截器中除了能调用目标对象的目标方法以外,功能是比较单一的,在这个例子中只能处理事务
2、拦截器中的invoke方法的if判断语句在真实的开发环境下是不靠谱的,因为一旦方法很多if语句需要写很多。
AOP相关术语
- 通知(Advice): AOP 框架中的增强处理。通知描述了切面何时执行以及如何执行增强处理。切面里的具体业务方法,这些方法要组合到核心业务上执行
- 通知分为五种类型:前置通知、后置通知、异常通知、最终通知、环绕通知,
- 连接点(join point): 连接点表示应用执行过程中能够插入切面的一个点,这个点可以是方法的调用、异常的抛出。在 Spring AOP 中,连接点总是方法的调用。激活点,告诉spring 切面要工作了
- 切点(PointCut): 可以插入增强处理的连接点。告诉 spring 那些核心业务方法被执行时需要被动态拦截
- 切面(Aspect): 切面是通知和切点的结合。非核心业务,比如日志切面或事务切面等
- 引入(Introduction):引入允许我们向现有的类添加新的方法或者属性。给类增加新功能,在不修改原类的基础上,把另一个实现新功能的类直接引入进来。这样原类就具有了新类的功能
- 织入(Weaving): 将增强处理添加到目标对象中,并创建一个被增强的对象,这个过程就是织入。
Spring + Mybatis + TomCat
-
把 spring 核心配置文件放在 web.xml
通过 contextListener 监听器的初始化方法,完成 IoC 容器的初始化
-
spring 框架依赖我们写的配置
- 初始化数据库连接
- 初始化 mybatis 的配置
- 初始化 javaBenan,初始化数据层和业务层的 javaBean
- 初始化事务管理
Spring 核心配置文件
事务注解
-
可以加在类上,也可以加在方法上面。
加在类上面表示所有数据操作的方法全开事务,力度较大影响并发。建议在需要开始事务的方法上加
-
依赖异常决定是否提交还是回滚
@Transactional(rollbackFor = {Exception.class})
默认回滚
RuntimeException