AOP(Aspect Orient Programming),面向切面编程
切面:公共的,通用的,重复的功能成为切面,面向切面编程就是将切面提取出来,单独开发,在需要的时候织入。
手写AOP框架
业务:图书购买业务
切面:事物
1)第一个版本:业务和切面紧耦合在一起,没有拆分
2)第二个版本:使用子类代理的方式拆分业务和切面
3)第三个版本:使用静态代理拆分业务和切面,业务和业务接口已分开,此时切面紧耦合在业务中
4)第四个版本:使用静态代理拆分业务和业务接口,切面和切面接口
5)第五个版本:使用动态代理完成第四个版本的优化
第一个版本:业务和切面紧耦合在一起,没有拆分
1 2 3 4 5 6 7 8 9 10 11
| public class BookServiceImpl{ public void buy(){ try{ System.out.println("事务开启。。。。"); System.out.println("图书购买业务功能实现。。。。"); System.out.println("事务提交。。。。"); }catch(Exception e){ System.out.println("事务回滚。。。。"); } } }
|
第二个版本:使用子类代理的方式拆分业务和切面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public class BookServiceImpl{ public void buy(){ System.out.println("图书购买业务功能实现。。。。"); } }
public class SubBookServiceImpl extends BookServiceImpl{ @Override public void buy(){ try{ System.out.println("事务开启。。。。"); super.buy(); System.out.println("事务提交。。。。"); }catch(Exception e){ System.out.println("事务回滚。。。。"); } } }
|
第三个版本:使用静态代理拆分业务和切面,业务和业务接口已分开,此时切面紧耦合在业务中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| public interface Service{ void buy(); }
public class BookSeviceImpl implements Service{ @Override public void buy(){ System.out.println("图书购买业务功能实现。。。。"); } }
public class ProductServiceImpl implements Service{ @Override public void buy(){ System.out.println("商品购买业务功能实现。。。。"); } }
public class Agent implements Service{ public Service target; public Agent(Service target){ this.target = target; } @Override public void buy(){ try{ System.out.println("事务开启。。。。"); super.buy(); System.out.println("事务提交。。。。"); }catch(Exception e){ System.out.println("事务回滚。。。。"); } } }
# Test public class Mytest{ @Test public void test(){ Service agent = new Agent(new BookSeviceImpl()); agent.buy(); Service agent = new Agent(new ProductServiceImpl()); agent.buy(); } }
|
第四个版本:使用静态代理拆分业务和业务接口,切面和切面接口
上个版本只实现了事务切面,如果要换成日志切面实现呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
| ###########################业务接口##################### public interface Service{ void buy(); }
public class BookSeviceImpl implements Service{ @Override public void buy(){ System.out.println("图书购买业务功能实现。。。。"); } }
public class ProductServiceImpl implements Service{ @Override public void buy(){ System.out.println("商品购买业务功能实现。。。。"); } }
#####################切面接口AOP######################## public interface AOP{ default void before(){}; default void after(){}; default void exception(){}; }
public class LogAop implements AOP{ @Override public void before(){ System.out.println("前置日志输出。。。。"); } }
public class TransAop implements AOP{ @Override public void before(){ System.out.println("事务开启。。。。"); }
@Override public void after(){ System.out.println("事务提交。。。。"); } @Override public void exception(){ System.out.println("事务回滚。。。。"); } }
#####################代理对象Agent######################## public class Agent implements Service{ Service target; Aop aop; public Agent(Service target, AOP aop){ this.target = target; this.aop = aop; } @Override public void buy(){ try{ aop.before(); target.buy(); aop.after(); }catch(Exception e){ aop.exception(); } } }
## test public class Mytest{ @Test public void test{ Serive agent = new Agent(new BookServiceImpl(),new LogAop()); agent.buy(); Serive agent = new Agent(new ProductServiceImpl(),new TransAop()); agent.buy(); } }
## 切入多个切面 ## test public class Mytest{ @Test public void test{ Serive agent = new Agent(new BookServiceImpl(),new LogAop()); agent.buy(); Serive agent1 = new Agent(agent,new TransAop()); agent1.buy(); } }
|
第五个版本:使用动态代理完成第四个版本的优化
业务功能的增加
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
| ###########################业务接口##################### public interface Service{ void buy(); default String show(int age){return null}; }
public class BookSeviceImpl implements Service{ @Override public void buy(){ System.out.println("图书购买业务功能实现。。。。"); } }
public class ProductServiceImpl implements Service{ @Override public void buy(){ System.out.println("商品购买业务功能实现。。。。"); } @Override public String show(int age){ System.out.println("这是show()方法调用。。。。"); return "abcd"; } }
#####################切面接口AOP######################## public interface AOP{ default void before(){}; default void after(){}; default void exception(){}; }
public class LogAop implements AOP{ @Override public void before(){ System.out.println("前置日志输出。。。。"); } }
public class TransAop implements AOP{ @Override public void before(){ System.out.println("事务开启。。。。"); }
@Override public void after(){ System.out.println("事务提交。。。。"); } @Override public void exception(){ System.out.println("事务回滚。。。。"); } }
####################动态代理#################### public class ProxyFactory{ public static Object getAgent(Service target,Aop aop){ return Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler(){ @Override public Object invoke( //生成的代理对象 Object proxy //正在被调用的目标方法buy(),show() ,Method method ,Object[] args) throws Throwable{ aop.before(); Object obj = method.invoke(target,args); aop.after(); return obj; } } ) } }
#test public class Mytest{ @Test public void test(){ Service agent = ProxyFactory.getAgent(new BookServiceImpl(),new TransAop()); agent.buy(); String s = agent.show(22); } }
|
AspectJ框架
AspectJ中常用的通知有几种类型:
0)@Aspect:作用是把当前类标识为一个切面供容器读取
1)前置通知@Before
2)后置通知@AfterReturning
3)环绕通知@Around
4)最终通知@After
5)定义切入点@Pointcut
使用@Pointcut注解,创建一个空方法,此方法的名称就是别名
@Pointcut:Pointcut是植入Advice的触发条件。每个Pointcut的定义包括2部分,一是表达式,二是方法签名。方法签名必须是 public及void型。可以将Pointcut中的方法看作是一个被Advice引用的助记符,因为表达式不直观,因此我们可以通过方法签名的方式为 此表达式命名。因此Pointcut中的方法只需要方法签名,而不需要在方法体内编写实际代码。
使用pointcut代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
| public void SomeServiceImpl implements SomeService{ @Override public String doSome(String name,int age){ System.out.println("doSome业务方法执行......."+name); return "abcd"; } }
@Aspect public class MyAspect{
@Before(value = "execution(public String com.yuting.SomeSeriveImpl.dosome(String,int))") # @Around(value="myCut()") public void myBefore(){ System.out.println("切面方法中的前置通知"); }
@After(value="execution(* com.yuting.s01.*.*(..))") # @Around(value="myCut()") public void myAfter(){ System.out.println("切面方法中的最终通知"); } @AfterReturning(value="execution(* com.yuting.s01.*.*(..))",returning="obj") # @Around(value="myCut()") public void myAfterReturning(Object obj){ System.out.println("后置通知的功能"); }
@Around(value="execution(* com.yuting.s01.*.*(...))") # @Around(value="myCut()") public Object myAround(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("环绕通知中的前置通知功能"); Object obj = pjp.proceed(pjp.getArgs()); System.out.println("环绕通知中的后置通知功能"); return obj.toString().toUpperCase(); # 改变了目标方法的返回值 } }
@PointCut(value = "execution(* com.yuting.s01.*.*(...))") public void myCut(){
}
注解配置 applicationContext.xml <!--基于注解的访问要添加包扫描--> <context:component-scan base-package="com.yuting"></context:component-scan> <!--绑定--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
|
使用annotation代码:
@annotation: 元注解的作用就是负责注解其他注解。Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它 annotation类型作说明。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 1.@Target 说明了Annotation所修饰的对象范围:Annotation可被用于 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。在Annotation类型的声明中使用了target可更加明晰其修饰的目标
2.@Retention @Retention定义了该Annotation被保留的时间长短==:某些Annotation仅出现在源代码中,而被编译器丢弃;而另一些却被编译在class文件中;编译在class文件中的Annotation可能会被虚拟机忽略,而另一些在class被装载时将被读取(请注意并不影响class的执行,因为Annotation与class在使用上是被分离的)。使用这个meta-Annotation可以对 Annotation的“生命周期”限制。
3.@Documented @Documented:用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。Documented是一个标记注解,没有成员 4.@Inherited @Inherited :元注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。 5.自定义注解 使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口,由编译程序自动完成其他细节。在定义注解时,不能继承其他的注解或接口。@interface用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum)。可以通过default来声明参数的默认值。
定义注解格式: public @interface 注解名 {定义体}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
| package com.trip.demo; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.METHOD }) public @interface SMSAndMailSender { String value() default ""; String smsContent() default ""; String mailContent() default ""; boolean isActive() default true; String subject() default ""; }
@Aspect @Component("smsAndMailSenderMonitor") public class SMSAndMailSenderMonitor { private Logger logger = LoggerFactory.getLogger(SMSAndMailSenderMonitor.class);
@AfterReturning(value="@annotation(com.trip.demo.SMSAndMailSender)", returning="result") public void afterReturning(JoinPoint joinPoint , Object result//注解标注的方法返回值) { MethodSignature ms = (MethodSignature) joinPoint.getSignature(); Method method = ms.getMethod(); boolean active = method.getAnnotation(SMSAndMailSender.class).isActive(); if (!active) { return; } String smsContent = method.getAnnotation(SMSAndMailSender.class).smsContent(); String mailContent = method.getAnnotation(SMSAndMailSender.class).mailContent(); String subject = method.getAnnotation(SMSAndMailSender.class).subject(); }
@AfterThrowing(value="@annotation(com.trip.order.monitor.SMSAndMailSender)",throwing = "ex") public void afterThrowing(JoinPoint joinPoint, Throwable ex//注解标注的方法抛出的异常) { MethodSignature ms = (MethodSignature) joinPoint.getSignature(); Method method = ms.getMethod(); String subject = method.getAnnotation(SMSAndMailSender.class).subject(); } }
@Service("testService ") public class TestService { @Override @SMSAndMailSender(smsContent = "MODEL_SUBMIT_SMS", mailContent = "MODEL_SUPPLIER_EMAIL", subject = "MODEL_SUBJECT_EMAIL") public String test(String param) { return "success"; } }
|
记得在配置文件中加上:
<aop:aspectj-autoproxy proxy-target-class="true"/>