java spring AOP

AOP(Aspect Oriented Programming):面向切面编程
一、AOP的思想
正常的传统的程序执行流程都是纵向执行流程,AOP(面向切面编程)在原有的纵向执行流程中添加横切面。
二、AOP的优点
1、AOP的使用不需要修改原有程序代码,
2、具有高扩展性,
3、原有的功能相当于释放了部分逻辑,让职责更加明确。
三、面向切面编程到底是什么?
在程序原有纵向执行流程中,针对某个或某一些方法添加通知,形成横切面过程就叫做面向切面编程

四、常用的概念
在这里插入图片描述编号2:切点 需要添加额外功能的方法,
编号3:前置通知 在切点之前执行的功能,before advice,
编号4:后置通知 在切点之后执行的功能,after advice,
异常通知: 如果切点执行过程中出现异常,会触发异常通知 throws advice,
编号1:切面 所有功能总称为切面,
织入: 把切面嵌入到原有功能的过程叫做织入。

五、spring 提供了2种AOP实现方式
1、Schema-based
(1)每一个通知都需要实现接口或者类,
(2)配置spring配置文件时,切面在aop:config标签下配置。
2、AspectJ
(1)每个通知不需要实现接口或类,
(2)配置spring配置文件时切面在aop:config的子标签aop:aspect下配置。

六、实现

方式一:Schema-based方式
1、导包
aopalliance-1.0.jar
aspectjrt-1.8.10.jar
aspectjweaver-1.8.10.jar
commons-logging-1.1.1.jar
spring-aop-4.3.10.RELEASE.jar
spring-beans-4.3.10.RELEASE.jar
spring-context-4.3.10.RELEASE.jar
spring-core-4.3.10.RELEASE.jar
spring-expression-4.3.10.RELEASE.jar

2.新建通知类
2.1 新建前置通知类
2.1.1 arg0 切点方法对象 Method 对象
2.1.2 arg1 切点方法参数
2.1.3 arg2 切点在哪个对象中

public class MyBeforeAdvice implements MethodBeforeAdvice{
    @Override
    public void before(Method arg0, Object[] arg1, Object arg2){
        System.out.println("执行前置通知");
    }
}

2.2 新建后置通知类
2.2.1 arg0 切点方法返回值
2.2.2 arg1 切点方法对象
2.2.3 arg2 切点方法参数
2.2.4 arg3 切点方法所在类的对象

public class MyAfterAdvice implements AfterReturningAdvice{
    @Override
public void afterReturning(Object arg0, Method arg1,Object[] arg2, Object 
arg3){
        System.out.println("执行后置通知");
    } 
}

3.配置 spring 配置文件
3.1 引入 aop 命名空间
3.2 配置通知类的
3.3 配置切面
3.4 * 通配符,匹配任意方法名,任意类名,任意一级包名
3.5 如果希望匹配任意方法参数 (…)

<?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"
	xsi:schemaLocation="http://www.springframework.org/sc hema/beans
		http://www.springframework.org/schema/beans/spring-be ans.xsd
		http://www.springframework.org/schema/aop
		http://www.springframework.org/schema/aop/spring-aop. xsd">
	<!-- 配置通知类对象,在切面中引入 -->
	<bean id="mybefore" class="com.woniuxy.advice.MyBeforeAdvice"></bean>
	<bean id="myafter" class="com.woniuxy.advice.MyAfterAdvice"></bean>
	<!-- 配置切面 -->
	<aop:config>
		<!-- 配置切点 -->
		<aop:pointcut expression="execution(* com.woniuxy.test.Demo.demo2())"
			id="mypoint" />
		<!-- 通知 -->
		<aop:advisor advice-ref="mybefore" pointcut-ref="mypoint" />
		<aop:advisor advice-ref="myafter" pointcut-ref="mypoint" />
	</aop:config>
	
	<!-- 配置 Demo 类,测试使用 -->
	<bean id="demo" class="com.woniuxy.test.Demo"></bean>
</beans>



4.编写测试代码

public class Test {
    public static void main(String[] args){
        // Demo demo = new Demo();
        // demo.demo1();
        // demo.demo2();
        // demo.demo3();
        ApplicationContext ac = 
new ClassPathXmlApplicationContext("applicationContext.xml");
        Demo demo = ac.getBean("demo", Demo.class);
        demo.demo1();
        demo.demo2();
        demo.demo3();
    }
}


通知类型:
前置通知
后置通知
环绕通知
异常通知
返回后通知

方式二:Aspectj方式
1、IUserDao

//IUserService接口
public interface IUserService {
	void save();
}

2、UserService实现类(实现接口)
UserDao实现类(有实现接口)

@Component   // 加入容器
public class UserServiceImpl implements IUserService{
	@Override
	public void save() {
		System.out.println("-----调用DAO核心业务:保存!!!------");
	}
}

3、切面类

public class Aop {
	public void begin() {
		System.out.println("开始事务/异常");
	}
	public void after() {
		System.out.println("无论程序正常执行还是有异常,只要程序执行完就会执行这个通知");
	}
	public void afterReturning() {
		System.out.println("程序执行到return,代表程序正常执行完毕,所以只有程序正常执行完,没有异常才会执行这个通知");
	}
	public void afterThrowing() {
		System.out.println("afterThrowing()");
	}
	public void around(ProceedingJoinPoint pjp) throws Throwable {
		System.out.println("环绕前....");
		pjp.proceed(); // 执行目标方法
		System.out.println("环绕后....");
	}
}

4、spring-context.xml

<!-- dao实例 -->
<bean id="userService" class="com.my.g_aop_xml.UserService"></bean>
<bean id="otherService" class="com.my.g_aop_xml.OtherService"></bean>
	
<!-- 切面类 -->
<bean id="aop" class="com.my.g_aop_xml.Aop"></bean>

<!-- AOP配置 -->
<aop:config>
	<!-- 定义一个切入点表达式 -->
	<aop:pointcut expression="execution(* com.my.g_aop_xml.*.*(..))" id="pt"/>
	<!-- 配置切面 -->
	<aop:aspect ref="aop">
		<!-- 前置通知 -->
		<aop:before method="begin" pointcut-ref="pt"/>
		<!-- 后置通知 -->
		<aop:after method="after" pointcut-ref="pt"/>
		<!-- 环绕通知 -->
		<aop:around method="around" pointcut-ref="pt"/>
		<!-- 返回后通知 -->
		<aop:after-returning method="afterReturning" pointcut-ref="pt"/>
		<!-- 异常通知 -->
		<aop:after-throwing method="afterThrowing" pointcut-ref="pt"/>
	</aop:aspect>
</aop:config>

5、测试类

public static void main(String[] args) {
	ApplicationContext ac = 
new ClassPathXmlApplicationContext("com/my/f_aop_anno/bean.xml");
		
	//目标对象有实现接口,spring会默认采用JDK代理
	IUserService userService = (IUserService) ac.getBean("userService");
	System.out.println(userService.getClass());//$Proxy001  
	userDao.save();
}

注解方式实现Aspectj
1、IUserService接口

public interface IUserService {
	void save();
}

2、UserDao实现类(实现接口)

@Component   // 加入容器
public class UserServiceImpl implements IUserService{
	@Override
	public void save() {
		System.out.println("-----调用DAO核心业务:保存!!!------");
	}
}
	

3、AOP切面类

@Component // 加入IOC容器
@Aspect // 指定当前类为切面类
public class Aop {
	// 指定切入点表达式:拦截哪些方法(为哪些类生成代理对象)
	@Pointcut("execution(* com.my.f_aop_anno.*.*(..))")
	public void pointCut_() {
	}
//JoinPoint对象封装了SpringAop中切面方法的信息,在切面方法中添加JoinPoint参数,就
//可以获取到封装了该方法信息的JoinPoint对象
	@Before("pointCut_()")
	public void begin(JoinPoint point) {
		System.out.println("开始事务/异常");
        //方法签名获取封装了署名信息的对象,在该对象中可以获取到目标方法名,所属类的Class
//等信息
		MethodSignature methodSignature = 
(MethodSignature)point.getSignature();
		System.out.println("调用"+methodSignature.getMethod().getName()+"方法");
	}
	// 后置/最终通知:在执行目标方法之后执行 【无论是否出现异常最终都会执行】
	@After("pointCut_()")
	public void after() {
		System.out.println("提交事务/关闭");
	}
	// 返回后通知: 在调用目标方法结束后执行 【出现异常不执行】
	@AfterReturning("pointCut_()")
	public void afterReturning() {
		System.out.println("afterReturning()");
	}
	// 异常通知: 当目标方法执行异常时候执行此关注点代码
	@AfterThrowing("pointCut_()")
	public void afterThrowing() {
		System.out.println("afterThrowing()");
	}
	// 环绕通知:环绕目标方式执行
    //ProceedingJoinPoint对象是JoinPoint的子接口,该对象只用在@Around的切面方法中
	@Around("pointCut_()")
	public void around(ProceedingJoinPoint pjp) {
		System.out.println("环绕前....");
        try{
		    pjp.proceed(); // 执行目标方法
        }catch (Throwable exception){

        }finaly{

        }
		System.out.println("环绕后....");
	}
}

4.4、bean.xml配置文件

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    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">
	
	<!-- 开启spring注解扫描 -->
	<context:component-scan 
base-package="com.my.f_aop_anno"></context:component-scan>
	
	<!-- 开启aop注解方式 -->
	<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

4.5、测试类

public static void main(String[] args) {
		ApplicationContext ac = 
new ClassPathXmlApplicationContext("com/my/f_aop_anno/bean.xml");
		
		//目标对象有实现接口,spring会默认采用JDK代理
		IUserService userService = 
(IUserService) ac.getBean("userDaoServiceImpl");
		System.out.println(userServiceImpl.getClass());//$Proxy001  
		userServiceImpl.save();
		
		//目标对象没有实现接口,spring默认采用cglib代理
		OtherServiceImpl otherServiceImpl = 
(OtherServiceImpl)ac.getBean("otherServiceImpl");
		System.out.println(otherServiceImpl.getClass());
		otherImpl.save();
}

4.6、OtherService类(未实现接口)

@Component
public class OtherServiceImpl {
	public void save() {
		System.out.println("执行没有接口的dao");
	}
}

补充:aop是基于代理模式实现的,spring中本身就嵌入了CGLIB,当包含切点的类实现了接口时,aop默认使用JDK代理;当包含切点的类没有实现接口那么aop就使用CGLIB代理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值