理解Spring的AOP和IOC实现原理
1、 IOC
IOC(控制反转)就是依赖倒置原则的一种代码设计思路。就是把原先在代码里面需要实现的对象创建、对象之间的依赖,反转给容器来帮忙实现。 Spring IOC容器通过xml,注解等其它方式配置类及类之间的依赖关系,完成了对象的创建和依赖的管理注入。实现IOC的主要设计模式是工厂模式。
这句话的意思是:
1、IOC控制反转的含义,它是把java面向对象的创建对象的过程,交由容器来进行管理一种机制。
2、它实现的方式,是通过XML,获取注解,配置类的方式实现类和类之间的依赖关系。
3、来完成的对象的创建和依赖管理的注入。
02、SpringIoc的设计初衷是什么?
- 集中管理,实现类的可配置和易管理。
- 降低了类与类之间的耦合度。实现java高内聚低耦合的设计思想
- 同时可以提升java应用程序的性能,减轻JVM的内存压力。
03、SpringIOC的核心概念
其实本质是通过:java语言衍生出来的框架和项目产品,只不过开源出来。你使用spring最大感受是什么?
是不是再也没有去new对象呢或者说你很少在去是实例化对象了
核心:创建对象,管理对象,注入对象,控制对象等,一句话:SpringIoc管理对象的整个生命周期。
非常经典的面试题:Spring的bean的生命周期
04、springioc容器的bean是单列的吗?
这个所谓单列不是指:用所谓的单列设计模式开发和你们所认识的单列不一样。
在你们眼中的单列模式:恶汉模式,懒汉模式,双重检查机制等等。
它们的共同点是:在内存空间中只有一份内存。思想是一样的。
那么sprignioc的bean的单列指是什么?
答案:就是一个容器Map,因为这个Map是一个全局的变量。
public class Factory{
private final Map<String, BeanDefinition> beanDefinitionMap;
beanDefinitionMap.put("userServiceImpl",new BeanDefinition());
beanDefinitionMap.put("userServiceImpl2",new BeanDefinition());
}
05、所谓的依赖注入
DI—Dependency Injection,即“依赖注入”:是组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。
理解DI的关键是:“谁依赖谁,为什么需要依赖,谁注入谁,注入了什么”,那我们来深入分析一下:
●谁依赖于谁:当然是应用程序依赖于IoC容器; ●为什么需要依赖:应用程序需要IoC容器来提供对象需要的外部资源; ●谁注入谁:很明显是IoC容器注入应用程序某个对象,应用程序依赖的对象; ●注入了什么:就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)。
IoC和DI由什么关系呢?其实它们是同一个概念的不同角度描述,由于控制反转概念比较含糊(可能只是理解为容器控制对象这一个层面,很难让人想到谁来维护对象关系),所以2004年大师级人物Martin Fowler又给出了一个新的名字:“依赖注入”,相对IoC 而言,“依赖注入”明确描述了“被注入对象依赖IoC容器配置依赖对象”。
所谓的依赖注入:其实就告诉你,在实际开发和生产中,类和类直接很多时候会存在所谓的依赖关系。为了这种一个对象依赖另外一个对象的时候,把存在依赖的对象实例化的过程称之为:依赖注入。
DI:它其实就把容器中创建存放好的对象,通过DI机制去完成类执行对象的实例化的问题。说白了就是:具体ioc动作。
1、IoC(控制反转)
==首先想说说IoC(Inversion of Control,控制反转)。这是spring的核心,贯穿始终。所谓IoC,对于spring框架来说,就是由spring来负责控制对象的生命周期和对象间的关系。==这是什么意思呢,举个简单的例子,我们是如何找女朋友的?常见的情况是,我们到处去看看找到自己喜欢的,然后打听她们的兴趣爱好、qq号、电话号………,想办法认识她们,投其所好送其所要……这个过程是复杂深奥的,我们必须自己设计和面对每个环节。传统的程序开发也是如此,在一个对象中,如果要使用另外的对象,就必须得到它(自己new一个),使用完之后还要将对象销毁,对象始终会和其他的接口或类耦合起来。
那么IoC是如何做的呢?有点像通过婚介找女朋友,在我和女朋友之间引入了一个第三者:婚姻介绍所。婚介管理了很多男男女女的资料,我可以向婚介提出一个要求的列表,告诉它我想找个什么样的女朋友,然后婚介就会按照我们的要求,提供一个女孩,我们只需要去和她谈恋爱、结婚就行了。简单明了,如果婚介给我们的人选不符合要求,我们就会抛出异常。整个过程不再由我自己控制,而是有婚介这样一个类似容器的机构来控制。Spring所倡导的开发方式就是如此,所有的类都会在spring容器中登记,告诉spring你是个什么东西,你需要什么东西,然后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由 spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转。
2、DI(依赖注入) ==IoC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(Dependency Injection,依赖注入)来实现的。比如对象A需要操作数据库,以前我们总是要在A中自己编写代码来获得一个Connection对象,有了 spring我们就只需要告诉spring,==A中需要一个Connection,至于这个Connection怎么构造,何时构造,A不需要知道。在系统运行时,spring会在适当的时候制造一个Connection,然后像打针一样,注射到A当中,这样就完成了对各个对象之间关系的控制。A需要依赖 Connection才能正常运行,而这个Connection是由spring注入到A中的,依赖注入的名字就这么来的。那么DI是如何实现的呢? Java 1.3之后一个重要特征是反射(reflection),它允许程序在运行的时候动态的生成对象、执行对象的方法、改变对象的属性,spring就是通过反射来实现注入的。
DI:它其实就把容器中创建存放好的对象,通过DI机制去完成类中存在依赖关系的对象实例化的问题。
如果你完成依赖注入的话:
就必须遵从它的注入规范:
1:set注入
2:构建函数注入
3:注解注入 @Resource/@Autowired
4:工厂方法注入
5:其他。。。。
06:总结
面试题:谈谈你对SpringIoc的理解和认识是什么?
我的理解是,首先spring框架是通过java语言开发出来的一个框架或者一个项目产品。我们java最大的特点是:面向对象,在没有使用spring框架的时候,都是用通过new去实例化对象,使用spring框架以后,它内部提供ioc机制,也就控制反转的含义,它核心基础是:把对象创建的过程,交由容器(Map)来进行管理一种机制,我们会把通过容器来管理对象的方式,将传统的创建方式更换成交由容器管理对象的过程称之为ioc控制反转的过程。总之如果要实现对象交由spring的ioc的来管理话,
就必须遵循spring提供的规范,而这些规范在传统spring开发中,是通过xml的方式来进行注入和管理的bean到容器中,现在主流的方式都是采用注解的方式进行管理和控制bean到容器中等,这样做得好处是:
- 集中管理,实现类的可配置和易管理。
- 降低了类与类之间的耦合度。实现java高内聚低耦合的设计思想 DI机制
- 同时可以提升java应用程序的性能,减轻JVM的内存压力。
我对DI的理解是:它其实就把容器中存在的类,容器中类和另外一个类存在依赖关系的话,DI机制会把这个类中存在的类进行实例化的过程称之为:依赖注入,但是前提是:必须要遵循依赖注入的规则:
1:set注入
2:构建函数注入
3:注解注入 @Resource/@Autowired
4:工厂方法注入
Springbean的三级缓存(循环依赖的问题)?
三个Map来处理,和交换它们的依赖关系
参考网址:https://www.cnblogs.com/ITtangtang/p/3978349.html
使用IOC的好处
- 集中管理,实现类的可配置和易管理。
- 降低了类与类之间的耦合度。
简单模拟IOC
public interface BeanFactory {
Object getBean(String id);
}
public class ClassPathXmlApplicationContext implements BeanFactory {
private Map<String, Object> container = new HashMap<String, Object>();
public ClassPathXmlApplicationContext(String fileName) throws Exception{
SAXBuilder sb = new SAXBuilder();
Document doc
=sb.build(ClassPathXmlApplicationContext.class.getResource("/"+fileName));
Element root = doc.getRootElement();
List<Element> list = XPath.selectNodes(root, "/beans/bean");
for (int i = 0; i < list.size(); i++) {
Element bean = list.get(i);
String id = bean.getAttributeValue("id");
String clazz = bean.getAttributeValue("class");
Object o = Class.forName(clazz).newInstance();
container.put(id, o);
}
}
@Override
public Object getBean(String id) {
return container.get(id);
}
}
需要导入 jdom.jar包。
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="people" class="com.ioc.People" />
<bean id="chicken" class="com.ioc.Chicken" />
<bean id="dog" class="com.ioc.Dog" />
</beans>
public interface Animal {
void say();
}
public class Dog implements Animal {
@Override
public void say() {
System.out.println("汪汪");
}
}
public class Chicken implements Animal {
@Override
public void say() {
System.out.println("鸡你很美");
}
}
public class People {
public void info(){
System.out.println("小明-23岁");
}
}
public static void main(String[] args) throws Exception {
//加载配置文件
BeanFactory f = new ClassPathXmlApplicationContext("applicationContext.xml");
Object os = f.getBean("dog");
Animal dog = (Animal)os;
dog.say();
Object op = f.getBean("chicken");
Animal chicken = (Animal)op;
chicken.say();
Object p = f.getBean("people");
People people= (Animal)p;
people.info();
}
2、AOP
AOP(面向切面)是一种编程范式,提供从另一个角度来考虑程序结构以完善面向对象编程(OOP)。 AOP为开发者提供了一种描述横切关注点的机制,并能够自动将横切关注点织入到面向对象的软件系统中,从而实现了横切关注点的模块化。 AOP能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任,例如事务处理、日志管理、权限控制等,封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。
01、为什么要学习AOP
场景:
- 其实在开发中,很多时候,有一些业务逻辑非常通用和公用,比如:接口的登录校验、限流处理、安全性处理等
- 或者开发很多业务会执行处理附属业务,比如:下单、用户保存日志,下单要发送系统消息等。
public class OrderService {
public void makeorder(){
}
public void reguser(){
}
}
public static void main(String[] args){
OrderService orderService = new OrderService();
orderService.makeorder();
orderService.reguser();
}
结论:
所谓的AOP:当对象执行方法的时候,会对你对象执行的方法进行横切面的拦截处理。并对其方法的业务进行织入逻辑增强处理.
02、如何进行的拦截的呢?
在java中只有两种中方式可以对对象执行方法进行拦截处理:
- jdk动态代理(必须要接口)
- 在平时的开发中,如果你有接口IUserService,进行子类的实例化的时候,其实就是使用jdk的动态代理,然后通过IOC的DI机制,完成注入。默认是:jdk动态代理
- cglib动态代理 (不需要接口)
- 在平时的开发中,如果没有接口IUserService,进行子类的实例化的时候,其实就是使用cglib的动态代理,然后通过IOC的DI机制,完成注入。
- Aop:采用就是:Jdk动态代理 和 cglib动态代理
public static void main(String[] args){
OrderService orderService = new OrderService();
orderService.makeorder();
orderService.reguser();
}
// orderService – 普通java对象 // 但是如果OrderService一旦被代理了,这个时候orderServiceproxy就是一个代理对象。
public static void main(String[] args){
OrderService orderServiceproxy = Proxy.newIntances(OrderService接口,LogAspect.class,xxx);
orderServiceproxy.makeorder();
orderServiceproxy.reguser();
}
// 只有是代理对象才可以进行拦截处理
而代理产生,必须通过jdk动态代理或者cglib代理才可以生成所谓的代理对象。
03、AOP是如何增强的呢?
通过切面类
public interface IOrderService {
public void makeorder();
public void reguser();
}
public class OrderService implments IOrderService{
public void makeorder(){
}
public void reguser(){
}
}
切面类:
public class LogAspect{
public void log(){
}
}
注意上面的切面类,只是一个概念,真正的切面类必须要遵循:jdk动态代理的规范。切面类必须使用接口:InvocationHandler
public class LogAspect implements InvocationHandler{
private Object targetObject;
public TimeHandler(Object targetObject){
this.targetObject = targetObject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object ret=null;
try{
System.out.println("方法之前:"+System.currentTimeMillis());
ret=method.invoke(targetObject, args);
System.out.println("方法之后:"+System.currentTimeMillis());
}catch(Exception e){
e.printStackTrace();
System.out.println("error");
throw e;
}
return ret;
}
}
public static void main(String[] args){
IOrderService orderServiceproxy = Proxy.newIntances(IOrderService.class,LogAspect.class,OrderService.class);
orderServiceproxy.makeorder();
orderServiceproxy.reguser();
}
总结
1、Aop是面向切面编程的一种思想,它是指对象 执行方法的时候,对执行的方法进行逻辑的增强处理
2、AOP是如何进行拦截处理的?采用jdk动态代理和cglib进行切换处理。
3、执行的对象必须是:代理对象,而代理对象产生必须使用jdk的动态代理和cglib来产生。
4:有代理对象,它是如何增强逻辑的呢?它是使用:切面类来进行增强逻辑处理。如何你使用的jdk的动态来来实现AOP的话,那么你切面类就必须实现InvocationHandler,覆盖invoke方法,这个方式就是让你去执行代码增强的方法。
5、使用AOP的好处
AOP的基本概念
-
连接点(JoinPoint):需要在程序中插入横切关注点的点,连接点可能是在类初始化、方法调用、字段调用或处理异常等等。Spring中只支持方法执行连接点。具体执行的方法 -
切入点(Pointcut):一组相关连接点的集合。一句话:你所以需要拦截方法的集合(@Poincut()) -
通知(Advice):在连接点上执行的行为,增强提供了在AOP中需要在切入点所选择的连接点处进行扩展现有行为的手段。包括前置增强(before advice)、后置增强 (after advice)、环绕增强 (around advice)。 其实就是指:具体处理业务的位置和时间点。它是切面类的invoke方法。其实通知:就是你具体要做得事情。比如日志处理 -
切面(Aspect):通知和切入点的结合。(具体增强逻辑处理的类,也就切面类) -
织入(Weaving):织入是一个过程,是将切面应用到目标对象从而创建出AOP代理对象的过程。)就把对象对象执行的方法和通知关联的过程称之为织入,一句话:代理对象执行方法,要去那个通知方法执行增强逻辑处理 -
代理(Proxy):通过代理方式来对目标对象应用切面。AOP代理可以用JDK动态代理或CGLIB代理实现。 -
目标对象(Target):需要被织入关注点的对象。即被代理的对象。具体的:orderServiceImpl,为什么要传递一个目标对象进来呢? 代理对象目的是什么?进行逻辑增强,增强以后你是不是还要去处理后续方法业务呢?
JDK动态代理模拟 JDK动态代理的两个核心接口(类)分别是InvocationHandler和Proxy。注意:只能代理接口。
public class TimeHandler implements InvocationHandler {
private Object targetObject;
public TimeHandler(Object targetObject){
this.targetObject = targetObject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object ret=null;
try{
System.out.println("方法之前:"+System.currentTimeMillis());
ret=method.invoke(targetObject, args);
System.out.println("方法之后:"+System.currentTimeMillis());
}catch(Exception e){
e.printStackTrace();
System.out.println("error");
throw e;
}
return ret;
}
}
TimeHandler 类实现了InvocationHandler接口。实现核心方法invoke,共有3个参数。第一个参数 生成的代理类实例,第二个参数 目标对象的方法,第三个参数 方法的参数值数组。
public class ProxyUtil {
@SuppressWarnings("unchecked")
public static <T> T proxyOne(ClassLoader loader,Class<?>[] clz,InvocationHandler handler){
return (T)Proxy.newProxyInstance(loader, clz, handler);
}
}
ProxyUtil 类简单封装了一下Proxy.newProxyInstance()方法。该方法也有3个参数。第一个参数产生代理对象的类加载器,第二个参数目标对象的接口数组,第三个参数就是实现InvocationHandler接口的类实例。
public interface UserManager {
public void addUser(String userId, String userName);
}
public class UserManagerImpl implements UserManager {
@Override
public void addUser(String userId, String userName) {
System.out.println("addUser(id:"+userId+",name:"+userName+")");
}
}
public static void main(String[] args) {
UserManager um=new UserManagerImpl();
LogHandler log =new LogHandler(um);
um=ProxyUtil.proxyOne(um.getClass().getClassLoader(),
um.getClass().getInterfaces(), log);
TimeHandler time = new TimeHandler(um);
um=ProxyUtil.proxyOne(um.getClass().getClassLoader(),
um.getClass().getInterfaces(), time);
um.addUser("1111", "张三");
}
为了演示需要,这边又增加了一个LogHandler,跟TimeHandler代码一样。
CGLIB动态代理模拟 CGLIB动态代理的两个核心接口(类)分别是MethodInterceptor和Enhancer。是不是跟JDK动态代理很相似,用法也差不多。但CGLIB可以代理类和接口。注意:不能代理final类。
public class TimeInterceptor implements MethodInterceptor {
private Object target;
public TimeInterceptor(Object target) {
this.target = target;
}
@Override
public Object intercept(Object proxy, Method method,
Object[] args, MethodProxy invocation) throws Throwable {
System.out.println("方法之前:"+System.currentTimeMillis());
Object ret = invocation.invoke(target, args);
System.out.println("方法之后:"+System.currentTimeMillis());
return ret;
}
}
intercept方法4个参数。1.生成的代理类实例。2.被代理对象的方法引用。3.方法参数值数组。4.代理类对方法的代理引用。
public class ProxyUtil {
@SuppressWarnings("unchecked")
public static <T> T proxyOne(Class<?> clz,MethodInterceptor interceptor){
return (T)Enhancer.create(clz, interceptor);
}
}
Enhancer类是CGLib中的字节码增强器。
public class UserManage {
public void addUser(String userId, String userName) {
System.out.println("addUser(id:"+userId+",name:"+userName+")");
}
}
public static void main(String[] args) {
UserManage um = new UserManage();
TimeInterceptor time = new TimeInterceptor(um);
um = ProxyUtil.proxyOne(um.getClass(), time);
um.addUser("111", "老王");
}
04、总结
1、Aop是一种面向切面编程思想,它是指对象执行方法的时候,对执行的方法进行逻辑的增强处理
2、AOP是如何进行拦截处理的?采用jdk动态代理和cglib进行切换处理。
3、执行的对象必须是:代理对象,而代理对象产生必须使用jdk的动态代理和cglib来产生。
4:有代理对象,它是如何增强逻辑的呢?它是使用:切面类来进行增强逻辑处理。如何你使用的jdk的动态来来实现AOP的话,那么你切面类就必须实现InvocationHandler,覆盖invoke方法,这个方式就是让你去执行代码增强的方法。
5、使用AOP的好处
05、谈谈你对Sprign的AOP的认识?
其实在之前项目开发中,很多时候,在处理一些业务的时候,很多业务逻辑非常通用和公用的,比如:接口的登录校验、限流处理、安全性处理、下单、用户保存日志,下单要发送系统消息等。都可以采用AOP的思想进行处理。AOP实现机制是通过采用jdk动态代理和cglib进行切换处理。默认情况下是采用:jdk动态代理实现,可以通过proxy=true/false来切换jdk动态代理还是cglib代代理,cglib代理和jdk动态区别就是有没有接口的区别。我以jdk动态代理为例,如果要实现方法的动态代理增强的话,就需要准备一个接口,一个实现类,一个代理对象、还有切面类,而这个切面类必须要实现接口InvocationHandler接口。覆盖invoke方法。把需要增强的逻辑放入到invoke方法进行处理,而代理对象的产生是通过Proxy.newInstance() 产生的。
而SpringAop把jdk动态代理的所谓的细节和控制进行了封装了。提出了很多的一个概念,其中就包括:
-
切面(Aspect):通知和切入点的结合。==(具体增强逻辑处理的类,也就切面类)==这个类必须要增加注解:@Aspect .来标准当前类是一个切面类。 -
代理(Proxy):通过代理方式来对目标对象应用切面。AOP代理可以用JDK动态代理或CGLIB代理实现。 -
通知(Advice):在连接点上执行的行为,增强提供了在AOP中需要在切入点所选择的连接点处进行扩展现有行为的手段。包括前置增强(before advice)、后置增强 (after advice)、环绕增强 (around advice)。 其实就是指:具体处理业务的位置和时间点。它是切面类的invoke方法。 其实通知:就是你具体要做得事情。比如日志处理 -
目标对象(Target):需要被织入关注点的对象。即被代理的对象。具体的:orderServiceImpl,为什么要传递一个目标对象进来呢? 代理对象目的是什么?进行逻辑增强,增强以后你是不是还要去处理后续方法业务呢? -
连接点(JoinPoint):需要在程序中插入横切关注点的点,连接点可能是在类初始化、方法调用、字段调用或处理异常等等。Spring中只支持方法执行连接点。具体执行要拦截的方法,一句话:就是代理对象执行的方法 -
切入点(Pointcut):一组相关连接点的集合。一句话:你所以需要拦截方法的集合,对所有满足@Poincut()条件的方法进行过滤。(@Poincut()) -
织入(Weaving):织入是一个过程,是将切面应用到目标对象从而创建出AOP代理对象的过程。)就把对象对象执行的方法和通知关联的过程称之为织入,一句话:代理对象执行方法,要去那个通知方法执行增强逻辑处理
|