您的位置:首页 > 编程语言 > Java开发

spring的IOC,DI,AOP理解

2019-03-11 15:35 429 查看

IOC即控制反转,是一种设计思想,由spring去管理javaBean,标记被spring管理的类都会再IOC容器中登记,需要使用时告诉spring你需要的使用的是哪个对象,通过byName或者byType的方式,spring就会在你需要的时候提供给你。springIoc容器相当于一个中介,当你想要什么房子的时候不需要满大街去找房子,只需要告诉中介你需要什么房子,中介就给你一个房子。
spring的Ioc容器会读取bean配置信息(xml,@Configuration,@Autowired),维护一张Bean定义的注册表,根据注册表去实例化bean,装配好bean的依赖关系,Ioc容器中的bean默认是单例的。

DI即依赖注入,应用程序依赖IOC容器,由IOC容器注入程序所需要的对象,上层controller需要service,只需要直接注入service,不需要关注service的具体实现,实现了解耦和,如果是通过new的方式创建的还需要了解service的实现,一旦service出现改动,那么controller也需要改动。spring通过反射来进行注入。

AOP即面向切面编程,主要是用来解决一些日志处理,事物控制,数据源切换。。比如说日志处理,每个方法都需要做,但是每个方法都写就太麻烦了,把那些需要进行日志管理的方法一刀切,方法执行之前干什么,方法执行之后干什么。spring的AOP基于jdk的动态代理,CGLib,如果目标类实现了接口使用jdk代理,否则使用cglib代理。

jdk动态代理和CGLib的区别
jdk的动态代理(核心是Proxy类,InvocationHandler接口,通过反射机制实现),反射机制在生成类的过程比较高效,
1.能够继承静态代理的全部优点.并且能够实现代码的复用.
2.JDK 的动态代理要求代理者必须实现接口, , 否则不能生成代理对象.

CGLib(以继承的方式动态生成代理对象,借助ASM实现),ASM在生成类的执行过程比较高效
1.不管有无接口都可以创建代理对象.
2.cglib创建的代理对象是目标对象的子类.,被代理的对象不能是Final类
3.在jdk1.8之后效率不如jdk的动态代理

AOP的基本概念
(1)Aspect(切面):通常是一个类,里面可以定义切入点和通知
(2)JointPoint(连接点):程序执行过程中明确的点,一般是方法的调用
(3)Advice(通知):AOP在特定的切入点上执行的增强处理,有before,after,afterReturning,afterThrowing,around
(4)Pointcut(切入点):就是带有通知的连接点,在程序中主要体现为书写切入点表达式
(5)AOP代理:AOP框架创建的对象,代理就是目标对象的加强。Spring中的AOP代理可以使JDK动态代理,也可以是CGLIB代理,前者基于接口,后者基于子类

通知类型
(1)Before(前置通知):在目标方法被调用之前做增强处理,@Before只需要指定切入点表达式即可
(2)AfterReturning(正常返回通知):在目标方法正常完成后做增强,@AfterReturning除了指定切入点表达式后,还可以指定一个返回值形参名returning,代表目标方法的返回值
(3)AfterThrowing(异常返回通知):主要用来处理程序中未处理的异常,@AfterThrowing除了指定切入点表达式后,还可以指定一个throwing的返回值形参名,可以通过该形参名来访问目标方法中所抛出的异常对象
(4)After(返回通知):在目标方法完成之后做增强,无论目标方法是否成功完成。@After可以指定一个切入点表达式
(5)Around(环绕通知):在目标方法完成前后做增强处理,环绕通知是最重要的通知类型,像事务,日志等都是环绕通知,注意编程中核心是一个ProceedingJoinPoint,
这里环绕通知是个坑,如果方法有返回值那么环绕通知必须返回,否则正常返回通知中不会接到返回值。如果方法中存在异常,环绕通知捕获异常必须抛出,否则不会进入异常返回通知,反而会进入正常返回通知,小伙伴们可以自己实验一下。

我看网上的通知类型执行顺序,查半天也没查出个所以然,这里就自己测试一下五种通知类型的执行顺序。

接口,这里如果没有引入CGlib包,那么被拦截的方法必须要实现接口

public interface UserService {
String run(String name);
}

被拦截的方法

@Service("userService")
public class UserServiceImpl implements UserService{

public String run(String name){
System.out.println(name);
return name;
}
}

增强类

@Component
@Aspect
public class Demo {

@Pointcut(value = "execution(* demo.spring.service.*.*(..))")
public void pointCut(){

}

@Before(value = "pointCut()")
public void doBefore(JoinPoint joinPoint){
//获取方法名
String name = joinPoint.getSignature().getName();
System.out.println("Before:"+name);
}

//切点和返回的值
@AfterReturning(value = "pointCut()",returning = "result")
public void doAfterReturning(JoinPoint joinPoint,Object result){
System.out.println("AfterReturning:"+result);
}

@After(value = "pointCut()")
public void doAfter(JoinPoint joinPoint){
String name = joinPoint.getSignature().getName();
System.out.println("After:"+name);
}

//切点和异常类型
@AfterThrowing(value = "pointCut()",throwing = "ex")
public void doAfterThrowing(JoinPoint joinPoint,Exception ex){
System.out.println("AfterThrowing");
throw new RuntimeException("程序出错:"+ex);
}

@Around(value = "pointCut()")
public Object around(ProceedingJoinPoint proceedingJoinPoint){
Object result = null;
String methodName = proceedingJoinPoint.getSignature().getName();
try {
System.out.println("Around方法执行前"+methodName);
//让方法继续进行,可以设置返回值
result = proceedingJoinPoint.proceed();
} catch (Throwable throwable) {
System.out.println("Around抛出异常"+methodName);
//必须抛出异常,否则不会进入异常通知
throw new RuntimeException(throwable);
}
System.out.println("Around方法执行后"+methodName);
//如果有返回值,必须返回,不然返回值为空
return result;
}
}

配置文件:

<context:component-scan base-package="demo"/>

<!--开启aop切面-->
<aop:aspectj-autoproxy/>

新建一个测试方法用于测试

@Test
public void run(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
"classpath:applicationContext.xml");

UserService userService = (UserService) applicationContext.getBean("userService");
String name = userService.run("张三");
}

接着我们进行测试
正常执行:

我们可以看到织入顺序
Around–Before—执行的方法—Around—After—AfterReturning

接着是异常执行:
我们只需要在执行方法中加个异常:

public String run(String name){
System.out.println(name);
int a = 10;
a = a / 0;
return name;
}


异常执行的织入顺序
Around–Before—执行的方法—Around—After—AfterThrowing

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: