Aop——面向切面編程

靜態代理和動態代理

aop底層是動態代理java

靜態代理

代理模型分析:spring

  1. 抽象角色:通常會使用抽象類或接口實現
  2. 真實角色:被的代理的角色
  3. 代理角色:代理真實角色後,通常會作一些附屬的操做
  4. 客戶:使用代理角色進行一些操做,從而獲得真實角色的東西和代理角色的特有東西

代碼實現:(咱們用租房子爲例)

  1. 接口:express

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

    //房東的這個房子要出租
    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. 假如咱們的真實角色變得很是多,代理類也會隨之增多,工做量變大,開發效率變低!

動態代理

因爲靜態代理開發效率低,因此咱們想須要一種可以有靜態代理的所有好處,可是又不存在這種缺點的東西。api

動態代理的角色和靜態代理的都同樣,可是動態代理是自動生成的。安全

  1. 動態代理分兩類:app

    1. 基於接口實現:jdkmaven

    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
相關文章
相關標籤/搜索