0
点赞
收藏
分享

微信扫一扫

《Java手写系列》-源码专题-手写SpringAop

1.AOP回顾

1.1.AOP的概念

AOP(Aspect Oriented Programming)是面向切面编程。是OOP面向对象编程思想的一种补充。
OOP通过继承,封装,多态等概念构建一个对象的层级结构。构建的是一个纵向的关系。面对横向的问题,实现起来比较复杂,比如日志的输出。使用面向对象的思想,每个类都需要增加日志打印的相关代码。但是使用aop就可以很简单的解决这个问题。

aop将影响了多个类的公共行为(如日志打印)封装为一个可重用模块,定义为一个切面(aspect)。切面中包括切入点,通知,连接点等概念。
切入点:就是需要做切面处理的位置,可以通过@PointCut中的execution值指定某个包,某个类或者某个方法。同时也可以使用自定义注解标注。
通知:包括5种,分别是前置,后置,返回,异常,环绕通知。分别定义增强代码执行的时机。
连接点:是可以用来做为切入点的位置。是程序执行的某个位置,可以为程序执行前,也可以是执行后或者抛出异常等一些时机点

aop作用是降低程序的耦合度,提高可重用性和开发效率

1.2.AOP简单使用

项目中导入Spring的依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>org.example</groupId>
<artifactId>spring-aop</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
</configuration>
</plugin>
</plugins>
</build>
</project>

项目切面类

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

@Aspect
@Component
@EnableAspectJAutoProxy
public class MyServiceAspect {

@Pointcut("execution(* com.itxiongmao.service.*..*(..))")
public void pointCut(){}

@Before("pointCut()")
public void before(){
System.out.println("前置方法执行了.....");
}
@After("pointCut()")
public void after(){
System.out.println("after方法执行了.....");
}

@AfterThrowing("pointCut()")
public void afterThrowing(){
System.out.println("afterThrowing方法执行了.....");
}

@AfterReturning("pointCut()")
public void afterReturning(){
System.out.println("afterReturning方法执行了.....");
}

@Around("pointCut()")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
MethodSignature signature = (MethodSignature)proceedingJoinPoint.getSignature();
Method method = signature.getMethod();
Parameter[] parameters = method.getParameters();
Object proceed = proceedingJoinPoint.proceed(proceedingJoinPoint.getArgs());
return proceed;
}
}

2.spring aop的底层原理-动态代理

spring AOP实现是依赖动态代理
jdk:当被代理类有接口的时候
cglib:asm字节码技术,没有接口就是使用cglib,也可以强制使用cglib

2.1.JDK动态代理

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class JdkProxyDemo<T> {
T obj;
//target:被代理类
public JdkProxyDemo(T target){
this.obj = target;
}
public T getInstance(){
return (T)Proxy.newProxyInstance(obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法执行前执行");
try {
Object invoke = method.invoke(obj, args);
System.out.println("方法执行后执行");
return invoke;
}catch (Exception e){
System.out.println("抛出了异常执行");
return null;
}

}
});
}
}

2.2.CGLIB动态代理

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class SampleClass {
public void test(){
System.out.println("hello world");
}

public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(SampleClass.class);
enhancer.setCallback(new MethodInterceptor() {
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("before method run...");
Object result = methodProxy.invokeSuper(o, objects);
System.out.println("after method run...");
return result;
}
}
);
SampleClass sample = (SampleClass) enhancer.create();
sample.test();
}
}

3.手写AOP步骤

3.1.自定义AOP大致思路

1、创建自己的Around注解,注解参数为Class。创建Aop方法存储容器。本处仅实现环绕通知。
2、在IOC创建Bean之前,优先扫描包下所有类的所有方法,把包含Around注解方法存储至容器。
3、在IOC扫描Bean的时候,检索Bean内部是否包含注解其类型为Around注明的Class。并选择性进行IOC是否需要代理
4、在需要代理的场景下进行切面的调用整合。执行前后进行控制。

3.2.自定义注解

自定义切面类注解Aspect

package org.springframework.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

//这个注解可以作用在运行期
@Retention(RetentionPolicy.RUNTIME)
//指定该注解可以作用在类上
@Target(ElementType.TYPE)
public @interface Aspect {
}

自定义切面类注解Around

package org.springframework.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

//这个注解可以作用在运行期
@Retention(RetentionPolicy.RUNTIME)
//指定该注解可以作用在类上
@Target(ElementType.METHOD)
public @interface Around {
String value();
}

3.2.自定义AOP参数类

package org.springframework.aop;

import java.lang.reflect.Method;

/**
* @BelongsProject: SpringIOC
* @BelongsPackage: org.springframework.aop
* @CreateTime: 2020-10-01 18:30
* @Description: TODO
*/
public class ProceedingJoinPoint {
//因为待会需要调用目标方法 这个就是目标方法
private Method method;
//方法参数
private Object[] args;
//对象
private Object obj;

public Object proceed() throws Throwable{
return method.invoke(obj,args);
}

public ProceedingJoinPoint(Method method, Object[] args, Object obj) {
this.method = method;
this.args = args;
this.obj = obj;
}

public Method getMethod() {
return method;
}

public void setMethod(Method method) {
this.method = method;
}

public Object[] getArgs() {
return args;
}

public void setArgs(Object[] args) {
this.args = args;
}

public Object getObj() {
return obj;
}

public void setObj(Object obj) {
this.obj = obj;
}
}

3.3.自定义AOP切面类

package com.itxiongmao.aop;

import org.springframework.annotation.Around;
import org.springframework.annotation.Aspect;
import org.springframework.aop.ProceedingJoinPoint;

/**
* @BelongsProject: SpringIOC
* @BelongsPackage: com.itxiongmao.aop
* @CreateTime: 2020-10-01 18:28
* @Description: TODO
*/
@Aspect
public class MyAop {

@Around(value = "com.itxiongmao.service.impl.UserServiceImpl.serviceMethod")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
System.out.println("前置通知");
Object proceed = proceedingJoinPoint.proceed();
System.out.println("后置通知");
return proceed;
}
}

3.4.获得并配置切面任务

在doinit中扫描切面类

//说明是切面类
if(aClass.isAnnotationPresent(Aspect.class)){
aopSet.add(aClass);
continue;
}
//装切面的集合
private Set<Class<?>> aopSet = new CopyOnWriteArraySet<Class<?>>();

private void doAop() {
if (aopSet == null || aopSet.size() == 0) return;
//循环获得每一个切面类 clazz:MyAop
for (Class<?> clazz : aopSet) {
//获得当前切面类所有的方法
Method[] declaredMethods = clazz.getDeclaredMethods();
if (declaredMethods == null) return;
try {
for (Method method : declaredMethods) {
boolean annotationPresent = method.isAnnotationPresent(Around.class);
if (annotationPresent) {
Around annotation = method.getAnnotation(Around.class);
//被切的方法
// com.itxiongmao.service.impl.UserServiceImpl.serviceMethod
String value = annotation.value();
// com.itxiongmao.service.impl.UserServiceImpl.
String classFullPath = value.substring(0, value.lastIndexOf("."));
//获取方法名
String methodName = value.substring(value.lastIndexOf(".") + 1);
//根据类名可以获得该类的class对象 根据class对象可以取ioc容器中获得它的对象
Class<?> aClass = Class.forName(classFullPath);
JdkProxyDemo<Object> objectJdkProxyDemo = new JdkProxyDemo<Object>(aClass, clazz, methodName, method);
//已经生成代理对象
Object instance = objectJdkProxyDemo.getInstance();
//替换掉容器中对象
iocMap.put(aClass,instance);

//默认等于首字母小写
String simpleName = aClass.getSimpleName();
String name = simpleName.substring(0, 1).toLowerCase() + simpleName.substring(1);
//修改名字 取原对象上面的所有注解 加了注解就应该以value属性为准
Service annotation1 = aClass.getAnnotation(Service.class);
if (annotation1 != null) {//说明被代理类就是加的service注解
String value1 = annotation1.value();
if (!value1.equals("")) {//说明指定了名字
name = value1;
}
} else {//说明加的controller注解
Controller controller = aClass.getAnnotation(Controller.class);
String value1 = controller.value();
if (!value1.equals("")) {//说明指定了名字
name = value1;
}

}
nameIocMap.put(name, instance); //根据名字替换原来的类成功

//开始替换接口
Class<?>[] interfaces = aClass.getInterfaces();
if (interfaces == null) continue;
//每个接口
for (Class<?> anInterface : interfaces) {
//当前接口所实现的所有子类对象
List<Object> objects = superIocMap.get(anInterface);
for (int i = 0; i < objects.size(); i++) {
Object eachObj = objects.get(i);
if (eachObj.getClass() == aClass) {
objects.set(i, instance);
break;
}
}
}

}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

//生成代理对象
private class JdkProxyDemo<T> {
//被代理对象
Class<?> aClass;
//切面对象
Class<?> clazz;
//被代理对象的方法名字,只能代理一个方法
String methodName;
//切面对象的方法
Method myMethod;
Object obj;

//target:被代理类
public JdkProxyDemo(Class<?> aClass, Class<?> clazz, String methodName, Method method) {
this.aClass = aClass;
this.clazz = clazz;
this.myMethod = method;
this.methodName = methodName;
obj = iocMap.get(aClass);
}

public Object getInstance() {
return Proxy.newProxyInstance(aClass.getClassLoader(),
aClass.getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//method:被代理对象得方法
if (method.getName().equals(methodName)) {//说明当前调用得方法 就是需要被代理得
ProceedingJoinPoint proceedingJoinPoint = new ProceedingJoinPoint(method, args, obj);
//切面方法
return myMethod.invoke(clazz.newInstance(), proceedingJoinPoint);
}
//不被代理
return method.invoke(obj, args);
}
});
}
}

3.5.测试

public class TestSpringIoc {

public static void main(String[] args) {
Container container=new Container("ioc.properties");
System.out.println(container);
UserController userController = (UserController)container.getBean(UserController.class);
userController.controllerMethod();
}
}


举报

相关推荐

0 条评论