AOP的使用
通过动态代理,可以在指定位置执行对应流程。这样就可以将一些横向的功能抽离出来形成一个独立的模块,然后在指定位置插入这些功能。这样的思想,被称为面向切面编程,亦即AOP
先定义配置文件 application-context.xml, application-common.xml,application-aop.xml,
application-context.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<import resource="classpath:application-beans.xml"/>
<import resource="classpath:application-common.xml"/>
<import resource="classpath:application-aop.xml"/>
</beans>
application-common.xml 见详情.
application-aop.xml 定义如下:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<!--两种方式公共的配置 -->
<aop:aspectj-autoproxy proxy-target-class="true"/>
<!-- 声明一个需要织入到虚拟切面的逻辑(切面) -->
<!--一 基于配置的方法使用的配置 -->
<!-- <bean id="logAspect" class="com.gupaoedu.vip.aop.aspect.LogAspect"></bean>
<aop:config>
<aop:aspect ref="logAspect">
<aop:pointcut expression="execution(* com.gupaoedu.vip.aop.service..*(..))" id="logPointcut"/>
<aop:before method="before" pointcut-ref="logPointcut"/>
<aop:after-returning method="afterReturn" returning="boolean" pointcut-ref="logPointcut"/>
<aop:after method="after" pointcut-ref="logPointcut"/>
<aop:after-throwing method="afterThrow" pointcut-ref="logPointcut"/>
</aop:aspect>
</aop:config> -->
<!-- 具体的方法就是切点 -->
<!-- 如何定义一个具体的方法 -->
<!-- 权限修饰符(public/private/protected可省略) 返回值(Void/自定义类型,用*表示任意返回值类型,必填的) 属于哪个包哪个类(省略).方法名称(参数列表,必填) throws 异常类型(可省略) -->
<!-- 参数列表的不同跟什么有关呢?
1、参数的个数
2、参数的顺序
3、参数的类型
跟参数的名字没有任何关系,形式参数
如果一个方法用final修饰了,就意味着这个方法不能被重写
也就意味着我们的子类不能对这个方法进行扩展
代理是是无法实现的
-->
<!-- execution(public * com.gupaoedu.vip.aop.service..*.*(..))-->
</beans>
1.定义一个service类,包含一系列的业务处理方法
package com.gupaoedu.vip.aop.service;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Service;
import com.gupaoedu.vip.model.Member;
@Service
public class MemberManagerService {
private final static Logger LOG = Logger.getLogger(MemberManagerService.class);
public Member add(Member member){
LOG.info("增加用户");
return new Member();
}
public boolean remove(long id) throws Exception{
LOG.info("删除用户");
throw new Exception("这是我们自己跑出来的异常");
}
}
--------------------------------------------------------------------
2.测试类 MemberManagerServiceTest
package com.gupaoedu.vip.aop.test;
import org.junit.Ignore;
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;
import com.gupaoedu.vip.aop.service.MemberManagerService;
@ContextConfiguration(locations={"classpath*:application-context.xml"})
@RunWith(SpringJUnit4ClassRunner.class)
public class MemberManagerServiceTest {
@Autowired MemberManagerService memberManagerService;
@Test
// @Ignore
public void testAdd(){
memberManagerService.add(null);
}
//做事务代理的时候
//TracationManage来管理事务操作(切面)
//DataSource ,SessionFactory(DataSource)
//DataSource 包含了连接信息,事物的提交或者回滚一些基础功能
//通过连接点是可以获取到方法(切点)具体操作哪个DataSource
//通过切面通知类型,去执行DataSource的功能方法
//完全裸露,一丝不挂
// @Test
// @Ignore
public void testRemove(){
try {
memberManagerService.remove(0);
} catch (Exception e) {
// e.printStackTrace();
}
}
}
一 基于配置
定义一个aspect类 里面定义了包含不同通知的时机和内容的通知方法
package com.gupaoedu.vip.aop.aspect;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
public class LogAspect {
private final static Logger LOG = Logger.getLogger(LogAspect.class);
public void before(JoinPoint joinPoint){
LOG.info("调用方法之前执行" + joinPoint);
}
public void after(JoinPoint joinPoint){
LOG.info("调用之后执行" + joinPoint);
}
public void afterReturn(JoinPoint joinPoint){
LOG.info("调用获得返回值之后执行" + joinPoint);
// return true;
}
public void afterThrow(JoinPoint joinPoint){
LOG.info("抛出异常之后执行" + joinPoint);
}
}
二 基于注解
定义切面类 AnnotationAspect @Pointcut注解定义一个切点, @Before,@After,@AfterReturning,@AfterThrowing定义通知
将切点传入通知注解就能将切点和通知结合起来,并通过joinPoint能获取切点指代的业务方法的信息
package com.gupaoedu.vip.aop.aspect;
import java.util.Arrays;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
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//声明这个类是被SpringIOC容器来管理的,如果不声明,就无法做到自动织入
@Aspect//这个类被声明为是一个需要动态织入到我们的虚拟切面中的类
public class AnnotationAspect {
private final static Logger LOG = Logger.getLogger(AnnotationAspect.class);
//声明切点
//因为要利用反射机制去读取这个切面中的所有的注解信息
@Pointcut("execution(* com.gupaoedu.vip.aop.service..*(..))")
public void pointcutConfig(){}
@Before("pointcutConfig()")
public void before(JoinPoint joinPoint){
LOG.info("调用方法之前执行" + joinPoint);
}
@After("pointcutConfig()")
public void after(JoinPoint joinPoint){
LOG.info("调用之后执行" + joinPoint);
}
@AfterReturning(returning="returnValue",value="pointcutConfig()")
public void afterReturn(JoinPoint joinPoint,Object returnValue){
LOG.info("调用获得返回值" + returnValue);
}
@AfterThrowing("pointcutConfig()")
public void afterThrow(JoinPoint joinPoint){
System.out.println("切点的参数" + Arrays.toString(joinPoint.getArgs()));
System.out.println("切点的方法" + joinPoint.getKind());
System.out.println(joinPoint.getSignature());
System.out.println(joinPoint.getTarget()); //生成以后的代理对象
System.out.println(joinPoint.getThis());//当前类的本身(通过反射机制去掉用)
LOG.info("抛出异常之后执行" + joinPoint);
}
}
切入点表达式的用法
切入点表达式 @Pointcut(“execution(* com.cjm.model….(…))”)
1、切入点表达式的格式:execution([可见性] 返回类型 [声明类型].方法名(参数) [异常])
2、切入点表达式通配符:
:匹配所有字符
…:一般用于匹配多个包,多个参数
+:表示类及其子类
3、切入点表达式支持逻辑运算符:&&、||、!
4、切入点表达式关键词:
1)execution:用于匹配子表达式。
//匹配com.cjm.model包及其子包中所有类中的所有方法,返回类型任意,方法参数任意
@Pointcut("execution( com.cjm.model….(…))")
public void before(){}
2)within:用于匹配连接点所在的Java类或者包。
//匹配Person类中的所有方法
@Pointcut(“within(com.cjm.model.Person)”)
public void before(){}
//匹配com.cjm包及其子包中所有类中的所有方法
@Pointcut(“within(com.cjm…*)”)
public void before(){}
3) this:用于向通知方法中传入代理对象的引用。
@Before(“before() && this(proxy)”)
public void beforeAdvide(JoinPoint point, Object proxy){
//处理逻辑
}
4)target:用于向通知方法中传入目标对象的引用。
@Before(“before() && target(target)
public void beforeAdvide(JoinPoint point, Object proxy){
//处理逻辑
}
5)args:用于将参数传入到通知方法中。
@Before(“before() && args(age,username)”)
public void beforeAdvide(JoinPoint point, int age, String username){
//处理逻辑
}
6)@within:用于匹配在类一级使用了参数确定的注解的类,其所有方法都将被匹配。
@Pointcut(”@within(com.cjm.annotation.AdviceAnnotation)")
//所有被@AdviceAnnotation标注的类都将匹配
public void before(){}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
@Inherited
public @interface AdviceAnnotation {
}
7)@target:和@within的功能类似,但必须要指定注解接口的保留策略为RUNTIME。
@Pointcut("@target(com.cjm.annotation.AdviceAnnotation)")
public void before(){}
8)@args:传入连接点的对象对应的Java类必须被@args指定的Annotation注解标注。
@Before("@args(com.cjm.annotation.AdviceAnnotation)")
public void beforeAdvide(JoinPoint point){
//处理逻辑
}
public class Person {
public void say(Address address){
//处理逻辑
}
}
@AdviceAnnotation
public class Address {
}
如果需要在Person类的say方法被调用时触发beforeAdvide通知,那么say方法的参数对应的Java类型Address类必须要被@AdviceAnnotation标注。
9)@annotation:匹配连接点被它参数指定的Annotation注解的方法。也就是说,所有被指定注解标注的方法都将匹配。
@Pointcut("@annotation(com.cjm.annotation.AdviceAnnotation)")
public void before(){}
public class Person {
@AdviceAnnotation
public void say(Address address){
//处理逻辑
}
}
Person类的say方法被@AdviceAnnotation标注,所以它匹配。
10)bean:通过受管Bean的名字来限定连接点所在的Bean。该关键词是Spring2.5新增的。
@Pointcut(“bean(person)”)
public void before(){}
id为person的受管Bean中的所有方法都将匹配。