Spring中AOP和动态代理的简单实例演示

Spring中AOP和动态代理的简单实例演示

AOP (Aspect-Oriented Programming) 面向切面编程

OOP (Object-Oriented Programming) 面向对象编程

想要了解AOP和动态代理,我们先看下面这个例子
假设我们现在有一个TestDao类中,有增删改查四种操作

@Repository
public class TestDao implements Dao{
    public void create(String table,String args){}
    public void update(String table,String args){}
    public void retrieve(String table,String args){}
    public void delete(String table,String args){}
}

某一天老板让你为数据库的增删改查操作添加日志,如果从面向对象的角度来考虑,我们需要创建一个Logger类,在数据库进行增删改查的方法内调用Logger类的方法,如下图所示:

@Component
public class Logger {

    public void log(Class c,String table,String args,String method) {
        System.out.println("Dao层中的"+c+"对表"+table+"和参数"+args+"进行了"+method+"操作");
    }
}
@Repository
public class TestDao implements Dao{
    @Autowired
    private Logger logger;

    public void create(String table,String args){
        String methodName = Thread.currentThread().getStackTrace()[1].getMethodName();
        logger.log(this.getClass(),table,args,methodName);
    }
    public void update(String table,String args){
        String methodName = Thread.currentThread().getStackTrace()[1].getMethodName();
        logger.log(this.getClass(),table,args,methodName);
    }
    public void retrieve(String table,String args){
        String methodName = Thread.currentThread().getStackTrace()[1].getMethodName();
        logger.log(this.getClass(),table,args,methodName);
    }
    public void delete(String table,String args){
        String methodName = Thread.currentThread().getStackTrace()[1].getMethodName();
        logger.log(this.getClass(),table,args,methodName);
    }
}

下面对以上述方法完成的日志记录功能进行测试

@Test
public void test() {
    ApplicationContext ac = new ClassPathXmlApplicationContext("aop.xml");
    TestDao bean = ac.getBean(TestDao.class);
    bean.create("student","张三");
    bean.update("teacher","李四");
    bean.retrieve("student","王五");
    bean.delete("teacher","李四");
}

得到以下结果

oop执行结果

尽管在调用log方法的时候使用了反射,栈等技巧避免了手动输入关键信息,但是仍然需要在每个方法中调用一次log方法。一旦方法过多,手动在方法中添加日志记录功能的方式就过于繁琐,还会造成耦合。

AOP思想

想要避免上述问题,就需要AOP的思想。AOP(Aspect-Oriented Programming)面向切面编程是把业务流程中通用的步骤抽取成为一个切面,然后根据业务流程将切面切入到指定的位置中。

什么是动态代理?

在理解动态代理之前,我们需要先了解代理模式,继续以增删改查为例

TestDao中的方法越来越多的时候,每个方法都去手动日志记录太麻烦了,占用了你大量的时间,这时你把日志记录的任务交给新来的实习生,让实习生负责TestDao中所有的日志记录。

当你需要日志信息的时候,直接找实习生就可以了,不需要再去访问TestDao,此时实习生就是TestDao的代理人,TestDao被实习生代理了。

在java中,我们需要创建代理类来代替实习生的作用,我们有需求直接去寻找代理类,而不需要访问原来的类。Spring为业务类创建一个代理类,我们把业务流程中通用的步骤抽取成切面放在代理类中,这就是Spring中AOP的实现原理。

代理模式又分为静态代理和动态代理。静态代理不在本篇博客的讨论范围之中,Spring使用了动态代理,而动态代理又分为Jdk动态代理和Cglib动态代理,我们将分开进行演示讲解,相信会对你理解AOP和动态代理有所帮助。

JDK动态代理

JDK1.8中创建代理类时使用的是java.lang.reflect.Proxy,JDK中还有一个java.net.Proxy,注意不要搞混。

Proxy文档

在JDK官方文档中,我们找到了Proxy代理类的创建方法,找到方法newProxyInstance的详细信息。

getProxyInstance

Proxy中的newProxyInstance方法有三个参数:ClassLoaderinterfacesInvocationHandler
ClassLoader为被代理对象的类加载器。
interfaces为被代理对象实现的接口。
InvocationHandler为方法调用器,帮被代理对象执行目标方法。

使用JDK动态代理必须有一个前提,就是被代理类必须实现了任意接口,下面我们来编写代理类。

public class DaoProxy{
    /**
     * 创建一个静态方法为Dao类型的被代理对象创建代理对象
     * @param dao 被代理对象
     * @return
     */
    public static Dao getDaoProxy(final TestDao dao) {
        //通过反射获取被代理对象的类加载器
        ClassLoader classLoader = dao.getClass().getClassLoader();
        //通过反射获取被代理对象实现的接口
        Class<?>[] interfaces = dao.getClass().getInterfaces();
        //创建匿名内部类的方式调用处理器,帮被代理对象执行目标方法
        InvocationHandler handler = new InvocationHandler() {
            /**
             * 对invoke方法进行重写
             * @param proxy 代理类对象
             * @param method 方法
             * @param args 方法参数
             * @return 方法调用后返回的结果
             * @throws Throwable
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //在方法调用后面可以添加业务代码
                String c = dao.getClass().getSimpleName();
                String table = (String) args[0];
                String args1 = (String) args[1];
                String method_name = method.getName();
                System.out.println("Dao层中的"+c+"对表"+table+"和参数"+args1+"进行了"+method_name+"操作");
                //使用反射执行目标方法
                Object result = method.invoke(dao, args);
                
                //在方法执行后面可以添加业务代码
                System.out.println("方法执行完毕");
                return result;
            }
        };
        //proxy为创建的代理对象
        Object proxy = Proxy.newProxyInstance(classLoader, interfaces, handler);
        return (Dao) proxy;
    }
}

代理类创建完成之后,进行功能测试

@Test
public void test() {
    //AopTest bean1 = ac.getBean(AopTest.class);
    //System.out.println(logger);
    //初始化SpringIOC容器
    ApplicationContext ac = new ClassPathXmlApplicationContext("aop.xml");
    //获取TestDao的Bean对象
    TestDao dao = ac.getBean(TestDao.class);
    //调用DaoProxy的getDaoProxy方法为dao创建代理对象
    Dao proxy = DaoProxy.getDaoProxy(dao);
    //使用代理对象执行目标方法
    proxy.create("student","张三");
    proxy.update("teacher","李四");
    proxy.retrieve("student","王五");
    proxy.delete("teacher","李四");
}

得到测试结果,代理类中的业务代码被正确执行

Dao层中的TestDao对表student和参数张三进行了create操作
方法执行完毕
Dao层中的TestDao对表teacher和参数李四进行了update操作
方法执行完毕
Dao层中的TestDao对表student和参数王五进行了retrieve操作
方法执行完毕
Dao层中的TestDao对表teacher和参数李四进行了delete操作
方法执行完毕
Cglib动态代理

JDK动态代理有一个缺点,那就是被代理类必须实现接口。为了解决这个问题,开发出了Cglib动态代理,笔者推荐大家看参考资料的中的博客,有详细的源码解析。

参考资料

t和参数王五进行了retrieve操作
方法执行完毕
Dao层中的TestDao对表teacher和参数李四进行了delete操作
方法执行完毕


#### Cglib动态代理

JDK动态代理有一个缺点,那就是被代理类必须实现接口。为了解决这个问题,开发出了Cglib动态代理,笔者推荐大家看参考资料的中的博客,有详细的源码解析。

#### 参考资料

[CGLIB动态代理实现原理](https://blog.csdn.net/yhl_jxy/article/details/80633194)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值