JavaEE开发框架-Spring-AOP面向切面-学习日记

JavaEE开发框架-Spring-AOC

AOP概述

AOP(Aspect Oriented Programming),面向切面编程,通过预编译的方式在运行期通过动态代理实现一种技术。

AOP是OOP(面向对象)延续,利用AOP可以实现业务和切面的逻辑分离,降低耦合度,程序的重用性提高

基本概念扫盲:

Aspect(切面):对横切性关注点(前置后置与异常情况下所做的事)的一种抽象

Jionpoint(连接点):要拦截的方法

Pointcut(切点):是连接点的集合

Advice(通知):在连接点前后或异常情况来做的事情,前置通知,后置通知,返回通知,异常通知,环绕通知

Target(目标对象):代理的目标对象

Weave(织入):切面应用到目标对象并且导致proxy对象创建的过程叫织入

Spring

第一步:引入Spring的aop的包

AOP环境配置

IDEA导包

配置文件内容(AplicationContext.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: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-3.2.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-3.2.xsd
           http://www.springframework.org/schema/aop
           http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">

    <!-- 开启以注解形式切面的驱动 -->
    <aop:aspectj-autoproxy/>
</beans>

定义切面

  1. 创建一个类,在上加入@Aspect注解
  2. 在任意方法上加上切点注解@PointCut并配好execution属性(确定拦截目标)
  3. 定义前置通知并关联切点注解的方法
package com.aspect;

import com.model.User;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
//切面类加上注解@Aspect
@Aspect
public class PermAspect {
    /*
    * 第一个*:拦截方法的返回值,*代表任意
    * 第一个..子包下
    * 第二个*:所有的类
    * 第三个*:所有的方法
    * 第二个..所有的参数
    * */
    @Pointcut("execution(* com.service..*.*(..))")
    public  void  anyMethod(){

    }
    /*
    *Before前置通知
    *anyMethod():指定前置通知的切点
    *args(参数)
    * */
    
    @Before("anyMethod()&&args(user)")
    /*或者写成以下形式
    * @Before(value = "anyMethod()&&args(param)",argNames = "param")
    * */
    public void preAdvice(User user){
        System.out.println(user);
        System.out.println("执行前置通知");
    }
}

在配置文件中配置切面bean与被切的目标bean

    <!--依赖注入-->
    <bean id="userDao" class="com.daoimpl.UserDaoImpl"></bean>
    <bean id="userDaoService" class="com.serviceimpl.UserDaoServiceImpl">
        <property name="userDao" ref="userDao"></property>
    </bean>

    <!--定义切面bean-->
    <bean id="permApect" class="com.aspect.PermAspect"></bean>

连接点:

package com.serviceimpl;

import com.dao.UserDao;
import com.model.User;
import com.service.UserDaoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;


public class UserDaoServiceImpl implements UserDaoService {

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    private UserDao userDao ;

    @Override
    public void save() {
        userDao.save();
    }
    /*连接点->要拦截的方法*/
    @Override
    public void saveUser(User user) {
        userDao.saveUser(user);
    }
}

测试

    public static void main(String[] args) {
        User user = new User("三金",15) ;
        //创建spring容器
        ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
        //从容器中获得bean
        UserDaoService service = (UserDaoService) context.getBean("userDaoService");
        //调用方法
        service.saveUser(user);
    }

结果

前置通知

在上面已经讲过,这里不再赘述

后置通知

定义一个void方法,在方法上加**@After**注解,在业务方法执行之后来执行,其余使用方法和前置拦截一样

返回通知

在返回通知里面可以对业务方法的返回值做最后的统一加工

切面代码:

    /**
     * pointcut:指定返回通知的切点
     * returning:指定返回值和返回通知的方法的参数名称要一致
     *
     * @param user
     */
    @AfterReturning(pointcut = "anyMethod()",returning = "user")
    public void returnAdvice(User user){
        user.setName("四金");
        System.out.println("执行返回通知");
        System.out.println(user);

    }

环绕通知

最常用的一种通知,该类型通知涵盖以上所有通知的功能

package com.aspect;

import com.model.User;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;

//切面类加上注解@Aspect
@Aspect
public class PermAspect {
    /*
    * 第一个*:拦截方法的返回值,*代表任意
    * 第一个..子包下
    * 第二个*:所有的类
    * 第三个*:所有的方法
    * 第二个..所有的参数
    * */
    @Pointcut("execution(* com.service..*.*(..))")
    public  void  anyMethod(){

    }
    
    @Around("anyMethod()")
    public Object aroundAdvice(ProceedingJoinPoint jp){
        //获得拦截的业务方法的参数
        Object[] args = jp.getArgs();
        System.out.println("环绕通知-前"+"-------传递的参数:"+args[0]);
        Object obj = null;
        User user = null;

        try {
            //proceed执行业务方法,也可以使用无参数,返回值就是业务方法的返回值
            obj = jp.proceed(args);
            user = (User) obj;
        } catch (Throwable e) {
            //例外通知处理逻辑可在catch中处理
            e.printStackTrace();
        }
        //后置通知
        user.setName("五金");
        System.out.println("环绕通知-后"+"-------返回值:"+obj);
        return obj;
    }
}

AOP配置文件中定义切面

<aop:config>
		<!-- 
			切点配置:
			expression:切点的表达式
			id:唯一标识
		 -->
		<aop:pointcut expression="execution(* com.rl.spring.service..*.*(..))" id="mycut"/>
		<!-- 切面配置
			ref:要关联的切面类,在切面类(不用加注解)中定义切面方法
		 -->
		<aop:aspect ref="permApect">
			<!-- 前置通知
				method:切面类中方法
				pointcut-ref:切点
			 -->
			<aop:before method="preAdvice" pointcut-ref="mycut"/>
			<!-- 后置通知 -->
			<aop:after method="afterAdvice" pointcut-ref="mycut"/>
			<!-- 返回通知
				returning:业务方法返回的值,也是切面方法的参数,一般设为为Object
			 -->
			<aop:after-returning method="returnAdvice" pointcut-ref="mycut" returning="returnval"/>
			<!-- 
				例外外通
				throwing:抛出的异常的变量名知
			 -->
			 <aop:after-throwing method="exceptionAdvice" pointcut-ref="mycut" throwing="ex"/>
			<!-- 
				环绕通知
			 -->
			 <aop:around method="aroundAdvice" pointcut-ref="mycut"/>
		</aop:aspect>
	</aop:config>

切面类permApect:

JoinPoint类可以方便我们操作参数信息

public class PermAspect {
	
	public void preAdvice(JoinPoint jp){
		Object[] args = jp.getArgs();
		if(args.length > 0)
		System.out.println("执行前置通知-----------业务参数:"+args[0]);
	}
	
	public void afterAdvice(JoinPoint jp){
		Object[] args = jp.getArgs();
		if(args.length > 0)
		System.out.println("执行后置通知-----------业务参数:"+args[0]);
	}
	
	public void returnAdvice(JoinPoint jp, Object returnval){
		if(returnval instanceof User){
			User user = (User) returnval;
			user.setUsername("六金");
		}
		System.out.println("返回通知----------返回的值"+returnval);
	}
	
	public void exceptionAdvice(JoinPoint jp, Exception ex){
		System.out.println("例外通知------------");
		ex.printStackTrace();
	}
	
	public Object aroundAdvice(ProceedingJoinPoint jp){
		Object[] objs = jp.getArgs();
		if(objs.length>0)
		System.out.println("执行环绕通知前-----------业务参数:"+objs[0]);
		Object obj = null;
		try {
			 obj = jp.proceed();
			 System.out.println("执行环绕通知后-----------业务参数:"+objs[0]);
		} catch (Throwable e) {
			e.printStackTrace();
		}
		return obj;
	}

}
发布了153 篇原创文章 · 获赞 93 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/JunSIrhl/article/details/104034370
今日推荐