Spring框架
Spring框架拥有两大特性:IoC和AOP。
IoC,英文全称Inversion of Control,意为控制反转。
AOP,英文全称Aspect-Oriented Programming,意为面向切面编程。
Spring核心容器的主要组件是Bean工厂(BeanFactory),Bean工厂使用控制反转(IoC)模式来降低程序代码之间的耦合度,并提供了面向切面编程(AOP)的实现。
简单来说,Spring是一个轻量级的控制反转(IoC)和面向切面编程(AOP)的容器框架。
1 IOC
控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。
说明: 将对象的创建的权利交给Spring管理,由Spring(第三方)管理对象的生命周期(创建/初始化/使用/销毁).
2 Spring容器如何创建对象
1).当Spring程序执行时,首先会根据配置文件的内容进行解析
2).当程序解析到bean标签时,则会根据反射的机制实例化对象
3).将实例化好的对象保存到超大的Map集合中<K,V> bean中的Id当做map的Key,实例化好的对象当做Value
Map<id,对象>
4). 从容器中获取对象. 则从Map集合中通过id获取对象即可.
3 Spring对象生命周期
- 实例化对象
- 初始化操作 (一般对对象的属性赋值)
- 用户使用对象(调用其中的方法)
- 对象销毁 (一般都是释放资源)
4 IoC的主要实现方式有两种:依赖查找、依赖注入
那么依赖查找和依赖注入有什么区别呢?
- 依赖查找,主要是容器为组件提供一个回调接口和上下文环境。这样一来,组件就必须自己使用容器提供的API来查找资源和协作对象,控制反转仅体现在那些回调方法上,容器调用这些回调方法,从而应用代码获取到资源。
- 依赖注入,组件不做定位查询,只提供标准的Java方法让容器去决定依赖关系。容器全权负责组件的装配,把符合依赖关系的对象通过Java Bean属性或构造方法传递给需要的对象
5 spring的bean的常用属性(application.xml)
1.通过bean标签进行创建 属性 id/class
2.单例多例/懒加载
1>.Spring容器中默认的对象都是单例对象(通过构造方法实例化对象)
2>.有时需要通过多例对象为用户提供服务(数据源链接)
scope="prototype" 多例设置 scope="singleton" 缺省值 单例
lazy-init="true" 开启懒加载(多例对象时)
- 属性注入的两种方式(依赖注入)
1>.set注入 2>.构造注入(必须有无参构造)
- 自动装配 autowired --byName byType
5.注解扫描 component-scan
6 全注解模式
-
Spring常用注解:
@Configuration 标识配置类
@ComponentScan 扫描注解路径
@PropertySource(value="classpath:/user.properties",encoding= "UTF-8") 用户配置文件加载
@Value("${user.id}") 属性动态赋值
@Bean 方法级别的注解,注解的方法会产生一个Bean对象,该对象由Spring管理并放到IoC容器中。
@Controller 用来标识Controller层的代码
@Service 用来标识Service层代码
@Repository 用来标识持久层即Dao层
@Component 万用注解
@Scope(“singleton”)
scope属性:
(1)singleton单例对象 默认值
只会创建一个action对象,每次访问都是同一个对象,容易产生并发问题,数据不安全
(2)prototype 多例对象
保证每一个请求有一个单独的action来处理,避免action的线程问题。,
- 用户配置文件user.properties
user.id user.username
3).三个属性注解
- @Autowired: 可以根据类型/属性名称进行注入 首先按照类型进行注入如果类型注入失败,则根据属性名称注入
- @Qualifier: 如果需要按照名称进行注入,则需要额外添加@Qualifier * 3.@Resource(type = "xxx.class",name="属性名称") * 关于注解补充: 由于@Resource注解 是由java原生提供的,不是Spring官方的.所以不建议使用
上述的属性的注入在调用时 自动的封装了Set方法,所以Set方法可以省略不写
7 Spring内存加载机制
1>.当程序启动Spring容器时 AnnotationConfigApplicationContext 利用beanFactory实例化对象
2>.根据配置类中的包扫描开始加载指定的注解(4个). 根据配置文件的顺序依次进行加载
3>.当程序实例化Controller时,由于缺少Service对象,所以挂起线程 继续执行后续逻辑.
当构建Service时,由于缺少Dao对象,所以挂起线程 继续执行后续逻辑.
当实例化Dao成功时,保存到Spring所维护的Map集合中. 执行之前挂起的线程.
所以以此类推 所有对象实现封装.最终容器启动成功
4>. 根据指定的注解/注入指定的对象.之后统一交给Spring容器进行管理.最终程序启动成功.
8关于Spring工厂模式说明
1>.Spring源码中创建对象都是采用工厂模式 接口:BeanFactory(顶级接口)
2>.Spring开发中需要手动的创建对象时,一般采用 FactoryBean(业务接口)
BeanFactory是个Factory,也就是IOC容器或对象工厂,FactoryBean是个Bean。在Spring中,所有的Bean都是由BeanFactory(也就是IOC容器)来进行管理的。但对FactoryBean而言,这个Bean不是简单的Bean,而是一个能生产或者修饰对象生成的工厂Bean,它的实现与设计模式中的工厂模式和修饰器模式类似。
9 AOP引入
说明: 由于业务需求 要求方法要么同时入库,要么同时回滚.所以必须通过事务进行控制.
问题:1.service层业务和事物控制耦合在一起
2.代码冗余 不便于大批量开发
解决方法:静态代理和动态代理
静态代理弊端:
1).静态代理只针对于某个接口 不能实现所有接口的代理 实用性较差
2).静态代理中所有的方法,都需要手动的添加事务开始/事务提交代码 代码冗余 不够简洁.
动态代理分类:
1.JDK代理:
要求: 要求目标对象必须实现接口
代理要求: 代理对象也必须实现目标对象的接口
目标对象/代理关系: 目标对象与代理对象兄弟关系.
2.CGlib代理
要求: 不管目标对象是否有接口,都可以为其创建代理对象
代理要求: 要求代理对象必须继承目标对象
目标对象/代理关系: 目标对象与代理对象是父子关系
优势:将公共的部分写到动态代理中,之后其他的业务类调用即可
区别:
1、Java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。而Cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
2、JDK动态代理只能对实现了接口的类生成代理,而不能针对类;Cglib是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,因为是继承,所以该类或方法最好不要声明成final 。
3、Cglib一个目标类方法会生成两个代理方法,一个重写目标方法,并实现代理逻辑,还有一个直接调用目标类方法。
4、@EnableAspectJAutoProxy(proxyTargetClass=false) 启动AOP注解 创建代理对象,默认启用JDK动态代理
-
- 目标对象没有实现接口时,则采用CGLIB
- 强制使用cglib proxyTargetClass=true
- JDK代理创建速度快.运行时稍慢
- CGLIB创建时速度较慢,运行时更快
10 Spring AOP
1.1 AOP介绍
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。比如业务A和业务B现在需要一个相同的操作,传统方法我们可能需要在A、B中都加入相关操作代码,而应用AOP就可以只写一遍代码,A、B共用这段代码。并且,当A、B需要增加新的操作时,可以在不改动原代码的情况下,灵活添加新的业务逻辑实现。
在实际开发中,比如商品查询、促销查询等业务,都需要记录日志、异常处理等操作,AOP把所有共用代码都剥离出来,单独放置到某个类中进行集中管理,在具体运行时,由容器进行动态织入这些公共代码。
总结:AOP利用动态代理的模式对业务逻辑的各个部分进行隔离,降低各业之间的耦合度,提高程序的可重用性和开发效率,扩展业务功能方法。
AOP主要一般应用于签名验签、参数校验、日志记录、事务控制、权限控制、性能统计、异常处理等。
知识扩展: 1.面向过程编程 C语言 谭浩强!!!
2.半面向对象的程序设计 C++
3.面向对象的编程技术 java
AOP编程方式: 1.面向接口编程 2.面向切面编程
切面注解:
@Component //AOP被Spring容器管理
@Aspect //标识切面类
@EnableAspectJAutoProxy(proxyTargetClass=false) //启动AOP注解 创建代理对象
1.2 关于AOP名词介绍
- .连接点(JoinPoint): 用户可以被扩展的方法
2).切入点(pointcut): 用户实际扩展的方法
3).通知(Advice): 扩展方法的具体实现
4).切面(Aspect): 将通知应用到切入点的过程
切面 = 切入点表达式 + 通知方法
1.3 通知类型(必会)
@before前置通知: 在目标方法执行之前执行
作用: 如果需要记录程序在方法执行前的状态,则使用前置通知.
@afterReturning返回通知: 在目标方法执行之后返回时执行
作用: 用来监控方法的返回值,进行日志的记录
@afterThrowing异常通知: 在目标方法执行之后,抛出异常时执行
作用: 当目标方法执行时,抛出异常时 可以使用AfterThrowing 进行记录.
@after后置通知: 无论程序是否执行成功,都要最后执行的通知
@around环绕通知: 在目标方法执行前后 都要执行的通知(完美体现了动态代理模式) 可以控制目标方法是否执行.
功能最为强大 只有环绕通知可以控制目标方法的执行
实际作用: 关于通知方法总结:
1.环绕通知是处理业务的首选. 可以修改程序的执行轨迹
2.另外的四大通知一般用来做程序的监控.(监控系统) 只做记录
1.4 切入点表达式
概念:当程序满足切入点表达式,才能进入切面,执行通知方法.
1.bean(“bean的ID”) 根据beanId进行拦截 只能匹配一个
2.within(“包名.类名”) 可以使用通配符*? 能匹配多个.
粒度: 上述的切入点表达式 粒度是类级别的. 粗粒度.
3.execution(返回值类型 包名.类名.方法名(参数列表…))
粒度: 控制的是方法参数级别. 所以粒度较细. 最常用的.
4.@annotation(包名.注解名) 只拦截注解.
粒度: 注解是一种标记 根据规则标识某个方法/属性/类 细粒度
目标对象(Target):那些即将切入切面的对象,也就是那些被通知的对象。这些对象专注业务本身的逻辑,所有的共有功能等待AOP容器的切入。
代理对象(Proxy):将通知应用到目标对象之后被动态创建的对象。可以简单地理解为,代理对象的功能等于目标对象本身业务逻辑加上共有功能。代理对象对于使用者而言是透明的,是程序运行过程中的产物。目标对象被织入共有功能后产生的对象。
织入(Weaving):将切面应用到目标对象从而创建一个新的代理对象的过程。这个过程可以发生在编译时、类加载时、运行时。Spring是在运行时完成织入,运行时织入通过Java语言的反射机制与动态代理机制来动态实现。
1.5 关于通知方法的执行顺序
- 执行around开始
2.执行before
3.执行目标方法
4.执行afterReturning
5.执行afterThrowing
6.执行after
7,执行around通知结束
1.6多个切面类执行顺序
@Order(n) 值越小 越先执行
11 工厂模式
问题:
通过spring容器创建的对象一般是通过反射机制调用.但是有的时候由于业务需要需要实例化抽象类的对象/复杂的接口对象
说明: Spring提供了工厂模式用于实例化复杂对象!!!
1> 静态工厂模式
静态方法特点: 1.静态方法调用可以通过类名直接调用. static
2> 实例化工厂模式
调用: 对象.方法()
3> Spring工厂模式 implements FactoryBean
说明: 如果需要创建复杂对象 首选工厂模式
12 Spring容器管理3层代码结构