IOC、AOP

https://blog.csdn.net/Sadlay/article/details/83276963

Spring

Spring两个核心理念,一个是控制反转(Inversion of Control,IoC),另一个是面向切面编程(Aspect Oriented Programming,AOP)

IOC(控制

IoCInversionof Control控制

 

IoC不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序

IOC没使用之前

1.)代码耦合度高;每处需要使用这个Javabean的地方都会与其产生耦合。

2.)可测试性差;例如要测试一个主业务流程,需要调用多个Javabean封装的子流程,那么一个头疼的问题是你需要初始化所有的Javabean,并进行组装,随后才能对主流程进行测试。

3.)重复代码多;对于同一个Javabean,所有用到的地方都需要通过new关键字和其setter方法进行初始化,其中便会产生许多重复代码。

4.)冗余对象多;事实上,这些封装了业务流程的Javabean,基本都是无状态的。也就是说,对于一个Javabean,我们的应用可能仅需要一个单例对象。

IOC使用之后

1.)代码耦合度高;使用XML配置注解结合的方式,具体的业务对象已不会与具体的Javabean耦合。

2.)可测试性差;使用XML配置注解结合的方式,IoC容器会自行为我们组装具体的业务对象,不需要手动干预。

3.)重复代码多;对于Javabean初始化的这段重复代码,被IoC容器抽象出来并作为Spring的基础设施了。

4.)冗余对象多;IOC容器可以支持单例对象。

IOC容器:就是具有依赖注入功能的容器,是可以创建对象的容器,IOC容器负责实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。通常new一个实例,控制权由程序员控制,而"控制反转"是指new实例工作不由程序员来做而是交给Spring容器来做。在Spring中BeanFactory是IOC容器的实际代表者。

IoC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(Dependency Injection,依赖注入)来实现的。

首先:第一个问题,参与者都有谁?

1对象2IOC/DI容器3某个对象的外部资源

第二问题:依赖,谁依赖谁?为什么需要依赖?

依赖,对象依赖于IOC/DI容器,至于为什么要依赖呢?因为对象需要IOC/DI容器来提供对象需要的外部资源。

第三个问题:注入,谁注入谁?又注入了什么呢?

IOC/DI容器注入对象,注射入了什么?那就是注入对象所需要的资源

第四个问题:控制反转,谁控制谁?控制什么?为什么叫反转呢?存在正转吗?

控制反转,控制什么?那就IOC/DI容器控制对象,主要是控制对象实例的创建反转是相对于正向而言的,那么什么算是正向的呢?考虑一下常规情况下的应用程序,如果要在A里面使用C,你会怎么做呢?当然是直接去创建C的对象,也就是说,是在A类中主动去获取所需要的外部资源C,这种情况被称为正向的。那么什么是反向呢?就是A类不再主动去获取C,而是被动等待,等待IoC/DI的容器获取一个C的实例,然后反向的注入到A类中。

第五个问题:控制反转和依赖注入式同一个概念吗?

控制反转和依赖注入是对同一件事情的不同描述,从某个方面讲,就是它们描述的角度不同。1控制反转是从容器的角度在描述:容器控制应用程序,由容器反向的向应用程序注入应用程序所需要的外部资源;(2依赖注入是从应用程序的角度在描述:应用程序依赖容器创建并注入它所需要的外部资源

IOC操作

@Repository        主要用于标识加入容器的对象是一个持久层的组件(类)

@Service           主要用于标识加入容器的对象是一个业务逻辑层的组件

@Controller        主要用于标识加入容器的对象是一个控制层的组件

@Component       把对象加入ioc容器,对象引用名称是类名,第一个字母小写

@Component(“name”) 把指定名称的对象,加入ioc容器

 

DI操作@Autowired和@Resource

@Autowired和@Resource:均可标注在字段或者属性的setter方法上。

@AutowiredSpring支持的注解,默认按类型装配,默认情况下必须要求依赖对象必须存在,如果要允许null值,可以设置它的required属性为false,如@Autowired(required=false) ,如果我们想使用名称装配可以结合@Qualifier注解进行使用,如下:

@Autowired() @Qualifier("baseDao") 

private BaseDao baseDao;

@ResourceJDK支持的注解,默认按名称装配,名称可以通过name属性进行指定。也提供按照byType 注入。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。

装配顺序
  1、如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常
  2、如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常
  3、如果指定了type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常
  4、如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配;

 

IOC初始化三个过程

1、Resource定位;指对BeanDefinition的资源定位过程。通俗地讲,就是找到定义Javabean信息的XML文件,并将其封装成Resource对象。

2、BeanDefinition的载入;把用户定义好的Javabean表示为IoC容器内部的数据结构,这个容器内部的数据结构就是BeanDefinition。

3、向IoC容器注册这些BeanDefinition。

这里谈的是IOC容器初始化过程,在这个过程中,一般不包含Bean依赖注入实现,在IOC设计中,Bean定义的载入和依赖注入是两个独立的过程。

依赖注入发生的时间:

Spring IoC容器完成了Bean定义资源的定位、载入和解析注册以后,IoC容器中已经管理类Bean定义的相关数据,但是此时IoC容器还没有对所管理的Bean进行依赖注入,依赖注入在以下两种情况发生:

(1).用户第一次通过getBean方法向IoC容索要Bean时,IoC容器触发依赖注入。

(2).当用户在Bean定义资源中为<Bean>元素配置了lazy-init属性,即让容器在解析注册Bean定义时进行预实例化,触发依赖注入。

lazy-init属性:

<bean id="testBean" class="cn.itcast.test.TestBean" />该bean默认的设置为:<bean id="testBean" calss="cn.itcast.test.TestBean" lazy-init="false" /> lazy-init="false":立即加载,表示在spring启动时,立刻进行实例化。

有时候这种默认处理可能并不是你想要的。如果你不想让一个singleton bean在ApplicationContext 实现初始化时被提前实例化,那么可以将bean设置为延时实例化: <bean id="testBean" calss="cn.itcast.test.TestBean" lazy-init="true" /> 延时加载,设置为lazy 的bean将不会在ApplicationContext启动时提前被实例化,而是第一次向容器通过getBean索取bean时实例化的。

如果一个设置了立即加载的bean1,引用了一个延时加载的bean2,那么bean1在容器启动时被实例化,而bean2 由于被bean1引用,所以也被实例化,这种情况也符合延时加载的bean在第一次调用时才被实例化的规则。

注意点:

lazy-init 设置只对scop属性为singleton的bean起作用如果一个bean的scope属性为scope="pototype"时,即使设置了lazy-init="false",容器启动时不实例化bean,而是调用getBean方法实例化的

Bean的生命周期

下面以BeanFactory为例,说明一个Bean的生命周期活动:

1、Bean的建立由BeanFactory读取Bean定义文件,并生成各个实例。

2、Setter注入执行Bean的属性依赖注入。

3、BeanNameAware的setBeanName()如果Bean类实现了org.springframework.beans.factory.BeanNameAware接口,则执行其setBeanName()方法。

4、BeanFactoryAware的setBeanFactory()如果Bean类实现了org.springframework.beans.factory.BeanFactoryAware接口,则执行其setBeanFactory()方法。

5、BeanPostProcessors的processBeforeInitialization()容器中如果有实现org.springframework.beans.factory.BeanPostProcessors接口的实例,则任何Bean在初始化之前都会执行这个实例的processBeforeInitialization()方法。

6、InitializingBean的afterPropertiesSet()如果Bean类实现了org.springframework.beans.factory.InitializingBean接口,则执行其afterPropertiesSet()方法。

7、Bean定义文件中定义init-method在Bean定义文件中使用“init-method”属性设定方法名称,如下:<bean id="demoBean" class="com.yangsq.bean.DemoBean" init-method="initMethod">

  .......

 </bean>

这时会执行initMethod()方法,注意,这个方法是不带参数的。

8、BeanPostProcessors的processAfterInitialization()容器中如果有实现org.springframework.beans.factory.BeanPostProcessors接口的实例,则任何Bean在初始化之前都会执行这个实例的processAfterInitialization()方法。

9、DisposableBean的destroy()在容器关闭时,如果Bean类实现了org.springframework.beans.factory.DisposableBean接口,则执行它的destroy()方法。

10、Bean定义文件中定义destroy-method 在容器关闭时,可以在Bean定义文件中使用“destory-method”定义的方法 <bean id="demoBean" class="com.yangsq.bean.DemoBean" destory-method="destroyMethod">

  .......

</bean>

Bean生命周期图:

初始化顺序:

1、Constructor(构造方法)

2、@PostConstruct标记的方法

3、InitializingBean中的afterPropertiesSet方法

4、xml的init-method指定的方法

5、Bean的行为。。。

6、@PreDestroy标记的方法

7、DisposableBean中的destroy方法

8、xml的destroy-method指定的方法

初始化源码解析参考:

https://www.cnblogs.com/yxh1008/p/6012230.html

AOP

AOP为Aspect Oriented Programming的缩写,意为:面向方面编程,可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。

AOP功能:

性能监控:在方法调用前后记录调用时间,方法执行太长或超时报警

缓存代理:缓存某方法的返回值,下次执行该方法时,直接从缓存里获取

软件破解:使用AOP修改软件的验证类的判断逻辑

记录日志:在方法执行前后记录系统日志

工作流系统:工作流系统需要将业务代码和流程引擎代码混合在一起执行,那么我们可以使用AOP将其分离,并动态挂接业务

权限验证:方法执行前验证是否有权限执行当前方法,没有则抛出没有权限执行异常,由业务代码捕捉

..........

AOP相关概念:

 1)方面(Aspect):一个关注点的模块化,这个关注点实现可能另外横切多个对象。事务管理是J2EE应用中一个很好的横切关注点例子。方面用Spring的Advisor或拦截器实现。

   2)切入点(Pointcut):指定一个通知将被引发的一系列连接点的集合。

   3)连接点(Joinpoint):程序执行过程中明确的点,如方法的调用或特定的异常被抛出。

   4)通知(Advice):在特定的连接点,AOP框架执行的动作。

   5)目标对象(Target Object):包含连接点的对象,也被称作被通知或被代理对象。

   6)AOP代理(AOP Proxy):AOP框架创建的对象,包含通知。在Spring中,AOP代理可以是JDK动态代理或CGLIB代理。

   7)引入(Introduction):添加方法或字段到被通知的类。Spring允许引入新的接口到任何被通知的对象。

   8)编织(Weaving):组装方面来创建一个被通知对象。

实现AOP的切面主要有以下几个要素:

使用@Aspect注解将一个java类定义为切面类

使用@Pointcut定义一个切入点,可以是一个规则表达式,比如下例中某个package下的所有函数,也可以是一个注解等。

根据需要在切入点不同位置的切入内容,5种类型的通知 

使用@Before在切入点开始处切入内容

使用@After在切入点结尾处切入内容

使用@AfterReturning在切入点return内容之后切入内容(可以用来对处理返回值做一些加工处理)

使用@Around在切入点前后切入内容,并自己控制何时执行切入点自身的内容

使用@AfterThrowing用来处理当切入内容部分抛出异常之后的处理逻辑

@order(i):i的值越小,优先级越高

在切入点前的操作,按order的值由小到大执行

在切入点后的操作,按order的值由大到小执行

execution表达式:

execution(* com.loongshawn.method.ces..*.*(..))

标识符

  含义

execution()

表达式的主体

第一个“*”符号

表示返回值的类型任意

com.loongshawn.method.ces

AOP所切的服务的包名,即,需要进行横切的业务类

包名后面的“..”

表示当前包及子包

第二个“*”

表示类名,*即所有类

.*(..)

表示任何方法名,括号表示参数,两个点表示任何参数类型

Aop的动态代理:

https://www.cnblogs.com/xiaoxiao7/p/6057724.html

代码相关地址:https://gitee.com/weixiaotao1992/Working/tree/master/technology_code/IOC_AOP

猜你喜欢

转载自www.cnblogs.com/weixiaotao/p/10551628.html