AOP:Aspect Oriented Programming 面向切面编程。AOP是对面向对象编程的一种补充,在运行时动态地将代码切入到类的指定方法、指定位置的编程思想。将不同的方法的同一位置抽象成一个切面对象,对该切面进行编程,就是AOP。
AOP更专业的说法:通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术, AOP是OOP的延续与补充。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高开发效率。
底层实现:
(1) JDK的动态代理:针对实现了接口的类产生代理;
(2) Cglib的动态代理:针对未是实现接口的类产生代理,应用的是底层字节码增强技术生成当前类的子类对象。
1 AOP 的优点
(1) 降低模块之间的耦合度
(2) 使系统更容易扩展
(3) 更好的代码复用
(4) 非业务代码更加集中,不分散,便于统一管理
(5) 业务代码更简洁纯粹,不掺杂其他代码影响
2 实例
2.1 动态代理实现AOP
Cal接口
public interface Cal { public int add(int num1, int num2); public int sub(int num1, int num2); public int mul(int num1, int num2); public int div(int num1, int num2); }
实现类
public class CalImpl implements Cal { public int add(int num1, int num2) { System.out.println("add方法的参数是["+num1+","+num2+"]"); int result = num1+num2; System.out.println("add方法的结果是"+result); return result; } public int sub(int num1, int num2) { System.out.println("sub方法的参数是["+num1+","+num2+"]"); int result = num1-num2; System.out.println("sub方法的结果是"+result); return result; } public int mul(int num1, int num2) { System.out.println("mul方法的参数是["+num1+","+num2+"]"); int result = num1*num2; System.out.println("mul方法的结果是"+result); return result; } public int div(int num1, int num2) { System.out.println("div方法的参数是["+num1+","+num2+"]"); int result = num1/num2; System.out.println("div方法的结果是"+result); return result; } }
上述代码中日志信息和业务逻辑的耦合性很高,不利于系统的维护,使用AOP可以进行优化。
如何来实现AOP?使用动态代理的方式来实现。给业务代码找一个代理,打印日志信息的工作交个代理来做,这样的话业务代码就只需要关注自身的业务即可。
动态代理
public class MyInvocationHandler implements InvocationHandler { /** * 接受代理对象 */ private Object object; /** * 返回代理对象 * @return */ public Object bind(Object object) { this.object = object; return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println(method.getName() + "方法的参数是:" + Arrays.toString(args)); Object result = method.invoke(object, args); System.out.println(method.getName() + "方法的计算结果是:" + result); return result; } }
测试
public class CalTest { public static void main(String[] args) { Cal cal = new CalImpl(); MyInvocationHandler handler = new MyInvocationHandler(); Cal cal1 = (Cal)handler.bind(cal); cal1.add(4, 2); cal1.sub(8, 2); cal1.mul(16, 2); cal1.div(32, 2); } }
2.2 Spring 实现AOP
切面
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
@Aspect @Component public class LoggerAspect { /** * 前置通知 * @param joinPoint */ @Before("execution (public int com.latiny.aopa.impl.CalImpl.*(..))") public void before(JoinPoint joinPoint) { String name = joinPoint.getSignature().getName(); String args = Arrays.toString(joinPoint.getArgs()); System.out.println(name + "方法的参数为:" + args); } /** * 后置通知 * @param joinPoint */ @After("execution (public int com.latiny.aopa.impl.CalImpl.*(..))") public void after(JoinPoint joinPoint) { String name = joinPoint.getSignature().getName(); System.out.println(name + "方法执行完毕。"); } @AfterReturning(value = "execution (public int com.latiny.aopa.impl.CalImpl.*(..))", returning = "result") public void afterReturn(JoinPoint joinPoint, Object result) { String name = joinPoint.getSignature().getName(); System.out.println(name + "方法返回结果为:" + result); } @AfterThrowing(value = "execution (public int com.latiny.aopa.impl.CalImpl.*(..))", throwing = "e") public void afterThrowing(JoinPoint joinPoint, Exception e) { String name = joinPoint.getSignature().getName(); System.out.println(name + "方法抛出的异常为:" + e); } }
被切的类需要加注解@Component
@Component public class CalImpl implements Cal { @Override public int add(int num1, int num2) { int result = num1 + num2; return result; } @Override public int sub(int num1, int num2) { int result = num1 - num2; return result; } @Override public int mul(int num1, int num2) { int result = num1 * num2; return result; } @Override public int div(int num1, int num2) { int result = num1 / num2; return result; } }
配置文件
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<?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:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd "> <!-- 自动扫描--> <context:component-scan base-package="com.latiny"></context:component-scan> <!--使Aspect注解自动生效,为目标类自动生成代理对象--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>
context:component-scan:将com.latiny 包中的所有类进行扫描,如果该类同时
添加了@Component,则将该类扫描IOC容器中,交给IOC进行管理。
测试
public class CalTest2 { public static void main(String[] args) { // 加载配置文件 ApplicationContext context = new ClassPathXmlApplicationContext("spring-aop.xml"); Cal cal1 = (Cal) context.getBean("calImpl"); cal1.add(4, 2); cal1.sub(8, 2); cal1.mul(16, 2); cal1.div(32, 0); } }
AOP相关概念
(1) 切面:横切关注点被模块化的抽象对象;
(2) 通知:切面完成的工作;
(3) 目标:被通知的对象,即被横切的对象;
(4) 代理:切面、通知、目标混合之后的对象;
(5) 连接点:通知要插入业务代码的具体位置;
(6) 切入点:AOP通过切点定位到连接点;