精华内容
下载资源
问答
  • AOP好处

    千次阅读 2018-09-13 19:54:05
    使用AOP三个层次来讲解一下我们使用AOP好处,希望这篇文章可以对大家有益。 原始代码的写法 既然要通过代码来演示,那必须要有例子,这里我的例子为: 有一个接口Dao有insert、delete、update三个方法,在...

    我将从最基础的原始代码–>使用设计模式(装饰器模式与代理)–>使用AOP三个层次来讲解一下我们使用AOP的好处,希望这篇文章可以对大家有益。

    原始代码的写法

    既然要通过代码来演示,那必须要有例子,这里我的例子为:

    有一个接口Dao有insert、delete、update三个方法,在insert与update被调用的前后,打印调用前的毫秒数与调用后的毫秒数

    首先定义一个Dao接口:

    public interface Dao {
    
        public void insert();
    
        public void delete();
    
        public void update();
    
    }
    •  

    然后定义一个实现类DaoImpl:

    public class DaoImpl implements Dao {
    
        @Override
        public void insert() {
            System.out.println("DaoImpl.insert()");
        }
    
        @Override
        public void delete() {
            System.out.println("DaoImpl.delete()");
        }
    
        @Override
        public void update() {
            System.out.println("DaoImpl.update()");
        }
    
    }

    最原始的写法,我要在调用insert()与update()方法前后分别打印时间,就只能定义一个新的类包一层,在调用insert()方法与update()方法前后分别处理一下,新的类我命名为ServiceImpl,其实现为:

    public class ServiceImpl {
    
        private Dao dao = new DaoImpl();
    
        public void insert() {
            System.out.println("insert()方法开始时间:" + System.currentTimeMillis());
            dao.insert();
            System.out.println("insert()方法结束时间:" + System.currentTimeMillis());
        }
    
        public void delete() {
            dao.delete();
        }
    
        public void update() {
            System.out.println("update()方法开始时间:" + System.currentTimeMillis());
            dao.update();
            System.out.println("update()方法结束时间:" + System.currentTimeMillis());
        }
    
    }

    这是最原始的写法,这种写法的缺点也是一目了然:

    方法调用前后输出时间的逻辑无法复用,如果有别的地方要增加这段逻辑就得再写一遍 
    如果Dao有其它实现类,那么必须新增一个类去包装该实现类,这将导致类数量不断膨胀

    使用装饰器模式

    接着我们使用上设计模式,先用装饰器模式,看看能解决多少问题。装饰器模式的核心就是实现Dao接口并持有Dao接口的引用,我将新增的类命名为LogDao,其实现为:

    public class LogDao implements Dao {
    
        private Dao dao;
    
        public LogDao(Dao dao) {
            this.dao = dao;
        }
    
        @Override
        public void insert() {
            System.out.println("insert()方法开始时间:" + System.currentTimeMillis());
            dao.insert();
            System.out.println("insert()方法结束时间:" + System.currentTimeMillis());
        }
    
        @Override
        public void delete() {
            dao.delete();
        }
    
        @Override
        public void update() {
            System.out.println("update()方法开始时间:" + System.currentTimeMillis());
            dao.update();
            System.out.println("update()方法结束时间:" + System.currentTimeMillis());
        }
    
    }
    

    在使用的时候,可以使用”Dao dao = new LogDao(new DaoImpl())”的方式,这种方式的优点为:

    透明,对调用方来说,它只知道Dao,而不知道加上了日志功能 
    类不会无限膨胀,如果Dao的其它实现类需要输出日志,只需要向LogDao的构造函数中传入不同的Dao实现类即可

    不过这种方式同样有明显的缺点,缺点为:

    输出日志的逻辑还是无法复用 
    输出日志的逻辑与代码有耦合,如果我要对delete()方法前后同样输出时间,需要修改LogDao

    但是,这种做法相比最原始的代码写法,已经有了很大的改进。

    使用代理模式

    接着我们使用代理模式尝试去实现最原始的功能,使用代理模式,那么我们就要定义一个InvocationHandler,我将它命名为LogInvocationHandler,其实现为:

    public class LogInvocationHandler implements InvocationHandler {
    
        private Object obj;
    
        public LogInvocationHandler(Object obj) {
            this.obj = obj;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            String methodName = method.getName();
            if ("insert".equals(methodName) || "update".equals(methodName)) {
                System.out.println(methodName + "()方法开始时间:" + System.currentTimeMillis());
                Object result = method.invoke(obj, args);
                System.out.println(methodName + "()方法结束时间:" + System.currentTimeMillis());
    
                return result;
            }
    
            return method.invoke(obj, args);
        }
    
    }
    

    其调用方式很简单,我写一个main函数:

    public static void main(String[] args) {
        Dao dao = new DaoImpl();
    
        Dao proxyDao = (Dao)Proxy.newProxyInstance(LogInvocationHandler.class.getClassLoader(), new Class<?>[]{Dao.class}, new LogInvocationHandler(dao));
    
        proxyDao.insert();
        System.out.println("----------分割线----------");
        proxyDao.delete();
        System.out.println("----------分割线----------");
        proxyDao.update();
    }
    

    结果就不演示了,这种方式的优点为:

    输出日志的逻辑被复用起来,如果要针对其他接口用上输出日志的逻辑,只要在newProxyInstance的时候的第二个参数增加Class

    public class DaoProxy implements MethodInterceptor {
    
        @Override
        public Object intercept(Object object, Method method, Object[] objects, MethodProxy proxy) throws Throwable {
            String methodName = method.getName();
    
            if ("insert".equals(methodName) || "update".equals(methodName)) {
                System.out.println(methodName + "()方法开始时间:" + System.currentTimeMillis());
                proxy.invokeSuper(object, objects);
                System.out.println(methodName + "()方法结束时间:" + System.currentTimeMillis());
    
                return object;
            }
    
            proxy.invokeSuper(object, objects);
            return object;
        }
    
    }
    

    代码调用方式为

    public static void main(String[] args) {
        DaoProxy daoProxy = new DaoProxy();
    
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(DaoImpl.class);
        enhancer.setCallback(daoProxy);
    
        Dao dao = (DaoImpl)enhancer.create();
        dao.insert();
        System.out.println("----------分割线----------");
        dao.delete();
        System.out.println("----------分割线----------");
        dao.update();
    }
    

    使用CGLIB解决了JDK的Proxy无法针对类做代理的问题,但是这里要专门说明一个问题:使用装饰器模式可以说是对使用原生代码的一种改进,使用Java代理可以说是对于使用装饰器模式的一种改进,但是使用CGLIB并不是对于使用Java代理的一种改进。

    前面的可以说改进是因为使用装饰器模式比使用原生代码更好,使用Java代理又比使用装饰器模式更好,但是Java代理与CGLIb的对比并不能说改进,因为使用CGLIB并不一定比使用Java代理更好,这两种各有优缺点,像Spring框架就同时支持Java Proxy与CGLIB两种方式。

    从目前看来代码又更好了一些,但是我认为还有两个缺点:

    无论使用Java代理还是使用CGLIB,编写这部分代码都稍显麻烦 
    代码之间的耦合还是没有解决,像要针对delete()方法加上这部分逻辑就必须修改代码

    使用AOP

    最后来看一下使用AOP的方式,首先定义一个时间处理类,我将它命名为TimeHandler:

    public class TimeHandler {
    
        public void printTime(ProceedingJoinPoint pjp) {
            Signature signature = pjp.getSignature();
            if (signature instanceof MethodSignature) {
                MethodSignature methodSignature = (MethodSignature)signature;
                Method method = methodSignature.getMethod();
                System.out.println(method.getName() + "()方法开始时间:" + System.currentTimeMillis());
    
                try {
                    pjp.proceed();
                    System.out.println(method.getName() + "()方法结束时间:" + System.currentTimeMillis());
                } catch (Throwable e) {
    
                }
            }
        }
    
    }
    

    到第8行的代码与第12行的代码分别打印方法开始执行时间与方法结束执行时间。我这里写得稍微复杂点,使用了aop:around的写法,其实也可以拆分为aop:before与aop:after两种,这个看个人喜好。

    这里多说一句,切面方法printTime本身可以不用定义任何的参数,但是有些场景下需要获取调用方法的类、方法签名等信息,此时可以在printTime方法中定义JointPoint,Spring会自动将参数注入,可以通过JoinPoint获取调用方法的类、方法签名等信息。由于这里我用的aop:around,要保证方法的调用,这样才能在方法调用前后输出时间,因此不能直接使用JoinPoint,因为JoinPoint没法保证方法调用。此时可以使用ProceedingJoinPoint,ProceedingPointPoint的proceed()方法可以保证方法调用,但是要注意一点,ProceedingJoinPoint只能和aop:around搭配,换句话说,如果aop.xml中配置的是aop:before,然后printTime的方法参数又是ProceedingJoinPoint的话,Spring容器启动将报错。

    接着看一下aop.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"
        xmlns:tx="http://www.springframework.org/schema/tx"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
    
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    
    
    http://www.springframework.org/schema/aop
    
    
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
    
        <bean id="daoImpl" class="org.xrq.spring.action.aop.DaoImpl" />
        <bean id="timeHandler" class="org.xrq.spring.action.aop.TimeHandler" />
    
        <aop:config>
            <aop:pointcut id="addAllMethod" expression="execution(* org.xrq.spring.action.aop.Dao.*(..))" />
            <aop:aspect id="time" ref="timeHandler">
                <aop:before method="printTime" pointcut-ref="addAllMethod" />
                <aop:after method="printTime" pointcut-ref="addAllMethod" />
            </aop:aspect>
        </aop:config>
    
    </beans>

    我不大会写expression,也懒得去百度了,因此这里就拦截Dao下的所有方法了。测试代码很简单:

    public class AopTest {
    
        @Test
        @SuppressWarnings("resource")
        public void testAop() {
            ApplicationContext ac = new ClassPathXmlApplicationContext("spring/aop.xml");
    
            Dao dao = (Dao)ac.getBean("daoImpl");
            dao.insert();
            System.out.println("----------分割线----------");
            dao.delete();
            System.out.println("----------分割线----------");
            dao.update();
        }
    
    }
    

    结果就不演示了。到此我总结一下使用AOP的几个优点:

    切面的内容可以复用,比如TimeHandler的printTime方法,任何地方需要打印方法执行前的时间与方法执行后的时间,都可以使用TimeHandler的printTime方法 
    避免使用Proxy、CGLIB生成代理,这方面的工作全部框架去实现,开发者可以专注于切面内容本身 
    代码与代码之间没有耦合,如果拦截的方法有变化修改配置文件即可

    下面用一张图来表示一下AOP的作用:

    这里写图片描述

    我们传统的编程方式是垂直化的编程,即A–>B–>C–>D这么下去,一个逻辑完毕之后执行另外一段逻辑。但是AOP提供了另外一种思路,它的作用是在业务逻辑不知情(即业务逻辑不需要做任何的改动)的情况下对业务代码的功能进行增强,这种编程思想的使用场景有很多,例如事务提交、方法执行之前的权限检测、日志打印、方法调用事件等等。

    AOP使用场景举例

    上面的例子纯粹为了演示使用,为了让大家更加理解AOP的作用,这里以实际场景作为例子。

    第一个例子,我们知道MyBatis的事务默认是不会自动提交的,因此在编程的时候我们必须在增删改完毕之后调用SqlSession的commit()方法进行事务提交,这非常麻烦,下面利用AOP简单写一段代码帮助我们自动提交事务(这段代码我个人测试过可用):

    public class TransactionHandler {
    
        public void commit(JoinPoint jp) {
            Object obj = jp.getTarget();
            if (obj instanceof MailDao) {
                Signature signature = jp.getSignature();
                if (signature instanceof MethodSignature) {
                    SqlSession sqlSession = SqlSessionThrealLocalUtil.getSqlSession();                
    
                    MethodSignature methodSignature = (MethodSignature)signature;
                    Method method = methodSignature.getMethod();
    
                    String methodName = method.getName();
                    if (methodName.startsWith("insert") || methodName.startsWith("update") || methodName.startsWith("delete")) {
                        sqlSession.commit();
                    }
    
                    sqlSession.close();
                }
            }
        }
    
    }
    

    这种场景下我们要使用的aop标签为aop:after,即切在方法调用之后。

    这里我做了一个SqlSessionThreadLocalUtil,每次打开会话的时候,都通过SqlSessionThreadLocalUtil把当前会话SqlSession放到ThreadLocal中,看到通过TransactionHandler,可以实现两个功能:

    insert、update、delete操作事务自动提交 
    对SqlSession进行close(),这样就不需要在业务代码里面关闭会话了,因为有些时候我们写业务代码的时候会忘记关闭SqlSession,这样可能会造成内存句柄的膨胀,因此这部分切面也一并做了

    整个过程,业务代码是不知道的,而TransactionHandler的内容可以充分再多处场景下进行复用。

    第二个例子是权限控制的例子,不管是从安全角度考虑还是从业务角度考虑,我们在开发一个Web系统的时候不可能所有请求都对所有用户开放,因此这里就需要做一层权限控制了,大家看AOP作用的时候想必也肯定会看到AOP可以做权限控制,这里我就演示一下如何使用AOP做权限控制。我们知道原生的Spring MVC,Java类是实现Controller接口的,基于此,利用AOP做权限控制的大致代码如下(这段代码纯粹就是一段示例,我构建的Maven工程是一个普通的Java工程,因此没有验证过):

    public class PermissionHandler {
    
        public void hasPermission(JoinPoint jp) throws Exception {
            Object obj = jp.getTarget();
    
            if (obj instanceof Controller) {
                Signature signature = jp.getSignature();
                MethodSignature methodSignature = (MethodSignature)signature;
    
                // 获取方法签名
                Method method = methodSignature.getMethod();
                // 获取方法参数
                Object[] args = jp.getArgs();
    
                // Controller中唯一一个方法的方法签名ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception;
                // 这里对这个方法做一层判断
                if ("handleRequest".equals(method.getName()) && args.length == 2) {
                    Object firstArg = args[0];
                    if (obj instanceof HttpServletRequest) {
                        HttpServletRequest request = (HttpServletRequest)firstArg;
                        // 获取用户id
                        long userId = Long.parseLong(request.getParameter("userId"));
                        // 获取当前请求路径
                        String requestUri = request.getRequestURI();
    
                        if(!PermissionUtil.hasPermission(userId, requestUri)) {
                            throw new Exception("没有权限");
                        }
                    }
                }
            }
    
        }
    
    }
    

    毫无疑问这种场景下我们要使用的aop标签为aop:before。这里我写得很简单,获取当前用户id与请求路径,根据这两者,判断该用户是否有权限访问该请求,大家明白意思即可。

    后记

    文章演示了从原生代码到使用AOP的过程,一点一点地介绍了每次演化的优缺点,最后以实际例子分析了AOP可以做什么事情。

    展开全文
  • https://blog.csdn.net/ljxzdn/article/details/79404353
    展开全文
  • 我们使用AOP好处

    万次阅读 2017-10-16 13:43:28
    我将从最基础的原始代码–>使用设计模式(装饰器模式与代理)–>使用AOP三个层次来讲解一下我们使用AOP好处

    我将从最基础的原始代码–>使用设计模式(装饰器模式与代理)–>使用AOP三个层次来讲解一下我们使用AOP的好处,希望这篇文章可以对大家有益。

    原始代码的写法

    既然要通过代码来演示,那必须要有例子,这里我的例子为:

    有一个接口Dao有insert、delete、update三个方法,在insert与update被调用的前后,打印调用前的毫秒数与调用后的毫秒数

    首先定义一个Dao接口:

    public interface Dao {
    
        public void insert();
    
        public void delete();
    
        public void update();
    
    }

    然后定义一个实现类DaoImpl:

    public class DaoImpl implements Dao {
    
        @Override
        public void insert() {
            System.out.println("DaoImpl.insert()");
        }
    
        @Override
        public void delete() {
            System.out.println("DaoImpl.delete()");
        }
    
        @Override
        public void update() {
            System.out.println("DaoImpl.update()");
        }
    
    }

    最原始的写法,我要在调用insert()与update()方法前后分别打印时间,就只能定义一个新的类包一层,在调用insert()方法与update()方法前后分别处理一下,新的类我命名为ServiceImpl,其实现为:

    public class ServiceImpl {
    
        private Dao dao = new DaoImpl();
    
        public void insert() {
            System.out.println("insert()方法开始时间:" + System.currentTimeMillis());
            dao.insert();
            System.out.println("insert()方法结束时间:" + System.currentTimeMillis());
        }
    
        public void delete() {
            dao.delete();
        }
    
        public void update() {
            System.out.println("update()方法开始时间:" + System.currentTimeMillis());
            dao.update();
            System.out.println("update()方法结束时间:" + System.currentTimeMillis());
        }
    
    }

    这是最原始的写法,这种写法的缺点也是一目了然:

    方法调用前后输出时间的逻辑无法复用,如果有别的地方要增加这段逻辑就得再写一遍
    如果Dao有其它实现类,那么必须新增一个类去包装该实现类,这将导致类数量不断膨胀

    使用装饰器模式

    接着我们使用上设计模式,先用装饰器模式,看看能解决多少问题。装饰器模式的核心就是实现Dao接口并持有Dao接口的引用,我将新增的类命名为LogDao,其实现为:

    public class LogDao implements Dao {
    
        private Dao dao;
    
        public LogDao(Dao dao) {
            this.dao = dao;
        }
    
        @Override
        public void insert() {
            System.out.println("insert()方法开始时间:" + System.currentTimeMillis());
            dao.insert();
            System.out.println("insert()方法结束时间:" + System.currentTimeMillis());
        }
    
        @Override
        public void delete() {
            dao.delete();
        }
    
        @Override
        public void update() {
            System.out.println("update()方法开始时间:" + System.currentTimeMillis());
            dao.update();
            System.out.println("update()方法结束时间:" + System.currentTimeMillis());
        }
    
    }
    

    在使用的时候,可以使用”Dao dao = new LogDao(new DaoImpl())”的方式,这种方式的优点为:

    透明,对调用方来说,它只知道Dao,而不知道加上了日志功能
    类不会无限膨胀,如果Dao的其它实现类需要输出日志,只需要向LogDao的构造函数中传入不同的Dao实现类即可

    不过这种方式同样有明显的缺点,缺点为:

    输出日志的逻辑还是无法复用
    输出日志的逻辑与代码有耦合,如果我要对delete()方法前后同样输出时间,需要修改LogDao

    但是,这种做法相比最原始的代码写法,已经有了很大的改进。

    使用代理模式

    接着我们使用代理模式尝试去实现最原始的功能,使用代理模式,那么我们就要定义一个InvocationHandler,我将它命名为LogInvocationHandler,其实现为:

    public class LogInvocationHandler implements InvocationHandler {
    
        private Object obj;
    
        public LogInvocationHandler(Object obj) {
            this.obj = obj;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            String methodName = method.getName();
            if ("insert".equals(methodName) || "update".equals(methodName)) {
                System.out.println(methodName + "()方法开始时间:" + System.currentTimeMillis());
                Object result = method.invoke(obj, args);
                System.out.println(methodName + "()方法结束时间:" + System.currentTimeMillis());
    
                return result;
            }
    
            return method.invoke(obj, args);
        }
    
    }
    

    其调用方式很简单,我写一个main函数:

    public static void main(String[] args) {
        Dao dao = new DaoImpl();
    
        Dao proxyDao = (Dao)Proxy.newProxyInstance(LogInvocationHandler.class.getClassLoader(), new Class<?>[]{Dao.class}, new LogInvocationHandler(dao));
    
        proxyDao.insert();
        System.out.println("----------分割线----------");
        proxyDao.delete();
        System.out.println("----------分割线----------");
        proxyDao.update();
    }
    

    结果就不演示了,这种方式的优点为:

    输出日志的逻辑被复用起来,如果要针对其他接口用上输出日志的逻辑,只要在newProxyInstance的时候的第二个参数增加Class

    public class DaoProxy implements MethodInterceptor {
    
        @Override
        public Object intercept(Object object, Method method, Object[] objects, MethodProxy proxy) throws Throwable {
            String methodName = method.getName();
    
            if ("insert".equals(methodName) || "update".equals(methodName)) {
                System.out.println(methodName + "()方法开始时间:" + System.currentTimeMillis());
                proxy.invokeSuper(object, objects);
                System.out.println(methodName + "()方法结束时间:" + System.currentTimeMillis());
    
                return object;
            }
    
            proxy.invokeSuper(object, objects);
            return object;
        }
    
    }
    

    代码调用方式为

    public static void main(String[] args) {
        DaoProxy daoProxy = new DaoProxy();
    
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(DaoImpl.class);
        enhancer.setCallback(daoProxy);
    
        Dao dao = (DaoImpl)enhancer.create();
        dao.insert();
        System.out.println("----------分割线----------");
        dao.delete();
        System.out.println("----------分割线----------");
        dao.update();
    }
    

    使用CGLIB解决了JDK的Proxy无法针对类做代理的问题,但是这里要专门说明一个问题:使用装饰器模式可以说是对使用原生代码的一种改进,使用Java代理可以说是对于使用装饰器模式的一种改进,但是使用CGLIB并不是对于使用Java代理的一种改进。

    前面的可以说改进是因为使用装饰器模式比使用原生代码更好,使用Java代理又比使用装饰器模式更好,但是Java代理与CGLIb的对比并不能说改进,因为使用CGLIB并不一定比使用Java代理更好,这两种各有优缺点,像Spring框架就同时支持Java Proxy与CGLIB两种方式。

    从目前看来代码又更好了一些,但是我认为还有两个缺点:

    无论使用Java代理还是使用CGLIB,编写这部分代码都稍显麻烦
    代码之间的耦合还是没有解决,像要针对delete()方法加上这部分逻辑就必须修改代码

    使用AOP

    最后来看一下使用AOP的方式,首先定义一个时间处理类,我将它命名为TimeHandler:

    public class TimeHandler {
    
        public void printTime(ProceedingJoinPoint pjp) {
            Signature signature = pjp.getSignature();
            if (signature instanceof MethodSignature) {
                MethodSignature methodSignature = (MethodSignature)signature;
                Method method = methodSignature.getMethod();
                System.out.println(method.getName() + "()方法开始时间:" + System.currentTimeMillis());
    
                try {
                    pjp.proceed();
                    System.out.println(method.getName() + "()方法结束时间:" + System.currentTimeMillis());
                } catch (Throwable e) {
    
                }
            }
        }
    
    }
    

    到第8行的代码与第12行的代码分别打印方法开始执行时间与方法结束执行时间。我这里写得稍微复杂点,使用了aop:around的写法,其实也可以拆分为aop:before与aop:after两种,这个看个人喜好。

    这里多说一句,切面方法printTime本身可以不用定义任何的参数,但是有些场景下需要获取调用方法的类、方法签名等信息,此时可以在printTime方法中定义JointPoint,Spring会自动将参数注入,可以通过JoinPoint获取调用方法的类、方法签名等信息。由于这里我用的aop:around,要保证方法的调用,这样才能在方法调用前后输出时间,因此不能直接使用JoinPoint,因为JoinPoint没法保证方法调用。此时可以使用ProceedingJoinPoint,ProceedingPointPoint的proceed()方法可以保证方法调用,但是要注意一点,ProceedingJoinPoint只能和aop:around搭配,换句话说,如果aop.xml中配置的是aop:before,然后printTime的方法参数又是ProceedingJoinPoint的话,Spring容器启动将报错。

    接着看一下aop.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"
        xmlns:tx="http://www.springframework.org/schema/tx"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
    
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    
    
    http://www.springframework.org/schema/aop
    
    
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
    
        <bean id="daoImpl" class="org.xrq.spring.action.aop.DaoImpl" />
        <bean id="timeHandler" class="org.xrq.spring.action.aop.TimeHandler" />
    
        <aop:config>
            <aop:pointcut id="addAllMethod" expression="execution(* org.xrq.spring.action.aop.Dao.*(..))" />
            <aop:aspect id="time" ref="timeHandler">
                <aop:before method="printTime" pointcut-ref="addAllMethod" />
                <aop:after method="printTime" pointcut-ref="addAllMethod" />
            </aop:aspect>
        </aop:config>
    
    </beans>

    我不大会写expression,也懒得去百度了,因此这里就拦截Dao下的所有方法了。测试代码很简单:

    public class AopTest {
    
        @Test
        @SuppressWarnings("resource")
        public void testAop() {
            ApplicationContext ac = new ClassPathXmlApplicationContext("spring/aop.xml");
    
            Dao dao = (Dao)ac.getBean("daoImpl");
            dao.insert();
            System.out.println("----------分割线----------");
            dao.delete();
            System.out.println("----------分割线----------");
            dao.update();
        }
    
    }
    

    结果就不演示了。到此我总结一下使用AOP的几个优点:

    切面的内容可以复用,比如TimeHandler的printTime方法,任何地方需要打印方法执行前的时间与方法执行后的时间,都可以使用TimeHandler的printTime方法
    避免使用Proxy、CGLIB生成代理,这方面的工作全部框架去实现,开发者可以专注于切面内容本身
    代码与代码之间没有耦合,如果拦截的方法有变化修改配置文件即可

    下面用一张图来表示一下AOP的作用:

    这里写图片描述

    我们传统的编程方式是垂直化的编程,即A–>B–>C–>D这么下去,一个逻辑完毕之后执行另外一段逻辑。但是AOP提供了另外一种思路,它的作用是在业务逻辑不知情(即业务逻辑不需要做任何的改动)的情况下对业务代码的功能进行增强,这种编程思想的使用场景有很多,例如事务提交、方法执行之前的权限检测、日志打印、方法调用事件等等。

    AOP使用场景举例

    上面的例子纯粹为了演示使用,为了让大家更加理解AOP的作用,这里以实际场景作为例子。

    第一个例子,我们知道MyBatis的事务默认是不会自动提交的,因此在编程的时候我们必须在增删改完毕之后调用SqlSession的commit()方法进行事务提交,这非常麻烦,下面利用AOP简单写一段代码帮助我们自动提交事务(这段代码我个人测试过可用):

    public class TransactionHandler {
    
        public void commit(JoinPoint jp) {
            Object obj = jp.getTarget();
            if (obj instanceof MailDao) {
                Signature signature = jp.getSignature();
                if (signature instanceof MethodSignature) {
                    SqlSession sqlSession = SqlSessionThrealLocalUtil.getSqlSession();                
    
                    MethodSignature methodSignature = (MethodSignature)signature;
                    Method method = methodSignature.getMethod();
    
                    String methodName = method.getName();
                    if (methodName.startsWith("insert") || methodName.startsWith("update") || methodName.startsWith("delete")) {
                        sqlSession.commit();
                    }
    
                    sqlSession.close();
                }
            }
        }
    
    }
    

    这种场景下我们要使用的aop标签为aop:after,即切在方法调用之后。

    这里我做了一个SqlSessionThreadLocalUtil,每次打开会话的时候,都通过SqlSessionThreadLocalUtil把当前会话SqlSession放到ThreadLocal中,看到通过TransactionHandler,可以实现两个功能:

    insert、update、delete操作事务自动提交
    对SqlSession进行close(),这样就不需要在业务代码里面关闭会话了,因为有些时候我们写业务代码的时候会忘记关闭SqlSession,这样可能会造成内存句柄的膨胀,因此这部分切面也一并做了

    整个过程,业务代码是不知道的,而TransactionHandler的内容可以充分再多处场景下进行复用。

    第二个例子是权限控制的例子,不管是从安全角度考虑还是从业务角度考虑,我们在开发一个Web系统的时候不可能所有请求都对所有用户开放,因此这里就需要做一层权限控制了,大家看AOP作用的时候想必也肯定会看到AOP可以做权限控制,这里我就演示一下如何使用AOP做权限控制。我们知道原生的Spring MVC,Java类是实现Controller接口的,基于此,利用AOP做权限控制的大致代码如下(这段代码纯粹就是一段示例,我构建的Maven工程是一个普通的Java工程,因此没有验证过):

    public class PermissionHandler {
    
        public void hasPermission(JoinPoint jp) throws Exception {
            Object obj = jp.getTarget();
    
            if (obj instanceof Controller) {
                Signature signature = jp.getSignature();
                MethodSignature methodSignature = (MethodSignature)signature;
    
                // 获取方法签名
                Method method = methodSignature.getMethod();
                // 获取方法参数
                Object[] args = jp.getArgs();
    
                // Controller中唯一一个方法的方法签名ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception;
                // 这里对这个方法做一层判断
                if ("handleRequest".equals(method.getName()) && args.length == 2) {
                    Object firstArg = args[0];
                    if (obj instanceof HttpServletRequest) {
                        HttpServletRequest request = (HttpServletRequest)firstArg;
                        // 获取用户id
                        long userId = Long.parseLong(request.getParameter("userId"));
                        // 获取当前请求路径
                        String requestUri = request.getRequestURI();
    
                        if(!PermissionUtil.hasPermission(userId, requestUri)) {
                            throw new Exception("没有权限");
                        }
                    }
                }
            }
    
        }
    
    }
    

    毫无疑问这种场景下我们要使用的aop标签为aop:before。这里我写得很简单,获取当前用户id与请求路径,根据这两者,判断该用户是否有权限访问该请求,大家明白意思即可。

    后记

    文章演示了从原生代码到使用AOP的过程,一点一点地介绍了每次演化的优缺点,最后以实际例子分析了AOP可以做什么事情。

    展开全文
  • 使用AOP三个层次来讲解一下我们使用AOP好处,希望这篇文章可以对大家有益。 原始代码的写法 既然要通过代码来演示,那必须要有例子,这里我的例子为: 有一个接口Dao有insert、delete、update三个方法,在...


    我将从最基础的原始代码–>使用设计模式(装饰器模式与代理)–>使用AOP三个层次来讲解一下我们使用AOP的好处,希望这篇文章可以对大家有益。

    原始代码的写法

    既然要通过代码来演示,那必须要有例子,这里我的例子为:

    有一个接口Dao有insert、delete、update三个方法,在insert与update被调用的前后,打印调用前的毫秒数与调用后的毫秒数

    首先定义一个Dao接口:

    public interface Dao {
    
        public void insert();
    
        public void delete();
    
        public void update();
    
    }

    然后定义一个实现类DaoImpl:

    public class DaoImpl implements Dao {
    
        @Override
        public void insert() {
            System.out.println("DaoImpl.insert()");
        }
    
        @Override
        public void delete() {
            System.out.println("DaoImpl.delete()");
        }
    
        @Override
        public void update() {
            System.out.println("DaoImpl.update()");
        }
    
    }

    最原始的写法,我要在调用insert()与update()方法前后分别打印时间,就只能定义一个新的类包一层,在调用insert()方法与update()方法前后分别处理一下,新的类我命名为ServiceImpl,其实现为:

    public class ServiceImpl {
    
        private Dao dao = new DaoImpl();
    
        public void insert() {
            System.out.println("insert()方法开始时间:" + System.currentTimeMillis());
            dao.insert();
            System.out.println("insert()方法结束时间:" + System.currentTimeMillis());
        }
    
        public void delete() {
            dao.delete();
        }
    
        public void update() {
            System.out.println("update()方法开始时间:" + System.currentTimeMillis());
            dao.update();
            System.out.println("update()方法结束时间:" + System.currentTimeMillis());
        }
    
    }

    这是最原始的写法,这种写法的缺点也是一目了然:

    方法调用前后输出时间的逻辑无法复用,如果有别的地方要增加这段逻辑就得再写一遍 
    如果Dao有其它实现类,那么必须新增一个类去包装该实现类,这将导致类数量不断膨胀

    使用装饰器模式

    接着我们使用上设计模式,先用装饰器模式,看看能解决多少问题。装饰器模式的核心就是实现Dao接口并持有Dao接口的引用,我将新增的类命名为LogDao,其实现为:

    public class LogDao implements Dao {
    
        private Dao dao;
    
        public LogDao(Dao dao) {
            this.dao = dao;
        }
    
        @Override
        public void insert() {
            System.out.println("insert()方法开始时间:" + System.currentTimeMillis());
            dao.insert();
            System.out.println("insert()方法结束时间:" + System.currentTimeMillis());
        }
    
        @Override
        public void delete() {
            dao.delete();
        }
    
        @Override
        public void update() {
            System.out.println("update()方法开始时间:" + System.currentTimeMillis());
            dao.update();
            System.out.println("update()方法结束时间:" + System.currentTimeMillis());
        }
    
    }
    

    在使用的时候,可以使用”Dao dao = new LogDao(new DaoImpl())”的方式,这种方式的优点为:

    透明,对调用方来说,它只知道Dao,而不知道加上了日志功能 
    类不会无限膨胀,如果Dao的其它实现类需要输出日志,只需要向LogDao的构造函数中传入不同的Dao实现类即可

    不过这种方式同样有明显的缺点,缺点为:

    输出日志的逻辑还是无法复用 
    输出日志的逻辑与代码有耦合,如果我要对delete()方法前后同样输出时间,需要修改LogDao

    但是,这种做法相比最原始的代码写法,已经有了很大的改进。

    使用代理模式

    接着我们使用代理模式尝试去实现最原始的功能,使用代理模式,那么我们就要定义一个InvocationHandler,我将它命名为LogInvocationHandler,其实现为:
     

    public class LogInvocationHandler implements InvocationHandler {
    
        private Object obj;
    
        public LogInvocationHandler(Object obj) {
            this.obj = obj;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            String methodName = method.getName();
            if ("insert".equals(methodName) || "update".equals(methodName)) {
                System.out.println(methodName + "()方法开始时间:" + System.currentTimeMillis());
                Object result = method.invoke(obj, args);
                System.out.println(methodName + "()方法结束时间:" + System.currentTimeMillis());
    
                return result;
            }
    
            return method.invoke(obj, args);
        }
    
    }
    

    其调用方式很简单,我写一个main函数:

    public static void main(String[] args) {
        Dao dao = new DaoImpl();
    
        Dao proxyDao = (Dao)Proxy.newProxyInstance(LogInvocationHandler.class.getClassLoader(), new Class<?>[]{Dao.class}, new LogInvocationHandler(dao));
    
        proxyDao.insert();
        System.out.println("----------分割线----------");
        proxyDao.delete();
        System.out.println("----------分割线----------");
        proxyDao.update();
    }
    

    结果就不演示了,这种方式的优点为:

    输出日志的逻辑被复用起来,如果要针对其他接口用上输出日志的逻辑,只要在newProxyInstance的时候的第二个参数增加Class

    public class DaoProxy implements MethodInterceptor {
    
        @Override
        public Object intercept(Object object, Method method, Object[] objects, MethodProxy proxy) throws Throwable {
            String methodName = method.getName();
    
            if ("insert".equals(methodName) || "update".equals(methodName)) {
                System.out.println(methodName + "()方法开始时间:" + System.currentTimeMillis());
                proxy.invokeSuper(object, objects);
                System.out.println(methodName + "()方法结束时间:" + System.currentTimeMillis());
    
                return object;
            }
    
            proxy.invokeSuper(object, objects);
            return object;
        }
    
    }
    

    代码调用方式为

    public static void main(String[] args) {
        DaoProxy daoProxy = new DaoProxy();
    
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(DaoImpl.class);
        enhancer.setCallback(daoProxy);
    
        Dao dao = (DaoImpl)enhancer.create();
        dao.insert();
        System.out.println("----------分割线----------");
        dao.delete();
        System.out.println("----------分割线----------");
        dao.update();
    }
    

    使用CGLIB解决了JDK的Proxy无法针对类做代理的问题,但是这里要专门说明一个问题:使用装饰器模式可以说是对使用原生代码的一种改进,使用Java代理可以说是对于使用装饰器模式的一种改进,但是使用CGLIB并不是对于使用Java代理的一种改进。

    前面的可以说改进是因为使用装饰器模式比使用原生代码更好,使用Java代理又比使用装饰器模式更好,但是Java代理与CGLIb的对比并不能说改进,因为使用CGLIB并不一定比使用Java代理更好,这两种各有优缺点,像Spring框架就同时支持Java Proxy与CGLIB两种方式。

    从目前看来代码又更好了一些,但是我认为还有两个缺点:

    无论使用Java代理还是使用CGLIB,编写这部分代码都稍显麻烦 
    代码之间的耦合还是没有解决,像要针对delete()方法加上这部分逻辑就必须修改代码

    使用AOP

    最后来看一下使用AOP的方式,首先定义一个时间处理类,我将它命名为TimeHandler:
     

    public class TimeHandler {
    
        public void printTime(ProceedingJoinPoint pjp) {
            Signature signature = pjp.getSignature();
            if (signature instanceof MethodSignature) {
                MethodSignature methodSignature = (MethodSignature)signature;
                Method method = methodSignature.getMethod();
                System.out.println(method.getName() + "()方法开始时间:" + System.currentTimeMillis());
    
                try {
                    pjp.proceed();
                    System.out.println(method.getName() + "()方法结束时间:" + System.currentTimeMillis());
                } catch (Throwable e) {
    
                }
            }
        }
    
    }
    

    到第8行的代码与第12行的代码分别打印方法开始执行时间与方法结束执行时间。我这里写得稍微复杂点,使用了aop:around的写法,其实也可以拆分为aop:before与aop:after两种,这个看个人喜好。

    这里多说一句,切面方法printTime本身可以不用定义任何的参数,但是有些场景下需要获取调用方法的类、方法签名等信息,此时可以在printTime方法中定义JointPoint,Spring会自动将参数注入,可以通过JoinPoint获取调用方法的类、方法签名等信息。由于这里我用的aop:around,要保证方法的调用,这样才能在方法调用前后输出时间,因此不能直接使用JoinPoint,因为JoinPoint没法保证方法调用。此时可以使用ProceedingJoinPoint,ProceedingPointPoint的proceed()方法可以保证方法调用,但是要注意一点,ProceedingJoinPoint只能和aop:around搭配,换句话说,如果aop.xml中配置的是aop:before,然后printTime的方法参数又是ProceedingJoinPoint的话,Spring容器启动将报错。

    接着看一下aop.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"
        xmlns:tx="http://www.springframework.org/schema/tx"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
    
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    
    
    http://www.springframework.org/schema/aop
    
    
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
    
        <bean id="daoImpl" class="org.xrq.spring.action.aop.DaoImpl" />
        <bean id="timeHandler" class="org.xrq.spring.action.aop.TimeHandler" />
    
        <aop:config>
            <aop:pointcut id="addAllMethod" expression="execution(* org.xrq.spring.action.aop.Dao.*(..))" />
            <aop:aspect id="time" ref="timeHandler">
                <aop:before method="printTime" pointcut-ref="addAllMethod" />
                <aop:after method="printTime" pointcut-ref="addAllMethod" />
            </aop:aspect>
        </aop:config>
    
    </beans>

    我不大会写expression,也懒得去百度了,因此这里就拦截Dao下的所有方法了。测试代码很简单:

    public class AopTest {
    
        @Test
        @SuppressWarnings("resource")
        public void testAop() {
            ApplicationContext ac = new ClassPathXmlApplicationContext("spring/aop.xml");
    
            Dao dao = (Dao)ac.getBean("daoImpl");
            dao.insert();
            System.out.println("----------分割线----------");
            dao.delete();
            System.out.println("----------分割线----------");
            dao.update();
        }
    
    }
    

    结果就不演示了。到此我总结一下使用AOP的几个优点:

    切面的内容可以复用,比如TimeHandler的printTime方法,任何地方需要打印方法执行前的时间与方法执行后的时间,都可以使用TimeHandler的printTime方法 
    避免使用Proxy、CGLIB生成代理,这方面的工作全部框架去实现,开发者可以专注于切面内容本身 
    代码与代码之间没有耦合,如果拦截的方法有变化修改配置文件即可

    下面用一张图来表示一下AOP的作用:

    è¿éåå¾çæè¿°

    我们传统的编程方式是垂直化的编程,即A–>B–>C–>D这么下去,一个逻辑完毕之后执行另外一段逻辑。但是AOP提供了另外一种思路,它的作用是在业务逻辑不知情(即业务逻辑不需要做任何的改动)的情况下对业务代码的功能进行增强,这种编程思想的使用场景有很多,例如事务提交、方法执行之前的权限检测、日志打印、方法调用事件等等。

    AOP使用场景举例

    上面的例子纯粹为了演示使用,为了让大家更加理解AOP的作用,这里以实际场景作为例子。

    第一个例子,我们知道MyBatis的事务默认是不会自动提交的,因此在编程的时候我们必须在增删改完毕之后调用SqlSession的commit()方法进行事务提交,这非常麻烦,下面利用AOP简单写一段代码帮助我们自动提交事务(这段代码我个人测试过可用):
     

    public class TransactionHandler {
    
        public void commit(JoinPoint jp) {
            Object obj = jp.getTarget();
            if (obj instanceof MailDao) {
                Signature signature = jp.getSignature();
                if (signature instanceof MethodSignature) {
                    SqlSession sqlSession = SqlSessionThrealLocalUtil.getSqlSession();                
    
                    MethodSignature methodSignature = (MethodSignature)signature;
                    Method method = methodSignature.getMethod();
    
                    String methodName = method.getName();
                    if (methodName.startsWith("insert") || methodName.startsWith("update") || methodName.startsWith("delete")) {
                        sqlSession.commit();
                    }
    
                    sqlSession.close();
                }
            }
        }
    
    }
    

    这种场景下我们要使用的aop标签为aop:after,即切在方法调用之后。

    这里我做了一个SqlSessionThreadLocalUtil,每次打开会话的时候,都通过SqlSessionThreadLocalUtil把当前会话SqlSession放到ThreadLocal中,看到通过TransactionHandler,可以实现两个功能:

    insert、update、delete操作事务自动提交 
    对SqlSession进行close(),这样就不需要在业务代码里面关闭会话了,因为有些时候我们写业务代码的时候会忘记关闭SqlSession,这样可能会造成内存句柄的膨胀,因此这部分切面也一并做了

    整个过程,业务代码是不知道的,而TransactionHandler的内容可以充分再多处场景下进行复用。

    第二个例子是权限控制的例子,不管是从安全角度考虑还是从业务角度考虑,我们在开发一个Web系统的时候不可能所有请求都对所有用户开放,因此这里就需要做一层权限控制了,大家看AOP作用的时候想必也肯定会看到AOP可以做权限控制,这里我就演示一下如何使用AOP做权限控制。我们知道原生的Spring MVC,Java类是实现Controller接口的,基于此,利用AOP做权限控制的大致代码如下(这段代码纯粹就是一段示例,我构建的Maven工程是一个普通的Java工程,因此没有验证过):
     

    public class PermissionHandler {
    
        public void hasPermission(JoinPoint jp) throws Exception {
            Object obj = jp.getTarget();
    
            if (obj instanceof Controller) {
                Signature signature = jp.getSignature();
                MethodSignature methodSignature = (MethodSignature)signature;
    
                // 获取方法签名
                Method method = methodSignature.getMethod();
                // 获取方法参数
                Object[] args = jp.getArgs();
    
                // Controller中唯一一个方法的方法签名ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception;
                // 这里对这个方法做一层判断
                if ("handleRequest".equals(method.getName()) && args.length == 2) {
                    Object firstArg = args[0];
                    if (obj instanceof HttpServletRequest) {
                        HttpServletRequest request = (HttpServletRequest)firstArg;
                        // 获取用户id
                        long userId = Long.parseLong(request.getParameter("userId"));
                        // 获取当前请求路径
                        String requestUri = request.getRequestURI();
    
                        if(!PermissionUtil.hasPermission(userId, requestUri)) {
                            throw new Exception("没有权限");
                        }
                    }
                }
            }
    
        }
    
    }
    

    毫无疑问这种场景下我们要使用的aop标签为aop:before。这里我写得很简单,获取当前用户id与请求路径,根据这两者,判断该用户是否有权限访问该请求,大家明白意思即可。

    后记

    文章演示了从原生代码到使用AOP的过程,一点一点地介绍了每次演化的优缺点,最后以实际例子分析了AOP可以做什么事情。

     

    展开全文
  • Spring AOP使用的好处

    千次阅读 2018-12-07 15:47:24
    1.通知自定义 2.切面获得返回值 3.解耦和 4.统一管理权限,统一管理异常抛出
  • 利用AOP好处就是可以对业务逻辑进行隔离,降低耦合度,提高程序的可重用性,同时提高了开发的效率。开源的AOP也有不少,我这里用的KingAOP. 1 项目结构 2 定义一个日志记录的实体类User和LoggingAspect切面日志类 ...
  • 主要给大家介绍了关于Spring Boot之AOP配自定义注解的最佳实践过程,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
  • aop好处: 不用写几遍重复代码,写一次就够了。 开发者只需要关注核心业务; 运行时期,执行核心业务代码时候动态植入需要的代码; aop就采用了动态代理模式。 面向切面编程,就是指 对很多功能都有的重复的代码...
  • Spring Aop到底有什么用处?

    千次阅读 2019-07-23 14:18:31
    假如没有aop,在做日志处理的时候,我们会在每个方法中添加日志处理,比如 但大多数的日子处理代码是相同的,为了实现代码复用,我们可能把日志处理抽离成一个新的方法。但是这样我们仍然必须手动插入这些方法。 ...
  • AOP全称(Aspect Oriented Programming)面向切片编程的简称 AOP的定义: AOP通过预编译方式和运行期动态代理实现,在不修改源代码的情况下,给程序动态统一添加功能的一种技术,简称AOP。是spring框架的一个重要...
  • 一.Aop 是什么? Aspect Oriented Programing 面向切面编程,相比较 oop 面向对象编程来说,Aop 关注的不再是程序代码中某个类,某些方法,而 aop 考虑的更多的是一种面到面的切入 即层与层之间的一种切入,所以称...
  • 1.AOP简介 1.AOP (面向切面编程):是一种新的方法论,是对...4.AOP好处: 1.每个事务逻辑位于一个位置,代码不分散,便于维护升级 2.业务模块更简洁,只包含核心业务代码 例如加减乘除的逻辑 1.代理对象:业务逻辑
  • 图解JAVA中Spring Aop作用

    千次阅读 2018-03-20 11:28:56
    假如没有aop,在做日志处理的时候,我们会在每个方法中添加日志处理,比如但大多数的日子处理代码是相同的,为了实现代码复用,我们可能把日志处理抽离成一个新的方法。但是这样我们仍然必须手动插入这些方法。但...
  • AOP

    2021-03-14 15:37:46
    任务二:AOP 一 转账案例 需求 使用spring框架整合DBUtils技术,实现用户转账功能 1.1 基础功能 步骤分析 创建java项目,导入坐标 编写Account实体类 编写AccountDao接口和实现类 编写AccountService接口和实现类 ...
  • Spring的优点,IoC和AOP的理解

    千次阅读 2018-07-09 15:26:54
    容器提供了AOP技术,利用它很容易实现如权限拦截,运行期监控等功能 5.容器提供了众多的辅助类,能加快应用的开发 6.spring对于主流的应用框架提供了集成支持,如hibernate,JPA,Struts等 7.spring属于低侵入...
  • AOP,全称Apect Oriented Programming,译为面向切面编程,简单的说它可以帮我们把程序中重复的代码抽取出去,在需要执行的时候,使用动态代理技术,在不修改源码的基础上,对我们已有的方法进行增强。 AOP的作用及...
  • 什么是AOPAOP(Aspect-Oriented-Programing)面向切面编程,是一种编程理念。该理念能够是那些为多个业务所共有,却与业务逻辑没有关系的模块抽取出来,形成一个切面。这样可以减少重复代码,并使业务逻辑保持...
  • [AOP] 2. AOP的两种实现-Spring AOP以及AspectJ

    万次阅读 多人点赞 2017-02-26 22:29:18
    在接触Spring以及种类繁多的Java框架时,很多开发人员(至少包括我)都会觉得注解是个很奇妙的存在,为什么加上了@Transactional...随着对AOP的逐渐应用和了解,才明白注解只是一个表象,在幕后Spring AOP/AspectJ做了大
  • Spring中AOP的作用 为什么要使用AOP

    千次阅读 2019-04-23 14:32:06
    假如没有aop,在做日志处理的时候,我们会在每个方法中添加日志处理,比如 但大多数的日子处理代码是相同的,为了实现代码复用,我们可能把日志处理抽离成一个新的方法。但是这样我们仍然必须手动插入这些方法。 ...
  • 一、AOP介绍 面向切片编程(AOP—Aspect Oriented Programming)可以说是对OOP(面向对象编程)的补充和完善,面向对象就是将事物的特性和行为抽象为一个对象,如people类有身高、体重、年龄等属性,也有吃饭、睡觉...
  • 举例理解AOP

    2019-03-09 11:27:19
    1、什么是AOP 软件工程有一个基本原则叫做“关注点分离”(Concern Separation),通俗的理解就是不同的问题交给不同的部分去解决,每部分专注于解决自己的问题。这年头互联网也天天强调要专注嘛! 这其实也是一种...
  • Spring AOP

    2019-09-02 17:43:16
    一、AOP的概念 AOP,面向切面编程,是对OOP的补充。从网上看到的一句话:这种在运行时,动态的将代码切入到类的指定方法或者指定位置上的编程思想,就是面向切面的编程。这是其中的一种方式,在运行时动态添加。...
  • 首先叙述一下业务,在每个模块中达到某要求时都要给当前用户添加积分,所以这里用到了注解搭配AOP。 首先自定义一个注解 /** * @author zhangGX * @date 2021-01-06 16:40 */ @Target({ElementType.PARAMETER, ...
  • aop

    2019-08-13 22:28:09
    AOP带来的好处 让我们可以 “专心做事” 案例: public void doSameBusiness (long lParam,String sParam){ // 记录日志 log.info (“调用 doSameBusiness方法,参数是:”+lParam); // 输入合法性验证 if ...
  • Spring的AOP实现例子

    千次阅读 2019-01-06 01:27:22
    一、为什么需要AOP 假如我们应用中有n个业务逻辑组件,每个业务逻辑组件又有m个方法,那现在我们的应用就一共包含了n*m个方法,我会抱怨方法太多。。。现在,我有这样一个需求,每个方法都增加一个通用的功能,常见...
  • 关于 Spring AOP (AspectJ) 你该知晓的一切

    万次阅读 多人点赞 2017-02-21 08:00:47
    【版权申明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权) ... 出自【zejian的博客】 关联文章: ...关于 Spring AOP (AspectJ) 你该知晓的一切本篇是年后第一篇博文,由于博主用了不少
  • 1分钟让你明白AOP是什么及它的好处

    万次阅读 多人点赞 2018-02-28 19:58:52
    大家应该明白一个道理,所有广为人用的框架/技术等.基本都是两个目的:1.软件开发期(写代码,测试,上线)内,让开发人员用更少的代码...1.AOP是什么?面向切面编程,能够让我们在不影响原有功能的前提下,为软件横向扩展 ...
  • AOP织入时机

    2020-06-10 14:05:09
    AOP最为特别并使其相对其他方法具有明显优点的部分就在于,它能够以多样的方式将程序中用到的多个方面灵活地织入(Weave)到一起,形成一个完整的应用程序。因而在学习AOP编程时,如何以准确、简洁、灵活的方式将...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 40,440
精华内容 16,176
关键字:

aop的好处