AOP

1.介绍

Aspect Oriented Programming 面向切面编程,将程序中的相同业务逻辑进行横向隔离,将重复的业务逻辑抽取到一个独立的模块。、

  • 连接点Joinpoint:程序执行过程中某个特定的节点
  • 通知Advice:在目标类连接点上执行的代码,有around,before等类型
  • 切点Pointcut:匹配连接点的断言,AOP通过其定位到连接点。
  • 目标对象Target:通知所作用的目标类。
  • 引介Introduction: 特殊的通知,为类添加属性和方法。动态地为业务类添加接口的实现逻辑。
  • 切面Aspect: 对系统中的横切关注点逻辑进行模块化封装的AOP概念实体。
  • 织入Weaving: 将通知添加到目标类具体连接点的过程,在编译,类加载,运行时完成,Spring动态代理织入,AspectJ编译期和类装载器织入。
  • 代理Proxy:目标类被织入增强后产生的结果类,融合了原类和增强的逻辑。

2.实现机制

2.1 JDK动态代理

JDK代理只能为接口创建代理实例

  • LoginService接口
public interface LoginService {
    public void login();
}
  • LoginServiceImpl
public class LoginServiceImpl implements LoginService{

    @Override
    public void login() {
        System.out.println("Login");
    }
}
  • PerformHandler类
public class PerformHandler implements InvocationHandler {
    private Object target;

    public PerformHandler(Object target){
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //增强的方法
        System.out.println("start");
        //执行被代理类的原方法
        Object invoke = method.invoke(target, args);
        System.out.println("end");
        return invoke;
    }
}
  • Test类
@Test
public void fun(){
    LoginService loginService = new LoginServiceImpl();
    //通过Proxy生成代理对象
    PerformHandler performHandler = new PerformHandler(loginService);
    loginService = (LoginService) Proxy.newProxyInstance(loginService.getClass().getClassLoader(),
            loginService.getClass().getInterfaces(),performHandler);
    loginService.login();
}

2.2 CGLib动态代理

CGLib动态代理为类创建实例 底层字节码技术 继承的方式实现
依靠Enhancer类创建代理实例 MethodInterceptor接口织入方法

  • CglibProxy
public class CglibProxy implements MethodInterceptor {

    private Enhancer enhancer = new Enhancer();

    //生成代理对象
    public Object getProxy(Class clazz){
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        return enhancer.create();
    }
    
    //回调方法 拦截目标类所有方法调用
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("start");
        Object invoke = methodProxy.invokeSuper(o, objects);
        System.out.println("end");
        return invoke;
    }
}
  • Test类
@Test
public void funG(){
    CglibProxy cglibProxy = new CglibProxy();
    //创建代理对象
    LoginServiceImpl loginService = (LoginServiceImpl) cglibProxy.getProxy(LoginServiceImpl.class);
    loginService.login();
}

3.实现方法

3.1 XML

  • spring.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:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
            https://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/aop/spring-aop.xsd
            ">
 <bean name="loginService" class="service.LoginServiceImpl"></bean>
 <bean name = "xmlAdvice" class="service.XmlAdvice"></bean>
<!--   Spring Aop-->
   <aop:config>
<!--      切点-->
      <aop:pointcut id="pointcut" expression="execution(* service.LoginServiceImpl.*(..))"/>
<!--      切面-->
      <aop:aspect ref="xmlAdvice">
<!--         前置-->
         <aop:before method="before" pointcut-ref="pointcut"></aop:before>
<!--         返回-->
         <aop:after-returning method="afterReturning" pointcut-ref="pointcut"></aop:after-returning>
<!--         环绕-->
         <aop:around method="around" pointcut-ref="pointcut"></aop:around>
<!--         异常-->
         <aop:after-throwing method="afterException" pointcut-ref="pointcut"></aop:after-throwing>
<!--         后置-->
         <aop:after method="after" pointcut-ref="pointcut"></aop:after>
      </aop:aspect>
   </aop:config>
</beans>
  • Test类
@Test
public void funXml(){
    ApplicationContext context = new ClassPathXmlApplicationContext("config/spring1.xml");
    LoginService loginService = context.getBean("loginService", LoginService.class);
    loginService.login();
}

3.2 注解

  • Spring.xml 修改
    <bean name="annoAdvice" class="service.AnnoAdvice"></bean>
<!--    自动动态代理-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

 <bean name="loginService" class="service.LoginServiceImpl"></bean>
  • AnnoAdvice类
@Aspect
public class AnnoAdvice {
    //切点
    @Pointcut("execution(* service.LoginServiceImpl.*(..))")
    public void pointcut(){

    }
    @Before("pointcut()")
    public void before(){
        System.out.println("before");
    }

    //后置通知 异常不执行
    @AfterReturning("pointcut()")
    public void afterReturning(){
        System.out.println("afterReturning");
    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint point) throws Throwable{
        System.out.println("around start");
        Object object = point.proceed();
        System.out.println("around end");
        return object;
    }

    @AfterThrowing("pointcut()")
    public void afterException(){
        System.out.println("Exception");
    }

    @After("pointcut()")
    public void after(){
        System.out.println("after");
    }
}

4.应用

4.1 性能监控

  • Service1
public class Service1 {
    public void service() throws Exception{
        System.out.println("service");
        Thread.sleep(1000);
    }
}
  • Record类:记录执行信息
public class Record {
    private String className;
    private String methodName;
    private Date recordTime;
    private Long expendTime;

    public String getClassName() {
        return className;
    }

    public void setClassName(String className) {
        this.className = className;
    }

    public String getMethodName() {
        return methodName;
    }

    public void setMethodName(String methodName) {
        this.methodName = methodName;
    }

    public Date getRecordTime() {
        return recordTime;
    }

    public void setRecordTime(Date recordTime) {
        this.recordTime = recordTime;
    }

    public Long getExpendTime() {
        return expendTime;
    }

    public void setExpendTime(Long expendTime) {
        this.expendTime = expendTime;
    }

    @Override
    public String toString() {
        return "Record{" +
                "className='" + className + '\'' +
                ", methodName='" + methodName + '\'' +
                ", recordTime=" + recordTime +
                ", expendTime=" + expendTime +
                '}';
    }
}
  • RecordAspect类
@Aspect
public class RecordAspect {
    @Pointcut("execution(* applicationAop.Service* .*(..))")
    public void record(){

    }

    @Around("record()")
    public Object recordTime(ProceedingJoinPoint point) throws Throwable{
        String className = point.getTarget().getClass().getName();
        String methodName = point.getSignature().getName();
        //方法执行时间
        long startTime = System.currentTimeMillis();
        Object result = point.proceed();
        long time = System.currentTimeMillis() - startTime;

        Record record = new Record();
        record.setExpendTime(time);
        record.setClassName(className);
        record.setMethodName(methodName);
        record.setRecordTime(new Date());
        System.out.println(record);
        return result;
    }
}
  • xml配置
<bean name="service1" class="applicationAop.Service1"></bean>
<bean name="recordAspect" class="applicationAop.RecordAspect"></bean>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
  • Test类
 @Test
public void test() throws Exception{
    ApplicationContext context = new ClassPathXmlApplicationContext("config/spring1.xml");
    Service1 service1 = context.getBean("service1",Service1.class);
    service1.service();
}

4.2 异常监控

  • MyException类
public class MyException extends Exception{
    private static final long serialVersionUID = 1l;
    private String msg;

    public MyException(String msg) {
        super();
        this.msg = msg;
    }

    public String getMsg() {
        return msg;
    }
}
  • Service2
public class Service2 {
    public void service() throws Exception{
        System.out.println("service2");
        if(true){
            throw new MyException("true");
        } else {
            System.out.println(false);
        }
    }
}
  • Message类
public class Message {
    private String className;
    private String methodName;
    private Date recordTime;
    private String exceptionMsg;

    public String getClassName() {
        return className;
    }

    public void setClassName(String className) {
        this.className = className;
    }

    public String getMethodName() {
        return methodName;
    }

    public void setMethodName(String methodName) {
        this.methodName = methodName;
    }

    public Date getRecordTime() {
        return recordTime;
    }

    public void setRecordTime(Date recordTime) {
        this.recordTime = recordTime;
    }

    public String getExceptionMsg() {
        return exceptionMsg;
    }

    public void setExceptionMsg(String exceptionMsg) {
        this.exceptionMsg = exceptionMsg;
    }

    @Override
    public String toString() {
        return "Message{" +
                "className='" + className + '\'' +
                ", methodName='" + methodName + '\'' +
                ", recordTime=" + recordTime +
                ", exceptionMsg='" + exceptionMsg + '\'' +
                '}';
    }
}
  • MessageAspect
@Aspect
public class MessageAspect {
    @Pointcut("execution(* applicationAop.Service* .*(..))")
    public void exceptionMsg(){

    }

    @Around("exceptionMsg()")
    public Object msgMethod(ProceedingJoinPoint point) throws Throwable{
        String className = point.getTarget().getClass().getName();
        String methodName = point.getSignature().getName();

        try {
            return point.proceed();
        }catch (MyException e){
            Message message = new Message();
            message.setClassName(className);
            message.setMethodName(methodName);
            message.setRecordTime(new Date());
            message.setExceptionMsg(e.getMsg());
            System.out.println(message);
        }


        return null;
    }
}
  • xml配置
<bean name="service2" class="applicationAop.Service2"></bean>
<bean name="mmessageAspect" class="applicationAop.MessageAspect"></bean>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

-Test类

@Test
public void test1() throws Exception{
    ApplicationContext context = new ClassPathXmlApplicationContext("config/spring1.xml");
    Service2 service2 = context.getBean("service2",Service2.class);
    service2.service();
}
posted @ 2023-04-18 19:07  lwx_R  阅读(18)  评论(0编辑  收藏  举报