Aop——面向切面编程

静态代理和动态代理

aop底层是动态代理

静态代理

代理模型分析:

  1. 抽象角色:一般会使用抽象类或接口实现
  2. 真实角色:被的代理的角色
  3. 代理角色:代理真实角色后,一般会做一些附属的操作
  4. 客户:使用代理角色进行一些操作,从而得到真实角色的东西和代理角色的特有东西

代码实现:(我们用租房子为例)

  1. 接口:

    //租房的接口:抽象
    public interface Rent {
        //租房
        void rent();
    }
    
  2. 真实对象:

    //房东的这个房子要出租
    public class Host implements Rent {//房东实现rent接口
        //出租
        public void rent(){
            System.out.println("host要出租房子");
        }
    
    }
    
  3. 的代对象:

    package com.david.staticproxy;
    
    //中介,即代理
    public class Proxy implements Rent {//中介实现rent接口
        //房东
        private Host host;
        public void setHost(Host host) {
            this.host = host;
        }
        public void rent() {
            lookHouse();//看房方法
            host.rent();//租房子方法
            fare();//收费方法
        }
        private void lookHouse(){
            System.out.println("中介带你看房");
        }
        private void fare(){
            System.out.println("收取中介费");
        }
    }
    
  4. 测试:

    public class You {
        public static void main(String[] args) {
    
            Host host = new Host();
    
            Proxy proxy = new Proxy();
            proxy.setHost(host);
            proxy.rent();
    
        }
    }
    

静态代理实务特点:

  1. 好处:
    1. 可以使真实角色更加纯粹,不用去关注一些公共的事情;
    2. 公共的业务由代理来完成,实现业务的分工;
    3. 公共业务的要扩展的话,可以更加集中和方便;
  2. 缺点:
    1. 假如我们的真实角色变得非常多,代理类也会随之增多,工作量变大,开发效率变低!

动态代理

由于静态代理开发效率低,所以我们想需要一种能够有静态代理的全部好处,但是又不存在这种缺点的东西。

动态代理的角色和静态代理的都一样,但是动态代理是自动生成的。

  1. 动态代理分两类:

    1. 基于接口实现:jdk

    2. 基于类实现:cglib

      当今用的比较多的是Javassist来生成动态代理

  2. 动态代理相关类:

    1. InvocationHandler是由代理实例的调用处理程序*实现的接口
      1. invoke(Object proxy, 方法 method, Object[] args)` 处理代理实例上的方法调用并返回结果。
    2. Proxy提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类。
      1. newProxyInstance(ClassLoader loader, 类<?>[] interfaces, InvocationHandler h) 返回指定接口的代理类的实例,该接口将方法调用分派给指定的调用处理程序
  3. 代码实现:

    1. 抽象角色

      package com.li.daili.dao;
      
      public interface Rent {
          void rent();
      }
      
    2. 真实角色

      package com.li.daili.dao;
      
      public class Host implements Rent{
          public void rent() {
              System.out.println("房东要租房");
          }
      }
      
    3. 动态代理生成的接口对象

      package com.li.daili.dao;
      
      import java.lang.reflect.InvocationHandler;
      import java.lang.reflect.Method;
      import java.lang.reflect.Proxy;
      
      public class InvocationHandlerProxy implements InvocationHandler {
          private Rent rent;
      
          public void setRent(Rent rent) {
              this.rent = rent;
          }
      
          public Object getProxy(){
              return Proxy.newProxyInstance(this.getClass().getClassLoader(),rent.getClass().getInterfaces(),this);
          }
          
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
              lookHouse();
              Object result = method.invoke(rent, args);
              takeMoney();
              return result;
          }
      
          private void takeMoney() {
              System.out.println("收费");
          }
      
          private void lookHouse() {
              System.out.println("看房");
          }
      }
      
    4. 测试

      package com.li.daili;
      
      import com.li.daili.dao.Host;
      import com.li.daili.dao.InvocationHandlerProxy;
      import com.li.daili.dao.Rent;
      
      public class TestDemo {
          public static void main(String[] args) {
              Host host = new Host();
              InvocationHandlerProxy ihp = new InvocationHandlerProxy();
              ihp.setRent(host);
              Rent proxy = (Rent) ihp.getProxy();
              proxy.rent();
          }
      }
      
  4. 动态代理的特点:

    1. 可以使真实角色更加纯粹,不用去关注一些公共的事情;
    2. 公共的业务由代理来完成,实现业务的分工;
    3. 公共业务的要扩展的话,可以更加集中和方便;
    4. 一个动态代理,一般代理一类的业务,一个动态代理可以代理多个类,代理接口;


AOP

aop底层是动态代理

AOP是OOP的延续,是Aspect Oriented Programming的缩写,意思是面向切面编程。可以通过预编译方式和运行期动态代理实现不修改源代码的情况下给程序动态统一添加功能的一种技术。AOP实际是GoF设计模式的延续,设计模式追求的是调用者和被调用者之间的解耦,AOP可以说也是这种目标的一种实现。

我们现在做的一些非业务,如:日志、事务、安全等都会写在业务代码中(也即是说,这些非业务类横切于业务类),但这些代码往往是重复的代码,会给程序的维护带来不便,AOP就实现了把这些业务需求与系统需求分开来做。这种解决的方式也称代理机制

1. 使用springAPI实现aop

  1. 编写业务类

    1. 接口

      package com.david.aop.service;
      
      public interface UserService {
          void add();
          void delete();
          void update();
          void query();
      }
      
    2. 实现类

      package com.david.aop.service;
      
      public class UserServiceImpl implements UserService{
          public void add() {
              System.out.println("添加一个用户");
          }
      
          public void delete() {
              System.out.println("删除一个用户");
          }
      
          public void update() {
              System.out.println("更新一个用户");
          }
      
          public void query() {
              System.out.println("查询一个用户");
          }
      }
      
      
  2. 定义日志增加类实现

    package com.david.aop.log;
    
    import org.springframework.aop.MethodBeforeAdvice;
    
    import java.lang.reflect.Method;
    
    public class Log implements MethodBeforeAdvice {
        public void before(Method method, Object[] objects, Object o) throws Throwable {
            System.out.println(o.getClass().getName()+"的"+method.getName()+"被执行了");
        }
    }
    
    package com.david.aop.log;
    
    import org.springframework.aop.AfterReturningAdvice;
    
    import java.lang.reflect.Method;
    
    public class AfterLog implements AfterReturningAdvice {
        public void afterReturning(Object returnValue, Method method, Object[] objects, Object target) throws Throwable {
            System.out.println("执行了"+target.getClass().getName() +"的"+method.getName()+"方法" +"返回值"+returnValue);
        }
    }
    
  3. 编写spring核心配置文件applaction-config.xml

    <?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: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/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <bean id="userService" class="com.david.aop.service.UserServiceImpl"/>
    
        <bean id="log" class="com.david.aop.log.Log"/>
        <bean id="afterLog" class="com.david.aop.log.AfterLog"/>
    
    
    
        <aop:config>
            <aop:pointcut id="pointcut" expression="execution(* com.david.aop.service.UserServiceImpl.*(..))"/>
            <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
            <aop:advisor  advice-ref="afterLog" pointcut-ref="pointcut"/>
        </aop:config>
    </beans>
    
  4. 测试类

    package com.david.service;
    
    import com.david.aop.service.UserService;
    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class SpringAopTest {
        @Test
        public void test(){
            ApplicationContext context = new ClassPathXmlApplicationContext("application-config.xml");
            UserService userService = (UserService) context.getBean("userService");
            userService.add();
            userService.update();
            userService.query();
            userService.delete();
        }
    }
    
    

    注意导入aop的织入包

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <parent>
            <artifactId>springstudy</artifactId>
            <groupId>com.li</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>springstudy03</artifactId>
    <dependencies>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.9</version>
        </dependency>
    </dependencies>
    </project>
    

    运行结果:

    com.david.aop.service.UserServiceImpl的add被执行了
    添加一个用户
    执行了com.david.aop.service.UserServiceImpl的add方法返回值null
    com.david.aop.service.UserServiceImpl的update被执行了
    更新一个用户
    执行了com.david.aop.service.UserServiceImpl的update方法返回值null
    com.david.aop.service.UserServiceImpl的query被执行了
    查询一个用户
    执行了com.david.aop.service.UserServiceImpl的query方法返回值null
    com.david.aop.service.UserServiceImpl的delete被执行了
    删除一个用户
    执行了com.david.aop.service.UserServiceImpl的delete方法返回值null
    

aop的思路很重要:横向编程(改变原代码,添加功能)。aop的本质还是动态代理,在代码中起到了解耦的作用

2. 自定义类实现AOP

相对使用springAPI实现aop更简单

  1. 真实对象和之前的一样

  2. 自定一个Aop增强类:也就是所谓的切面

    package com.david.aop.diy;
    
    public class Diy {
        public void before(){
            System.out.println("===========before============");
        }
        public void after(){
            System.out.println("===========after============");
        }
    }
    
  3. 注入bean,使用aop进行增强beans.xml

    <?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: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/aop
           http://www.springframework.org/schema/aop/spring-aop.xsd">
        <bean id="userService" class="com.david.aop.service.UserServiceImpl"/>
        <bean id="diy" class="com.david.aop.diy.Diy"/>
        <aop:config>
            <aop:aspect ref="diy">
                <aop:pointcut id="diyPointcut" expression="execution(* com.david.aop.service.UserServiceImpl.*(..))"/>
                <aop:before method="before" pointcut-ref="diyPointcut"/>
                <aop:after method="after" pointcut-ref="diyPointcut"/>
            </aop:aspect>
        </aop:config>
    </beans>
    
  4. 测试类

    package com.david.service;
    
    import com.david.aop.service.UserService;
    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class SpringAopTest {
        @Test
        public void test(){
            ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
            UserService userService = (UserService) context.getBean("userService");
            userService.add();
            userService.update();
            userService.query();
            userService.delete();
        }
    }
    
  5. 运行结果:

    ===========before============
    添加一个用户
    ===========after============
    ===========before============
    更新一个用户
    ===========after============
    ===========before============
    查询一个用户
    ===========after============
    ===========before============
    删除一个用户
    ===========after============
    

3. 使用注解实现AOP

@Aspect、@before、@after、@Around

  1. 目标对象不变

  2. 编写增强的类,写注解

    1. 注意点:需要将类注解为 切面
    2. 方法上就是,切入点,增强
  3. 类:

    package com.david.aop.anno;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.*;
    
    @Aspect
    public class Anno {
    
        @Before("execution(* com.david.aop.service.UserServiceImpl.*(..))")
        public void before(){
            System.out.println("===========方法执行前==========");
        }
    
        @After("execution(* com.david.aop.service.UserServiceImpl.*(..))")
        public void after(){
            System.out.println("==========执行方法后===========");
        }
    
        @Around("execution(* com.david.aop.service.UserServiceImpl.*(..))")
        public void around(ProceedingJoinPoint joinPoint) throws Throwable {
            System.out.println("环绕前");
            System.out.println("签名"+joinPoint.getSignature());
            Object proceed = joinPoint.proceed();
            System.out.println("环绕后");
            System.out.println(proceed);
        }
    }
    
  4. 配置文件:anno.xml

    <?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: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/aop
            http://www.springframework.org/schema/aop/spring-aop.xsd">
        <bean id="userService" class="com.david.aop.service.UserServiceImpl"/>
        <bean id="anno" class="com.david.aop.anno.Anno"/>
        <aop:aspectj-autoproxy/><!--自动代理-->
    </beans>
    
  5. 测试类:

    package com.david.service;
    
    import com.david.aop.service.UserService;
    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class SpringAopTest {
        @Test
        public void test(){
            ApplicationContext context = new ClassPathXmlApplicationContext("anno.xml");
            UserService userService = (UserService) context.getBean("userService");
            userService.add();
            userService.update();
            userService.query();
            userService.delete();
        }
    }
    

AOP总结

  1. 本质就是动态代理

  2. 需要到一个包,用来进行aop织入的包: aspectjweaver

  3. 打代码的过程中注意别遗漏了切面

  4. 三种实现AOP的方法

      • 使用SpringAPI来实现AOP
      • 使用自定义类来实现AOP
      • 使用注解实现AOP
原文地址:https://www.cnblogs.com/a-xia/p/11400968.html