Spring是什么
首先Spring是一个轻量级控制反转(IoC)和面向切面(AOP)的容器框架
Spring框架是由于软件开发的复杂性而创建的。Spring使用的是基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅仅限于服务器端的开发。从简单性、可测试性和松耦合性角度而言,绝大部分Java应用都可以从Spring中受益。
也就是说,我们学习Spring是学习其理念思想也就是IoC和AOP。其适用范围很广,以至于任何应用都能从中受益。
Spring的优势
- 低侵入 / 低耦合 (降低组件之间的耦合度,实现软件各层之间的解耦)
- 声明式事务管理(基于切面和惯例)
- 方便集成其他框架(如MyBatis、Hibernate)
- 降低 Java 开发难度
- Spring 框架中包括了 J2EE 三层的每一层的解决方案(一站式)
Spring框架结构
一些概念
- pojo即 Plain Old Java Objects,简单老式 Java 对象
- JavaBean即符合 JavaBean 规范的 Java 类。JavaBean 是一种JAVA语言写成的可重用组件。为写成JavaBean,类必须是具体的和公共的,并且具有无参数的构造器。JavaBean 通过提供符合一致性设计模式的公共方法将内部域暴露成员属性,set和get方法获取。众所周知,属性名称符合这种模式,其他Java 类可以通过自省机制(反射机制)发现和操作这些JavaBean 的属性。
IoC
IoC:Inverse of Control(控制反转)
- 读作“反转控制”,更好理解,不是什么技术,而是一种设计思想,就是将原本在程序中手动创建对象的控制权,交由Spring框架来管理。
- 正控:若要使用某个对象,需要自己去负责对象的创建
- 反控:若要使用某个对象,只需要从 Spring 容器中获取需要使用的对象,不关心对象的创建过程,也就是把创建对象的控制权反转给了Spring框架
- 好莱坞法则:Don’t call me ,I’ll call you
一个常见的例子
传统方法:
对于传统模式,我们有这样代码实现
Juice juice = new Juice("橙子","多糖","超大杯");
工厂模式:
同样的,我们有如下的代码实现:
Shop shop = new Shop("橙子","多糖","超大杯");
Juice juice = Shop.makeJuice();
事实上对于这样简单的类,我们不需要引入工厂模式来增加复杂度,但是对于更加复杂的类(假如橙子,糖都是一个比较复杂的类),我们使用工厂的话会减少代码的管理成本。
先记住这个例子,我们编写一个简单的Spring程序。
首先编写一个pojo(传统对象)
package pojo;
public class Source {
private String fruit; // 类型
private String sugar; // 糖分描述
private String size; // 大小杯
/* setter and getter */
}
编写配置文件装配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 name="source" class="pojo.Source">
<property name="fruit" value="橙子"/>
<property name="sugar" value="多糖"/>
<property name="size" value="超大杯"/>
</bean>
</beans>
接着来试一下Spring
ApplicationContext context = new ClassPathXmlApplicationContext(
new String[]{"applicationContext.xml"}
);
Source source = (Source) context.getBean("source");
System.out.println(source.getFruit());
System.out.println(source.getSugar());
System.out.println(source.getSize());
输出如下:
(我们的工程文件结构如下)
(如何创建Spring工程?)
我们使用IDEA
这样就获得了一个包含Spring环境的工程。
AOP
如果说 IoC 是 Spring 的核心,那么面向切面编程就是 Spring 最为重要的功能之一了,在数据库事务中切面编程被广泛使用
AOP 即 Aspect Oriented Program 面向切面编程
首先,在面向切面编程的思想里面,把功能分为核心业务功能,和周边功能。
- 所谓的核心业务,比如登陆,增加数据,删除数据都叫核心业务
- 所谓的周边功能,比如性能统计,日志,事务管理等等
周边功能在 Spring 的面向切面编程AOP思想里,即被定义为切面
在面向切面编程AOP的思想里面,核心业务功能和切面功能分别独立进行开发,然后把切面功能和核心业务功能 “编织” 在一起,这就叫AOP
AOP 当中的概念:
- 切入点(Pointcut) 在哪些类,哪些方法上切入(where)
- 通知(Advice) 在方法执行的什么实际(when:方法前/方法后/方法前后)做什么(what:增强的功能)
- 切面(Aspect) 切面 = 切入点 + 通知,通俗点就是:在什么时机,什么地方,做什么增强!
- 织入(Weaving) 把切面加入到对象,并创建出代理对象的过程。(由 Spring 来完成)
AOP 编程
我们还是采用配置文件的方式来进行一个演示
首先我们编写核心功能:
package service;
public class ProductService {
public void doSomeService(){
System.out.println("doSomeService");
}
}
还有周边功能
package aspect;
//这里的依赖包需要额外安装,建议看这个教程:https://blog.csdn.net/shuduti/article/details/53069241
import org.aspectj.lang.ProceedingJoinPoint;
public class LoggerAspect {
public Object log(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("start log:" + joinPoint.getSignature().getName());
Object object = joinPoint.proceed();
System.out.println("end log:" + joinPoint.getSignature().getName());
return object;
}
}
按照IoC的方式进行配置文档编辑
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<bean name="productService" class="service.ProductService" />
<bean id="loggerAspect" class="aspect.LoggerAspect"/>
</beans>
编写测试方法
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext(
new String[]{"applicationContext.xml"}
);
ProductService productService = (ProductService) context.getBean("productService");
productService.doSomeService();
}
运行
接下来,就是本节重点
我们使用AOP来切入
我们希望以doSomeService()为切入点,在其执行前后增强日志功能
这样配置
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<bean name="productService" class="service.ProductService" />
<bean id="loggerAspect" class="aspect.LoggerAspect"/>
<!-- 配置AOP -->
<aop:config>
<!-- where:在哪些地方(包.类.方法)做增加 -->
<aop:pointcut id="loggerCutpoint"
expression="execution(* service.ProductService.*(..)) "/>
<!-- what:做什么增强 -->
<aop:aspect id="logAspect" ref="loggerAspect">
<!-- when:在什么时机(方法前/后/前后) -->
<aop:around pointcut-ref="loggerCutpoint" method="log"/>
</aop:aspect>
</aop:config>
</beans>
运行:
我们获得了日志功能,但是没有修改业务代码