1 Spring AOP入门代码
AOP是一种思想,很多公司都实现了AOP。
Spring框架使用AOP
Spring框架自己实现的AOP:
7个基础包+aop联盟包(组织,定制规范,接口,门面)+aop.jar(Spring针对AOP的实现)
引入第三方jar包,使用ApectJ实现,Spring推荐使用
7个基础包+aop联盟包(组织,定制规范,接口,门面)+aop.jar(Spring针对AOP的实现)+aspectj.jar(ApectJ针对AOP的实现)+spring-aspectj.jar(Spring对ApectJ的整合包)
使用Aop暂时所需jar包(日志包导入之后,可以自行配置日志文件)
1 AOP概述:
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP(面向对象编程)的延续
oop是从静态角度考虑程序结构,AOP是从动态的角度考虑程序运行过程.
AOP底层是采用动态代理模式实现. 采用了一下两种动态代理
1 :JDK的动态代理(增强的目标类必须实现一个接口)
2 :CGLIB的动态代理(没有接口的类也可以增强)
面向切面编程就是将交叉业务逻辑(系统级服务,系统级业务逻辑)封装成切面,利用AOP容器的功能将切面织入到主业务逻辑中. 所谓交叉业务逻辑是指:通用的,与主业务逻辑无关的代码(如:安全检查,日志记录,性能统计,事务处理,异常处理 等等)
2 AOP编程术语
1 :切面(Aspect) :交叉业务逻辑(对主业务逻辑的增强)
事务处理,日志处理可以理解为切面 常用的切面有通知和顾问
2 :织入(Weaving) :将切面代码插入到目标对象的过程
3 :连接点(JoinPoint) :可以被切面织入的方法(业务接口中的方法均为连接点)
4 :切入点(Pointcut) :切面具体织入的方法(被final修饰的方法不能作为连接点和切入点)
5 :目标对象(Target) :将要被增强的对象(包含主业务逻辑的类的对象)
6 :通知(Advice) :是切面的一种实现,可以完成简单的织入功能(织入功能就是在这里完成),通知定义了增强代码切入到目标代码的时间点(是目标方法执行之前执行还是之后执行).通知类型不同,切入时间不同,切入点定义切入的位置,通知定义切入的时间
7 :顾问(Advisor) :是切面的另一种实现,是将通知包装为更加复杂切面的装配器,能够将通知以更为复杂的方式织入到目标对象中
3 AspectJ 的切入点表达式
AspectJ 表达式的原型是:
execution ( [modifiers-pattern] 访问权限类型
ret-type-pattern 返回值类型
[declaring-type-pattern] 全限定性类名
name-pattern(param-pattern) 方法名(参数名)
[throws-pattern] 抛出异常类型
execution( 五个部分,两个不能省略 )
定义方法声明 访问修饰符 返回值类型 方法名(参数) throws 异常{ //方法体 }
切入点表达式要匹配的对象就是目标方法的方法名。所以,execution 表达式中明显就是方法的签名。注意,表达式中加[ ]的部分表示可省略部分,各部分间用空格分开。在其中可以使用以下符号:
表达式举例
execution(* *(..)) 任意方法
execution(public * *(..)) 指定切入点为:任意公共方法。
execution(* set*(..)) 指定切入点为:任何一个以“set”开始的方法。
// 任意返回值 包名.任意类.任何方法(任意参数)
execution(* com.xyz.service.*.*(..))
指定切入点为:定义在 com.xyz.service 包里的任意类的任意方法。
//访问修饰符省略,任意
//返回值类型*,任意
execution(* com.xyz.service..*.*(..))
//定义在 com.xyz.service 包及其子包的任意类的任意方法。
execution(* com.xyz.service..*.*(..))
指定切入点为:定义在 service 包或者子包里的任意类的任意方法。“..”出现在类名中时,后
面必须跟“*”,表示包、子包下的所有类。
//execution(访问修饰符任意 第一层包名任意.service.类名任意.方法名任意(..))
execution(* *.service.*.*(..))
指定只有一级包下的 serivce 子包下所有类(接口)中所有方法为切入点
execution(* *..service.*.*(..))
指定所有包下的 serivce 子包下所有类(接口)中所有方法为切入点
execution(* *.ISomeService.*(..))
指定只有一级包下的 ISomeSerivce 接口中所有方法为切入点
execution(* *..ISomeService.*(..))
指定所有包下的 ISomeSerivce 接口中所有方法为切入点
execution(* com.xyz.service.IAccountService.*(..))
指定切入点为: IAccountService 接口中的任意方法。
execution(* com.xyz.service.IAccountService+.*(..))
指定切入点为: IAccountService 若为接口,则为接口中的任意方法及其所有实现类中的任
意方法;若为类,则为该类及其子类中的任意方法。
execution(* joke(String,int)))
指定切入点为:所有的 joke(String,int)方法,且 joke()方法的第一个参数是 String,第二个参
数是 int。如果方法中的参数类型是 java.lang 包下的类,可以直接使用类名,否则必须使用
全限定类名,如 joke( java.util.List, int)。
execution(* joke(String,*)))
指定切入点为:所有的 joke()方法,该方法第一个参数为 String,第二个参数可以是任意类
型,如 joke(String s1,String s2)和 joke(String s1,double d2)都是,但 joke(String s1,double d2,String
s3)不是。
execution(* joke(String,..)))
指定切入点为:所有的 joke()方法,该方法第 一个参数为 String,后面可以有任意个参数且
参数类型不限,如 joke(String s1)、joke(String s1,String s2)和 joke(String s1,double d2,String s3)
都是。
execution(* joke(Object))
指定切入点为:所有的 joke()方法,方法拥有一个参数,且参数是 Object 类型。 joke(Object
ob)是,但,joke(String s)与 joke(User u)均不是。
execution(* joke(Object+)))
指定切入点为:所有的 joke()方法,方法拥有一个参数,且参数是 Object 类型或该类的子类。不仅 joke(Object ob)是,joke(String s)和 joke(User u)也是。
2 增强一个类功能可以选则:
1 :继承
增强的对象不能变,增强的功能可以变
class Coffe{void test(){}}
class MilkCoffe extends Coffe { void Coffe (){}}
class SugarCoffe extends Coffe { void test(){}}
2: 装饰者模式
增强的对象可以变,增强的功能不能变
3: 动态代理
增强的对象可以变,增强的功能也可以变
手写简单动态代理
1 接口代码:
package com.woniu.dongtai;
//接口
public interface DynamicAgent {
void res();
String reString();
void des();
}
2 实现类代码:
package com.woniu.dongtai;
//实现类
public class DynamicAgentimp implements DynamicAgent{
@Override
public void res() {
System.out.println("这是res");
}
@Override
public String reString() {
System.out.println("这是reString");
return "asdqwe";
}
@Override
public void des() {
System.out.println("这是des");
}
}
3 具体实现(未加条件)
package com.woniu.dongtai;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class MyTest01 {
public static void main(String[] args) {
//目标对象
DynamicAgent da=new DynamicAgentimp();
//JDk自带动态代理
DynamicAgent dgt =(DynamicAgent) Proxy.newProxyInstance(
da.getClass().getClassLoader(),//getClassLoader Loader 类加载器
da.getClass().getInterfaces(),//getInterfaces Interfaces 要增强对象的接口类型
new InvocationHandler() {//InvocationHandler h 执行扩展的增强功能,实现接口方法,实现增强
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/*
Object proxy:代理对象,目标对象增强后的对象
Method method:目标方法
Object[] args:目标方法的参数
*/
System.out.println("增强目标方法");
return method.invoke(da, args);
}
});
dgt.res();
//class com.sun.proxy.$Proxy0(增强代理后的类都带有$符号)
System.out.println(dgt.getClass());
//增强代理的对象就是接口的实现类的对象
System.out.println(dgt instanceof DynamicAgent);
String str="阿达";
System.out.println(str.getClass().getTypeName());
}
}
带简单条件判断
package com.woniu.dongtai;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Mytest02 {
public static void main(String[] args) {
DynamicAgent da=new DynamicAgentimp();
DynamicAgent dat=(DynamicAgent) Proxy.newProxyInstance(
da.getClass().getClassLoader(),//getClassLoader Loader 类加载器
da.getClass().getInterfaces(),//getInterfaces Interfaces 要增强对象的接口类型
new InvocationHandler() {//InvocationHandler h 执行扩展的增强功能,实现接口方法,实现增强
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/*
Object proxy:代理对象,目标对象增强后的对象
Method method:目标方法
Object[] args:目标方法的参数
*/
//method.getName().startsWith("re") 判断方法名是否包含"re"
if(method.getName().startsWith("re")) {
System.out.println("前置增强方法");
Object obj=method.invoke(da, args);
if(obj!=null&&
obj.getClass().getTypeName().equals("java.lang.String") //判断获取到的值得格式
) {
obj=obj.toString().toUpperCase();//将值变大写
}
System.out.println("后置增强方法");
return obj;
}else {
return method.invoke(da, args);
}
}
});
dat.res();
System.out.println("------------");
dat.des();
System.out.println("------------");
System.out.println(dat.reString());
}
}
3 AOP的五种通知(普通版)
接口:
package com.woniu.befor;
public interface DynamicAgent {
void res();
String reString();
void des();
}
实现类:
package com.woniu.befor;
public class DynamicAgentimp implements DynamicAgent{
@Override
public void res() {
System.out.println("这是res");
}
@Override
public String reString() {
System.out.println("这是reString");
return "asdqwe";
}
@Override
public void des() {
System.out.println("这是des");
}
}
测试代码 :
package com.woniu.befor;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Mytest {
@Test
public void test01(){
String path="com/woniu/befor/applocationContext.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(path);
DynamicAgent dy = (DynamicAgent) ac.getBean("dynamicAgent");
dy.des();
System.out.println("-------------");
System.out.println(dy.reString());
System.out.println("-------------");
dy.res();
}
}
主配置文件 :(使用AOP需要导入相应的约束)
<?xml version="1.0" encoding="UTF-8"?>
<!-- 这些所有可以在帮助文档中通过搜索 .xsd 复制而得
使用aop 需要导入
xmlns:aop="http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
-->
<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"
xmlns:context="http://www.springframework.org/schema/context"
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">
<!-- 注册目标对象 -->
<bean id="dynamicAgent" class="com.woniu.befor.DynamicAgentimp"></bean>
<!-- 注册切面对象 -->
<bean id="myAspectj" class="com.woniu.befor.MyAspectj"></bean>
<!-- aop织入 -->
<aop:aspectj-autoproxy />
</beans>
1 前置通知
package com.woniu.befor;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class MyAspectj {
//无参前置通知
@Before("execution(* *..DynamicAgent.de*(..))")
public void befor() {
System.out.println("前置通知");
}
//有参前置通知
@Before("execution(* *..DynamicAgent.de*(..))")
public void befor(JoinPoint join) {
System.out.println("前置通知 JoinPoint:"+join);
}
}
2 后置通知
package com.woniu.afterReturning;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class MyAspectj {
//无参后置通知
@AfterReturning("execution(* *..DynamicAgent.re*(..))")
public void after() {
System.out.println("后置通知");
}
//有参后置通知( 将获取到的值装为大写)
@AfterReturning(value="execution(* *..DynamicAgent.re*(..))",returning="obj")
public void after(Object obj) {
if(obj!=null&&obj.getClass().getTypeName().equals("java.lang.String")) {
obj=obj.toString().toUpperCase();
}
System.out.println("转大写 obj:"+obj);
}
}
3 环绕通知 :功能最强的,可以代替其他的所有通知
package com.woniu.around;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class MyAspectj {
//环绕通知(修改了获取到的值)
@Around("execution(* *..DynamicAgent.reString(..))")
public Object surround(ProceedingJoinPoint po) throws Throwable {
Object obj = po.proceed();
System.out.println("@Around:"+obj);
obj=obj.toString().toUpperCase();
System.out.println("环绕后:"+obj);
return obj;
}
}
4 异常通知
package com.woniu.exception;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class MyAspectj {
//异常通知:根据抛的异常类型进行匹配
@AfterThrowing("execution(* *..DynamicAgent.*(..))")
public void ex1() {
System.out.println("异常通知");
}
//异常通知
@AfterThrowing(value="execution(* *..DynamicAgent.*(..))" ,throwing="ex")
public void ex1(Exception ex) {
System.out.println("异常通知: Exception"+ex);
}
//抛的异常为 RuntimeException 时运行下方代码
@AfterThrowing(value="execution(* *..DynamicAgent.*(..))" ,throwing="ex")
public void ex1(RuntimeException ex) {
System.out.println("运行时异常通知: RuntimeException"+ex);
}
//异常通知
@AfterThrowing(value="execution(* *..DynamicAgent.*(..))" ,throwing="ex")
public void ex1(NullPointerException ex) {
System.out.println("空指针异常通知: NullPointerException"+ex);
}
}
5 最终通知 :不管有没有异常,最后都会执行
package com.woniu.exception;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class MyAspectj {}
//最终通知:不管有没有异常,最后都会执行
@After("execution(* *..DynamicAgent.*(..))")
public void after() {
System.out.println("最终通知");
}
}
4 AOP的五种通知(注解版)
接口
package com.woniu.annotation;
public interface DynamicAgent {
void res();
String reString();
void des();
}
实现类
package com.woniu.annotation;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class DynamicAgentimp implements DynamicAgent{
@Override
public void res() {
System.out.println("这是res");
}
@Override
public String reString() {
System.out.println("这是reString");
return "asdqwe";
}
@Override
public void des() {
System.out.println("这是des");
}
}
测试代码
package com.woniu.annotation;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Mytest {
@Test
public void test01(){
String path="com/woniu/annotation/applocationContext.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(path);
DynamicAgent dy = (DynamicAgent) ac.getBean("dynamicAgent");
dy.des();
System.out.println("-------------");
System.out.println(dy.reString());
System.out.println("-------------");
dy.res();
}
}
1 前置通知
切面
package com.woniu.annotation;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class MyAspectj {
//无参前置通知
public void before() {
System.out.println("前置通知");
}
//有参前置通知
public void before(JoinPoint join) {
System.out.println("前置通知 JoinPoint:"+join);
}
}
主配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!-- 这些所有可以在帮助文档中通过搜索 .xsd 复制而得
使用aop 需要导入
xmlns:aop="http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
-->
<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"
xmlns:context="http://www.springframework.org/schema/context"
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">
<!-- 注册目标对象 -->
<bean id="dynamicAgent" class="com.woniu.annotation.DynamicAgentimp"></bean>
<!-- 注册切面对象 -->
<bean id="myAspectj" class="com.woniu.annotation.MyAspectj"></bean>
<!--aop配置 -->
<aop:config>
<!-- 配置切入点 expression:加aspectJ表达式 id:给这个表达式取的名字-->
<!-- <aop:pointcut expression="execution(* *..DymaicAgent.res(..))" id="resPSointcut"/> -->
<aop:pointcut expression="execution(* *..DynamicAgent.reString(..))" id="reStringPSointcut"/>
<!-- 配置织入 就是加@Apectj注解 -->
<!--ref="myAspectj" 由上方注册切面对象的id所得 -->
<aop:aspect ref="myAspectj">
<!-- 方法一 : method:切入的方法名 pointcut:切入点的方法名(如果这样使用,就不用配置切入点)-->
<aop:before method="before" pointcut="execution(* *..DynamicAgent.res(..))"/>
<!-- 方法二: method:切入的方法名 pointcut-ref:上方配置切入点的id-->
<aop:before method="before" pointcut-ref="reStringPSointcut"/>
<aop:before method="before(org.aspectj.lang.JoinPoint)" pointcut-ref="reStringPSointcut"/>
</aop:aspect>
</aop:config>
</beans>