zoukankan      html  css  js  c++  java
  • Spring框架——AOP

    代理模式

    代理模式创建代理对象,让代理对象控制目标对象的访问,并且可以在不改变目标对象的情况下添加一些额外的功能

    不破坏原来代码的业务逻辑结构

    静态代理

    代理对象与被代理对象必须实现同一接口,在代理对象中实现日志

    实现

    • 被代理对象

    public class UserServiceImpl implements UserService {
    	
    	public boolean login(String name, String password){
    		if(name.equals("adi") && password.equals("123")){
    			System.out.println("业务逻辑:用户adi登录成功");
    			return true;
    		}else{
    			System.out.println("业务逻辑:用户adi登录失败");
    			return false;
    		}
    	}
    }
    
    
    • 代理对象

    package staticproxy;
    
    import java.util.Date;
    
    public class UserServiceStaticProxy implements UserService {
    
    	private UserService userService;
    
    	public UserServiceStaticProxy(UserService userService) {
    		this.userService = userService;
    	}
    
    	@Override
    	public boolean login(String name, String password) {
    		boolean isLogin = userService.login(name, password);
    		System.out.println("日志:" + name + "于" + new Date().toLocaleString() + "登录");
    		return isLogin;
    	}
    }
    
    
    • 使用

    UserServiceStaticProxy proxy = new UserServiceStaticProxy(new UserServiceImpl());
    proxy.login("xxx","xxx");
    

    缺点:

    1. 代理对象的一个接口只服务于一种类型的对象
    2. 如果要代理的方法很多,要为每种方法进行代理
    3. 静态代理在程序规模稍大时无法胜任

    动态代理

    1. InvocationHandler接口,
    2. 操作代理对象时会执行invoke()方法,
    3. 使用Proxy.newProxyInstance()静态方法建立一个代理类对象(必须告知要代理的接口

    实现

    public class LoggerHandler implements InvocationHandler {
    
    	private Object delegate;
    
    	public Object bind(Object delegate) {
    		this.delegate = delegate;
    		return Proxy.newProxyInstance(delegate.getClass().getClassLoader(), delegate.getClass().getInterfaces(), this);
    	}
    
    	@Override
    	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    		Object result = null;
    		System.out.println("方法名是:"+method.getName());
    		result = method.invoke(delegate, args);
    		System.out.println("日志:" + args[0] + "于" + new Date().toLocaleString() + "登录");
    		return result;
    	}
    
    }
    
    
    LoggerHandler lh = new LoggerHandler();
    UserService us = (UserService) lh.bind(new UserServiceImpl());
    us.login("adi", "123");
    

    AOP (Aspect-Oriented Programmin) 面向切面编程

    独立于Spring框架,但Spring实现了AOP。

    • 提供一种新的组织程序结构的思路
    • OOP补充,可OOP一起使用
    • OOP核心单位为类、AOP核心单位为切面

    概念

    • 关注点:特定的问题、概念、或成需要达到的目标。
    • 横切关注点:一个关注点被多个类或者方法引用
    • 切面:一个切面是对一个横切关注点的模块化,(实现关注点的方法)
    • 连接点:程序执行过程中的某个点,方法调用或者抛出异常
    • 通知:在特定连接点应执行动作(定义何时)
    • 切入点:什么地方植入通知(何地
    • 目标对象:被切面所通知的对象(被代理的对象
    • 织入:将切面应用到目标对象,从而创建新的代理对象

    通知类型

    • 前置通知(Before advice): 在某连接点之前执行的通知
    • 后置通知(After returning advice): 在某连接点正常完成后执行的通知
    • 异常通知(After throwing advice): 在方法抛出异常退出时执行的通知
    • 最终通知(After finally advice): 当某连接点退出的时候执行的通知
    • 环绕通知(Around advice): 包围一个连接点的通知,这是最强大的一种通知类型

    AOP实现方式

    SpringAPI传统方式

    前置通知 MethodBeforeAdvice接口

    public class Md5Advice implements MethodBeforeAdvice {
    
    	@Override
    	public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
    		System.out.println(arg0.getName());
    		System.out.println(arg1[1]);
    		System.out.println(arg2.getClass().getName());
    		String newPwd = Md5Encode.getMD5(arg1[1].toString().getBytes());
    		arg1[1] = newPwd;
    	}
    }
    

    后置通知 AfterReturningAdvice接口

    public class LogAdvice implements AfterReturningAdvice {
    
    	@Override
    	public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable {
    		System.out.println(arg0);
    		System.out.println(arg3);
    		System.out.println(arg2[0]+"在"+new Date().toLocaleString()+"登录");
    		arg0=false;
    	}
    
    }
    

    环绕通知 MethodInterceptor接口

    也可决定方法是否执行。

    public class TimeAdvice implements MethodInterceptor {
    
    	@Override
    	public Object invoke(MethodInvocation arg0) throws Throwable {
    		System.out.println("方法开始执行时间:"+new Date().toLocaleString());
    		
    		Object result = arg0.proceed();
    		
    		System.out.println("方法结束执行时间:"+new Date().toLocaleString());
    		return result;
    	}
    }
    
    

    异常通知 ThrowsAdvice接口

    public class RegistExceptionAdvice implements ThrowsAdvice {
    	
    	public void afterThrowing(Exception e) {
    		System.out.println("异常通知发生异常"+e.getMessage());
    	}
    }
    

    xml配置代理模式

    <!--注入代理的接口-->
    <!--注入通知,list配置-->
    <!--注入代理目标-->
    <bean>
    	<bean id="UserProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
    		<property name="proxyInterfaces" value="com.user.UserService"></property>
    		<property name="interceptorNames">
    			<list>
    				<value>Md5Advice</value>
    				<value>TimeAdvice</value>
    			</list>
    		</property>
    		<property name="target" ref="UserServiceImpl"></property>
    	</bean>
    </beans>
    

    使用

    ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
    //接口获取代理对象
    UserService us = (UserService)ctx.getBean("UserProxy");
    us.regist("adi", "123");
    boolean bool = us.login("adi", "123");
    System.out.println(bool);
    

    纯POJO类-基于xml

    • 通知类java

    1. 方法名自己写
    public class MyAdvice {
    	
    	public void before(JoinPoint joinPoint) {
    		System.out.println("前置通知");
    	}
    	//参数和配置名一致
    	public void afterReturning(JoinPoint joinPoint, Object result) {
    		System.out.println("后置通知");
    	}
    	
    	public void after(JoinPoint joinPoint) {
    		System.out.println("最终通知");
    	}
    	
    	public Object around(ProceedingJoinPoint joinPoint) {
    		System.out.println("环绕通知");
    		Object[] args = joinPoint.getArgs();
    		Object result = null;
    		try {
    			result = joinPoint.proceed(args);
    		} catch (Throwable e) {
    			e.printStackTrace();
    		}
    		System.out.println("环绕通知");
    		return result;
    	}
    	
    	public void throwMethod(Exception ex) {
    		System.out.println("异常通知");
    	}
    
    }
    
    • xml配置

      • 导入aop的命名空间
      • 配置bean元数据
      • 配置<aop:config>
    <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"
    	xsi:schemaLocation="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
    	http://www.springframework.org/schema/aop  
    	http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
    	">
    	<!-- 业务逻辑 -->
    	<bean id="UserServiceImpl" class="com.user.UserServiceImpl"></bean>
    	<!-- advices -->
    	<bean id="MyAdvice" class="com.advices.MyAdvice"></bean>
    	<aop:config>
    		<!--
                    定义切点
    		<aop:pointcut expression="execution(* com.user.*.*(..))" id="mypc"/>
                    切面
    		<aop:aspect id="MyAspect" ref="MyAdvice" order="2"> 
                            通知
    			<aop:before method="before" pointcut-ref="mypc"/>
    		</aop:aspect>
    		-->
    		
    		<aop:aspect id="MyAspect" ref="MyAdvice" order="2">
    			<aop:before method="before" pointcut="execution(* com.user.*.*(..))" />
    			<aop:after-returning method="afterReturning" pointcut="execution(* com.user.*.*(..))" returning="result"/>
    			<aop:around method="around" pointcut="execution(* com.user.*.*(..))" />
    			<!--aop:after-throwing method="throwExMethod" pointcut="execution(* service.*.*(..))" throwing="ex"/-->
    			<aop:after method="after" pointcut="execution(* com.user.*.*(..))" />
    		</aop:aspect>
    	</aop:config>
    </beans>
    
    

    执行顺序

    前置通知
    环绕通知
    业务执行....
    最终通知
    环绕通知
    后置通知

    纯POJO类-基于注解驱动

    • xml配置

      • aop自动代理
      • 注解配置
        <aop:aspectj-autoproxy />属性proxy-target-class
      • false ,jdk动态代理织入增强-代理模式
      • true,cglib动态代理织入增强-代理模式(不用加接口)
    <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"
    	xsi:schemaLocation="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
    	http://www.springframework.org/schema/aop  
    	http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
    	">
    	<aop:aspectj-autoproxy />
    	<context:annotation-config />
    	<context:component-scan base-package="com" />
    </beans>
    

    注解配置

    • 组件注解
    • 切面注解
    • 前置通知注解
    • 后置通知注解
    • 环绕通知注解
    • 最终通知注解
    • 异常通知注解
    • @Order(int)同类型通知排序(针对类),在类上加
    @Component
    @Aspect
    public class MyAdvice {
    	
    //	@Before("execution(* com.user.*.*(..))")
    	public void before(JoinPoint joinPoint) {
    		System.out.println("前置通知");
    		
    	}
    	
    //	@AfterReturning(pointcut = "execution(* com.user.UserServiceImpl.*(..))", returning="result")
    	public void afterReturning(JoinPoint joinPoint, Object result) {
    		System.out.println("后置通知");
    	}
    	
    //	@After("execution(* com.user.*.*(..))")
    	public void after(JoinPoint joinPoint) {
    		System.out.println("最终通知");
    	}
    	
    	@Around("execution(* com.user.*.*(..))")
    	public Object around(ProceedingJoinPoint joinPoint) {
    		System.out.println("环绕通知");
    		Object[] args = joinPoint.getArgs();
    		Object result = null;
    		try {
    			result = joinPoint.proceed(args);
    		} catch (Throwable e) {
    			e.printStackTrace();
    		}
    		System.out.println("环绕通知");
    		return result;
    	}
    	
    //	@AfterThrowing(value = "execution(* com.user.*.*(..))", throwing="ex")
    	public void throwMethod(Exception ex) {
    		System.out.println("异常通知");
    	}
    
    }
    
    

    通知顺序

    环绕通知
    前置通知
    业务执行,登录成功
    环绕通知
    最终通知
    后置通知

    后置通知和最终通知的区别

    后置通知如果出现异常,无法返回数据时,后置通知无法执行,但是最终通知可以执行。

    不管有没有异常,最终通知一定执行,
    没有异常,返回值正常时,后置通知才执行。

    使用AspectJ切面

    • 自己找素材看

    每日英语

    proxy 代理
    Invocation 求助,调用
    Handler 组织者、操作者
    pointcut 切点

  • 相关阅读:
    关于Python中的yield
    Python的getattr(),setattr(),delattr(),hasattr()
    django Forgienkey字段 在前台用js做处理
    利用checkbox的到值,并且存到数据库修改的话要显示之前选择的
    关于django批量上传图片
    block extends include三者的差别跟用法
    全智能建造
    共享经济
    新工匠
    运营方案
  • 原文地址:https://www.cnblogs.com/occlive/p/13558853.html
Copyright © 2011-2022 走看看