Spring IOC AOP

IOC

IOC 概念

Inversion of Control 控制反转

面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫"依赖查找"(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。

(1)控制反转,把对象创建和对象之间的调用过程,交给Spring进行管理
(2)使用IOC目的:为了降低耦合度

IOC原理

  • xml解析
  • 工厂模式
  • 反射

IOC思想基于IOC容器完成,IOC容器底层就是对象工厂。

Service 层 调用 DAO 层

原始方法

耦合度太高

class UserService{
    public void execute(){
        UserDao dao = new UserDao();
        dao.add();
    }
}
class UserDao{
    public void add(){
        //...
    }
}

工厂模式

目的:
耦合度降低最低限度

class UserService{
    public void execute(){
        UserDao dao = UserFactory.getDao();
        dao.add();
    }
}
class UserDao{
    public void add(){
        //...
    }
}
class UserFactory{
	public UserDao getDao(){
        return new UserDao();
    }
}

IOC过程

  • 第一步 xml配置文件,配置创建的对象

    <bean id="dao" class=ncom.atguigu.UserDaob></bean>
    
  • 第二步 有service类和dao类,创建工厂类

    class UserFactory{
    	public static UserDao getDao(){
            String classValue = class属性值;  //1 xml解析
            Class clazz = Class.forName(classValue); //2 通过反射创建对象
            return (UserDao)clazz.newInstance();
        }
    }
    

进一步降低耦合度。

IOC 接口

BeanFactory

  • IOC容器基本实现,是Spring 内部的使用接口,不提供开发人员进行使用。

  • 加载配置文件时候不会创建对象,在获取对象(使用)才去创建对象。

ApplicationContext

  • 接口的子接口,提供更多更强大的功能,一般由开发人员进行使用。

  • 加载配置文件时候就会把在配置文件对象进行创建。

ApplicationContext实现类

  • ClassPathXmlApplicationContext

表示读取的配置文件在工程路径下

  • FileSystemXmlApplicationContext

表示读取的配置文件在磁盘路径下

AOP

AOP 简介

面向方面编程(Aspect Oriented Programming)

函数式编程的一种衍生范式。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各模块之间的遇合度降低,提高程序的可重用性,同时提高了开发的效率。

  • 例子
    不修改源码,添加新的功能。
    在这里插入图片描述

AOP原理

AOP底层使用动态代理

有两种情况动态代理

  • 有接口 使用JDK动态代理
    创建接口实现类代理对象,增强类的方法
  • 无接口,使用CGLIB动态代理
    创建子类的代理对象,增强类的方法

JDK动态代理

调用Proxy.newProxyInstance(1, 2, 3)获取代理对象

  • 第一个参数为类加载器
  • 第二个是实现类实现的接口数组
  • 第三个是InvocationHandler实现类,实现invoke方法
public class ProxyDemo {
    public static void main(String[] args) {
        Class<?>[] interfaces = {UserDao.class};
        UserDao userDao = new UserDaoImpl();
        UserDao proxyInstance = (UserDao)Proxy.newProxyInstance(ProxyDemo.class.getClassLoader(), interfaces, new UserDaoProxy(userDao));
        int res = proxyInstance.add(1, 3);
        System.out.println("res = " + res);
    }
}
class UserDaoProxy implements InvocationHandler{
    private final Object obj; //代理对象
    public UserDaoProxy(Object obj){
        this.obj = obj;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("方法调用之前:" + method.getName() + " 参数:" + Arrays.toString(args));
        Object res = method.invoke(obj, args);
        System.out.println("方法调用之后:" +obj);
        return res;
    }
}

IOP 术语

  • 连接点
    类里面哪些方法可以被增强,这些方法称为连接点
  • 切入点
    实际被真正增强的方法,称为切入点
  • 通知(增强)
    • 实际增强的逻辑部分称为通知(增强)
    • 通知类型
      • 前置通知
      • 后置通知
      • 环绕通知
      • 最终通知
  • 切面
    把通知应用到切入点的过程称为切面

IOP操作

准备

  • Spring 框架一般都是基于AspectJ实现AOP操作。

  • 什么是AspectJ
    AspectJ不是Spring组成部分,独立AOP框架。一般把AspectJ和Spring 框架一起使用,进行AOP操作。

  • 基于Aspectj实现AOP操作。
    (1)基于xml 配置文件
    (2)基于注解方式实现

  • 项目中引入AOP依赖

  • 切入点表达式

  1. 切入点表达式作用:知道对哪个类里面的哪个方法进行增强。

  2. 语法结构:

    execution([权限修饰符][返回类型][类全路径][方法名称]([参数列表])
    
  3. 举例

    • 对com.zx.dao.BookDao类里面的add进行增强

      execution(*com.zx.dao.BookDao.add(..)
      
    • 对com.zx.dao.BookDao 类里面的所有的方法进行增强。

      execution(*com.zx.dao.BookDao.*(..)
      
    • 对com.zx.dao 包里面所有类,类里面所有方法进行增强。

      execution(*com.zx.dao.*.*(..)
      

注解方式

模拟AspectJ 注解方式 实现AOP操作

  • 创建类,定义方法
public class User {
    public void update(){
        System.out.println("update...");
    }
}
  • 创建增强类(编写增强逻辑)
public class UserProxy {
    public void before(){
        System.out.println("before...");
    }
}
  • 进行通知配置

    • 在spring配置文件中,开启注解扫描

    • 使用注解创建 User 和 UserProxy 对象

    • 在增强类上面添加注解@Aspect

      @Component
      public class User {
          public void update(){
              System.out.println("update...");
          }
      }
      
      @Component
      @Aspect
      public class UserProxy {
          public void before(){
              System.out.println("before...");
          }
      }
      
  • 在spring配置文件中开启生成代理对象。

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
                    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--    开启注解扫描-->
        <context:component-scan base-package="com.zx.spring.aopAnnotation"/>
    <!--    开启生成代理对象-->
        <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    </beans>
    
  • 配置不同类型通知

    在增强类的里面,在作为通知方法上面添加通知类型注解,使用切入点表达式配置。

    @Before(value = "execution(* com.zx.spring.aopAnnotation.User.update(..))")
    public void before(){
        System.out.println("before...");
    }
    
  • 测试

    @Test
    public void testAnnotation(){
        ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
        User user = context.getBean("user", User.class);
        user.update();
    }
    

    在update方法之前执行before方法

    before…
    update…

通知类型
  • @Before 方法执行前
  • @After 方法执行后
  • @AfterReturing 方法返回后
  • @AfterThrowing 异常抛出后
  • @Around 返回执行前后执行
@Before(value = "execution(* com.zx.spring.aopAnnotation.User.update(..))")
public void before(){
    System.out.println("before...");
}
@After(value = "execution(* com.zx.spring.aopAnnotation.User.update(..))")
public void after(){
    System.out.println("after...");
}
@AfterReturning(value = "execution(* com.zx.spring.aopAnnotation.User.update(..))")
public void afterReturning(){
    System.out.println("afterReturning...");
}
@AfterThrowing(value = "execution(* com.zx.spring.aopAnnotation.User.update(..))")
public void afterThrowing(){
    System.out.println("afterThrowing...");
}
@Around(value = "execution(* com.zx.spring.aopAnnotation.User.update(..))")
public void around(ProceedingJoinPoint point){
    System.out.println("环绕前...");
    try {
        point.proceed();
    } catch (Throwable e) {
        e.printStackTrace();
    }
    System.out.println("环绕后...");
}
公共切入点提取
  • 写一个方法
  • 使用Pointcut注解指定value
//公共切入点提取
@Pointcut(value = "execution(* com.zx.spring.aopAnnotation.User.update(..))")
public void point(){

}
  • 使用
//    @Before(value = "execution(* com.zx.spring.aopAnnotation.User.update(..))")
@Before(value = "point()")
public void before(){
    System.out.println("before...");
}
增强类优先级

有多个增强类对同一个方法进行增强,设置增强类优先级

  • 在增强类上面添加注解@Order(数字类型值),数字类型值越小优先级越高
完全注解开发
  • 配置类
@Configuration
@ComponentScan(basePackages = {"com.zx"})
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class ConfigAop {
}

配置文件方式

  1. 创建两个类,增强类和被增强类,创建方法

    public class Book {
        public void buy(){
            System.out.println("buy...");
        }
    }
    
    public class BookProxy {
        public void before(){
            System.out.println("before...");
        }
    }
    
  2. 在 spring 配置文件中创建两个类对象。

    <!--    创建bean对象-->
    <bean id="book" class="com.zx.spring.aopXml.Book"/>
    <bean id="bookProxy" class="com.zx.spring.aopXml.BookProxy"/>
    
  3. 在spring配置文件中配置切入点。

    <!--    配置aop增强-->
    <aop:config>
        <!--        配置切入点-->
        <aop:pointcut id="p" expression="execution(* com.zx.spring.aopXml.Book.buy(..))"/>
        <!--        配置切面-->
        <aop:aspect ref="bookProxy">
            <!--            增强作用在具体方法上-->
            <aop:before method="before" pointcut-ref="p"/>
        </aop:aspect>
    </aop:config>
    
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值