本文已参与「新人创作礼」活动,一起开启掘金创作之路。 AOP面向切面编程,增强方法功能。
举例说明:
比如你要给你几个方法都增加一个打印日志的功能,传统办法是每个方法内都增加打印日志的代码,这样的问题是增加了代码的复杂度,aop思想就是可以使用代理的方式,通过映射这些方法,就可以使用一次代码给每个方法增加 打印日志的功能。
切面实现方式都是基于代理的方式。
一。java jdk实现AOp编程实例
我的目的:要给MyCalculator所有接口方法都打印日志。 切面(共同之处):都打印日志。
1.接口MyCalculator.interface
package com.chuan.method;
public interface MyCalculator {
int add(int a ,int b);
int sub(int a ,int b);
}
2.接口实现类MyCalculator.java
package com.chuan.Impl;
import com.chuan.method.MyCalculator;
/**
* @Author 张川
* @博客 zhangchuan.blog.csdn.net
* @Date 2020-09-11-10:29
*/
/**
*
* 实现MyCalculator接口
*/
public class MyCalculatorImpl implements MyCalculator {
//实现add方法
public int add(int a, int b) {
return a+b;
}
//实现sub方法。
public int sub(int a, int b) {
return a-b;
}
}
3.代理类 MyCalculatorProxy.java
package com.chuan.Agent;
import com.chuan.Impl.MyCalculatorImpl;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @Author 张川
* @博客 zhangchuan.blog.csdn.net
* @Date 2020-09-11-10:31
*/
public class MyCalculatorProxy {
//获得一个代理实例 参数是MyCalculator接口
public static Object getInstance(final MyCalculatorImpl myCalculator){
/**
*
* newProxyInstance参数1 是代理实例映射,第二个是接口映射,第三个是动态代理具体方法。
*/
return Proxy.newProxyInstance (MyCalculatorProxy.class.getClassLoader (), myCalculator.getClass ().getInterfaces (), new InvocationHandler () {
/**
* @param proxy 代理对象
* @param method 代理方法 这里就是myCalculator实现的接口方法add
* @param args 代理方法的参数。 指的就是add方法参数 int a, int b
* @return 返回值就是方法
* @throws Throwable
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object invoke = null;
System.out.println (method.getName ()+"开始执行方法");
invoke = method.invoke (myCalculator, args); //调用myCalculator里面的接口方法,指向Mycalculator所有实现的接口方法
System.out.println (method.getName ()+"方法执行完了");
return invoke;
}
});
}
}
//我们可以看到就用了一次代码,就实现了对所有接口方法增加打印日志功能。
4.main方法调用
package com.chuan.Main;
import com.chuan.Agent.MyCalculatorProxy;
import com.chuan.Impl.MyCalculatorImpl;
import com.chuan.method.MyCalculator;
/**
* @Author 张川
* @博客 zhangchuan.blog.csdn.net
* @Date 2020-09-11-10:47
*/
public class Main {
public static void main(String[] args)
MyCalculatorImpl myCalculator = new MyCalculatorImpl ();
//使用代理MyCalculatorProxy调用
MyCalculator calculator = ((MyCalculator) MyCalculatorProxy.getInstance (myCalculator));
int add = calculator.add (3, 4);
System.out.println ("add = " + add);
int sub = calculator.sub (5, 4);
System.out.println ("sub = " + sub);
}
}
- 代码结果 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6103JYnS-1607788957397)(i.niupic.com/images/2020…)]
二。spring里面的AOP编程
AOP思想:使用不侵入的方式,给一些方法增加你想要增加的功能。
切面:里面存放了很多切点
切点:就是你想要增强的的方法
实现方式:
- spring xml配置方式
- 注解的方式
AOP里面的五种通知。
- 前置通知
- 后置通知
- 异常通知
- 返回通知
- 环绕通知
最常见的是要给一些方法中都增加通知,那么就有以上通知类型。 其中 环绕通知是 前置通知 和后置通知集成。
使用到的注解:
- Aspect 标识这是切面
- @Pointcut("execution(* com.chuan.method.CalculatorImpl.add(..)))") 标识切点
- @AfterReturning @Before (前置通知) @After(后置通知) @AfterThrowing(异常通知) 几种通知使用的注解
spring aop使用到的包:
<!--aop切面编程使用的两个包-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.5</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
我们创建一个接口Calculator:
package com.chuan.method;
public interface Calculator {
void add(int a ,int b);
int min(int a, int b);
}
实现接口CalculatorImpl:
package com.chuan.method;
import org.springframework.stereotype.Component;
/**
* @Author 张川
* @博客 zhangchuan.blog.csdn.net
* @Date 2020-09-11-21:13
*/
@Component
//这是一个组件
public class CalculatorImpl implements Calculator {
public void add(int a, int b) {
System.out.println (a+"+"+b+"="+(a+b));
}
public int min(int a, int b) {
System.out.println (a+"-"+b+"="+(a-b));
return (a-b);
}
}
创建一个切面类:
package com.chuan.method;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* @Author 张川
* @博客 zhangchuan.blog.csdn.net
* @Date 2020-09-12-14:42
*/
/**
*
* Aspect这是一个切面
*/
@Component
@Aspect
public class LogAspect {
/**
* 这是自定义的切点pointcut, execution是自定义规则拦截,也就是符合这些规则的方法 将增加这些通知
*/
@Pointcut("execution(* com.chuan.method.CalculatorImpl.add(..)))") //*是 任意
public void pointcut(){
}
/**
* 前置通知,满足切点pointcut就会通知,在方法执行前通知
* @param joinPoint 指向满足切点规则的方法对象
*/
@Before ("pointcut())")
public void before(JoinPoint joinPoint){
String name = joinPoint.getSignature ().getName ();
System.out.println (name+"方法开始执行了。。。");
}
/**
* 满足切点是pointcut的,就会有后置通知
* 他在方法执行完之后执行
* @param joinPoint
*/
@After (value = "pointcut()")
public void after(JoinPoint joinPoint){
String name = joinPoint.getSignature ().getName ();
System.out.println (name+"方法执行完了。。。");
}
@AfterReturning(value = "pointcut()",returning ="r" )
//返回通知,returning是返回的参数名称
public void failException(JoinPoint joinPoint,Object r){
String name = joinPoint.getSignature ().getName ();
System.out.println (name+"方法返回通知"+r);
}
/**
* 异常通知,满足切点是pointcut的就会有异常通知
*/
@AfterThrowing(value = "pointcut()",throwing = "e")
public void Throwing(JoinPoint joinPoint,Exception e){
String name = joinPoint.getSignature ().getName ();
System.out.println (name+"方法异常通知:"+e.getMessage ());
}
}
切面里面通常有5种通知,我们自定义切点,通过切点,给需要的方法,增加这些通知。
JavaConfig配置文件
IOC就是Spring的一个容器,将一些类通过注解@Configuration注入IOC容器中,成为一个个Bean,需要使用的时候直接使用:AnnotationConfigApplicationContext加载JavaCOnfig配置文件,就可以获取Bean了,这样有利于降低耦合度。
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext (JavaConfig.class);
Calculator calculatorImpl = (Calculator ) ctx.getBean ("CalculatorImpl",Calculator.class);
JavaConfig.class:
package com.chuan.method;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
/**
* @Author 张川
* @博客 zhangchuan.blog.csdn.net
* @Date 2020-09-12-13:55
*/
//Configuration 使得可以注入IOC
@Configuration
@EnableAspectJAutoProxy //很关键 要开启切面自动代理
public class JavaConfig {
//这是一个Bean 名称是Calculator
@Bean("CalculatorImpl")
Calculator calculatorImpl(){
return new CalculatorImpl ();
}
}
Main文件测试:
package com.chuan.method;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
* @Author 张川
* @博客 zhangchuan.blog.csdn.net
* @Date 2020-09-11-21:15
*/
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext (JavaConfig.class);
Calculator calculatorImpl = (Calculator ) ctx.getBean ("CalculatorImpl",Calculator.class);
calculatorImpl.add (3,4);
calculatorImpl.min (3,4);
}
}
总的来说思想方法:
创建一个切面,自定义切点,定义规则 需要在哪些方法 切入通知。使用到的注解
-
@Aspect,
-
@Pointcut
-
@Before
-
@After
-
@AfterThrowing ,
-
@AfterReturning ,
-
@Around