当前位置:首页 > Java > 正文内容

Spring的AOP编程

flowstone8年前 (2017-10-16)Java533

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

AOP的好处:在不修改源代码的情况下,可以实现功能的增强


JDK动态代理

缺点:只能针对实现了接口的类实现代理

/**
 * Jdk的动态代理
 * @author kevin
 */
public class JdkProxy implements InvocationHandler{

    //要代理的对象
    private CustomerDao customerDao;

    public JdkProxy(CustomerDao customerDao){
        this.customerDao = customerDao;
    }
    /**
    * 生成代理对象的方法
    * @return
    */
    public CustomerDao createProxy(){
        CustomerDao proxy = (CustomerDao) Proxy.newProxyInstance(customerDao.getClass().getClassLoader(), customerDao.getClass().getInterfaces(), this);
        return proxy;
    }

    /**
    * 增强的方法
    */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("权限校验...");
        return method.invoke(customerDao, args);
    }
}

//编写测试代码:

@Test
public void test(){
    CustomerDao customerDao = new CustomerDaoImpl();
    JdkProxy jdkProxy = new JdkProxy(customerDao);
    CustomerDao proxy = jdkProxy.createProxy();
    proxy.save();
}


CGLIB动态代理(了解)

可以针对那些没有实现接口的类实现代理

public class CglibProxy implements MethodInterceptor{

    //要代理的对象
    private LinkManDao linkManDao;

    public CglibProxy(LinkManDao linkManDao) {
        this.linkManDao = linkManDao;
    }

    public LinkManDao createProxy(){
        //创建Cglib核心类
        Enhancer enhancer = new Enhancer();
        //设置父类
        enhancer.setSuperclass(linkManDao.getClass());
        //设置回调
        enhancer.setCallback(this);
        //生成代理
        LinkManDao proxy = (LinkManDao) enhancer.create();
        return proxy;
    }

    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("日志记录");
        Object obj = methodProxy.invokeSuper(proxy, args);
        return obj;
    }
}

//编写测试代码
@Test
public void test2(){
    LinkManDao linkManDao = new LinkManDao();
    CglibProxy cglibProxy = new CglibProxy(linkManDao);
    LinkManDao proxy = cglibProxy.createProxy();
    proxy.save();
}


术语

  1. Target: 目标对象。要对类增强,增强的类就称为目标对象。

  2. JoinPoint: 连接点。可以被增强的点就称为JoinPoint。

  3. PointCut: 切入点。在实际开发中真正被增强了的点就称为PointCut。

  4. Advice: 增强(通知)。现在需要对save方法增强一个日志记录的功能,就需要编写一个日志记录的方法。那么这个日志记录的方法就称为Advice。

  5. Weaving: 织入。 把Advice应用到目标的过程叫织入。

  6. Introduction: 引介。 指的是类层面的增强。

  7. Proxy: 代理。 增强之后的对象。

  8. Aspect: 切面。 多个增强和多个切入点的组合就叫切面。

public class UserDaoImpl{
    public void save(){}
    public void list(){}
    public void delete(){}
    public void update(){}
}

SpringXML形式的AOP入门

配置

导入jar包(11) 

红线画起来的表示:

AOP联盟的jar包:com.springsource.org.aopalliance-1.0.0.jar

Spring提供的AOP的jar包:spring-aop-4.2.4.RELEASE.jar

AspectJ的jar包:com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar

Spring整合AspectJ的jar包:spring-aspects-4.2.4.RELEASE.jar

配置文件

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

    <bean id="customerDao" class="me.xueyao.dao.impl.CustomerDaoImpl"></bean>
    <bean id="myAspectXml" class="me.xueyao.aspect.MyAspectXml"></bean>
    <!-- AOP配置 -->
    <aop:config>
            <!-- 配置切入点 -->
            <aop:pointcut expression="execution(* me.xueyao.dao.impl.CustomerDaoImpl.save(..))" id="pointcut1"/>
            <!-- 配置切面 -->
            <aop:aspect ref="myAspectXml">
                <aop:before method="checkPrivilege" pointcut-ref="pointcut1"/>
            </aop:aspect>
    </aop:config>
   </beans>

切入点表达式

语法:[修饰符] 返回类型 包名.类名.方法名(形式参数)

常见写法:

execution(public * *(..))  所有的public方法
execution(* set(..))    所有set开头的方法
execution(* com.xyz.service.AccountService.*(..))   AccountService类中的所有方法
execution(* com.xyz.service.*.*(..))    com.xyz.service包下所有的方法
execution(* com.xyz.service..*.*(..))   com.xyz.service包及其子包下所有的方法

Spring中AOP的通知类型

1.前置通知: 在方法执行之前增强。可以获得切入点信息。

<!-- 配置切面 -->
<aop:aspect ref="myAspectXml">
    <aop:before method="checkPrivilege" pointcut-ref="pointcut1"/>
</aop:aspect>


2.后置通知: 在方法执行完之后增强。可以获取返回值信息。

<aop:aspect ref="myAspectXml">
    <!-- 前置通知 -->
    <aop:before method="checkPrivilege" pointcut-ref="pointcut1"/>
    <!-- 后置通知 -->
    <aop:after-returning method="afterReturn" pointcut-ref="pointcut2" returning="result"/>
</aop:aspect>


3.环线通知: 在方法执行前后都进行增强。可以阻止方法的执行。

public class CustomerDaoImpl implements CustomerDao {
    @Override
    public void update() {
        System.out.println("持久层:客户更新");
    }

}

public class MyAspectXml {
    public Object around(ProceedingJoinPoint joinpoint){
        System.out.println("环绕前执行");
        Object obj = null;
        try {
            obj = joinpoint.proceed();
        } catch (Throwable e) {
            e.printStackTrace();
        }
        System.out.println("环绕后执行");
        return obj;
    }
}
<!-- AOP配置 -->
<aop:config>
    <!-- 配置切入点 -->
    <aop:pointcut expression="execution(* me.xueyao.dao.impl.CustomerDaoImpl.update(..))" id="pointcut3"/>
    <!-- 配置切面 -->
    <aop:aspect ref="myAspectXml">
        <!-- 环绕通知 -->
        <aop:around method="around" pointcut-ref="pointcut3"/>
    </aop:aspect>
</aop:config>

4.异常抛出通知: 当发生异常之后增强,可以获取异常信息

public class CustomerDaoImpl implements CustomerDao {
    @Override
    public void find() {
        System.out.println("持久层:查询");
        int i = 10/0;
    }
}

public class MyAspectXml {
    public void afterThrowing(Exception ex){
        System.out.println("抛出异常通知:" + ex.getMessage());
    }
}
<!-- AOP配置 -->
<aop:config>
    <!-- 配置切入点 -->
    <aop:pointcut expression="execution(* me.xueyao.dao.impl.CustomerDaoImpl.find(..))" id="pointcut4"/>
    <!-- 配置切面 -->
    <aop:aspect ref="myAspectXml">
        <!-- 抛出异常通知 -->
        <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut4" throwing="ex"/>
    </aop:aspect>
</aop:config>


5.最终通知: 不管是否有异常,都会执行的

public class CustomerDaoImpl implements CustomerDao {
    @Override
    public void find() {
        System.out.println("持久层:查询");
        int i = 10/0;
    }
}

public class MyAspectXml {
    public void after(){
        System.out.println("最终通知");
    }
}
<!-- AOP配置 -->
<aop:config>
    <!-- 配置切入点 -->
    <aop:pointcut expression="execution(* me.xueyao.dao.impl.CustomerDaoImpl.find(..))" id="pointcut4"/>
    <!-- 配置切面 -->
    <aop:aspect ref="myAspectXml">
        <!-- 最终通知 -->
        <aop:after method="after" pointcut-ref="pointcut4"/>
    </aop:aspect>
</aop:config>


最终通知和后置通知的区别:

最终通知,不管异常与否,都执行;而后置通知在异常时不执行。

Spring注解形式的AOP入门 创建工程,引入jar包,创建核心配置文件(和XML引入的包相同)

配置文件

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

       <context:component-scan base-package="me.xueyao"></context:component-scan>
        <!-- 开启自动代理注解 -->
       <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
   </beans>

创建接口和实现类

public interface CustomerDao {
    /**
     * 持久层:客户保存
     */
    public void save();

    public int delete();

    public void update();

    public void find();

}

@Repository("customerDao")
public class CustomerDaoImpl implements CustomerDao {

    @Override
    public void save() {
        System.out.println("持久层:客户保存...");
    }

    public int delete(){
        System.out.println("持久层:客户删除...");
        return 100;
    }

    @Override
    public void update() {
        System.out.println("持久层:客户更新");
    }

    @Override
    public void find() {
        System.out.println("持久层:查询");
    }

}

编写切面类

@Component("myAspectAnnotation")
@Aspect 
//可以省略id
public class MyAspect {    
    @Before("execution(* me.xueyao.dao.impl.CustomerDaoImpl.save(..))")
    public void checkPrivilege(JoinPoint joinPoint){
        System.out.println("权限校验..." + joinPoint.toString());
    }

}

Spring的AOP中注解通知

1.前置通知

/**
* 前置通知
* @param joinPoint
*/
@Before("execution(* me.xueyao.dao.impl.CustomerDaoImpl.save(..))")
public void checkPrivilege(JoinPoint joinPoint){
    System.out.println("权限校验..." + joinPoint.toString());
}

2.后置通知

@Aspectpublic class MyAspect {    /**
    * 后置通知
    * @param result
    */
    @AfterReturning(value="execution(* me.xueyao.dao.impl.CustomerDaoImpl.delete(..))",returning="result")
    public void afterReturning(Object result){
        System.out.println("后置通知:" + result);
    }
}

3.环绕通知

@Around("execution(* me.xueyao.dao.impl.CustomerDaoImpl.update(..))")
public Object after(ProceedingJoinPoint joinpoint) throws Throwable{
    System.out.println("环绕通知前增强");
    Object obj = joinpoint.proceed();
    System.out.println("环绕通知后增强");    return obj;
}


4.异常通知

@AfterThrowing(value="execution(* me.xueyao.dao.impl.CustomerDaoImpl.find(..))",throwing="ex")
public void afterThrowing(Exception ex){
    System.out.println("抛出异常通知");
}

5.最终通知

@After("execution(* me.xueyao.dao.impl.CustomerDaoImpl.find(..))")public void after(){
    System.out.println("最终通知");
}

6.PointCut注解(了解)

作用:用于定义切入点表达式的一个注解。

@Component("myAspectAnnotation")
@Aspect
public class MyAspect {    /**
        * 前置通知
        * @param joinPoint
        */
    @Before("execution(* me.xueyao.dao.impl.CustomerDaoImpl.save(..))")
    public void checkPrivilege(JoinPoint joinPoint){
        System.out.println("权限校验..." + joinPoint.toString());
    }    
    @AfterReturning(value="execution(* me.xueyao.dao.impl.CustomerDaoImpl.delete(..))",returning="result")
    public void afterReturning(Object result){
        System.out.println("后置通知:" + result);
    }    
    @Around("execution(* me.xueyao.dao.impl.CustomerDaoImpl.update(..))")
    public Object aroung(ProceedingJoinPoint joinpoint) throws Throwable{
        System.out.println("环绕通知前增强");
        Object obj = joinpoint.proceed();
        System.out.println("环绕通知后增强");        
        return obj;
    }    
    //  @AfterThrowing(value="execution(* me.xueyao.dao.impl.CustomerDaoImpl.find(..))",throwing="ex")
    @AfterThrowing(value="MyAspect.pointcut()",throwing="ex")
    public void afterThrowing(Exception ex){
        System.out.println("抛出异常通知");
    }    
    //  @After("execution(* me.xueyao.dao.impl.CustomerDaoImpl.find(..))")
    @After("MyAspect.pointcut()")
    public void after(){
        System.out.println("最终通知");
    }    
    
    @Pointcut("execution(* me.xueyao.dao.impl.CustomerDaoImpl.find(..))")
    public void pointcut(){

    }

}

扫描二维码推送至手机访问。

版权声明:本文由薛尧的博客发布,如需转载请注明出处。

本文链接:https://flowstone.sourceforge.io/?id=18

分享给朋友:

相关文章

关于equals()方法两边变量如何放置

两个变量放置位置如果是两个都是变量,可以放在equals任意一边,没有区别常量、变量放置位置如果有一个是常量,equals()方法在使用时,建议equals()方法前面放常量。因为equals()是Object类中定义的,任何对象都可以调用...

Spring Cloud入门教程一之Eureka Server

项目环境MacOSJDK1.8IntelliJ IDEA 2018.2Maven 3.5.4创建项目采用Spring Initializr创建项目选择Cloud Discovery->Eureka Discovery->项目名称...

Lombok的使用

近期在众多微信公众号中,都看到了许多大牛,写了Lombok的文章,我看了一下,基本上就围绕着如何减少代码来做说明,我来总结一下。公司现在的项目没有使用Lombok,一些实体类都是我们用IDEA提供的快捷方式生成的,后来,公司新来了一个大牛,...

Java编程思想学习总结一(一切都是对象)

存储位置寄存器堆栈 存储对象引用,堆栈指针向下移动,分配新的内存,向上移动,释放内存堆 存储Java对象常量存储 存储常量值非RAM存储 存储流对象和持久化对象基本类型所占存储空间基本类型大小包装器类型默认值boolean16bitBool...

Java内存缓存-通过Map定制简单缓存

缓存在程序中,缓存是一个高速数据存储层,其中存储了数据子集,且通常是短暂性存储,这样日后再次请求此数据时,速度要比访问数据的主存储位置快。通过缓存,可以高效地重用之前检索或计算的数据。为什么要用缓存场景在Java应用中,对于访问频率高,更新...

Java多线程-程序运行堆栈分析

class文件内容class文件包含JAVA程序执行的字节码;数据严格按照格式紧凑排列在class文件中的二进制流,中间无任何分隔符;文件开头有一个0xcafebabe(16进制)特殊的一个标志。JVM运行时数据区线程独占:每个线程都会有它...