栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Spring AOP

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Spring AOP

AOP概述

AOP:Aspect Oriented Programming,面向切面编程
AOP把程序中重复的代码抽取出来,在需要执行的时候,使用动态代理的技术,在不修改源码的基础上,对程序进行增强。例如:权限校验,日志记录,性能监控,事务控制。

代理模式

代理模式,就是给—个对象提供一种代理对象以控制对该对象的访问。在这个模式中,我们通过创建代理对象作为替身替代了原有对象,从而达到我们控制对象访问的目的。
代理模式能带给我们控制访问某个对象的能力,在某些情况下,一个对象的某些方法想要进行屏蔽或者某种逻辑的控制,则我们可以通过代理的方式进行。再此能力上,引申出来的作用,也是目前在开发中经常使用的一个作用,就是在不修改原对象代码的基础上,对原对象的功能进行修改或者增强。

java中的代理模式

Java中代理有三种方式来创建代理对象:静态代理、基于JDK(接口)的动态代理、基于CGLB的动态代理。

相关类

目标类:原对象,我们需要通过代理对象控制它的访问,扩展其功能。
代理类:代理模式产生的对象是原对象的替身,已经在原有基础上修改了逻辑。

静态代理

静态代理,也就是我们会手写代理类的代码,工程中有代理类的源码,代理类会编译后执行。
编写代理类,实现目标类的接口或者直接继承目标类,完成逻辑的修改。

  1. 继承的方式
//目标类,顾客类
public class Customer{
	public String order(String foodName){
		return "点了"+foodName;
	}
}
//代理类,外卖员类
public class DeliveryClerk extends Customer{
	@Override
	public String order(String foodName){
		String result = super.order(foodName);
		System.out.println("接单,正在取餐");
		System.out.println("已经取餐,正在派送");
		return result;
	}
}
//测试类
public class Test{
	public static void main(String[] args){
		Customer customer = new DeliveryClerk();
		String result = customer.order("秘制小汉堡");
		System.out.println(result);
	}
}
  1. 接口的方式
//接口
public interface OrderInterface{
	String order(String foodName);
}
//目标类
public class Customer implements OrderInterface{
	@Override
	public String order(String foodName){
		return "点了"+foodName;
	}
}
//代理类
public class DeliveryClerk implements OrderInterface{
	//把目标类保存在成员位置,多态
	private OrderInterface source;
	public DeliveryClerk(OrderInterface source){
		this.source = source;
	}
	@Override
	public String order(String foodName){
		String result = source.order(foodName);
		System.out.println("接单,正在取餐");
		System.out.println("已经取餐,正在派送");
		return result;
	}
}
//测试
public class Test{
	public static void main(String[] args){
		Customer customer = new Customer();
		DeliveryClerk deliveryClerk = new DeliveryClerk(customer);
		String result = deliveryClerk.order("华莱士全家桶");
		System.out.println(result);
	}
}

缺点:一旦接口或者父类发生了变动,则代理类的代码就得随之修改,代理类多的时候维护比较麻烦。所以在实际开发的时候,一般使用动态代理的方式。

动态代理

动态代理技术是在内存中生成代理对象的一种技术。也就是整个代理过程在内存中进行,我们不需要手写代理类的代码,也不会存在代理类编译的过程,而是直接在运行期,由JVM中造出—个代理类对象供我们使用。

基于jdk的动态代理

JDK自带的动态代理技术,需要使用个静态方法来创建代理对象。
它要求被代理对象,也就是目标类,必须实现接口。
生成的代理对象和原对象都实现相同的接口,是兄弟关系。

//接口
public interface OrderInterface{
	String order(String foodName);
}
//目标类
public class Customer implements OrderInterface{
	@Override
	public String order(String foodName){
		return "点了"+foodName;
	}
}
//测试类
public class Test{
	public static void main(String[] args){
		//创建目标类
		Customer customer = new customer();
		
		//使用jdk的api,动态生成代理对象
		OrderInterface deliveryClerk = (OrderInterface)proxy.newProxyInstance(
							customer.getclass.getClassLoader,
							customer.getClass.getInterfaces(),
							 new InvocationHandler() {
								 public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {					
								 		if("order".eqials(method.getName)){
								 			System.out.println("接单,正在取餐");
											System.out.println("已经取餐,正在派送");
								 			Object result = method.invoke(customer, args);
								 			return result;
								 		}else{
								 			return method.invoke(customer, args);
								 		}
							 		}
							 	}	
							);
		//执行代理类的方法					
		System.out.println(deliveryClerk.order("汉堡");	
	}
}

ClassLoader loader:固定写法,指定目标类对象的类加载器即可。用于加载目标类及其接口的字节码文件

Class[] interfaces:固定写法,指定目标类的实现的所有接口的字节码对象的数组,通常,使用目标类的字节码对象调用 getInterfaces()方法即可得到

Invocation Handler h:这个参数是一个接口,主要关注它里面唯—个方法, invoke方法。它会在代理类对象调用方法时执行,也就是说,我们在代理类对象中调用任何接口中的方法时,都会执行到invoke中。我们在此方法中完成增强或者扩展代码的逻辑编写

proxy:就是代理类对象的一个引用,也就是 Proxy.newProxyInstance的返回值,此引用几乎不回用到,忽略即可。

method:在代理对象上调用的接口方法的实例

args:代理对象调用接口方法时传递的实际参数

基于Cglib(父类)的动态代理

第三方CGLB的动态代理技术,也是可以使用一个静态方法来创建代理对象。它不要求目标类实现接口,但是要求目标类不能是最终类,也就是不能被final修饰。因为CGLB是基于目标类生成该类的一个子类作为代理类,所以目标类必须可被继承。
CGLIB是第三方提供的包,所以需要引入jar包的坐标:


    cglib
    cglib
    2.2.2

//目标类
public class Customer implements OrderInterface{
	@Override
	public String order(String foodName){
		return "点了"+foodName;
	}
}
//测试类
public class Test(){
	public static void main(String[] args){
		Customer customer = new Customer();
		
		//使用CgLib创建代理对象
		Customer deliveryClerk = Enhancer.create(Customer.getClass,new MethodInterceptor(){
				public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
					if("order".equals(method.getName)){
						System.out.println("接单,正在取餐");
						System.out.println("已经取餐,正在派送");
						Object result = method.invoke(customer,args);
						return result;
					}else{
						return method.invoke(customer,args);
					}
			})
	}
	
	System.out.println(deliveryClerk.order("汉堡"));
}

Class type:指定要代理的目标类的字节码对象

callback:此单词的意思叫做回调,意思就是我们提供个方法,它会在合适的时候帮我们调用它。回来调用的意思
Callback是一个接口,由于该接口只是一个名称定义的作用,并不包含方法的声明。所以我们使用
时通常使用它的一个子接口 MethodInterceptor,此单词的意思叫做方法拦截器
Methodintercepto接口中也只有一个方法,叫做 intercept

proxy:就是代理类对象的一个引用,也就是 Enhancer.create的返回值,此引用几乎不回用到,忽略即可。

method:在代理对象上调用的接口方法的实例

args:代理对象调用接口方法时传递的实际参数

methodProxy:方法的代理,一般不做处理,可以暂时忽略

AOP的相关术语
术语含义
连接点(joinpoint)spring允许你插入通知的地方,和方法有关的前前后后(抛出异常),都是连接点。Spring只支持方法类型的连接点
切入点(pointcut)切入点是插入通知的连接点
通知(advice)要执行的增强代码,通知分为前置、后置、异常、最终、环绕通知五类
切面(aspect)切面是通知和切入点的结合
引介(introduction)是一种特殊的通知,在不修改代码的前提下,引介可以在运行期为类动态地添加一些方法或字段
目标对象(Target)要代理的目标对象(要增强的类)
织入(weave)将增强应用到目标的过程将advice应用到target的过程
代理(Proxy)一个类被AOP织入增强之后,就产生一个代理类
AOP配置
    
        
        
            org.springframework
            spring-context
            5.1.8.RELEASE
        
    
//持久层实现类
public class UserDaoImpl(){
	System.out.println("123");
}
//业务层实现类
public class UserServiceImpl implements UserService {

    private UserDao userDao;

    public void addUser(){
        userDao.addUser();
    }
}
//增强,advice
public class MyLogger{
	public void beforeMethodLog(){
        System.out.println("方法开始时间:"+new Date());
    }
    public void afterMethodLog(){
        System.out.println("方法开始时间:"+new Date());
    }
}
//applicationContext.xml


	
	
	
		
	
	
	

	
	
		 
	
	
	
	
		
		
	

execution=[修饰符] 返回值类型 包名.类名.方法名(参数)
例如:​
execution(* com.qf.service.UserService.add(…))
​execution(* com.qf.service.UserService.(…))`
​execution(
com.usian.service..(…))

method:用于指定通知类中的增强方法名称
ponitcut-ref:用于指定切入点

//测试类
public class Test{
	public static void main(String[] args) {
		ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService = ac.getBean("userService",UserService.class);
        userService.addUser();
	}
}
基于注解的aop配置
    
        
        
            org.springframework
            spring-context
            5.1.8.RELEASE
        
        
            org.springframework
            spring-aspects
            5.1.8.RELEASE
        
    
//持久层实现类
@Repository
public class UserDaoImpl implements UserDao {

    @Override
    public void addUser(){
        System.out.println("123");
    }
}
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;

    public void addUser() {
        userDao.addUser();
    }
}
//增强,advice
@Component
@Aspect
public class MyLogger {

    @Before("execution(* com.qf.service.*.*(..))")
    public void beforeMethodLog(){
        System.out.println("方法开始时间:"+new Date());
    }

    @After("execution(* com.qf.service.*.*(..))")
    public void afterMethodLog(){
        System.out.println("方法开始时间:"+new Date());
    }
}



    
    
	

常用注解

常用注解

  • @Aspect:把当前类声明为切面类
  • @Before:前置通知,可以指定切入点表达式
  • @AfterReturning:后置【try】通知,可以指定切入点表达式
  • @AfterThrowing:异常【catch】通知,可以指定切入点表达式
  • @After:最终【finally】通知,可以指定切入点表达式
  • @Around:环绕通知,可以指定切入点表达式
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/324649.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号