AOP
前言
AOP作为OOP的延续,可以进一步解耦,提高开发和维护效率。
一、AOP
1、AOP基本概念
AOP(Aspect Oriented Programming),面向切面编程,是OOP的延续。AOP可以降低业务逻辑各部分之间的耦合度,提高程序的可重用性和开发效率。
根据它以上的特点,所以主要用户以下场景,
1)日志记录
2)性能统计
3)安全控制
4)事务处理
5)异常处理
在这些场景下,可以将以上部分与事务隔离开来,放到非业务部分,进而改变这些代码时,不会影响实际业务。
举一个实际例子,
如果登录要判断其权限问题,原始的做法就是在登录的逻辑中写判断代码,而AOP的方式为,单独写好权限控制模块,然后通过配置将该模块配置到登录逻辑的主干部分,而不用修改原来的登录逻辑。
2、AOP底层逻辑
AOP通过动态代理来增强类中某个方法的功能,代理对象比原始对象能做更强的事情。
动态代理的两种情况,
1)有接口的情况,使用JDK动态代理。
2)无接口的情况,使用CGLIB动态代理。
3、Case
A.JDK动态代理
1)写一个接口和实现类
2)使用Proxy类来创建代理对象
JDK代理的方法,
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h) {
Objects.requireNonNull(h);
final Class<?> caller = System.getSecurityManager() == null
? null
: Reflection.getCallerClass();
/*
* Look up or generate the designated proxy class and its constructor.
*/
Constructor<?> cons = getProxyConstructor(caller, loader, interfaces);
return newProxyInstance(caller, cons, h);
}
具体实现,
package com.xhu.aop;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JDKProxy {
private static UserDAO ud = new UserDAOImpl();
public static void main(String[] args) {
UserDAO udProxy = (UserDAO) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), new Class[]{UserDAO.class}, new UserDAOProxy(ud));
System.out.println(udProxy.add(1, 2));
}
}
class UserDAOProxy implements InvocationHandler {
private Object obj;
public UserDAOProxy(Object obj) {
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//方法之前
System.out.println("methodName:" + method.getName());
//执行原方法
Object invoke = method.invoke(obj, args);
//方法之后
System.out.println("obj:" + obj);
return invoke;
}
}
B.CGLIB动态代理
4、AOP术语
1)连接点,类中可以被增强的方法,这些方法称为连接点。
2)切入点,实际被增强的方法,这些方法称为切入点。
3)通知(增强),真正被增强的逻辑部分,比如在方法最后加个日志,加这个日志就是通知。或者登录之前权限判断,这个权限判断就是通知。
注:通知类型如下,
A)前置通知,方法之前通知。
B)后置通知,方法之后通知。
C)环绕通知,方法之前之后都通知。
D)异常通知,当方法出现异常,则通知。
E)最终通知,类finally,如果方法发生异常,则后置通知肯定不执行,但是要执行最终通知。
4)切面,把通知应用到切入点的过程就是切面。
5、AspectJ
AspectJ是一个独立的AOP框架,可以结合Spring一起来进行Spring项目的AOP操作。可XML配置可注解。
A.切入点表达式
用于对那个类的那个方法进行增强,即找到切入点。
1)语法结构,
execution([权限修饰符][返回路径][类全路径][方法名称][参数列表])
2)例子
例1)对com.xhu.dao.BookDAO 的方法add进行增强.
execution(* com.xhu.dao.BookDAO.add(…)),返回类型省略,用空格替代。
例2)对com.xhu.dao.BookDAO 的所有方法进行增强.
execution(* com.xhu.dao.BookDAO.*(…))
例3)对com.xhu.dao包的所有类的所有方法进行增强.
execution(* com.xhu.dao.*.*(…))
6、AspectJ+Spring
A.基于注解
1)两种基本类,
被增强类、增强类1、增强类2、
package com.xhu.aop;
import org.springframework.stereotype.Component;
/**
* 被增强类
*/
@Component
public class User {
public void add(){
System.out.println("add......................");
}
}
package com.xhu.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/**
* 通知类
*/
@Component
@Aspect
@Order(2)//越大优先级越小
public class UserProxy {
//相同切入点抽取
@Pointcut(value = "execution(* com.xhu.aop.User.add(..))")
public void point() {
}
//多个增强类对同一个方法增强,设置增强的优先级。
/**
* 前置通知
*/
@Before(value = "point()")
public void before() {
System.out.println("before............................");
}
/**
* 最终通知
*/
@After(value = "execution(* com.xhu.aop.User.add(..))")
public void after() {
System.out.println("after.......................................");
}
/**
* 后置通知
*/
@AfterReturning(value = "execution(* com.xhu.aop.User.add(..))")
public void afterReturning() {
System.out.println("afterReturning.......................................");
}
/**
* 异常通知
*/
@AfterThrowing(value = "execution(* com.xhu.aop.User.add(..))")
public void afterThrowing() {
System.out.println("afterThrowing.......................................");
}
/**
* 环绕通知
*
* @param pjp
* @throws Throwable
*/
@Around(value = "execution(* com.xhu.aop.User.add(..))")
public void around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("around前.......................................");
pjp.proceed();
System.out.println("around后.......................................");
}
}
package com.xhu.aop;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Component
@Aspect
@Order(1)//越小优先级越高
public class UserProxyPro {
/**
* 前置通知
*/
@Before(value = "execution(* com.xhu.aop.User.add(..))")
public void before() {
System.out.println("before PRO............................");
}
}
2)配置类
package com.xhu.aop;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
/**
* @Configuration 告诉spring这是一个配置类
*/
@Configuration
//配置要做的事为,开启组件扫描和启用AspectJ自动代理
@ComponentScan(basePackages = {"com.xhu.aop"})
@EnableAspectJAutoProxy
public class SpringConfig {
}
3)测试类
package com.xhu.aop;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class TestAOP {
public static void main(String[] args) {
ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfig.class);
User user = ac.getBean("user", User.class);
user.add();
}
}
B.基于XML
1)配置xml中的组件扫描和Aspect自动代理
<!-- 配置对象-->
<bean id="user" class="com.xhu.aop.User"></bean>
<bean id="userProxy" class="com.xhu.aop.UserProxy"></bean>
<!-- 配置aop具体信息-->
<aop:config>
<!-- 切入点-->
<aop:pointcut id="point" expression="execution(* com.xhu.aop.User.add(..))"/>
<!-- 配置切面-->
<aop:aspect ref="userProxy">
<!-- 配置增强-->
<aop:before method="before" pointcut-ref="point"></aop:before>
</aop:aspect>
</aop:config>
</beans>
2)test类
package com.xhu.aop;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestAOP {
public static void main(String[] args) {
//1.获取ApplicationContext对象来关联配置文件,为接下来的IOC做准备
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//2.获取对象实例
User user = ac.getBean("user", User.class);
user.add();
}
}
总结
1)AOP作为OOP的延续,可以进一步解耦,提高开发和维护效率。
参考文献
[1]Spring5 尚硅谷