1.什么是面向切面编程
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率.
把业务核心代码和非核心业务代码分离,如果我们写的核心业务代码,需要非核心业务代码功能时,在不影响核心代码的前提下,可以添加进去
2.为什么使用面向切面编程
例子:
创建一个接口类
//加法
public Double add(Double a,Double b);
//减法
public Double sub(Double a,Double b);
//乘法
public Double cf(Double a,Double b);
//除法
public Double chufa(Double a,Double b);
实现接口
public class MethodsServiceImpl implements MethodsService{
@Override
public Double add(Double a, Double b) {
Double result=a+b;
return result;
}
@Override
public Double sub(Double a, Double b) {
Double result=a-b;
return result;
}
@Override
public Double cf(Double a, Double b) {
Double result=a*b;
return result;
}
@Override
public Double chufa(Double a, Double b) {
Double result=a/b;
return result;
}
}
创建测试类
public static void main(String[] args) {
//创建对象
MethodsService methodsService=new MethodsServiceImpl();
Double result = methodsService.add(10.0, 5.0);
System.out.println(result);
}
思考:如果我们要在实现类中加入日志,如果实现类中的方法有n多个,我们就要写n多个日志,或者我们已经写好了日志,想要修改,就要修改n多个日志
解决方案:
1.抽取方法---缺点:需要在n多个方法中进行调用
2.动态代理---实现方式有两种:[1]JDK原生动态代理 [2] cglib动态代理
3.aop面向切面编程--aop的底层代理就是基于动态代理
1.动态代理
1.JDK原生动态代理--缺点:基于接口
1.创建一个工厂类
public class ProxyFactory {
//声明被代理的对象
private Object target;
//实现被代理的对象
public ProxyFactory(Object target) {
this.target = target;
}
//获取被代理的对象
public Object getProxy(){
//jdk中提供一个代理类Froxy,我们可以通过该类,获取代理的对象
//newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
//ClassLoader loader--被代理对象需要的类加载器
ClassLoader loader=target.getClass().getClassLoader();
//Class<?>[] interfaces--被代理对象实现的接口
Class<?>[] interfaces=target.getClass().getInterfaces();
// InvocationHandler h--被代理对象的方法值
InvocationHandler h=new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//method.getName()获取方法名
//Arrays.asList(args) 参数
System.out.println("before "+method.getName()+" method begin with"+ Arrays.asList(args));
//method:代理对象要代理的方法--指得测试类中的add方法
//invoke:回调该方法执行--指的是target
//ages:方法需要的参数
Object result = method.invoke(target, args);
return result;
}
};
Object o = Proxy.newProxyInstance(loader, interfaces, h);
return o;
}
}
2.测试
public static void main(String[] args) {
//创建被代理对象
MethodsServiceImpl target=new MethodsServiceImpl();
//创建代理工厂--有参,需要代理对象
ProxyFactory factory=new ProxyFactory(target);
//获取代理对象
MethodsService proxy = (MethodsService) factory.getProxy();
//执行相应的业务代码
Double add = proxy.add(20.0, 10.0);
System.out.println(add);
}
2.cglib动态代理
1.引入相关的依赖
<!--cglib-->
<dependencies>
<dependency>
<groupId>repMaven.cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.5</version>
</dependency>
2.创建一个代理工厂类 并实现接口
public class ProxyFactory implements MethodInterceptor {
//声明被代理的对象
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
//实现被代理的对象
public Object gettarget(){
Enhancer enhancer=new Enhancer();
//1.指定代理对象的父类
enhancer.setSuperclass(target.getClass());
//2.指定该类的回调类
enhancer.setCallback(this);
//3.创建代理对象
return enhancer.create();
}
//执行代理方法是,触发的方法
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("=============");
Object invoke = method.invoke(target, args);
return invoke;
}
}
3.测试
public static void main(String[] args) {
//创建被代理对象
MethodsServiceImpl target=new MethodsServiceImpl();
//创建代理工厂--有参,需要代理对象
ProxyFactory factory=new ProxyFactory(target);
//获取代理对象
MethodsServiceImpl gettarget = (MethodsServiceImpl) factory.gettarget();
Double add1 = gettarget.add(20.0, 10.0);
//执行相应的业务代码
}
3.使用spring-aop的功能
1.引入相关的依赖
<dependency>
<groupId>repMaven.org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version> 5.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>repMaven.org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version> 5.2.9.RELEASE</version>
</dependency>
2.配置spring文件
<!--包扫描-->
<comtext:component-scan base-package="aop"/>
<!--开启切面注解驱动 让spring 可以熟悉切面的注解-->
<aop:aspectj-autoproxy/>
3.创建一个类
@Component //表示交于spring容器帮你创建和管理该类的对象
@Aspect //表示该对象为切面
public class LogAspet {
//Before:表示在execution表达式之前执行该方法
//execution:表示那些方法
@Before(value ="execution(public double aop.MethodsServiceImpl.add(double,double))")
public void ha(){
System.out.println("开始============");
}
}
4.测试
//读取spring配置文件
ApplicationContext app=new ClassPathXmlApplicationContext("spring.xml");
//从spring容器获取指定的对象
MethodsService methodsServiceImpl = (MethodsService) app.getBean("methodsServiceImpl");
System.out.println(methodsServiceImpl.sub(10.0, 5.0));
如果要有多个方法添加切面统治者,如果都是用||链接,比较麻烦,我们可以使用通配符来解决
execution(* com.grt.aop.*.*(..))
//第一个*代表任意修饰符和返回类型
//第二个* 代表该包下的任意类
//第三个* 代表任意类下的任意方法
// .. 代表任意参数类型
aop中常见的术语
1.切面
横切关注点(跨越应用成语多个模块的功能),被模块化的特殊对象
2.连接点
在那些方法上可以通知
3.切点
真实在那些方法上执行的通知
4.通知
表示什么时候完成切面内容。前置通知,后置通知,返回通知,异常通知
aop中常用的注解
1.@After
@After(value="execution(* com.grt.aop.*.*(..))")
public void ha(){}
//After 表示在最后执行,不管方法有没有异常,都会执行--相当于try cath finally中的finally
2.@AfterReturning
@AfterReturning(value="execution(* com.grt.aop.*.*(..))")
//AfterReturning--碰到return关键字后才会被执行
3.@AfterThrowing
@AfterThrowing(value="execution(* com.grt.aop.*.*(..))")
//AfterThrowing--碰到异常的时候才会执行