一、Ioc
IoC是控制反转,通俗讲就是把对象创建和对象之间的调用交给Spring管理,通过简单的xml配置(或者注解、Java8)就可以创建和调用对象,其主要目的就是解耦,降低代码之间的耦合度
代码之间解耦的方式
接口解耦: 使得UserService和UserDao1, UserDao2之间的联系没那么紧密,Dao都实现了UserDao接口。这是一种弱耦合的关系。但是会发现接口和实现类中有强耦合关系了。比如UserDao dao = new UserDao2();
工厂模式解耦:所谓的工厂模式也就是将所有的创建对象任务交给了一个“中间人”,也就是工厂类来实现,要想使用对象,直接找工厂类,实现类必须要从工厂中取出来。
创建对象只需要调用工厂类BeanFactory中的方法即可,调用时不是直接通过接口,而是通过工厂类,将创建对象交给了工厂类,就降低了接口和实现类之间的耦合
但是,这样接口和工厂类之间就产生了耦合,为了再次解耦,我们引入了反射+xml配置文件的方式进行再次解耦xml配置+反射+工厂解耦(IoC底层的实现)
<bean id="userDao" class="**.UserDaoImpl">
class BeanFactory {
public static UserDao getUserDao(String id) {
// String className = 解析配置文件xml 拿到id对应的class
// 反射
class clazz = class.forName(className);
return clazz.newInstance();
}
}
没有直接像上面那样直接new对象,而是使用了xml解析和反射方式创建对象,分析如下:
- 通过xml解析获取对象中属性的值
- 通过反射得到字节码文件
- 通过字节码文件创建对象
这样实现改UserDao实现类型,只要改xml文件(后面是改配置类),实现解耦
- Ioc的解读
Ioc是一种思想,将设计好的对象交给容器,不是传统的对象内部直接控制。
- 传统的Java是主动创建对象,Ioc是有专门的容器来创建这些对象,即由IoC容器来控制对 象的创建
- IoC容器控制了对象;主要控制了外部资源获取
- 反转是由容器来帮忙创建及注入依赖对象
- 因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转.依赖对象的获取被反转了
- Ioc能做什么
- 有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是 松散耦合,
这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活 - 应用程序原本是老大,要获取什么资源都是主动出击,但是在IoC/DI思想中,应用程序就变成被动的了,
被动的等待IoC容器来创建并注入它所需要的资源了。 - IoC很好的体现了面向对象设计法则之一—— 好莱坞法则:“别找我们,我们找你”;
即由IoC容器帮对象找相应的依赖对象并注入,而不是由对象主动去找
- DI(依赖注入)
- 组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中
- 依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台
- 通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现
谁依赖于谁:当然是应用程序依赖于IoC容器;
为什么需要依赖:应用程序需要IoC容器来提供对象需要的外部资源;
谁注入谁:很明显是IoC容器注入应用程序某个对象,应用程序依赖的对象;
注入了什么:就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)
- IoC和DI有什么关系
它们是同一个概念的不同角度描述,由于控制反转概念比较含糊(可能只是理解为容器控制对象这一个层面,很难让人想到谁来维护对象关系),
所以2004年大师级人物Martin Fowler又给出了一个新的名字:“依赖注入”,相对IoC 而言,“依赖注入”明确描述了“被注入对象依赖IoC容器配置依赖对象”
依赖注入的意思是你需要的东西不是由你创建的,而是第三方,或者说容器提供给你的。这样的设计符合正交性,即所谓的松耦合
二、AOP
面向切面编程,指在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的操作。如:性能监控、日志记录、权限控制等,
通过AOP解决代码耦合问题,让职责更加单一动态代理增强
- 动态代理,需要一个代理类,这个代理类是动态生成的,字节码要用的时候就创建,要用的时候就加载,在不修改源码的基础上对方法进行增强
- 有两种代理机制,一种是基于JDK的动态代理,另一种是基于CGLib的动态代理。其中JDK代理必须要实现了接口才行。
spring aop 的底层,也是使用的动态代理
// CGLib
final UserService bean = new UserService();
UserService cglibProducer = (UserService) Enhancer.create(bean.getClass(), new MethodInterceptor(){
/**
* 作用:执行被代理对象的任何借口方法都会经过该方法
* @param proxy:代理对象的引用
* @param method:当前执行的方法
* @param args:当前执行方法所需的参数
* @return:和被代理对象方法有相同的返回值
* @throws Throwable
*/
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy)
throws Throwable {
System.out.println("记录日志");
Object result = method.invoke(bean, args);
return result;
}
});
- 启用AOP的步骤
- @EnableAspectJAutoProxy注解 开启
- @Aspect注解声明一个切面,@Pointcut()声明切点,并使用@Around、@Before、@After等注解表明连接点
- 简单的来说,@Aspect告诉系统这里是切面了,@Pointcut表示切在哪里(比如某个类里的所有方法), @Around表示怎么切,是方法前后切,方法后切等
- APO术语
- 连接点(Joinpoint): 比如某个方法调用前、调用后、抛出异常后,某个边界性质(某个时机),如@Before,@Around
- 切点(Pointcut):指的是连接点的集合,我理解就是切入在哪里。通过切点定位到在哪里执行,通过连接点确定执行时机
- 通知(Advice): 简单的说,就是需要做什么,就是在连接点的基础上
- 目标对象:被增强的对象
- 引介:允许我们向现有的类添加新方法属性
- 织入:织入是将通知添加到目标类具体连接点上的过程。有三种织入方式:
动态代理织入(在运行期为目标类添加通知生成子类的方式); 类装载织入(要求使用特殊的类装载器); 编译期织入(使用特殊的Java编译器);、
Spring用的动态代理,AspectJ使用的是编译织入和类装载织入 - 代理对象(Proxy): AOP织入通知后,会产出一个结果类,就是代理对象
- 切面(AspectJ): 连接点和切点以及通知所在的那个类称为切面
- AOP原理