1.利用一个简单的静态的代理体会AOP思想(举例)
所谓的静态的代理就是在程序创建之前,代理类利用java代码实现
(1)首先创建一个接口:
public interface ProductService {
public void biz1();
public void biz2();
}
(2)创建一个目标的类,实现这个接口:
public class ProductTarget implements ProductService{
@Override
public void biz1() {
System.out.println("事务biz1");
}
@Override
public void biz2() {
System.out.println("事务biz2");
}
}
(3):编写一个代理类实现同样的接口:
public class ProductProxy implements ProductService{
private ProductAdvice advice = new ProductAdvice();
//获取一个通知类
private
static Method biz1;
static Method biz2;
static {
try {
// 利用反射的机制,获取方法的信息
biz1 = ProductService.class.getMethod("biz1");
biz2 = ProductService.class.getMethod("biz2");
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
@Override
public void biz1(){
try {
advice.invoke(this, biz1, new Object[]{});
}catch (Throwable e){
e.printStackTrace();
}
}
@Override
public void biz2() {
try {
advice.invoke(this,biz2,new Object[]{});
}catch (Throwable e){
e.printStackTrace();
}
}
}
(4)编写一个通知类:
public class ProductAdvice implements InvocationHandler {
private ProductTarget target = new ProductTarget();
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("事务开始");
Object object = null;
try {
object = method.invoke(target,args);
System.out.println("事务提交");
}catch (Throwable e){
e.printStackTrace();
System.out.println("事务回滚");
}
System.out.println("事务结束");
return object;
}
}
分析:
让一个代理类和一个目标类(被代理的类)实现一个同样的接口,目的让调用者察觉不出是代理替换了原来的对象
代理类:
1:调用了通知类的invoke的方法
2:获取目标类的方法的对象和方法的实际的参数
通知类:
1:利用反射的技术调用目标类的方法,将事务的代码和重复的部分相互结合
2:实现一个InvocationHandler的接口,重写它的invoke的方法
参数1:Object proxy 代理类对象
参数2:Method method 方法的对象用来反射的技术来调用目标类的业务的方法
参数3:Object[] args 方法的参数
2.spring框架AOP思想的概述
spring的框架的另一个核心的思想为:AOP的思想
那么什么为AOP的思想?
AOP的思想就是面向切面的编程(Aspect Oriented Programming ):
具体的思想:
代理:Proxy
目标:Target 也是被代理的对象
通知:Advice 负责将重复的代码和业务的代码进行一个整合
切面:就是 等价于 切点(代表的是一种匹配的规)+通知(重复的代码和业务代码的整合)
3.动态的代理的技术
之前我们在程序应用之前将代理类用Java代码编写好的过程称之为静态的代理过程,但是代理类的编写的过程随着目标类的业务的方法变多的情况下,就会变得复杂,但是jdk我们提供了一个动态代理的技术,在应用程序运行的过程中,将代理类创建好。
举例:
@Test
public void test2() throws Throwable {
// jdk的动态的代理的技术
// 获取一个动态的代理的对象
// 参数一:类加载器
// 参数二:接口的class的文件
// 参数三:通知类的对象
ClassLoader loader = TestAop.class.getClassLoader();
ProductService o = (ProductService)java.lang.reflect.Proxy.newProxyInstance(loader, new Class[]{ProductService.class}, new ProductAdvice());
System.out.println(o.getClass());
o.biz1();
}
直接先生成.class的字节码的文件,然后加载到jvm上
4.spring 的AOP的使用
(1):pom.xml的文件的配置,添加相关的依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.17.RELEASE</version>
</dependency>
<!--第三方 aop依赖 aspectJ-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.10</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.10</version>
</dependency>
(2):编写相关的目标类和通知类,其中目标类实现一个接口
接口类
public interface UserService {
public void biz1();
public void biz2();
public void biz3();
public void biz4();
public void biz4(int i);
}
目标类
public class UserTarget implements UserService {
@Override
public void biz1() {
System.out.println("事务 biz1");
}
@Override
public void biz2() {
System.out.println("事务biz2");
}
@Override
public void biz3() {
System.out.println("事务biz3");
}
@Override
public void biz4() {
System.out.println("事务biz4");
}
@Override
public void biz4(int i) {
System.out.println("事务带参biz4 ");
}
}
通知类,用来完成目标类业务的重复的代码与目标类的重复的代码进行整合
注解:
1:@aspect 意思是切面
2: @Around(“within(com.www.spring.service.UserTarget)”) 通知的类型:决定了通知的方法要与目标的哪些的方法进行结合
@Aspect
public class UserAdvice {
@Around("within(com.www.spring.service.UserTarget)")
public Object test1(ProceedingJoinPoint pjp){
System.out.println("事务开始");
Object object = null;
try {
// 调用目标的方法
object = pjp.proceed();
System.out.println("提交事务");
}catch (Throwable e){
System.out.println("回滚事务");
e.printStackTrace();
}
return object;
}
}
测试:
static ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-AOP.xml");
@Test
public void test1(){
UserService userService = context.getBean(UserService.class);
userService.biz1();
}
返回的对象是一个代理类的对象,这就体现了动态代理的技术
5.通知的类型和切点
5.1 通知类型
下面对主要的通知的类型进行举例:
(1)环绕的通知:@Around("") 环绕的通知 功能是最为齐全的一个方法 在通知方法的内部调用的是目标的方法
(2)前置通知: @Before("") 前置的通知 在目标方法调用之前就会调用通知的方法
(3)正常返回的通知:@AfterReturning ("") 正常返回的通知的方法,在目标方法正常调用完成之后就会调用正常返回的通知的方法
(4)结束通知:@After("") 就是在目标方法结束的时候(无论是正常的结束,还是异常的结束)都会被调用通知方法(在正常返回和异常返回的通知之前)
(5)异常通知:@AfterReturning("") 就是在目标的方法出现异常的时候会背带调用通知方法
5.2 切点
通俗的说就是一个匹配的规则:
有以下的几种的切点:
(1):within(“包名.类名”) :匹配的是这个类中的所有的方法
(2):execution(“权限修饰符.返回值类型.包名.类名.方法名(…)”)
- 代表的是所有 (…) 匹配的是方法的参数
(3):annotation(包名.注解名):根据方法上的注解来进行匹配,如果有注解就会匹配如果没有的话,就不会匹配
注意:正常返回通知和异常的返回的通知针对的是目标类的目标的方法
6.常见的spring动态的创建代理的技术
spring有两种创建动态代理的技术:
(1)jdk的动态的代理的技术:
适用于目标类是实现了至少一个一个接口的情况之下
(2)cglib的动态的代理的技术:
如果目标类没有实现一个接口的情况下,就会适应cglib的技术来产生一个动态的代理
它的class的是:
class com.www.spring.service.UserTarget2$$EnhancerBySpringCGLIB$$bc3b1d3b
7.spring中AOP的过程的分析
当spring的容器启动的时候:
(1):spring的容器会先检查是否要要给容器的目标类创建代理
(2):检查切点的表达式,如果切点匹配到了目标类的话,就会给其创建代理
(3):在获取对象的时候,会检查是否有代理对象,如果有的话,就会优先创建代理的对象
(4):在获取到了代理对象的时候,会按照切点进行匹配进入多个通知类
Spring的专业名词
JoinPiont:连接点(可以被连接到的点,可以被拦截的点)
PiontCut:被真正拦截到的点
Advice:通知(目标方法执行的前后的执行的方法 )
Introduction:类层面的增强
Target:被增强的对象
Weaving:将通知应用到到Target的方法中的过程
Proxy:增强之后产生的代理
Aspect:多个通知和多个切入点的组合