IOC
spring的两个核心功能分别是IOC AOP
IOC(控制反转)包括正转和反转
正转:开发人员掌握对象的创建,属性赋值,对象从生成到销毁的全部过程
反转:把原来的开发人员管理,创建对象的权限转移给代码之外的容器实现。由容器代替开发
总的来说spring框架将对象交给容器去创建和管理,我们要使用对象时直接从容器中去取对象就可以了
白话来讲就是相当于托管,中间过程我们不用管ioc全管完了,我们只要从容器中取出来用就行。
好比吃饭,以前我们是自己在家做饭,做了然后再吃,现在ioc就像餐馆我们只要给了钱,中间饭怎么做的我们都不管,我们只管等人家做好了拿来吃就行了
AOP
学习java我们知道了OOP就是面向对象编程,AOP就是面向切面编程
AOP(Aspect Orient Programming):面向切面编程
Aspect:表示切面,给业务方法增加的功能,叫做切面。切面一般都是业务功能,而且切面功能一般都是可以服用的。例如:日志功能,事务功能,权限检查,参数检查,统计信息等等。
Orient:面向,对着
Programming:编程
AOP作用
1)能让切面功能复用
2)能让开发人员专注页面逻辑
3)提高开发的效率
4)能实现业务功能和其他非业务功能解耦合
5)给存在的业务方法,增加功能,不用修改原来的代码
AOP中的术语
1)Aspect:切面,给业务方法增加功能。
2)JoinPoint:连接点,连接切面的业务方法,在这个业务方法执行时,会同时执行切面的方法。
3)Pointcut:切入点,是一个或者多个连接点的集合,表示这些方法执行时,都能增加切面的功能。
表示切面执行的位置
4)target:目标对象,给那个对象增加切面的功能,这个对象就是目标对象
5)Advice:通知(增强),表示切面的执行时间。在目标方法之前执行切面,还是目标方法之后执行的切面。
AOP中重要的三个要素Aspect Pointcut Advice
这个概念的理解是:在Advice的时间,在Pointcut 的位置,执行Aspect
AOP是一个动态的思想。在程序运行期间,创建代理(ServiceProxy),使用代理方法时,增加切面的功能,这个代理对象是存在内存中的。
举个小例子:你现在有个业务方法就是商品的价格,现在有一个新的需求要求提供日志功能
如果没有AOP思想那么就会直接在该业务中直接添加一个方法,如果后期又有其他需求或者对之前的需求有改变,又得回到这个业务中去修改代码。
直接的影响就是代码的可维护性低,并且还会加业务和非业务的混在一起
AOP思想例子:
public class SomeSerivceImpl implements SomeSerivce {
@Override
public void dosome(String name,Integer age) {
System.out.println("业务方法doSome,计算商品的购买金额");
}
@Override
public void doOther() {
System.out.println("业务方法doOther,消减库存的");
}
}
/**
* 调用ServiceProxy类方法的时候, 调用真正的目标方法
* 调用目标方法是。增加一些新的功能。
*
* ServiceProxy叫做代理 代理对目标的操作
*
* 创建代理,可以完成对目标方法的调用,增减功能。
* 保持目标方法内容不变
*/
public class ServiceProxy implements SomeSerivce {
SomeSerivce target=new SomeSerivceImpl();
@Override
public void dosome(String name,Integer age) {
System.out.println("日志功能记录方法的时间"+new Date());
target.dosome("gjx",23);
System.out.println("事务功能:提交事务");
}
@Override
public void doOther() {
System.out.println("日志功能记录方法的时间"+new Date());
target.doOther();
System.out.println("事务功能:提交事务");
}
}
这样的好处能保证一开始的业务方法不改变,利于维护,同时也能让开发人员专注于业务开发
@Before:前置通知(在目标代码前运行)
//定义方法,来表示切面的具体功能
/**
* 前置通知方法的定义
* 1)方法是public
* 2)方法是void
* 3)方法名称自定义
* 4)方法可以有参数,如果有是JoinPoint
* 也可以没有
*/
/**
* @Before:前置通知
* 属性:value 切入点的表达式 ,表示切面的位置,在目标方法之前先执行。
* 在这个方法时,会同时执行切面的功能
* 位置:在方法的上面
* 特点:
* 1)执行时间:在目标方法之前执行
* 2)不会影响目标方法的执行
* 3)不会修改目标方法的执行结果
*/
// @Before(value="execution(public void com.gjx.serivce.impl.SomeSerivceImpl.doSome(String, Integer))")
// public void myBefore(){
// //切面代码
// System.out.println("前置通知:切面的功能,在目标代码执行之前执行:"+new Date());
// System.out.println("\r\n");
// }
// @Before(value="execution( void com.gjx.serivce.impl.SomeSerivceImpl.doSome(String, Integer))")
// public void myBefore1(){
// //切面代码
// System.out.println("前置通知:切面的功能,在目标代码执行之前执行:"+new Date());
// System.out.println("");
// }
/**
* 切面类的通知方法可以有参数
* JoinPoint必须是他
*
* JOinPoint:表示正在执行业务方法,相当于反射Method
* 使用要求:必须是参数列表的第一个
* 作用:获取方法执行时的信息,例如方法的名称,方法的参数集合
*/
@Before(value="execution( * *..SomeSerivceImpl.do*(..))")
public void myBefore2(JoinPoint jp){
//获取方法的定义
System.out.println("前置通知,获取目标方法的定义:"+jp.getSignature());
System.out.println("前置通知中,获取方法的名称====="+jp.getSignature().getName());
//获取方法执行的参数
Object arge[]= jp.getArgs();//数组存放的是,方法中所有的参数
for (Object obj:arge) {
System.out.println("前置通知,获取方法的参数:"+obj);
}
//切面代码
System.out.println("前置通知:切面的功能,在目标代码执行之前执行:"+new Date());
System.out.println("");
}
@AfterRetunring:后置通知(在目标方法后执行的)
@Aspect
public class MyAspect {
//定义方法,来表示切面的具体功能
/**
* 后置通知方法的定义
* 1)方法是public
* 2)方法是void
* 3)方法名称自定义
* 4)方法有参数,推荐使用Object类型。
*
*/
/**
* @AfterReturning:后置通知
* 属性:value 切入点表达式
* returning 自定义的变量,表示目标方法的返回值的。
* 自定义变量名称必须和通知方法的形参名一样
* 位置:在方法的上面
* 特点:1在目标方法之后,执行
* 2.能获取到目标方法的执行结果
* 3.不会影响目标方法的执行
*
* 方法的参数:
* Object res:表示目标方法的返回值,使用res接收doOther的调用结果。
* Object res=doOther();
*
* 后置通知的执行顺序:
* Object res=SOmeServiceImpl.doOther(..);
* myAfterReturning(res);
* 思考:
* 1.doOther返回是String, Integer, Long等基本类型,在后置通知中,修改返回值,是
* 不会影响目标方法的最后调用结果的。
* 2.doOther返回的结果是对象类型,例如Student。
* 在后置通知方法中,修改这个Student对象的属性值,会不会影响最后调用结果?
*
*
*/
@AfterReturning(value="execution(* *..SomeSerivceImpl.doOther(..))",returning = "res")
public void myAfterReturning(Object res){
System.out.println("后置通知:在目标方法之后执行的。能拿到执行结果"+res);
//Object res有什么作用
if("abcd".equals(res)){
System.out.println("根据返回值的不同,做不同的增强功能");
}else if ("add".equals(res)){
System.out.println("doOthder做了添加数据库,我做备份数据");
}
}
@Around:环绕通知
@Aspect
public class MyAspect {
//定义方法,来表示切面的具体功能
/**
* 环绕通知方法的定义
* 1)方法是public
* 2)方法是必须有返回值,推荐使用Object
* 3)方法名称自定义
* 4)方法必须有ProceedingJoinPoint
*
*/
/**
* @Around:环绕通知
* 属性:value 切入点表达式
* 位置:在方法定义的上面
* 返回值:Object表示调用目标方法希望得到的执行结果(不一定是目标方法自己的返回值)
* 参数:ProceedingJoinPoint 相当于反射中的Method
* 作用:执行目标方法的,等于Method.invoke()
* 特点:
* 1.在目标方法的前和后都能增强功能。
* 2.控制目标方法是否执行。
* 3.修改目标方法的执行结果。
*/
@Around(value = "execution(* *..SomeSerivce.doFrist(..))")
public Object myAround(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("执行了环绕通知的myAroud方法");
Object methodReturn;
//想执行目标方法 ProceedingJoinPoint,表示doFrist
methodReturn= pjp.proceed();//method.invoke(),表示执行doFirst()方法本事
return "HellAround,表示目标方法执行的结果";
}
@AfterThrowing:异常通知
@After:最终通知