【深入理解Spring原理】Spring AOP 原理 理解

更新时间:2019-08-19 16:40:43点击次数:325次
    Spring AOP 原理 理解
 

AOP基本概念
 

1)通知(Advice):织入到目标类连接点上的一段程序代码。通知分为五种类型:
- Before:在方法被调用之前调用
- After:在方法完成后调用通知,无论方法是否执行成功
- After-returning:在方法成功执行之后调用通知
- After-throwing:在方法抛出异常后调用通知
- Around:通知了好、包含了被通知的方法,在被通知的方法调用之前后调用之后执行自定义的行为

2)切点(Pointcut):AOP通过“切点”定位特定的连接点
3)连接点(Joinpoint):程序执行的某个特定位置:如类开始初始化前、类初始化后、类某个方法调用前、调用后、方法抛出异常后。这些代码中的特定点,称为“连接点”。
4) 织入(Weaving):织入是将增强添加到目标类具体连接点上的过程。AOP有三种织入方式:
- 编译期织入,这要求使用特殊的Java编译器;
- 类装载期织入,这要求使用特殊的类装载器;
- 动态代理织入,在运行期为目标类添加增强生成子类的方式。
Spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入。

5)切面(Aspect)切面由切点和通知组成,它既包括了横切逻辑的定义,也包括了连接点的定义

 

Spring 是切片流程
  

代理模式
 就好比明星和经纪人,经纪人负责接活,负责明星唱歌前会场准备,明星唱歌后收钱。 代理类其实就是在代理对象,前后做了一些其他的处理, 这已经具备 AOP的轮廓了

静态代理模式栗子
// 接口
public interface IUserDao {
    void save();
 
    void find();
}
 
// 目标对象
public class UserDao implements IUserDao {
    @Override
    public void save() {
        System.out.println("模拟:保存用户!");
    }
 
    @Override
    public void find() {
        System.out.println("模拟:查询用户");
    }
}
 
/**
 * 静态代理 特点:
 *  1. 目标对象必须要实现接口 
 * 2. 代理对象,要实现与目标对象一样的接口
 */
public class UserDaoProxy implements IUserDao {
 
    // 代理对象,需要维护一个目标对象
    private IUserDao target = new UserDao();
 
    @Override
    public void save() {
        System.out.println("代理操作: 开启事务...");
        target.save(); // 执行目标对象的方法
        System.out.println("代理操作:提交事务...");
    }
 
    @Override
    public void find() {
        target.find();
    }
}
 
public class StaticProxyTest {
 
    /**
     * @param args
     */
    public static void main(String[] args) {
 
        IUserDao proxy = new UserDaoProxy();
        proxy.save();
    }
 
}
 

可以看到,静态代理是必须要有接口的,代理类和目标类都得事先接口,这样就显得很麻烦了,每次代理一个目标对象,就得做一个代理类,有没有一种方式,可以使用一个代理 ,代理多个目标对象呢?

动态代理模式栗子
动态代理不需要提前建立代理,而是在运行时,为目标对象动态生成代理类

// 接口
public interface IUserDao {
    void save();
 
    void find();
}
 
// 目标对象
public class UserDao implements IUserDao {
 
    @Override
    public void save() {
        System.out.println("模拟: 保存用户!");
    }
 
    @Override
    public void find() {
        System.out.println("查询");
    }
}
 
 
/**
 * 动态代理:JDK 动态代理采用的是反射机制实现
 * 代理工厂,给多个目标对象生成代理对象!
 *
 */
public class ProxyFactory {
 
    // 接收一个目标对象
    private Object target;
 
    public ProxyFactory(Object target) {
        this.target = target;
    }
 
    // 返回对目标对象(target)代理后的对象(proxy)
    public Object getProxyInstance() {
        Object proxy = Proxy.newProxyInstance(
            target.getClass().getClassLoader(),  // 目标对象使用的类加载器
            target.getClass().getInterfaces(),   // 目标对象实现的所有接口
            new InvocationHandler() {            // 执行代理对象方法时候触发
 
                @Override
                public Object invoke(Object proxy, Method method, Object[] args)
                        throws Throwable {
 
                    // 获取当前执行的方法的方法名
                    String methodName = method.getName();
                    // 方法返回值
                    Object result = null;
                    if ("find".equals(methodName)) {
                        // 直接调用目标对象方法
                        result = method.invoke(target, args);
                    } else {
                        System.out.println("开启事务...");
                        // 执行目标对象方法
                        result = method.invoke(target, args);
                        System.out.println("提交事务...");
                    }
                    return result;
                }
            }
        );
        return proxy;
    }
}
 
 
 
public class JDKDynamicProxyTest {
 
    /**
     * @param args
     */
    public static void main(String[] args) {
        // 目标对象
        IUserDao target = new UserDao();
        // 创建代理类
        IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance();
        System.out.println("代理对象: "+proxy.getClass());
        proxy.save();
 
    }
 
}
静态代理动态代理总结
可以总结一下,JDK 动态代理 

        // 创建代理类
        IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance();
实际上 隐藏了动态的去实现 目标对象的一个接口

class $jdkProxy implements IUserDao{}
也就是说 使用JDK 反射方式实现动态代理,目标对象必须实现一个接口,这就可能出现问题了,假设一个类,就是没有实现接口怎么办?

CGLIB实现动态代理实现栗子
CGLIB是使用继承的方式动态生成代理类,这个代理类是继承目标类的,这个就有个问题,需要目标类不被final修饰。

有点类似

public class UserDao{}
 
// CGLIB 是以动态生成的子类继承目标的方式实现,程序执行时,隐藏了下面的过程
public class $Cglib_Proxy_class  extends UserDao{}
CGLIB实现

// 目标对象
public class UserDao {
 
    public void save() {
        System.out.println("模拟: 保存用户!");
    }
 
    public void find() {
        System.out.println("查询");
    }
}
 
public class CGLIBProxyFactory {
 
    static class SimpleInterceptor implements MethodInterceptor {
        /**
         * @see org.springframework.cglib.proxy.MethodInterceptor#intercept(java.lang.Object,
         *      java.lang.reflect.Method, java.lang.Object[],
         *      org.springframework.cglib.proxy.MethodProxy)
         */
        @Override
        public Object intercept(Object target, Method methmod, Object[] args, MethodProxy proxy) throws Throwable {
            System.out.println("entering " + methmod.getName());
            Object result = proxy.invokeSuper(target, args);
            System.out.println("leveaing " + methmod.getName());
            return result;
        }
 
    }
 
    public static <T> T getProxy(Class<T> cls) {
        Enhancer enhancer = new Enhancer();
        // 继承目标类
        enhancer.setSuperclass(cls);
        enhancer.setCallback(new SimpleInterceptor());
        return (T) enhancer.create();
    }
}
 
public class CGLIBProxyTest {
    public static void main(String[] args) {
        //生成代理  没有接口
        UserDao proxy = (UserDao) CGLIBProxyFactory.getProxy(UserDao.class);
        System.out.println("代理类:"+ proxy.getClass());
        proxy.save();
 
    }
}
JDK和CGLIB的区别
1.上面可以可以看到 CGLIB是不需要 目标对象实现接口的, CGLIB是通过继承来实现的

2. JDK动态代理是需要一个具体的目标对象的,但是CGLIB是不需要这么一个具体对象的

 

Spring AOP 是怎样实现的呢?

1.AOP 是基于动态代理模式

2.AOP是方法级别的

3.AOP可以分类业务代码和关注点代码(日志,事务等)

 

AOP 是什么时候生成代理的?

AOP 代理主要分为静态代理和动态代理两大类,静态代理以 AspectJ 为代表;而动态代理则以 Spring AOP 为代表

IOC容器BeanDefinitionMap里面结构看内容是啥

bean: class [org.springframework.aop.aspectj.AspectJPointcutAdvisor];
 scope=; 
 abstract=false;
 lazyInit=false; 
 autowireMode=0; 
 dependencyCheck=0; 
 autowireCandidate=true; 
 primary=false; 
 factoryBeanName=null; 
 factoryMethodName=null;
 initMethodName=null;
 destroyMethodName=null, 
 org.springframework.aop.aspectj.AspectJPointcutAdvisor#2=Root 
 bean: class [org.springframework.aop.aspectj.AspectJPointcutAdvisor]; 
 scope=; 
 abstract=false;
 lazyInit=false; 
 autowireMode=0; 
 dependencyCheck=0; 
 autowireCandidate=true;
 primary=false;
 factoryBeanName=null; 
 factoryMethodName=null;
 initMethodName=null;
 destroyMethodName=null,
 org.springframework.aop.aspectj.AspectJPointcutAdvisor#3=Root
 bean:class [org.springframework.aop.aspectj.AspectJPointcutAdvisor]; 
 scope=; 
 abstract=false;
 lazyInit=false;
 autowireMode=0; 
 dependencyCheck=0;
 autowireCandidate=true; 
 primary=false; 
 factoryBeanName=null;
 factoryMethodName=null; 
 initMethodName=null;
 destroyMethodName=null, 
 org.springframework.aop.aspectj.AspectJPointcutAdvisor#0=Root 
 

本站文章版权归原作者及原出处所有 。内容为作者个人观点, 并不代表本站赞同其观点和对其真实性负责,本站只提供参考并不构成任何投资及应用建议。本站是一个个人学习交流的平台,网站上部分文章为转载,并不用于任何商业目的,我们已经尽可能的对作者和来源进行了通告,但是能力有限或疏忽,造成漏登,请及时联系我们,我们将根据著作权人的要求,立即更正或者删除有关内容。本站拥有对此声明的最终解释权。

  • 项目经理 点击这里给我发消息
  • 项目经理 点击这里给我发消息
  • 项目经理 点击这里给我发消息
  • 项目经理 点击这里给我发消息