SpringAOP
AOP:Aspect Oriented Programming,意思为面向切面编程
面向切面编程:对于不同的模块,在具有相同共性的情况下,由切面的增强来负责统一处理;其本质就是动态代理
相对于OOP(面向对象)来说,AOP对于过程的管理更加的精细,能够进一步来完成解耦工作,在程序运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想,将不同的方法的同一个位置抽象成一个切面对象
AOP的特点:
-
降低模块之间的耦合度。
-
使系统更容易扩展。
-
更好的代码复用。
-
非业务代码更加集中,不分散,便于统一管理。
-
业务代码更加简洁纯粹,不参杂其他代码的影响。
AOP相关术语:
- 增强(Advice)
- 切入点(Pointcut)
- 连接点(Joinpoint)
- 切面(Aspect)
- 代理(Proxy)
- 目标对象(Target)
- 织入(Weaving)
动态代理
场景:苹果手机与苹果电脑,华为手机与华为电脑的销售
接口:
package com.m.staticproxy;
public interface Phone {
public String salePhone();
}
package com.m.staticproxy;
public interface Computer {
public String saleComputer();
}
实现类:
package com.m.staticproxy.impl;
import com.m.staticproxy.Computer;
import com.m.staticproxy.Phone;
public class Apple implements Phone,Computer {
@Override
public String salePhone() {
return "销售Apple手机";
}
@Override
public String saleComputer() {
return "销售Apple电脑";
}
}
package com.m.staticproxy.impl;
import com.m.staticproxy.Computer;
import com.m.staticproxy.Phone;
public class HuaWei implements Phone,Computer {
@Override
public String salePhone() {
return "销售HuaWei手机";
}
@Override
public String saleComputer() {
return "销售HuaWei电脑";
}
}
MyInvocationHandler类:
package com.m.staticproxy.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
public class MyInvocationHandler implements InvocationHandler {
private Object proObject;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Object bind(Object object) {
this.proObject = object;
return Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(this.proObject);
System.out.println(name+"卖的好,这个"+name+"物美价廉,赶快来抢购吧!");
System.out.println(result);
System.out.println(method.getName()+"方法的结果是"+result);
return result;
}
}
测试类:
package com.m.staticproxy;
import com.m.staticproxy.Phone;
import com.m.staticproxy.impl.Apple;
import com.m.staticproxy.impl.HuaWei;
import com.m.staticproxy.proxy.MyInvocationHandler;
import com.m.staticproxy.proxy.PhoneProxy;
public class Test {
public static void main(String[] args) {
Phone phone1 = new Apple();
Phone phone2 = new HuaWei();
Computer computer1 = new Apple();
Computer computer2 = new HuaWei();
MyInvocationHandler myInvocationHandler = new MyInvocationHandler();
myInvocationHandler.setName(phone1.salePhone().substring(2));
phone1 = (Phone) myInvocationHandler.bind(phone1);
phone1.salePhone();
myInvocationHandler.setName(phone2.salePhone().substring(2));
phone2 = (Phone) myInvocationHandler.bind(phone2);
phone2.salePhone();
myInvocationHandler.setName(computer1.saleComputer().substring(2));
computer1 = (Computer) myInvocationHandler.bind(computer1);
computer1.saleComputer();
myInvocationHandler.setName(computer2.saleComputer().substring(2));
computer2 = (Computer) myInvocationHandler.bind(computer2);
computer2.saleComputer();
}
}
用静态代理得写手机代理类和电脑代理类,动态代理用的是多态思想,一个类搞定。
Spring-aop
在Spring框架中,我们不需要创建动态代理类,只需要创建一个切面类,由该切面类产生的对象就是切面对象,可以将非业务代码写入到切面对象中,再切入到业务方法中,Spring框架底层会自动根据切面类以及目标类生成一个代理对象。
1.配置pom环境
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
2.spring-aop.xml
<?xml version="1.0" encoding="UTF-8"?>
<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"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd
">
<!-- 自动扫码 -->
<context:component-scan base-package="com.m"></context:component-scan>
<!-- 使Aspect注解生效,为委托类自动生成代理对象 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
目标类需要添加@Component注解
package com.m.action_proxy.impl;
import com.m.action_proxy.Cal;
import org.springframework.stereotype.Component;
@Component
public class CalImpl implements Cal {
@Override
public int add(int num1, int num2) {
return num1 + num2;
}
@Override
public int sub(int num1, int num2) {
return num1 - num2;
}
@Override
public int mul(int num1, int num2) {
return num1 * num2;
}
@Override
public int div(int num1, int num2) {
return num1 / num2;
}
}
LoggerAspect类定义处添加了两个注解:
- @Aspect:表示该类是切面类。
- @Component:将该类注入到IoC容器中。
3.创建LoggerAspect类
package com.m.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;
@Aspect
@Component
public class LoggerAspect {
@Before(value = "execution(public int com.m.action_proxy.impl.CalImpl.*(..))")
public void before(JoinPoint joinPoint){
//获取方法名
String name = joinPoint.getSignature().getName();
//获取参数列表
String args = Arrays.toString(joinPoint.getArgs());
System.out.println(name+"的参数是:"+args);
}
@After("execution(public int com.m.action_proxy.impl.CalImpl.*(..))")
public void after(JoinPoint joinPoint){
//获取方法名
String name = joinPoint.getSignature().getName();
System.out.println(name+"方法结束");
}
@AfterReturning(value = "execution(public int com.m.action_proxy.impl.CalImpl.*(..)))",returning = "result")
public void afterReturning(JoinPoint joinPoint,Object result){
//获取方法名
String name = joinPoint.getSignature().getName();
System.out.println(name+"方法的结果是:"+result);
System.out.println("---------------------------------");
}
@AfterThrowing(value = "execution(public int com.m.action_proxy.impl.CalImpl.*(..)))",throwing = "exception")
public void afterThrowing(JoinPoint joinPoint,Exception exception){
//获取方法名
String name = joinPoint.getSignature().getName();
System.out.println(name+"方法抛出异常:"+exception);
}
}
方法注解:
-
@Before:表示切面方法执行的时机是业务方法执行之前。
-
@After:表示切面方法执行的时机是业务方法执行之后。
-
@AfterReturning:表示切面方法执行的时机是业务方法return之后。
-
@AfterThrowing:表示切面方法执行的时机是业务方法抛出异常之后。
-
execution(public int com.southwind.util.CalImpl.*(..)):表示切入点是com.southwind.util包下CalImpl类中的所有方法,即CalImpl所有方法在执行时会优先执行切面方法。