Spring 的AOP 面向切面编程

        我们知道,Spring中有两大块核心的思想,一个是IOC,一个是AOP;AOP的话,是面向切面编程,而java是面向对象的;顾名思义,就是我们的代码都是纵向的,而我们的AOP是在某一个横向切一刀,然后注入对应想要的内容(其实是有点与拦截器有点相似)

        主要用于将日志记录,性能统计,安全控制,事务处理,异常处理代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码

        例如我们需要去计算项目中各个方法跑下来所花费的时间是多少,我们不能到一个个方法中去添加对应的计算方法,而且如果是已经上线的项目,我们随意改项目中的代码是很危险的一件事情的;所以这个时候我们就可以使用我们的AOP帮我们进行对代码某些方法前、后、加入对应的处理内容

       AOP代理主要分为静态代理和动态代理,静态代理的代表为AspectJ;而动态代理则以Spring AOP为代表,包括JDK动态代理和CGLIB动态代理。静态代理是编译期实现,动态代理是运行期实现,可想而知前者拥有更好的性能。

        静态代理是编译阶段生成AOP代理类(需要手动去生成代理类,会生成对应的class文件),也就是说生成的字节码就织入了增强后的AOP对象;动态代理则不会修改字节码,而是在内存中临时生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。

        Spring的静态代理以接口实现的方式为例:我们需手动去写一个实现类,并且还需编写一个代理类,将之前的实现类进行注入,调用实现类的对应方法,将所需进行操作的方法前面进行增强操作

        Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理。一般来说接口实现类的方式,是采用JDK动态代理,但是我们也可以将其转换为CGLIB动态代理;非接口实现类的方式的话,一般是采用CGLIB的动态代理(底层是通过继承的方式,重写对应方法,再进行调用父类对应的方法进行增强处理)

  JDK动态代理

      利用拦截器(拦截器必须实现InvocationHanlder)加上反射机制生成一个实现代理接口的匿名类,再调用具体方法前调用InvokeHandler来处理。      

  CGLiB动态代理    

         如果目标类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成某个类的子类(一般代理类是存储在内存中)注意,CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final或是一些是private修饰的方法,那么它是无法使用CGLIB做动态代理的切面,这就是他的一个弊端

何时使用JDK还是CGLiB

1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP

2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP

3、如果目标对象没有实现了接口,必须采用CGLIB库,Spring会自动在JDK动态代理和CGLIB之间转换。

        在工作中,AOP主要用于例如,一些日志打印(例如入参与返回值,便于后续的bug排查)、统计各个方法的执行耗时还可以做一些代码鉴权(鉴别查看有没有登入信息等等),这个也可以用SpringMvc的拦截器来实现,

静态代理:

 

  1. 生成对应的接口
package com.cmj2;

public interface Study {
	void study();
}
  1. 设置对应的类、属性,实现接口
package com.cmj2;

public class Student implements Study {

	public void study() {
		System.out.println("学生要学习");

	}

}
  1. 生成代理类去实现对应接口,并且生成对应的第一个类中的对象,调用这个对象的方法,并且调用的前后可以加上我们所需的log等等
package com.cmj2;

public class StudentImpl implements Study {
	 Student student=new Student();
	
	public StudentImpl(Student student) {
		super();
		this.student = student;
	}

	public StudentImpl() {
		super();
		// TODO Auto-generated constructor stub
	}

	public void study() {
		System.out.println("before studet");
		student.study();
		System.out.println("affter student");

	}

}

对应的测试类:

package com.cmj.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.cmj2.StudentImpl;


public class StudentTest {
	public static void main(String[] args) {
			StudentImpl stu=new StudentImpl();
			stu.study();
	
	}
	
}

最后的结果:

        我们就在这个stuy这个方法的前后加上我们所需要的内容

 这个就是静态代理,他的缺点:

        相对比较复杂,我们需要自己手动去进行配置;并且每代理一个类时,都需要手动生成一个;量非常的大

动态代理:

        不需要我们手动去进行配置(在内存中生成代理类),我们只需要把对应的切点配置详细,就可以直接在我们所需要的地方加上所需内容,这不会动到项目中的代码

        首先我们需要创建我们需要的User类:

package com.cmj.entity;

public class User {

}

        创建对应UserService接口

package com.cmj.service;

import java.util.List;

import com.cmj.entity.User;

public interface UserService {
	 User findByID(int uid);

	  List<User> findAll();
}

创建接口的实现类:

package com.cmj.service;

import java.util.List;

import org.springframework.stereotype.Service;

import com.cmj.entity.User;

@Service//定义为Service
public class UserServiceImpl implements UserService {

	public User findByID(int uid) {
		System.out.println("findByID 运行");
		return null;
	}

	public List<User> findAll() {
		System.out.println("findAll Service运行");
		return null;
	}
	
}

定义切面配置

package com.cmj.aspect;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class MyAspect {
	@Pointcut("execution(* com.cmj.service.UserServiceImpl.*(..))")//表示切点在在某个包下,某个类下的所有方法
    public void point(){

    }
	@Before("point()")
    public void takeBefore() {
        System.out.println("前置通知");
    }

    @After("point()")
    public void testAfter(){
        System.out.println("后置通知");
    }

    @AfterReturning("point()")
    public void testAfterRuning(){
        System.out.println("最终通知");
    }

}

        这边一定要注意,我们需要用@Component与@Aspect修饰,这样Spring才会知道这个是我们的切面,

        @Pointcut("execution(* com.cmj.service.UserServiceImpl.*(..))")//表示切点在在某个包下,某个类下的所有方法 

 测试类:

package com.cmj.test;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.cmj.service.UserService;


public class SpringAopTest {
	public static void main(String[] args) {
		
			ApplicationContext ctx=new ClassPathXmlApplicationContext("application.xml");
			UserService UserService	=ctx.getBean(UserService.class);
			UserService.findAll();
	
	}
	
}

结果:

         这也就说,我们后面可以在任何地方任何位置加上我们所需要的内容,例如可以加上对应log等等;这些的话,后续是只需要修改切面文档中的@Pointcut("execution(* com.cmj.service.UserServiceImpl.*(..))")//表示切点在在某个包下,某个类下的所有方法  (这边所需要修改的位置就可以)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值