基于动态代理的,可以使用JDK,cglib两种代理方式
aop就是动态代理的规范化,把动态代理的实现步骤定义,让开发人员用统一的方式使用动态代理。
aop: Aspect Orient Programming
Aspect:切面,给目标类增加的功能,就是切面
特点:一般都是非业务方法,可以独立使用的;
Orient:面向
Programming:编程
怎么理解面向切面编程?
1.需要在分析项目功能时找出切面
2.合理的安排切面的执行时间,在目标方法之前还是之后
3.合理的安排切面执行的位置,在哪个类,哪个方法增加增强功能
1.切面:
表示增强的功能,完成某一个功能,非业务的,常见的有日志,事务、统计信息
2.JoinPoint:连接点,链接业务方法和切面的位置,某类中的业务方法
3.Pointcut:切入点,指多个连接点方法的集合,多个方法
4.目标对象:给哪个类的方法增加功能,这个类就叫做目标类
5.Advice:通知,表示切面功能执行的时间
一个切面有3个关键要素
1.切面的功能代码,作用
2.切面的执行位置,使用Pointcut表示切面执行的位置
3.切面的执行时机,使用Advice表示执行时间,在目标方法之前还是目标方法执行之后
public class MyUtil { //切面功能 public static void log(){ System.out.println("增加日志功能"); } public static void trans(){ System.out.println("增加事务处理"); } }
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object obj = null; String name = method.getName(); if ("some".equals(name)){ //joinPoint 多个点是Pointcut MyUtil.log(); obj = method.invoke(target,args); MyUtil.trans(); }else { obj = method.invoke(target,args); } return obj; }
aop的实现
aop是一个规范,是动态的一个规范化,一个标准
aop的技术实现框架:
spring:主要在事务处理时使用aop,很少使用,比较笨重
aspectJ:开源的,专门做aop的框架
1.使用xml配置文件
2.使用注解(一般使用注解)
学习aspectJ框架的使用:aspectJ的5个通知注解
1.切面的执行时间,在规范中叫做Advice(通知,增强)
1)@Before
2)@AfterReturning
3)@Around
4)@AfterThrowing
5)@After
2.表示切面执行的位置,使用的是切入点表达式。(*表示需要有的,?表示可选,各部分之间使用空格隔开)
execution(访问权限? 方法的返回值* 方法声明*(参数) 异常类型?)
* 0至多个任意字符
.. 用在方法参数中,表示任意多个参数;用在包名后表示当前包及其子包路径
+ 用在类名后,表示当前类及其子类;用在接口后,表示当前接口及其实现类
示例:execution(public * *(..))
解释:指定切入点为任意公共方法
execution(* set*(..))
解释:任何一个以set开始的方法
execution(* com.xyz.service.*.*(..))
解释:指定包下的所有类的方法(不包含子包)
execution(* com.xyz.service..*.*(..))
解释:指定包下的所有类的方法(包含子包)
aop的实现步骤总结
1.创建maven项目,加入spring和aspectJ依赖
2.创建接口和实现类
3.创建切面类,类上添加@Aspects注解,添加增加功能的方法,方法上标注执行的时机和执行的位置,例如@Before(value="excution(* *..impl.*.*(..))")
4.创建spring配置文件,声明目标对象和代理类对象,声明自动代理 <aop:aspectj-autoproxy/>
5.创建测试类,获取目标类对象,执行方法
实现步骤:
1.新建maven项目
2.加入依赖
spring
aspectJ
junit
3.创建目标类,接口和实现类
目的:给类中的方法增加功能
4.创建切面类
在类的上面加注解@Aspect
在类中定义方法,方法就是切面要执行的功能代码
在方法的上面加入aspectJ中的通知注解,例如@Before
需要指定切入点表达式execution()
5.创建spring配置文件,声明对象,把对象交给容器统一管理
使用注解或则使用配置文件的bean标签
1.声明目标对象
2.声明切面类对象
3.声明AspectJ框架中的自动代理生成器标签
自动代理生成器:用来完成代理对象的自动创建功能的
6.创建测试类
从spring容器中获取目标对象(实际就是代理对象),通过代理执行方法,实现aop的功能增强
示例:
public interface SomeService { void some(String name,Integer age); }
public class SomeServiceImpl implements SomeService{ @Override public void some(String name, Integer age) { System.out.println("some service 执行"); } }
@Aspect public class MyAspects { @Before(value = "execution(* *..impl.*.*(..))") public void exe(){ System.out.println("执行时间:"+new Date()); } }
<bean class="com.demo.service.impl.SomeServiceImpl" id="service"/> <bean class="com.demo.service.aspect.MyAspects" id="aspects"/> <aop:aspectj-autoproxy/>
public class MyTest { @Test public void test(){ String config = "ApplicationContext.xml"; ApplicationContext ac = new ClassPathXmlApplicationContext(config); SomeService service = (SomeService) ac.getBean("service"); service.some("lise",20); } }
参数joinPoint使用:
@Aspect public class MyAspects { @Before(value = "execution(* *..impl.*.*(..))") public void exe(JoinPoint joinPoint){ System.out.println("方法的签名(定义)="+joinPoint.getSignature()); System.out.println("方法的名称"+joinPoint.getSignature().getName()); //方法的实参 Object[] args = joinPoint.getArgs(); for (Object arg:args){ System.out.println(arg); } System.out.println("执行时间:"+new Date()); } }
后置通知:
public interface SomeService { void doSome(String name,Integer age); String doOther(String name,Integer age); }
public class SomeServiceImpl implements SomeService { @Override public void doSome(String name, Integer age) { System.out.println("dosome 方法执行成功"); } @Override public String doOther(String name, Integer age) { System.out.println("doother 方法执行成功"); return "abc"; } }
@Aspect public class MyAspect { @Before(value = "execution(* *..ba01.*impl.*(..))") public void some(){ System.out.println("执行时间:"+new Date()); } /** * @AfterReturning 后置通知 * value表示切入点表达式 * returning自定义的变量,表示目标方法的返回值,自定义变量名必须和通知方法的形参名一样 * * 特点: * 在目标方法执行之后执行 * 能够获取到目标方法的返回值,可以根据这个返回值做不同的处理功能 * 可以修改这个返回值 * 相当于:Object res = doOther(); * @param res */ @AfterReturning(value = "execution(* *..*Impl.doOther(..))",returning = "res") public void myAfterReturning(Object res){ //res是目标方法执行之后的返回值,根据返回值做切面的处理功能 System.out.println("目标方法执行之后获取到的返回值是:"+res); if (res.equals("abc")){ System.out.println("操作成功 ,提交事务"); }else { System.out.println("发生异常 ,事务回滚"); } } }
<bean class="com.demo.ba01.SomeServiceImpl" id="service"/> <bean class="com.demo.ba01.MyAspect" id="aspect"/> <aop:aspectj-autoproxy/>
public class MyTest { @Test public void test01(){ String config = "ApplicationContext.xml"; ApplicationContext ac = new ClassPathXmlApplicationContext(config); SomeService service = (SomeService) ac.getBean("service"); //service.doSome("lisi",20); //System.out.println("===================="); String other = service.doOther("mike", 21); } }
思考:后置通知是在目标方法执行之后执行,如果目标方法返回值是一个引用类型,在后置通知方法中对引用类型的属性值就行修改,会不会影响最后的调用结果?