Spring之IOC和AOP及DI
一、IOC(Inversion of Control
)控制反转
1. 概念
IoC是 Inversion of Control
的简写,译为“控制反转”,它不是一门技术,而是一种设计思想,是一个重要的面向对象编程法则,能够指导我们如何设计出松耦合、更优良的程序。
Spring通过IoC容器
来管理所有 Java 对象的实例化和初始化,控制对象与对象之间的依赖关系。我们将由 IoC 容器管理的 Java 对象称为 Spring Bean,它与使用关键字 new 创建的 Java 对象没有任何区别。
IoC容器
是Spring框架中最重要的核心组件之一,它贯穿了Spring从诞生到成长的整个过程。
2. 实现方式
此处先要想清楚一个事情,
控制反转
就是把对象的实例化
和初始化
交给Spring去管理,而不是我们自己用到的时候自己去实例化,那么它时如何管理呢?换句话说,Spring如何利用IOC思想创建对象
和给对象赋值
呢?此处只是说明大致过程,详细了解 Spring IOC 容器源码分析
通过解析
xml
或者注解
的方式拿到类的相关信息通过
BeanFactory工厂
,在工厂内部利用反射完成类的实例化和初始化并将结果放入一个
ConcurrentHashMap<String, BeanDefinition>
中,其中 Key默认是类名首字母小写 , BeanDefinition存的是类的定义(描述信息)
3. 注意事项
Spring利用
IOC容器
进行Bean的容器管理,IOC 容器
底层就是对象工厂Spring 使用工厂模式可以通过
BeanFactory
或ApplicationContext
创建 bean 对象BeanFactory::IOC容器基本实现,是Spring内部的使用接口,不提供开发人员进行使用,加载配置文件时候不会创建对象,在获取对象(使用) 才去创建对象
ApplicationContext:BeanFactor接口的子接口,提供更多更强大的功能,一般由开发人员进行使用,加载配置文件时候就会对象创建
ApplicationContext
的三个实现类:ClassPathXmlApplication
:把上下文文件当成类路径资源。FileSystemXmlApplication
:从文件系统中的 XML 文件载入上下文定义信息。XmlWebApplicationContext
:从 Web 系统中的 XML 文件载入上下文定义信息。
二、DI(Dependency Injection):依赖注入
通过IOC
将bean交给Spring进行管理了,我们如何使用Spring管理的bean呢?通过依赖注入
依赖注入
指Spring创建对象的过程中,将对象依赖属性通过配置进行注入
注入的方式参考 Spring之基于注解的Bean管理
三、AOP(Aspect Oriented Programming):面向切面编程
AOP
(Aspect Oriented Programming)是一种设计思想,是软件设计领域中的面向切面编程,它是面向对象编程的一种补充和完善,它以通过预编译方式和运行期动态代理方式实现,在不修改源代码的情况下,给程序动态统一添加额外功能的一种技术。
利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
1. 代理
动态代理分为JDK动态代理和cglib动态代理
当目标类有接口的情况使用JDK动态代理和cglib动态代理,没有接口时只能使用cglib动态代理
JDK动态代理动态生成的代理类会在com.sun.proxy包下,类名为$proxy1,和目标类实现相同的接口
cglib动态代理动态生成的代理类会和目标在在相同的包下,会继承目标类
动态代理(InvocationHandler):JDK原生的实现方式,需要被代理的目标类必须实现接口。因为这个技术要求代理对象和目标对象实现同样的接口(兄弟两个拜把子模式)。
cglib:通过继承被代理的目标类(认干爹模式)实现代理,所以不需要目标类实现接口。
AspectJ:是AOP思想的一种实现。本质上是静态代理,将代理逻辑“织入”被代理的目标类编译得到的字节码文件,所以最终效果是动态的。weaver就是织入器。Spring只是借用了AspectJ中的注解。
2. 如何使用
@Aspect //表示这个类是一个切面类
@Component //保证这个切面类能够放入IOC容器
public class LogAspect {
//前置通知:使用@Before注解标识,在被代理的目标方法前执行
@Before("execution(public int com.litecode.aop.annotation.CalculatorImpl.*(..))")
public void beforeMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
String args = Arrays.toString(joinPoint.getArgs());
System.out.println("Logger-->前置通知,方法名:"+methodName+",参数:"+args);
}
//后置通知:使用@After注解标识,在被代理的目标方法最终结束后执行
@After("execution(* com.litecode.aop.annotation.CalculatorImpl.*(..))")
public void afterMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
System.out.println("Logger-->后置通知,方法名:"+methodName);
}
//返回通知:使用@AfterReturning注解标识,在被代理的目标方法成功结束后执行
//属性returning,用来将通知方法的某个形参,接收目标方法的返回值(可省略)
@AfterReturning(value = "execution(* com.litecode.aop.annotation.CalculatorImpl.*(..))", returning = "result")
public void afterReturningMethod(JoinPoint joinPoint, Object result){
String methodName = joinPoint.getSignature().getName();
System.out.println("Logger-->返回通知,方法名:"+methodName+",结果:"+result);
}
//使用@AfterThrowing注解标识,在被代理的目标方法异常结束后执行
//属性throwing,用来将通知方法的某个形参,接收目标方法的异常(可省略)
@AfterThrowing(value = "execution(* com.litecode.aop.annotation.CalculatorImpl.*(..))", throwing = "ex")
public void afterThrowingMethod(JoinPoint joinPoint, Throwable ex){
String methodName = joinPoint.getSignature().getName();
System.out.println("Logger-->异常通知,方法名:"+methodName+",异常:"+ex);
}
//环绕通知:使用@Around注解标识,使用try...catch...finally结构围绕整个被代理的目标方法,包括上面四种通知对应的所有位置
@Around("execution(* com.atguigu.aop.litecode.CalculatorImpl.*(..))")
public Object aroundMethod(ProceedingJoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
String args = Arrays.toString(joinPoint.getArgs());
Object result = null;
try {
System.out.println("环绕通知-->目标对象方法执行之前");
//目标对象(连接点)方法的执行
result = joinPoint.proceed();
System.out.println("环绕通知-->目标对象方法返回值之后");
} catch (Throwable throwable) {
throwable.printStackTrace();
System.out.println("环绕通知-->目标对象方法出现异常时");
} finally {
System.out.println("环绕通知-->目标对象方法执行完毕");
}
return result;
}
}
3. 切入点表达式
3.1切入点表达式语法
3.2重用切入点表达式
①声明
@Pointcut("execution(* com.atguigu.aop.annotation.*.*(..))")
public void pointCut(){}
②在同一个切面中使用
@Before("pointCut()")
public void beforeMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
String args = Arrays.toString(joinPoint.getArgs());
System.out.println("Logger-->前置通知,方法名:"+methodName+",参数:"+args);
}
③在不同切面中使用
@Before("com.atguigu.aop.CommonPointCut.pointCut()")
public void beforeMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
String args = Arrays.toString(joinPoint.getArgs());
System.out.println("Logger-->前置通知,方法名:"+methodName+",参数:"+args);
}