AOP的概念:
Aop即面向切面编程,是对oop的一种补充和完善,在oop中有大量重复代码,不利于各个模块的重用,Aop采用一种叫做”横切“的技术,剖解开封装的对象内部,并将那些公共行为封装到一个可重用模块,并将其命名为“Aspect",即切面。
- Aspect(切面):封装了共性方法的类,里面可以定义切入点和通知,是对横切关注点的抽象。
- Pointcut(切入点):带有通知的连接点,用一个表达式来提现
- Advice(通知):增强功能的一些方法,包含before,after,afterReturning,afterThrowing,around
- JoinPoint(连接点): 程序执行过程中明确的点,一般是方法的调用,常作为方法的形参。
- Waving(织入):将切面应用到目标对象并导致产生代理对象的过程。
第一种:使用配置文件application-aop.xml
直接上代码,定义一个业务逻辑类:
package com.daling.config.springAop.aop.service;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Service;
@Service
public class MemberService {
private final static Logger log = Logger.getLogger(MemberService.class);
public void add(){
System.out.println("======================================添加会员");
}
public void remove(Integer id) throws Exception{
System.out.println("======================================删除会员");
throw new Exception("这是我们自己抛出的异常");
}
public void update(Integer id){
System.out.println("======================================更改会员");
}
public void query(Integer id){
System.out.println("======================================查询会员");
}
}
定义一个切面类:
package com.daling.config.springAop.aop.aspect;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
public class LoginAspect {
private final static Logger log = Logger.getLogger(LoginAspect.class);
public void before(JoinPoint joinpoint){
log.info("======================================调用方法之前执行" + joinpoint);
}
public void after(JoinPoint joinpoint){
log.info("======================================调用方法之后执行" + joinpoint);
}
public boolean afterReturn(JoinPoint joinpoint){
log.info("======================================调用方法返回之前执行" + joinpoint);
return true;
}
public void afterThrow(JoinPoint joinpoint){
log.info("======================================抛出异常之后执行" + joinpoint);
}
}
application-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: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">
<!-- 开启注解扫描-->
<context:component-scan base-package="com.daling.config.springAop.aop"></context:component-scan>
<!--aop自动代理-->
<aop:aspectj-autoproxy proxy-target-class="true"/>
<!--声明一个需要织入到虚拟切面的切面-->
<bean id="logAspect" class="com.daling.config.springAop.aop.aspect.LoginAspect"></bean>
<aop:config>
<!--声明切入点,id用来标记这个切入点; expresssion: 满足expression中的方法调用之后,就会去进行切面操作,类似于触发了切面
第一个* 代表返回任意类型 ,
第一个.. 所有的子包
* 方法名任意
第二个.. 表示方法的参数是任意数量和类型
-->
<aop:pointcut id="logPointcut" expression="execution(* com.daling.config.springAop.aop.service..*(..))" ></aop:pointcut>
<!--
简单说就是,只要com.daling.config.springAop.aop.service这个包中的所有子包,以及包下的所有类的任意一个函数被调用,不管你的返回值是什么,都会触发开关,
我就会去执行切面,也就是辅助功能,但是辅助功能是什么呢,就是下面两句:
-->
<aop:aspect id="" ref="logAspect">
<aop:before method="before" pointcut-ref="logPointcut"></aop:before>
<aop:after method="after" pointcut-ref="logPointcut"></aop:after>
<aop:after-returning method="afterReturn" returning="boolean" pointcut-ref="logPointcut"></aop:after-returning>
<aop:after-throwing method="afterThrow" pointcut-ref="logPointcut"></aop:after-throwing>
</aop:aspect>
</aop:config>
</beans>
测试类进行测试:
package com.daling.test;
import com.daling.config.springAop.aop.service.MemberService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@ContextConfiguration(locations = {"classpath:config/application-aop.xml"})
@RunWith(SpringJUnit4ClassRunner.class)
public class AopTest {
@Autowired
private MemberService memberService;
@Test
public void testAdd(){
memberService.add();
}
@Test
public void testRemove(){
try {
memberService.remove(1);
} catch (Exception e) {
e.printStackTrace();
}
}
@Test
public void testUpdate(){
memberService.update(1);
}
@Test
public void testQuery(){
memberService.query(1);
}
}
测试结果:
第二种:使用注解
MemberService类不变,切面类改为如下类:
package com.daling.config.springAop.aop.aspect;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;
@Component //实现自动注入
@Aspect //声明该类为切面类
public class AnnotationAspect {
//Waving(织入):将切面应用到目标对象并导致代理对象创建的过程
private final static Logger log = Logger.getLogger(AnnotationAspect.class);
/**
* 声明一个切入点: 带有通知的连接点,在程序上体现为切入点表达式
* expresssion: 满足expression中的方法调用之后,就会去进行切面操作,类似于触发了切面
*/
@Pointcut("execution(* com.daling.config.springAop.aop.service..*(..))")
public void pointCutConfig(){}
/**
* Advice:通知 aop在特定的切入点上执行的增强处理,包含before,after,afterReturning,afterThrowing,around
* @param joinpoint
*/
@Before("pointCutConfig()")
public void before(JoinPoint joinpoint){//JoinPoint:连接点
log.info("======================================调用方法之前执行" + joinpoint);
}
@After("pointCutConfig()")
public void after(JoinPoint joinpoint){
log.info("======================================调用方法之后执行" + joinpoint);
}
@AfterReturning("pointCutConfig()")
public boolean afterReturn(JoinPoint joinpoint){
log.info("======================================调用方法返回之前执行" + joinpoint);
return true;
}
@AfterThrowing("pointCutConfig()")
public void afterThrow(JoinPoint joinpoint){
System.out.println("切点的参数:" + Arrays.toString(joinpoint.getArgs()));
System.out.println("切点的方法:" + joinpoint.getSignature());
System.out.println("生成以后的代理对象: " + joinpoint.getTarget());//生成以后的代理对象
System.out.println("this: " + joinpoint.getThis());//当前类的本身(反射调用)
log.info("======================================抛出异常之后执行" + joinpoint);
}
}
配置文件开启注解扫描和aop自动代理不能少:
<?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: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">
<!-- 开启注解扫描-->
<context:component-scan base-package="com.daling.config.springAop.aop"></context:component-scan>
<!--aop自动代理-->
<aop:aspectj-autoproxy proxy-target-class="true"/>
</beans>
测试类不变:结果如下,测试remove()方法