(9).spring 体系结构
一。Spring框图:
1.这个框架图是从下往上看的,可以看出测试的重要性。
2.servlet会进行容器管理,controler,不会再有重定型和跳转界面,只会进行传输json数据。可以解决多端问题。
3.在测试的基础上,核心容器Core Container对象,两个作用,一:可以帮我们生成控件,二,可以帮我们维护对象之间的关系。
4.容器帮我们创建的对象是带有上下文(context)的,就是运行环境,servlet访问上下文是通过application,application是servletcontext的。
5.Core Container也叫做ioc:控制反转,也叫做注入依赖;
6.
Spring是面相Bean的编程
Spring的优点:
- 低侵入式设计。(1.不用继承Spring任何的父类。2.在学到SpringMVC时,我们要使用Controler代替servlet。3.Controler就是javaBean)
- 独立于各种应用服务器
- 注入依赖特性组件关系透明化,降低耦合度
- 面向切面编程特征允许将通用任务进行集中式处理
- 与第三方框架的良好整合
ioc:控制反转也称为依赖注入
控制反转的正式定义:将对象的控制权从代码本身转移到外部容器。
控制反转:原本自己手工new对象现在转换为容器帮忙创建,手动控制变为容器控制。学了SPring之后学会让容器创建对象,把自己创建对象交给容器管理。
依赖注入:我们需要使用A方法,而使用A方法需要实例化并调用B方法,我们说A类依赖B类。但是创建和调用B均是由容器完成的,而我们不关心B,我们只关心A,这个就是B就是注入。
组件化的思想:分离关注点,使用接口,不再关注实现。要点:明确定义组件间的接口。
Aop:面向切面编程。
Aop的目标:让我们可以“专心做事”
Aop的原理:
- 将复杂的需求分解出不同的方面,将散布在系统中的公共功能集中解决。(抽出公共功能形成切面)
- 采用代理机制组装起来运行,在不改变原程序的基础上对代码段进行增强处理,增加新的功能。(增强:把抽出来的功能再放回去)(通过代理对象来调用对象的方法,代理对象前后都可插入代码,这些代码就是增强处理。)
- 所谓面向切面:是以一种通过预编译和运行期动态代理的方式实现在不修改源代码的情况下给程序动态添加功能的技术。
- 与面向切面的一些相关术语:增强处理(Advice)(有前置增强,后置增强,环绕增强,异常抛出增强,最终增强等类型。),切入点(Pointcut),连接点(Join Point),切面(Aspect),目标对象(Target objec),Aop代理(AOP proxy),织入(Weaving).
AOP面向切面:1.散布在业务系统中的共性内容取出来形成切面;
2.通过预编译/代理技术在程序运行时将切面内容切入业务系统。
Spring开发开发流程:
一。创建新的一个模块,然后引入jar包(all所有的jar包),
然后记得在项目结构中添加项目的jar包以及tomcat服务器。
当我们把之前的所有引入到新的spring01模块的时候,可能会报错,此时的原因可能是lib未引入。
此时右下角有一个fix(修复),点击“应用”,然后tomcat服务器添加这个项目,项目可能会报错,如果报错,运行之后再点击项目右键选择redeploy。
二:通过实例增加对Spring的理解:
1.1创建一个Person类,覆toString方法.
package com.service; public class Person { private String name; @Override public String toString() { return "我的名字是:" + name; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
1.2再创建一个helloPerson,每个HellowPerson类当中都有一个Person类。
package com.service; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class HelloPerson { private Person person; public void show() { System.out.println(person); } public Person getPerson() { return person; } public void setPerson(Person person) { this.person = person; } public static void main(String[] args) { //<bean class="com.service.Person" id="person"> Person person = new Person(); person.setName("齐德龙"); HelloPerson helloPerson = new HelloPerson(); helloPerson.setPerson(person); // <property name="person" ref="person"/> // 调用helloPerson的方法 helloPerson.show(); // 创建工厂,下面是通过spring完成,上面是常规创建 ApplicationContext ctx = new ClassPathXmlApplicationContext("core.xml"); // 从工厂获取对象 HelloPerson hp = (HelloPerson) ctx.getBean("helloPerson"); hp.show(); } }
1.3创建spring配置文件。(在src下创建一个xml中的spring config配置文件 core.xml)
刚创建时是这样的:
<?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"> </beans>
解释:bean中第一行:xmlns是命名空间。是主配置,可以在浏览器中打开网址,是一些配置模板。默认的命名空间
第二行:
第三行:后面网址是记录的版本号,可以参考模板,如果不知道版本号,可以直接spring-beans.xsd。xsd定义标签的作用。
引入其他命名空间步骤:
1.在第二行下,引入所需要的命名空间,eg:xmlns="http://www.springframework.org/schema/aop" xmlns="http://www.springframework.org/schema/mvc"
2.在第三行里引入:eg:http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd(黑色是需更改)
之后我们完成注入依赖与控制反转:
eg:
<?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:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/context/spring-mvc.xsd"> <bean class="com.service.Person" id="person"> <property name="name" value="钱东强"/> </bean> <bean class="com.service.HelloPerson" id="helloPerson"> <property name="person" ref="person"/> </bean> </beans>
说明:一个property对应一个属性,由于name是string类型,使用value。而类类型是引用类型,参数类型设为ref;
而在spring中我们想要调用,首先需要创建工厂:(注意这里的bean实例化参数与xml文件中bean的id是一致的)
ApplicationContext ctx = new ClassPathXmlApplicationContext("core.xml"); // 从工厂获取对象 HelloPerson hp = (HelloPerson) ctx.getBean("helloPerson"); hp.show();
巩固对spring的理解:
spring中配置过的类都可以通过工厂进行使用:
Spring工厂:
1.ClasspathXmlApplicationContext classpath中搜索spring配置文件
2.FileSystemXmlApplicationContext 文件系统
3.XmlWebApplicationContext web
Bean标签:进行控制反转,并且可以控制作用域 通过scope:有四个取值。单例:
1.singleton(所有的都是同一个,默认)
2.prototype(每次读取都会返回新的,像每次new新对象一样)
3.request (一个请求一个,在web下才有效果)
4.session (一个会话内同一个)
AOP:面向切面编程:
1.使用Spring AOP实现日志输出,步骤:
- 在项目中添加Spring AOP的jar文件
- 编写前置增强和后置增强实现日志功能
- 编写Spring配置文件,对业务方法进行增强处理
- 编写代码获取带有增强处理的业务对象
对应第二步:(本例子是打印机,对应Print类当中的print方法)
package com.service; import org.aspectj.lang.JoinPoint; import java.util.Arrays; import java.util.logging.Logger; // 日志切面 public class LogAdvice { static final Logger log = Logger.getLogger("LogAdvice"); // 前置增强 public void before(JoinPoint jp){ //JoinPoint引入点 String info = String.format("开始——目标对象:%s——目标方法:%s——参数列表:" + "%s",jp.getTarget(),jp.getSignature().getName(), Arrays.toString(jp.getArgs())); log.info(info); } // 后置增强 public void after(JoinPoint jp, Object result){ String info = String.format("结束——目标对象:%s——目标方法:%s——参数列表:" + "%s——返回值:%s",jp.getTarget(),jp.getSignature().getName(), Arrays.toString(jp.getArgs()),result); log.info(info); } }
对应第3步:概念:
实例:
<bean class="com.service.Printer" id="printer"><!--控制反转--> <property name="ink"> <!--依赖注入--> <bean class="com.service.ColorInk"/> </property> <property name="pagers"> <list> <bean class="com.service.A4Paper"/> <bean class="com.service.B5Paper"/> </list> </property> </bean> <!--定义切面对象--> <bean class="com.service.LogAdvice" id="logAdvice"/> <!--切面配置--> <aop:config> <aop:pointcut id="pc" expression="execution(* com.service.Printer.print(..))"/> <aop:aspect ref="logAdvice"><!--引入切面--> <aop:before method="before" pointcut-ref="pc"/><!--方法,配置切入点--> <aop:after-returning method="after" pointcut-ref="pc" returning="result"/> </aop:aspect> </aop:config>
单元测试:
package com.service; import org.junit.BeforeClass; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class PrinterTest { static ApplicationContext ctx = null; @BeforeClass public static void load(){ ctx = new ClassPathXmlApplicationContext("core.xml"); } @Test public void print(){ IPrinter printer = (IPrinter) ctx.getBean("printer"); printer.print("这是一段要打印的话!!!",3, Paper.A4); printer.print("这是一段要打印的话!!!",3, Paper.B5); } }
在getbean使用代理增强的时候,返回的应该是print的子类,如果动态生成他的子类,那么应该引入一些类库。解决方法:我们可以给打印机print创建一个接口,然后把print实现接口,是其中一种面向切面的方法。
接口:
package com.service; public interface IPrinter { void print(String msg, int count, Paper.Size size); }
织入:将通知与前置增强与后置增强相配置就是织入。
在上面中关于接口的问题,其实是使用了代理模式,是动态编译,如下为静态的,可供参考:
package com.service; public class $Proxy5Test implements IPrinter { private IPrinter target; // 目标对象 private LogAdvice logAdvice; // Advice @Override public void print(String msg, int count, Paper.Size size) { // 1、执行前 logAdvice.before(null); // 2、执行 target.print(msg,count,size); // 3、执行后 logAdvice.after(null,null); } }
至于如果通过预编译处理,则会通过修改源代码进行处理。