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)的对象进行管理(工厂思想+反射)。
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 }
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 }
1 public class SpringDao { 2 public void test() { 3 System.out.println("SpringDao被调用!"); 4 } 5 }
优点:不需要每次更改Dao层数据都需要整个流程都进行更改,只需要修改BeanFactory中的“com.emo.dao.SpringDao”。
缺点:还是需要进行手动修改代码。
工厂思想还是需要改代码,于是在这个基础上加上配置文件(ApplicationContext.xml)进行改造,这样只需要修改配置文件基本不需要修改代码。
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>
public class SpringDao { public void test() { System.out.println("SpringDao被调用!"); } }
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层的“属性”或者“元素”,注入到配置文件中。
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>
1 public class SpringDao { 2 public void test() { 3 System.out.println("SpringDao被调用!"); 4 } 5 }
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 }
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:
1 public interface People { 2 public void eat() ; 3 }
1 public class Student implements People { 2 3 public void eat() { 4 System.out.println("吃东西!"); 5 } 6 7 }
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 }
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:
1 public class Dog { 2 public void bark() { 3 System.out.println("汪汪。。。"); 4 } 5 }
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 }
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代理:基于类的代理,不需要基于接口,会生成目标对象类型的子对象。