理解Spring的AOP和IOC实现原理

本文深入探讨了AOP(面向切面编程)与IOC(控制反转)的概念、原理及其实现方式,详细介绍了AOP的连接点、切入点、通知、切面、织入和代理等基本概念,同时对比了JDK动态代理与CGLIB动态代理的用法。此外,还讲解了IOC的依赖注入机制及其在Spring框架中的应用。

1.AOP

AOP(面向切面)是一种编程范式,提供从另一个角度来考虑程序结构以完善面向对象编程(OOP)。
AOP为开发者提供了一种描述横切关注点的机制,并能够自动将横切关注点织入到面向对象的软件系统中,从而实现了横切关注点的模块化。
AOP能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任,例如事务处理、日志管理、权限控制等,封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。

使用AOP的好处

  • 降低模块的耦合度
  • 使系统容易扩展
  • 提高代码复用性

AOP的基本概念

  • 连接点(JoinPoint):需要在程序中插入横切关注点的点,连接点可能是在类初始化、方法调用、字段调用或处理异常等等。Spring中只支持方法执行连接点。
  • 切入点(Pointcut):一组相关连接点的集合。
  • 通知(Advice):在连接点上执行的行为,增强提供了在AOP中需要在切入点所选择的连接点处进行扩展现有行为的手段。包括前置增强(before advice)、后置增强 (after advice)、环绕增强 (around advice)。
  • 切面(Aspect):通知和切入点的结合。
  • 织入(Weaving):织入是一个过程,是将切面应用到目标对象从而创建出AOP代理对象的过程。
  • 代理(Proxy):通过代理方式来对目标对象应用切面。AOP代理可以用JDK动态代理或CGLIB代理实现。
  • 目标对象(Target):需要被织入关注点的对象。即被代理的对象。

     

实现AOP的主要设计模式就是动态代理。
Spring的动态代理有两种:一是JDK的动态代理;另一个是cglib动态代理。

JDK动态代理模拟
JDK动态代理的两个核心接口(类)分别是InvocationHandler和Proxy。注意:只能代理接口。

 

public class TimeHandler implements InvocationHandler {  
      
    // 目标对象  
    private Object targetObject;  
    
    public TimeHandler(Object targetObject){
          this.targetObject = targetObject;
    }
    @Override  
    //关联的这个实现类的方法被调用时将被执行  
    /*InvocationHandler接口的方法,proxy表示代理,method表示原对象被调用的方法,      
        args表示方法的参数*/  
    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", "老王");
    }

2.IOC

IOC(控制反转)就是依赖倒置原则的一种代码设计思路。就是把原先在代码里面需要实现的对象创建、对象之间的依赖,反转给容器来帮忙实现。
Spring IOC容器通过xml,注解等其它方式配置类及类之间的依赖关系,完成了对象的创建和依赖的管理注入。实现IOC的主要设计模式是工厂模式。


使用IOC的好处

 

  • 集中管理,实现类的可配置和易管理。
  • 降低了类与类之间的耦合度。

简单模拟IOC

 

public interface BeanFactory {
    Object getBean(String id);  
}

public class ClassPathXmlApplicationContext implements BeanFactory {
    //容器,用来存放注入的Bean  
    private Map<String, Object> container = new HashMap<String, Object>();  

    //解析xml文件,通过反射将配置的bean放到container中  
    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();  
    } 

参考资料



作者:课间指针
链接:https://www.jianshu.com/p/78ba8bafb90a
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

### 回答1: SpringAOPIOC实现原理AOP(面向切面编程)是Spring框架的一个重要特性,它通过在运行时动态地将代码切入到类的方法中,实现了对业务逻辑的解耦横向切割。SpringAOP实现原理是基于动态代理字节码生成技术,通过在运行时动态地生成代理对象,将切面逻辑织入到目标对象的方法中。 IOC(控制反转)是Spring框架的另一个重要特性,它通过将对象的创建依赖关系的管理交给Spring容器来实现,从而实现了对象之间的解耦灵活性。SpringIOC实现原理是基于反射XML配置文件,通过读取配置文件中的信息,动态地创建对象并注入依赖关系,从而实现了对象的管理控制。 ### 回答2: Spring框架作为Java企业应用中广泛使用的开发框架,其内部实现AOPIOC功能是其较为重要的组成部分。AOP(面向切面编程)IOC(控制反转)是两个不同的概念,但它们之间存在紧密联系。在回答任何一个问题之前,需要更深入地了解这些术语以及它们的目的。 首先,了解AOP实现原理AOP的主要原理是将整个应用程序分解成特定的相关部分,并标识出这些部分的职责。然后,AOP框架将横切关注点(Crosscutting Concerns)划分为一个个切面(Aspect),并实现将目标对象在运行时交织上这些切面的功能。这个交织的过程称为织入(Weaving),这样应用程序中的某些功能可以在运行时动态地添加到对象中,而无需对其进行静态编码。在Spring框架中,主流的AOP实现采用代理模式(Proxy)。它将目标对象切面运作在代理对象之间,代理对象则在运行时插入切面功能。 其次,了解IOC实现原理。控制反转是指将对象间的依赖关系的控制权从目标对象自身转移到容器中管理,从而实现对象的松耦合。Spring框架是典型的IOC容器,它利用依赖注入(Dependency Injection)技术,通过在配置文件(XML或JavaConfig)中定义对象之间的依赖关系,在容器中实例化对象并完成之间的依赖注入。在依赖注入中,容器将对象之间的依赖关系检测到,并自动为其中的依赖添加实例。这样,程序员就不需要在代码中显式地将对象之间的依赖关系硬编码,而可以通过注解、XML配置文件或JavaConfig等方式来管理这些对象。 综上所述,AOPIOCSpring框架的核心概念之一。通过使用Spring框架的AOPIOC功能,开发人员可以编写更简洁、高效的代码,并更容易地实现面向对象编程的最佳实践。 ### 回答3: SpringAOPIOC(控制反转)是Spring框架中非常重要的两个部分,能够让开发人员更好的实现面向切面编程解耦,提高代码可维护性灵活性。 首先,让我们来看看SpringIOC实现原理SpringIOC是控制反转的实现,即通过容器管理对象之间的依赖关系,而不是在代码中直接通过new关键词来创建对象。SpringIOC实现的核心思想是依赖注入(Dependency Injection,DI),即对象通过Setter、构造函数、注解等方式获取它所需要的依赖对象。通过Spring容器来管理维护所有应用程序需要的对象实例依赖关系。在实现IOC的时候,Spring会通过配置文件或者注解方式把对象的创建、初始化依赖注入关系都详细地描述出来,然后在运行时,通过反射机制自动创建对象,再将依赖注入进去,以此实现对象的控制权转由Spring容器来掌控。这样的实现可以减轻程序员的工作负担,提高程序的复用性扩展性。 其次,SpringAOP实现原理也是很重要的。AOP可以让程序员将一些横切逻辑业务处理逻辑分离出来,达到代码解耦的目的。在Spring中,AOP使用代理(Proxy)的方式实现,通过对需要执行的目标方法进行拦截,并在目标方法执行前后执行额外的逻辑,如开启/关闭事务、安全认证、日志记录等。代理类可以使用JDK自带的动态代理机制或CGLIB等第三方代理框架来生成,而切面对象则负责维护切面所需要拦截的目标对象、目标方法、切面执行顺序等信息。SpringAOP框架为切面提供丰富的表达式语言来指定需要拦截的方法,如通配符、正则表达式AspectJ注解等。 因此,SpringAOPIOC实现原理都是非常重要的,它们的实现可以让应用程序代码更灵活、更容易维护,同时也使得程序员的工作更加高效。但是需要注意的是,在使用SpringAOPIOC时,要注意配置的正确性合理性,否则可能会带来一些潜在的问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值