java实现AOP编程

两个时机:

1.在编译java源代码的时候 ----编译时增强
2.在运行时动态地修改类 ----运行时增强(动态代理)

实现代理的方法

        JDK的方法和CGLIB的方法

        1.JDK方法:利用java.lang.reflect.Proxy类,但局限在于需要被代理的对象必须实现一个接口,如果被代理对象没有实现任何接口,或者被代理的业务方法没有相应的接口,就无法得到代理对象,这个时候就需要CGLIB方式产生代理对象。

        2.CGLIB方法:原理实际上是动态生成被代理类的子类字节码,由于其字节码都是按照jvm编译后的class文件的规范编写的,因而其可以被jvm正常加载并运行,当然他也有局限,如果被代理类的方法被声明final类型,那么Cglib代理是无法正常工作的,因为final类型方法不能被重写。
 

一.静态代理模式  

1.1目标类    创建Subject接口

package com.itcast.aop;

public interface Subject {
    void request();
}

1.1.1目标类   创建SubjectTmp1 继承Subject,这是真实的访问类,Proxy类继承Subject这是代理类

package com.itcast.aop;

public class SubjectTpl1 implements Subject {
    public void request() {
        System.out.println("real subject do request");
    }
}

 1.2创建代理类    代理对象中request方法返回的其实是subjectTpl1.request()执行的结果,

package com.itcast.aop;

/**
 * 代理对象
 */
public class Proxy implements Subject {
    private SubjectTpl1 subjectTpl1;

    /**
     * 真实目标对象需要通过构造方法传进来
     * @param subjectTpl1
     */
    public Proxy(SubjectTpl1 subjectTpl1) {
        this.subjectTpl1 = subjectTpl1;
    }

    public void request() {
        System.out.println("before real request");
        try {
           result = subjectTpl1.request();
        }catch(Exception e){
            System.out.println("exception:"+e.getMessage());
            throw e;
        }finally {
            System.out.println("after real request");
        }
        return result;
    }
}

 1.3 客户端其实是访问代理类的request

package com.itcast.aop;

public class Client {
    public static void main(String[] arg){
        //获取代理对象,然后调用代理对象的方法
        Subject subject = new Proxy(new SubjectTpl1());
        try {
            subject.request();
        }catch (Exception e){

        }
    }
}

代理类和目标类都继承同一个接口,然后代理类在模板类方法的结果上加入其它逻辑,就实现了侵入;缺点就是每次增加一个方法,都要在代理类重写一遍。

二 .动态代理  

1.基于接口的代理  jdk代理   

1.1创建一个代理类,实现InvocationHandler接口

package com.itcast.aop;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * 代理类,需要实现InvocationHandler接口
 */
public class JdkProxySubject implements InvocationHandler {
    //还是需要引入目标类对象
    private SubjectTpl1 subjectTpl1;

    public JdkProxySubject(SubjectTpl1 subjectTpl1) {
        this.subjectTpl1 = subjectTpl1;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = null;
        try {
            System.out.println("before");
            //调用目标对象的方法,使用反射
            result = method.invoke(subjectTpl1, args);
        }catch (Exception e){
            throw e;
        }finally {
            System.out.println("after");
            return result;
        }
    }
}

1.2 client端调用也是先获取代理类对象 ,使用java.lang.reflect.Proxy

package com.itcast.aop;

import java.lang.reflect.Proxy;

public class Client {
    public static void main(String[] arg){
        //获取代理对象
        Subject subject = (Subject) Proxy.newProxyInstance(Client.class.getClassLoader(), new Class[]{Subject.class}, new JdkProxySubject(new SubjectTpl1()));
        try {
            subject.request();
        }catch (Exception e){

        }
    }
}

1.3 jdk动态代理的原理,其实就是通过Proxy.newProxyInstance()动态生成代理类,传入不同的接口,就生成不同的代理类。

2.基于继承的代理Cgllib代理

2.1 生成动态代理类 CglibProxy

package com.itcast.cglib;


import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * cglib动态代理类
 */
public class CglibProxy implements MethodInterceptor{
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        Object result = null;
        try {
            System.out.println("before cglib");
            result = methodProxy.invokeSuper(o,objects);
        }catch (Exception e){
            throw e;
        }finally {
            System.out.println("after cglib");
        }
        return result;
    }
}

2.2 客户端调用

import com.itcast.aop.JdkProxySubject;
import com.itcast.aop.Subject;
import com.itcast.aop.SubjectTpl1;
import com.itcast.cglib.CglibProxy;
import org.junit.Test;
import org.springframework.cglib.proxy.Enhancer;

public class CglibTest {
    @Test
    public void test() throws Exception {
        //获取代理类对象
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(SubjectTpl1.class);
        enhancer.setCallback(new CglibProxy());
        Subject subject = (Subject) enhancer.create();
        subject.request();
    }
}

 

三.责任链模式处理多个AOP

1.责任链模式演示

 1.1首先新建一个Handler抽象类,所有的操作节点继承它

package com.itcast.chain;

public abstract class Handler {
    //下一个链节点
    private Handler successor;

    public Handler getSuccessor() {
        return successor;
    }
    public void setSuccessor(Handler successor) {
        this.successor = successor;
    }

    //节点要做的事情
    public abstract void handlerProcess();

    //节点要执行的方法,先执行本节点的事情,如果存在下个节点,则调用下个节点的excute
    public void excute(){
        this.handlerProcess();
        if (this.successor != null) {
            this.successor.excute();
        }
    }


}

1.2 创建操作节点,实现节点自己的逻辑handlerProcess; 可以创建多个节点,HandlerTpl1,HandlerTpl2...

package com.itcast.chain;

public class HandlerTpl1 extends Handler {
    public void handlerProcess() {
        System.out.println("这是Tpl1的处理逻辑");
    }
}

1.3 要实现链式操作,需要实例化链上所有节点,然后按节点顺序传入。

import com.itcast.chain.Handler;
import com.itcast.chain.HandlerTpl1;
import com.itcast.chain.HandlerTpl2;
import org.junit.Test;

public class ChainTest {
    @Test
    public void run(){
        HandlerTpl1 handler1 = new HandlerTpl1();
        HandlerTpl2 handler2 = new HandlerTpl2();
        
        //按照节点顺序,把后一个节点传入到前一个节点
        handler1.setSuccessor(handler2);
        //只要执行第一个节点的excute,就会执行整个链的节点的逻辑
        handler1.excute();
    }
}

2. 责任链模式改进

2.1引入一个Chain类,管理这条链,传入一个list,让Chain对象来执行对应节点的excute方法

package com.itcast.chain;

import java.util.List;

/**
 * 通过list来管理链
 */
public class Chain {
    //所有节点放到list中,而且必须把list传进来;
    private List handlers;
    private int index = 0;

    public Chain(List handlers) {
        this.handlers = handlers;
    }

    /**
     * 每执行一次,就执行下一个节点的excute,并且把游标向后移动一位
     */
    public void process(){
        if (this.index < this.handlers.size() ) {
            //根据下标获取节点,执行excute,需要把chain对象传过去,在下个节点执行chain.process
            this.handlers.get(index++).excute(this);
        }else{
            return;
        }
    }
}

2.2 节点的父抽象类修改为这样

package com.itcast.chain;

public abstract class Handler {

    //节点要做的事情
    public abstract void handlerProcess();

    //节点要执行的方法,
    public void excute(Chain chain){
        this.handlerProcess();
        chain.process();
    }

}

 2.3 节点类不变

package com.itcast.chain;

public class HandlerTpl1 extends Handler {
    public void handlerProcess() {
        System.out.println("这是Tpl1的处理逻辑");
    }
}

2.4 客户端使用时,只需要操作Chain对象即可

import com.itcast.chain.Chain;
import com.itcast.chain.Handler;
import com.itcast.chain.HandlerTpl1;
import com.itcast.chain.HandlerTpl2;
import org.junit.Test;

import java.util.Arrays;
import java.util.List;

public class ChainTest {
    @Test
    public void run(){
        //得到节点的list作为参数,得到对应的Chain对象。
        List handlers = Arrays.asList(
                new HandlerTpl1(),
                new HandlerTpl2()
        );

        Chain chain = new Chain(handlers);
        chain.process();
    }
}

四.springAOP   spring框架对应AOP实现有写好的jar,方便我们实现AOP编程

原理就是让spring生成代理对象,然后从容器中获取代理对象。

1.需要引入的包

aopalliance    aop联盟接口

spring-aop   实现联盟接口的包

org.aspectjrt   用于@Aspect 使用注解实现aop

org.aspectjweaver  @Aspect依赖包

2.xml配置实现AOP

2.1创建目标类(同上,SubjectTpl1)

2.2 创建通知类 MyAdvice  自定义一些通知方法

package com.itcast.springAOP;

import org.aspectj.lang.ProceedingJoinPoint;

public class MyAdvice {
    //前置通知
    public void before(){
        System.out.println("前置通知");
    }
    //后置通知
    public void afterReturn(){
        System.out.println("后置通知 (只出现在没有发生异常)");
    }

    //环绕通知 常用
    //需要ProceedingJoinPoint接口作为参数
    public Object around(ProceedingJoinPoint pjp) throws Throwable{
        System.out.println("环绕通知的之前部分");
        Object proceed = pjp.proceed();
        System.out.println("环绕通知的之后部分");
        return proceed;
    }

    //异常抛出通知
    public void afterException(){
        System.out.println("异常出现之后的通知");
    }

    //最终通知
    public void after(){
        System.out.println("后置通知(不管是否发生异常)");
    }

}

2.3 xml配置





    
    
    
    

    
        
        
            
            
            
            
        
    

2.4 测试类 正常调用就行

 @Test
    public void run() throws Exception {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        SubjectTpl1 subject = (SubjectTpl1)context.getBean("subjectTpl1", SubjectTpl1.class);
        subject.request();
    }

 这个是通过aop:config,aop:aspect 把通知类和切点关联起来,得到一个切面,我们可以通过注解的方式简化操作

3.注解实现aop 

3.1 创建切面类 @Aspect (切点,通知方法都在这个切面类定义)

package com.itcast.springAOP;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

/**
 * 切面类需要@Component注入到容器,@Aspect定义类是切面类
 *
 */
@Component
@Aspect
public class MyAspect{

    //定义切点,切点名称为doLog
    @Pointcut("execution(* com.itcast.aop.Bean1.*(..))")
    public void doLog(){}

    //after通知
    @After("doLog()")
    public void after(){
        System.out.println("do log");
    }

    //before通知
    @Before("doLog()")
    public void before(){
        System.out.println("before action");
    }
}

3.2 在spring配置文件开启注解扫描,开启aop注释自动代理 




    
    

    
    

3.3 客户端调用(完全按照正常的方式使用)

package com.itcast.aop;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


public class AopTest {
   
    @Test
    public void zhujie(){
        ApplicationContext context = new ClassPathXmlApplicationContext("beanAOP.xml");
        Bean1 bean1 = (Bean1)context.getBean("bean1",Bean1.class);
        bean1.run();
    }
}

 

你可能感兴趣的:(java)