Spring介绍
1、什么是Spring?
百度:
Spring是JavaEE编程领域的一个轻量级开源框架,该框架由一个叫Rod Johnson的程序员在 2002年最早提出并随后创建,是为了解决企业级编程开发中的复杂性,实现敏捷开发的应用型框架 。 Spring是一个开源容器框架,它集成各类型的工具,通过核心的Bean factory实现了底层的类的实例化和生命周期的管理。
补充:想要具体了解学习Spring,建议多逛逛Spring官网,不一定要全部看懂,一开始只是混个脸熟也有利于后面进一步的学习。
2、我们常说的Spring指什么?
如果我们打开Spring官网,打开project会发现:Spring就像是一个大的容器型框架,下面还有比较熟知的常见项目和框架,例如:Springboot,SpringCloud,SpringData等等。其中“SpringFramework”的Core technologies(核心技术),会发现一些比较常见的关键词:DI(IOC)和AOP,就是常说的控制反转和面向切面编程。所以,我们常说的“Spring”大部分情况下是指“SpringFramework”。
Springframework核心技术
IOC(控制反转):
一般程序的设计思想是从Controller/Servlet层到Service层再到Dao/Mapper层,如果对某个下游的类进行更改名字,或者进行更换,则整个流程都需要进行相应的更改,所以代码的耦合性太高,牵一发而动全身,所以为了解决这个问题设计出了IOC(控制反转)的思想:把创建下游类的功能交给一个“类似工厂”(BeanFactory)的对象进行管理(工厂思想+反射)。
![](https://i-blog.csdnimg.cn/blog_migrate/9d48b10d837eab8899d73c4535d26e95.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/a23630af174e5ead9de88639220840bc.gif)
1 public class MyController {
2 public static void main(String[] args) {
3 MyBeanFactory myBeanFactory = new MyBeanFactory();
4 SpringDao springDao = (SpringDao) myBeanFactory.getBean();
5 springDao.test();
6 }
7
8 }
![](https://i-blog.csdnimg.cn/blog_migrate/8e3f01444548e3529f2f4bc1cc5ad2b3.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/0bf80edce68eae5236e46f0a09028c0b.gif)
1 public class MyBeanFactory {
2 public Object getBean() {
3 Object bean = null;
4 try {
5 bean = Class.forName("com.emo.dao.SpringDao").newInstance();
6 } catch (InstantiationException e) {
7 e.printStackTrace();
8 } catch (IllegalAccessException e) {
9 e.printStackTrace();
10 } catch (ClassNotFoundException e) {
11 e.printStackTrace();
12 }
13 return bean;
14 }
15 }
![](https://i-blog.csdnimg.cn/blog_migrate/da13a938b1a659b7c5e934b66ce0cf78.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/2a0016d2a3aa84e70f1e9f29eb6fc199.gif)
1 public class SpringDao {
2 public void test() {
3 System.out.println("SpringDao被调用!");
4 }
5 }
优点:不需要每次更改Dao层数据都需要整个流程都进行更改,只需要修改BeanFactory中的“com.emo.dao.SpringDao”。
缺点:还是需要进行手动修改代码。
工厂思想还是需要改代码,于是在这个基础上加上配置文件(ApplicationContext.xml)进行改造,这样只需要修改配置文件基本不需要修改代码。
![](https://i-blog.csdnimg.cn/blog_migrate/d0419686125e39de2ca7f00a58e65835.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/afe48832fe102517d2ba178e3377244b.gif)
1 <?xml version="1.0" encoding="UTF-8"?>
2 <beans xmlns="http://www.springframework.org/schema/beans"
3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 xsi:schemaLocation="
5 http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
6
7 <!-- bean definitions here -->
8 <bean id="springDao" class="com.emo.dao.SpringDao"></bean>
9 </beans>
![](https://i-blog.csdnimg.cn/blog_migrate/a698d045f539967121cdd89b590c62ec.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/5880802f2dc6a783079fce5093030198.gif)
public class SpringDao {
public void test() {
System.out.println("SpringDao被调用!");
}
}
![](https://i-blog.csdnimg.cn/blog_migrate/e9569858a2657224bfab049f86d87218.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/1c563cb0490c00db7940063cf63653cb.gif)
1 public class MyController {
2 public static void main(String[] args) {
3 //spring配置方式,创建spring工厂,加载spring配置文件
4 ApplicationContext ac = new ClassPathXmlApplicationContext("ApplicationContext.xml");
5 //从spring工厂中获取对象,通过bean的id/name
6 SpringDao springDao = (SpringDao) ac.getBean("springDao");
7 springDao.test();
8 }
9 }
注意:记得要导入SpringFramework依赖
1 <dependency>
2 <groupId>org.springframework</groupId>
3 <artifactId>spring-context</artifactId>
4 <version>5.1.7.RELEASE</version>
5 </dependency>
DI(依赖注入):
如何通过调用Service,继而调用Dao层?为了解决这个问题,就有了依赖注入,把Dao成当作Service层的“属性”或者“元素”,注入到配置文件中。
![](https://i-blog.csdnimg.cn/blog_migrate/b2fb02fb89f1b61e4d7c16888e4b08f2.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/95ec60859e1b3807d694f15c96948953.gif)
1 <?xml version="1.0" encoding="UTF-8"?>
2 <beans xmlns="http://www.springframework.org/schema/beans"
3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 xsi:schemaLocation="
5 http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
6
7 <!-- bean definitions here -->
8 <bean id="springDao" class="com.emo.dao.SpringDao"></bean>
9 <bean id ="springService" class="com.emo.service.SpringService">
10 <!-- 注入对象 -->
11 <!-- property 根据类中的setter方法进行属性注入 -->
12 <!-- name:setter方法的后缀小写,比如setXxx 对应的name为xxx -->
13 <!-- ref:引用哪一个bean(对象),值为bean的id/name -->
14 <property name="springDao" ref="springDao" />
15 </bean>
16
17 </beans>
![](https://i-blog.csdnimg.cn/blog_migrate/324d2e3d3ec3282cc612544eb9264154.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/e12bd6428c87739559f03d57f66bb809.gif)
1 public class SpringDao {
2 public void test() {
3 System.out.println("SpringDao被调用!");
4 }
5 }
![](https://i-blog.csdnimg.cn/blog_migrate/2cf33d20b2d2db0834741b9ca25c6457.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/367bc610404f2a588824b339b710e7a3.gif)
1 public class SpringService {
2 SpringDao springDao = null;
3
4 public void setSpringDao(SpringDao springDao) {
5 this.springDao = springDao;
6 }
7
8 public void test() {
9 System.out.println("SpringService被调用!");
10 springDao.test();
11 }
12
13 }
![](https://i-blog.csdnimg.cn/blog_migrate/80cd54ed4b236d510a819d6db5cefc4f.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/8998630bdbd9570999fe754a12b5384f.gif)
1 public class MyController {
2 public static void main(String[] args) {
3 //spring配置方式,创建spring工厂,加载spring配置文件
4 ApplicationContext ac = new ClassPathXmlApplicationContext("ApplicationContext.xml");
5 //从spring工厂中获取对象,通过bean的id/name
6 SpringService springService = (SpringService) ac.getBean("springService");
7 springService.test();
8 }
9 }
注意:把Dao层注入到Service层时,要把Dao层当作Service层的属性或者元素,所以要在Service层提供setXxxDao()方法。
1 public class SpringService {
2 SpringDao springDao = null;
3
4 public void setSpringDao(SpringDao springDao) {
5 this.springDao = springDao;
6 }
7
8 public void test() {
9 System.out.println("SpringService被调用!");
10 springDao.test();
11 }
12
13 }
简要总结:IOC就是由原本手动创建对象交给Spring工厂去创建对象,DI就是把一个对象A当作另一个对象B的属性或者元素注入到配置文件的B对象中。
AOP(面向切面编程):
AOP (Aspect Oriented Programing) 称为:面向切面编程,它是一种编程思想。AOP 是 OOP(面向对象编程(Object Oriented Programming,OOP,面向对象程序设计)是一种计算机编程架构),思想延续,它是基于代理思想,对原来目标对象,创建代理对象,在不修改原对象代码情况下,通过代理对象,调用增强功能的代码,从而对原有业务方法进行增强!
关于代理相关资料:https://www.cnblogs.com/Bernard94/p/12358728.html
AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码的编写方式(例如性能监视、权限管理、事务管理、安全检查、缓存、日志记录等)。
相关术语:
- Aspect(切面): 是通知和切入点的结合,通知和切入点共同定义了关于切面的全部内容---它的功能、在何时和何地完成其功能
- joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点.
- Pointcut(切入点):所谓切入点是指我们要对哪些joinpoint进行拦截的定义.通知定义了切面的”什么”和”何时”,切入点就定义了”何地”.
- Advice(通知):所谓通知是指拦截到joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)
- Target(目标对象):代理的目标对象
- weaving(织入):是指把切面应用到目标对象来创建新的代理对象的过程.切面在指定的连接点织入到目标对象
- Introduction(引介):在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field.
JDK动态代理实现AOP:
![](https://i-blog.csdnimg.cn/blog_migrate/6862223fd7b9c58e48c3aa345830f136.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/61d7e62f5e4c760c35abf78d69e9c6ba.gif)
1 public interface People {
2 public void eat() ;
3 }
![](https://i-blog.csdnimg.cn/blog_migrate/cdfd8873636156d7a945689910cc237d.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/d99b54b65d01b9c85bafbc67fabb0ae9.gif)
1 public class Student implements People {
2
3 public void eat() {
4 System.out.println("吃东西!");
5 }
6
7 }
![](https://i-blog.csdnimg.cn/blog_migrate/aebfa1fcbbf097f969a72e6895dae3f6.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/a8acde1b91ea51c5904c2c45af89eba0.gif)
1 public class ProxyObject {
2 private Object target;
3
4 public ProxyObject(Object target) {
5 this.target = target;
6 }
7
8 public Object getProxyObject() {
9 // 参数1:目标对象的类加载器
10 // 参数2:目标对象实现的接口
11 // 参数3:回调方法对象
12 return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
13 new InvocationHandler() {
14
15 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
16 // 如果是保存的方法,执行记录日志操作
17 if (method.getName().equals("eat")) {
18 ProxyEat();
19 }
20 // 目标对象原来的方法执行
21 Object object = method.invoke(target, args);// 调用目标对象的某个方法,并且返回目标对象方法的返回值
22
23 return object;
24 }
25 });
26 }
27
28 private void ProxyEat() {
29 System.out.println("eat方法加强!");
30 }
31 }
![](https://i-blog.csdnimg.cn/blog_migrate/c4d6c036be0c2e355a0c8b1f3c6719ac.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/468cf1bf61287c5ca9edb688cee23739.gif)
1 public class test {
2 public static void main(String[] args) {
3 Student student = new Student();
4 ProxyObject proxyObject = new ProxyObject(student);
5 People proxyStudenty = (People) proxyObject.getProxyObject();
6 proxyStudenty.eat();
7 }
8 }
注意:因为JDK必须对接口生成代理,所以代理对象返回的是接口:People,不能是具体类:Student。
CGLIB动态代理实现AOP:
![](https://i-blog.csdnimg.cn/blog_migrate/207da6d995296bc951d4c0db071d51c2.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/2e88ce0c237a9c5a4d2406b65bdc75b3.gif)
1 public class Dog {
2 public void bark() {
3 System.out.println("汪汪。。。");
4 }
5 }
![](https://i-blog.csdnimg.cn/blog_migrate/c75962a2ef37a361623b1c06180a52e8.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/e67d5735e213aa6ffb42376c0a2dca39.gif)
1 public class ProxyObject implements MethodInterceptor {
2 private Object target;
3
4 public ProxyObject(Object target) {
5 this.target = target;
6 }
7
8 // 获取代理对象
9 public Object getProxyObject() {
10 // 1.代理对象生成器(工厂思想)
11 Enhancer enhancer = new Enhancer();
12 // 2.在增强器上设置两个属性
13 // 设置要生成代理对象的目标对象:生成的目标对象类型的子类型
14 enhancer.setSuperclass(target.getClass());
15 // 设置回调方法
16 enhancer.setCallback(this);
17 // Callback
18 // 3.创建获取对象
19 return enhancer.create();
20 }
21
22 // 回调方法(代理对象的方法)
23 // 参数1:代理对象
24 // 参数2:目标对象的方法对象
25 // 参数3:目标对象的方法的参数的值
26 // 参数4:代理对象的方法对象
27 public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
28 // 如果是保存的方法,执行记录日志操作
29 if (method.getName().equals("bark")) {
30 ProxyBark();
31 }
32 // 目标对象原来的方法执行
33 Object object = method.invoke(target, args);// 调用目标对象的某个方法,并且返回目标对象方法的执行结果
34 return object;
35 }
36
37 private void ProxyBark() {
38 System.out.println("Bark方法加强!");
39 }
40
41 }
![](https://i-blog.csdnimg.cn/blog_migrate/127ab309ec15ab1117b52eecedeb19d1.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/c11205c1359262129c7d8fe822a345ca.gif)
1 public class demo {
2 public static void main(String[] args) {
3 Dog dog = new Dog();
4 ProxyObject proxyObject = new ProxyObject(dog);
5 Dog proxyDog = (Dog) proxyObject.getProxyObject();
6 proxyDog.bark();
7 }
8 }
注意:Cglib要实现“MethodInterceptor”接口,此接口在SpringFramework中,所以要引入spring-context依赖:
1 <dependency>
2 <groupId>org.springframework</groupId>
3 <artifactId>spring-context</artifactId>
4 <version>5.1.7.RELEASE</version>
5 </dependency>
总结:
- Jdk代理:基于接口的代理,一定是基于接口,会生成目标对象的接口类型的子对象。
- Cglib代理:基于类的代理,不需要基于接口,会生成目标对象类型的子对象。