Spring框架的AOP

Spring学习笔记(四)

本文目录

1 AOP的介绍

2 Spring的AspectJ实现AOP(annotation)

3 Spring的AspectJ实现AOP (XML)

Spring文档http://docs.spring.io/spring/docs/4.0.0.BUILD-SNAPSHOT/spring-framework-reference/htmlsingle/

一 AOP介绍

spect Oriented Programming(AOP),面向切面编程。AOP主要实现的目的是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间降低耦合的隔离效果

简单来说就是在方法的前后加一层过滤的方法

如果类实现了interface接口,通过使用动态代理(Proxy),通过拦截一个对象的行为并添加我们需要的功能来完成

如果没有实现接口的类,可以使用Cglib代理实现AOP 

 

 AOP用途比较广,适合切面的功能都可以使用AOP,比如打日志,声明式的事务的管理,性能测试,权限检查等

通过invocationhandle和Proxy实现

测试代码

接口DO

package proxy.service;

public interface Do {
    public void doSomething();

}

DO接口的实现

package proxy.service;

public class DoImpl implements Do {

    @Override
    public void doSomething() {
        System.out.println("doimpl...");
        
    }

}

动态代理的实现,invocationhandle和Proxy

package proxy.service;

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

public class dynamicProxy implements InvocationHandler {
    private Object target;

    public dynamicProxy(Object target) {
        this.target = target;
    }

    public void before(Method method) {
        System.out.println(method.getName() + "调用开始");
    }

    public void after(Method method) {
        System.out.println(method.getName() + "调用结束");
    }

    public static Object newInstance(Object target) {
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), new dynamicProxy(target));
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) {
        Object result = null;
        try {
            before(method);
            result = method.invoke(target, args);
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } finally {
            after(method);
        }
        return result;
    }
}

测试类

package proxy.service.test;

import static org.junit.Assert.*;

import org.junit.Before;
import org.junit.Test;

import proxy.service.Do;
import proxy.service.DoImpl;
import proxy.service.dynamicProxy;

public class dynamicProxyTest {

    @Before
    public void setUp() throws Exception {
    }

    @Test
    public void testDynamicProxy() {
            Do doSth = (Do) dynamicProxy.newInstance(new DoImpl());
            doSth.doSomething();
        }
}

执行结果

doSomething调用开始
doimpl...
doSomething调用结束

在不改动实现类和接口的情况下,通过invocationhandleProxy实现动态代理在方法的前后插入了业务逻辑

二、Spring的AspectJ实现AOP(annotation)

1 添加JAR包

aspectjrt.jar    aspectjweaver.jar

2 XML的配置

xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation = "http://www.springframework.org/schema/aop
                     http://www.springframework.org/schema/aop/spring-aop.xsd"
<?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:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">

    <context:annotation-config/>
    <context:component-scan base-package="com"></context:component-scan>
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    

</beans>

 
3 新建切面类

package com.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class logInterceptor {
    @Before("execution(public void com.daoImpl.UserDaoImpl.*(..))")
    public void beforMethod(){
        System.out.println("aspectJ before method");
    }
    @AfterReturning("execution(public void com.daoImpl.UserDaoImpl.*(..))")
    public void afterMethod(){
        System.out.println("aspectJ after method");
    }

}

@Aspect在类名上方注解表示实现Aspect的类

@Before表示方法运行前

@AfterReturning表示在方法执行之后

他们的语法建议记住 execution即可

execution(<修饰符模式>? <返回类型模式> <方法名模式>(<参数模式>) <异常模式>?)  除了返回类型模式、方法名模式和参数模式外,其它项都是可选的,可使用通配符
例如
execution( public void com.daoImpl.UserDaoImpl.* (com.entity.User) )//这里没有异常
         (<修饰符模式>? <返回类型模式>         <方法名模式>            (<参数模式>)         <异常模式>?)

具体详见传送门http://dylanxu.iteye.com/blog/1312454

测试类
package com.serviceImpl.test;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.entity.User;
import com.serviceImpl.UserServiceImpl;

public class UserServiceImplTest {
    User user;

    @Before
    public void setUp() throws Exception {
        user = new User();
        user.setName("testName");
        user.setRemark("testRemark");
    }

    @Test
    public void testAdd() {
        ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("beans.xml");
         UserServiceImpl UserServiceImpl = (UserServiceImpl)app.getBean("userServiceImpl");
         UserServiceImpl.add(user);//调用方法
         UserServiceImpl.update(user);//调用方法
    }
}

执行结果

aspectJ before method
testName-->testRemark save --调用UserDaoImpl!
aspectJ after method
aspectJ before method
testName-->testRemark update --调用UserDaoImpl!
aspectJ after method

通过@Aspect,@Before,@AfterReturning 成功的在方法的前后加入了切面逻辑


介绍另外几个常用的annotation

@Pointcut

@Pointcut("execution(public void com.daoImpl.UserDaoImpl.*(com.entity.User))")

Pointcut提供了一个切面的切入点切必须写在一个空方法上面

public class logInterceptor {
    @Pointcut("execution(public void com.daoImpl.UserDaoImpl.*(com.entity.User))")
    public void myAop(){};
    
    @Before("myAop()")
    public void beforMethod(){
        System.out.println("aspectJ before method");
    }
    @AfterReturning("myAop()")
    public void afterMethod(){
        System.out.println("aspectJ after method");
    }
}
@Before("myAop()") 效果等于 @Before("execution(public void com.daoImpl.UserDaoImpl.*(com.entity.User))")

执行效果与之前的执行效果一致

@Around

@Around("execution(public void com.daoImpl.UserDaoImpl.*(com.entity.User))")

Around标签,可循环执行方法  注意---> pjp.proceed();重复了三次 

package com.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class logInterceptor {
    @Pointcut("execution(public void com.daoImpl.UserDaoImpl.*(com.entity.User))")
    public void myAop(){};
@Around(
"myAop()") public void around(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("aspectJ before method"); pjp.proceed(); pjp.proceed(); pjp.proceed(); System.out.println("aspectJ after method"); } }

测试类

package com.serviceImpl.test;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.entity.User;
import com.serviceImpl.UserServiceImpl;

public class UserServiceImplTest {
    User user;

    @Before
    public void setUp() throws Exception {
        user = new User();
        user.setName("testName");
        user.setRemark("testRemark");
    }

    @Test
    public void testAdd() {
        ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("beans.xml");
         UserServiceImpl UserServiceImpl = (UserServiceImpl)app.getBean("userServiceImpl");
         UserServiceImpl.add(user);//调用方法
         UserServiceImpl.update(user);//调用方法
    }
}

执行结果

aspectJ before method
testName-->testRemark save --调用UserDaoImpl!
testName-->testRemark save --调用UserDaoImpl!
testName-->testRemark save --调用UserDaoImpl!
aspectJ after method
aspectJ before method
testName-->testRemark update --调用UserDaoImpl!
testName-->testRemark update --调用UserDaoImpl!
testName-->testRemark update --调用UserDaoImpl!
aspectJ after method

pjp.proceed();重复了三次 

从结果中得到方法也确实执行了三次

通过around可以有效控制方法在这个切面里的执行次数,甚至不执行

annotation介绍到这里

通过上述资料可以掌握 @Aspect @Pointcut @Before @AfterReturning @Around 以及 execution语法

3 Spring的AspectJ实现AOP (XML)

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:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">

    <context:annotation-config/>
    <context:component-scan base-package="com"></context:component-scan>
    <aop:config>
        <aop:aspect id="logInterceptor" ref="logInterceptor">
            <aop:pointcut expression="execution(public void com.daoImpl.UserDaoImpl.*(..))" id="myAop" />
            <aop:before pointcut-ref="myAop" method="beforMethod" /><aop:after pointcut-ref="myAop" method="afterMethod" />  
        </aop:aspect>
    </aop:config>
    

</beans>

由于我使用的是annotation的component组件bean配置

定义<aop:config>配置

aspect的ref表示Spring管理的bean,id应该是随意的

pointcut +表达式 + id(方法名)

before/after在与method方法之间需要+表达式或者是pointcut的id来明确切入点

切面类

package com.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Component
public class logInterceptor {
    public void myAop(){};
    public void beforMethod(){
        System.out.println("aspectJ before method");
    }
    public void afterMethod(){
        System.out.println("aspectJ after method");
    }
//    @Around("myAop()")
//    public void around(ProceedingJoinPoint pjp) throws Throwable{
//        System.out.println("aspectJ before method");
//        pjp.proceed();
//        System.out.println(pjp.getStaticPart());
//        System.out.println("aspectJ after method");
//    }

}

测试类

package com.serviceImpl.test;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.entity.User;
import com.serviceImpl.UserServiceImpl;

public class UserServiceImplTest {
    User user;

    @Before
    public void setUp() throws Exception {
        user = new User();
        user.setName("testName");
        user.setRemark("testRemark");
    }

    @Test
    public void testAdd() {
        ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("beans.xml");
         UserServiceImpl UserServiceImpl = (UserServiceImpl)app.getBean("userServiceImpl");
         UserServiceImpl.add(user);//调用方法
         UserServiceImpl.update(user);//调用方法
    }
}

执行结果与annotation的一致

aspectJ before method
testName-->testRemark save --调用UserDaoImpl!
aspectJ after method
aspectJ before method
testName-->testRemark update --调用UserDaoImpl!
aspectJ after method

around的XML配置

        <aop:aspect id="logInterceptor" ref="logInterceptor">
            <aop:pointcut expression="execution(public void com.daoImpl.UserDaoImpl.*(..))" id="myAop" />
            <aop:before pointcut-ref="myAop" method="beforMethod" /><aop:after pointcut-ref="myAop" method="afterMethod" />  
            <aop:around pointcut-ref="myAop" method="around"/>
        </aop:aspect>

切面类

package com.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Component
public class logInterceptor {
    public void myAop(){};
    public void beforMethod(){
        System.out.println("aspectJ before method");
    }
    public void afterMethod(){
        System.out.println("aspectJ after method");
    }
    public void around(ProceedingJoinPoint pjp) throws Throwable{
        System.out.println("aspectJ before method");
        pjp.proceed();
        System.out.println(pjp.getStaticPart());
        System.out.println("aspectJ after method");
    }

}

执行结果

aspectJ before method
aspectJ before method
testName-->testRemark save --调用UserDaoImpl!
aspectJ after method
execution(void com.dao.UserDao.save(User))
aspectJ after method
aspectJ before method
aspectJ before method
testName-->testRemark update --调用UserDaoImpl!
aspectJ after method
execution(void com.dao.UserDao.update(User))
aspectJ after method

个人建议使用XML来完成AOP的切面配置,这样代码的可读性会比较强,而且配置较为灵活。

另外如果非实现接口的类需要做切面处理的话,需要引入JAR包,这里没继续研究。

通过上述资料可以掌握 @Aspect @Pointcut @Before @AfterReturning @Around注解和XML配置 以及 execution语法

 
原文地址:https://www.cnblogs.com/sunfan1988/p/3477539.html